Support proper %g, %f, and %a float formatting

See #61
See #104
This commit is contained in:
Justine Tunney 2021-03-05 10:31:16 -08:00
parent e26bdbec52
commit f064183646
48 changed files with 1034 additions and 921 deletions

View file

@ -1,21 +0,0 @@
#if 0
/*─────────────────────────────────────────────────────────────────╗
To the extent possible under law, Justine Tunney has waived
all copyright and related or neighboring rights to this file,
as it is written in the following disclaimers:
http://unlicense.org/ │
http://creativecommons.org/publicdomain/zero/1.0/ │
*/
#endif
#include "libc/errno.h"
#include "libc/stdio/stdio.h"
int main(int argc, char *argv[]) {
printf("abcdefghijklmnopqrstuvwxyz "
"ABCDEFGHIJKLMNOPQRSTUVWXYZ "
"!@#$$%%^&*(){}%%* "
"0123456789 "
"%3d\n",
argc);
return errno;
}

View file

@ -58,7 +58,7 @@ int(vdprintf)(int fd, const char *fmt, va_list va) {
struct VdprintfState df;
df.n = 0;
df.fd = fd;
if (palandprintf(vdprintfputchar, &df, fmt, va) == -1) return -1;
if (__fmt(vdprintfputchar, &df, fmt, va) == -1) return -1;
if (vdprintf_flush(&df, df.n & (ARRAYLEN(df.buf) - 1)) == -1) return -1;
return df.n;
}

View file

@ -82,7 +82,6 @@ div_t div(int, int) pureconst;
ldiv_t ldiv(long, long) pureconst;
lldiv_t lldiv(long long, long long) pureconst;
imaxdiv_t imaxdiv(intmax_t, intmax_t) pureconst;
double RoundDecimalPlaces(double, double, double (*)(double));
/*───────────────────────────────────────────────────────────────────────────│─╗
cosmopolitan § conversion » optimizations

657
libc/fmt/fmt.c Normal file
View file

@ -0,0 +1,657 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2021 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/bits/bits.h"
#include "libc/bits/weaken.h"
#include "libc/fmt/conv.h"
#include "libc/fmt/fmt.h"
#include "libc/fmt/fmts.h"
#include "libc/fmt/internal.h"
#include "libc/mem/mem.h"
#include "libc/runtime/internal.h"
#include "libc/str/str.h"
#include "libc/sysv/errfuns.h"
#include "third_party/gdtoa/gdtoa.h"
static int __fmt_atoi(const char **str) {
int i;
for (i = 0; '0' <= **str && **str <= '9'; ++*str) {
i *= 10;
i += **str - '0';
}
return i;
}
/**
* Implements {,v}{,s{,n},{,{,x}as},f,d}printf domain-specific language.
*
* Type Specifiers
*
* - `%s` char * (thompson-pike unicode)
* - `%ls` wchar_t * (32-bit unicode thompson-pike unicode)
* - `%hs` char16_t * (16-bit unicode thompson-pike unicode)
* - `%b` int (radix 2 binary)
* - `%o` int (radix 8 octal)
* - `%d` int (radix 10 decimal)
* - `%x` int (radix 16 hexadecimal)
* - `%X` int (radix 16 hexadecimal uppercase)
* - `%p` pointer (48-bit hexadecimal)
* - `%u` unsigned
* - `%g` double (smart formatting)
* - `%e` double (expo formatting)
* - `%f` double (ugly formatting)
* - `%a` double (hex formatting)
* - `%Lg` long double
*
* Size Modifiers
*
* - `%hhd` char (8-bit)
* - `%hd` short (16-bit)
* - `%ld` long (64-bit)
* - `%lu` unsigned long (64-bit)
* - `%lx` unsigned long (64-bit hexadecimal)
* - `%jd` intmax_t (128-bit)
*
* Width Modifiers
*
* - `%08d` fixed columns w/ zero leftpadding
* - `%8d` fixed columns w/ space leftpadding
* - `%*s` variable column string (thompson-pike)
*
* Precision Modifiers
*
* - `%.8s` supplied byte length (obeys nul terminator)
* - `%.*s` supplied byte length argument (obeys nul terminator)
* - ``%`.*s`` supplied byte length argument c escaped (ignores nul term)
* - `%#.*s` supplied byte length argument visualized (ignores nul term)
* - `%.*hs` supplied char16_t length argument (obeys nul terminator)
* - `%.*ls` supplied wchar_t length argument (obeys nul terminator)
*
* Formatting Modifiers
*
* - `%,d` thousands separators
* - `%'s` escaped c string literal
* - ``%`c`` c escaped character
* - ``%`'c`` c escaped character quoted
* - ``%`s`` c escaped string
* - ``%`'s`` c escaped string quoted
* - ``%`s`` escaped double quoted c string literal
* - ``%`c`` escaped double quoted c character literal
* - `%+d` plus leftpad if positive (aligns w/ negatives)
* - `% d` space leftpad if positive (aligns w/ negatives)
* - `%#s` datum (radix 256 null-terminated ibm cp437)
* - `%#x` int (radix 16 hexadecimal w/ 0x prefix if not zero)
*
* @note implementation detail of printf(), snprintf(), etc.
* @see printf() for wordier documentation
* @note netlib.org is so helpful
* @asyncsignalsafe
* @vforksafe
*/
hidden int __fmt(void *fn, void *arg, const char *format, va_list va) {
union {
double d;
unsigned int u[2];
} pun;
void *p;
char qchar;
char *s, *se;
bool longdouble;
long double ldbl;
wchar_t charbuf[1];
const char *alphabet;
int (*out)(long, void *);
unsigned char signbit, log2base;
int c, d, k, w, i1, ui, bw, bex;
int sgn, alt, sign, prec, prec1, flags, width, decpt, lasterr;
lasterr = errno;
out = fn ? fn : (void *)missingno;
while (*format) {
/* %[flags][width][.prec][length] */
if (*format != '%') {
/* no */
if (out(*format, arg) == -1) return -1;
format++;
continue;
} else {
/* yes, evaluate it */
format++;
}
/* evaluate flags */
sign = 0;
flags = 0;
getflag:
switch (*format++) {
case '0':
flags |= FLAGS_ZEROPAD;
goto getflag;
case '-':
flags |= FLAGS_LEFT;
goto getflag;
case '+':
sign = '+';
flags |= FLAGS_PLUS;
goto getflag;
case ' ':
sign = ' ';
flags |= FLAGS_SPACE;
goto getflag;
case '#':
flags |= FLAGS_HASH;
goto getflag;
case ',':
flags |= FLAGS_GROUPING;
goto getflag;
case '`':
flags |= FLAGS_REPR;
/* fallthrough */
case '\'':
flags |= FLAGS_QUOTE;
goto getflag;
default:
format--;
break;
}
/* evaluate width field */
width = 0;
if (isdigit(*format)) {
width = __fmt_atoi(&format);
} else if (*format == '*') {
w = va_arg(va, int);
if (w < 0) {
flags |= FLAGS_LEFT; /* reverse padding */
width = -w;
sign = '-';
} else {
width = w;
}
format++;
}
/* evaluate prec field */
prec = 0;
if (*format == '.') {
flags |= FLAGS_PRECISION;
format++;
if (isdigit(*format)) {
prec = __fmt_atoi(&format);
} else if (*format == '*') {
prec = va_arg(va, int);
format++;
}
}
if (prec < 0) {
prec = 0;
}
/* evaluate length field */
signbit = 31;
longdouble = false;
switch (*format) {
case 'j': /* intmax_t */
format++;
signbit = sizeof(intmax_t) * 8 - 1;
break;
case 'l':
if (format[1] == 'f' || format[1] == 'F') {
format++;
break;
}
if (format[1] == 'l') format++;
/* fallthrough */
case 't': /* ptrdiff_t */
case 'z': /* size_t */
case 'Z': /* size_t */
format++;
signbit = 63;
break;
case 'L': /* long double */
format++;
longdouble = true;
break;
case 'h':
format++;
if (*format == 'h') {
format++;
signbit = 7;
} else {
signbit = 15;
}
break;
default:
break;
}
/* evaluate specifier */
qchar = '"';
log2base = 0;
alphabet = "0123456789abcdef";
switch ((d = *format++)) {
case 'p':
flags |= FLAGS_ZEROPAD;
width = POINTER_XDIGITS;
log2base = 4;
signbit = 47;
goto FormatNumber;
case 'X':
alphabet = "0123456789ABCDEF";
/* fallthrough */
case 'x':
log2base = 4;
goto FormatNumber;
case 'b':
log2base = 1;
goto FormatNumber;
case 'o':
log2base = 3;
goto FormatNumber;
case 'd':
case 'i':
flags |= FLAGS_ISSIGNED;
/* fallthrough */
case 'u': {
flags &= ~FLAGS_HASH; /* no hash for dec format */
FormatNumber:
if (__fmt_ntoa(out, arg, va, signbit, log2base, prec, width, flags,
alphabet) == -1) {
return -1;
}
break;
}
case 'c':
prec = 1;
flags |= FLAGS_PRECISION;
qchar = '\'';
p = charbuf;
charbuf[0] = va_arg(va, int);
goto FormatString;
case 'm':
p = weaken(strerror) ? weaken(strerror)(lasterr) : "?";
signbit = 0;
goto FormatString;
case 'r':
flags |= FLAGS_REPR;
/* fallthrough */
case 'q':
flags |= FLAGS_QUOTE;
/* fallthrough */
case 's':
p = va_arg(va, void *);
FormatString:
if (__fmt_stoa(out, arg, p, flags, prec, width, signbit, qchar) == -1) {
return -1;
}
break;
case 'f':
if (!(flags & FLAGS_PRECISION)) prec = 6;
if (longdouble) {
pun.d = va_arg(va, long double);
} else {
pun.d = va_arg(va, double);
}
FormatDtoa:
if (!weaken(__fmt_dtoa)) {
p = "nan";
goto FormatString;
}
s = weaken(__fmt_dtoa)(pun.d, 3, prec, &decpt, &sgn, &se);
if (decpt == 9999) {
Format9999:
prec = alt = 0;
flags &= ~FLAGS_PRECISION;
if (*s == 'N') {
p = s;
goto FormatString;
}
decpt = strlen(s);
}
FormatReal:
if (sgn) sign = '-';
if (prec > 0) width -= prec;
if (width > 0) {
if (sign) --width;
if (decpt <= 0) {
--width;
if (prec > 0) --width;
} else {
if (s == se) decpt = 1;
width -= decpt;
if (prec > 0 || alt) --width;
}
}
if (width > 0 && !(flags & FLAGS_LEFT)) {
if (flags & FLAGS_ZEROPAD) {
if (sign) out(sign, arg);
sign = 0;
do out('0', arg);
while (--width > 0);
} else
do out(' ', arg);
while (--width > 0);
}
if (sign) out(sign, arg);
if (decpt <= 0) {
out('0', arg);
if (prec > 0 || alt) out('.', arg);
while (decpt < 0) {
out('0', arg);
prec--;
decpt++;
}
} else {
do {
if ((c = *s)) {
s++;
} else {
c = '0';
}
out(c, arg);
} while (--decpt > 0);
if (prec > 0 || alt) out('.', arg);
}
while (--prec >= 0) {
if ((c = *s)) {
s++;
} else {
c = '0';
}
out(c, arg);
}
while (--width >= 0) {
out(' ', arg);
}
continue;
case 'G':
case 'g':
if (!(flags & FLAGS_PRECISION)) prec = 6;
if (longdouble) {
pun.d = va_arg(va, long double);
} else {
pun.d = va_arg(va, double);
}
if (prec < 0) prec = 0;
if (!weaken(__fmt_dtoa)) {
p = "nan";
goto FormatString;
}
s = weaken(__fmt_dtoa)(pun.d, prec ? 2 : 0, prec, &decpt, &sgn, &se);
if (decpt == 9999) goto Format9999;
c = se - s;
prec1 = prec;
if (!prec) {
prec = c;
prec1 = c + (s[1] || alt ? 5 : 4);
/* %.0g gives 10 rather than 1e1 */
}
if (decpt > -4 && decpt <= prec1) {
if (alt) {
prec -= decpt;
} else {
prec = c - decpt;
}
if (prec < 0) prec = 0;
goto FormatReal;
}
d -= 2;
if (!alt && prec > c) prec = c;
--prec;
goto FormatExpo;
case 'e':
case 'E':
if (!(flags & FLAGS_PRECISION)) prec = 6;
if (longdouble) {
pun.d = va_arg(va, long double);
} else {
pun.d = va_arg(va, double);
}
if (prec < 0) prec = 0;
if (!weaken(__fmt_dtoa)) {
p = "nan";
goto FormatString;
}
s = weaken(__fmt_dtoa)(pun.d, 2, prec + 1, &decpt, &sgn, &se);
if (decpt == 9999) goto Format9999;
FormatExpo:
if (sgn) sign = '-';
if ((width -= prec + 5) > 0) {
if (sign) --width;
if (prec || alt) --width;
}
if ((c = --decpt) < 0) c = -c;
while (c >= 100) {
--width;
c /= 10;
}
if (width > 0 && !(flags & FLAGS_LEFT)) {
if (flags & FLAGS_ZEROPAD) {
if (sign) out(sign, arg);
sign = 0;
do out('0', arg);
while (--width > 0);
} else {
do out(' ', arg);
while (--width > 0);
}
}
if (sign) out(sign, arg);
out(*s++, arg);
if (prec || alt) out('.', arg);
while (--prec >= 0) {
if ((c = *s)) {
s++;
} else {
c = '0';
}
out(c, arg);
}
out(d, arg);
if (decpt < 0) {
out('-', arg);
decpt = -decpt;
} else {
out('+', arg);
}
for (c = 2, k = 10; 10 * k <= decpt; c++, k *= 10) {
}
for (;;) {
i1 = decpt / k;
out(i1 + '0', arg);
if (--c <= 0) break;
decpt -= i1 * k;
decpt *= 10;
}
while (--width >= 0) {
out(' ', arg);
}
continue;
case 'a':
alphabet = "0123456789abcdefpx";
goto FormatBinary;
case 'A':
alphabet = "0123456789ABCDEFPX";
FormatBinary:
if (longdouble) {
pun.d = va_arg(va, long double);
} else {
pun.d = va_arg(va, double);
}
if ((pun.u[1] & 0x7ff00000) == 0x7ff00000) {
goto FormatDtoa;
}
if (pun.d) {
c = '1';
if (pun.u[1] & 0x80000000) {
sign = '-';
pun.u[1] &= 0x7fffffff;
}
bex = (pun.u[1] >> 20) - 1023;
pun.u[1] &= 0xfffff;
if (bex == -1023) {
++bex;
if (pun.u[1]) {
do {
--bex;
pun.u[1] <<= 1;
if (pun.u[0] & 0x80000000) pun.u[1] |= 1;
pun.u[0] <<= 1;
} while (pun.u[1] < 0x100000);
} else {
while (!(pun.u[0] & 0x80000000)) {
--bex;
pun.u[0] <<= 1;
}
bex -= 21;
pun.u[1] = pun.u[0] >> 11;
pun.u[0] <<= 21;
}
}
} else {
c = '0';
bex = 0;
}
if (flags & FLAGS_PRECISION) {
if (prec > 13) prec = 13;
if (pun.d && prec < 13) {
pun.u[1] |= 0x100000;
if (prec < 5) {
ui = 1 << ((5 - prec) * 4 - 1);
if (pun.u[1] & ui) {
if (pun.u[1] & ((ui - 1) | (ui << 1)) || pun.u[0]) {
pun.u[1] += ui;
BexCheck:
if (pun.u[1] & 0x200000) {
++bex;
pun.u[1] >>= 1;
}
}
}
} else if (prec == 5) {
if (pun.u[0] & 0x80000000) {
BumpIt:
++pun.u[1];
goto BexCheck;
}
} else {
i1 = (13 - prec) * 4;
ui = 1 << (i1 - 1);
if (pun.u[0] & ui && pun.u[0] & ((ui - 1) | (ui << 1))) {
pun.u[0] += ui;
if (!(pun.u[0] >> i1)) goto BumpIt;
}
}
}
} else {
if ((ui = pun.u[0])) {
for (prec = 6; (ui = (ui << 4) & 0xffffffff); ++prec) {
}
} else {
for (prec = 0, ui = pun.u[1] & 0xfffff; ui;
++prec, ui = (ui << 4) & 0xfffff) {
}
}
}
bw = 1;
if (bex) {
if ((i1 = bex) < 0) i1 = -i1;
while (i1 >= 10) {
++bw;
i1 /= 10;
}
}
if ((sgn = pun.u[1] & 0x80000000)) {
pun.u[1] &= 0x7fffffff;
if (pun.d || sign) sign = '-';
}
if ((width -= bw + 5) > 0) {
if (sign) --width;
if (prec || alt) --width;
}
if (width > 0 && !(flags & FLAGS_LEFT)) {
if (flags & FLAGS_ZEROPAD) {
if (sign) {
out(sign, arg);
sign = 0;
}
do out('0', arg);
while (--width > 0);
} else {
do out(' ', arg);
while (--width > 0);
}
}
if (sign) out(sign, arg);
out('0', arg);
out(alphabet[17], arg);
out(c, arg);
if (prec > 0 || alt) out('.', arg);
if (prec > 0) {
if ((i1 = prec) > 5) i1 = 5;
prec -= i1;
do {
out(alphabet[(pun.u[1] >> 16) & 0xf], arg);
pun.u[1] <<= 4;
} while (--i1 > 0);
while (prec > 0) {
--prec;
out(alphabet[(pun.u[0] >> 28) & 0xf], arg);
pun.u[0] <<= 4;
}
}
out(alphabet[16], arg);
if (bex < 0) {
out('-', arg);
bex = -bex;
} else {
out('+', arg);
}
for (c = 1; 10 * c <= bex; c *= 10) {
}
for (;;) {
i1 = bex / c;
out('0' + i1, arg);
if (!--bw) break;
bex -= i1 * c;
bex *= 10;
}
continue;
case '%':
if (out('%', arg) == -1) return -1;
break;
default:
if (out(format[-1], arg) == -1) return -1;
break;
}
}
return 0;
}

View file

@ -27,7 +27,7 @@ int vsscanf(const char *, const char *, va_list);
int vcscanf(int (*)(void *), int (*)(int, void *), void *, const char *,
va_list);
int strerror_r(int, char *, size_t) nothrow nocallback;
int palandprintf(void *, void *, const char *, va_list) hidden;
int __fmt(void *, void *, const char *, va_list) hidden;
char *itoa(int, char *, int) compatfn;
char *fcvt(double, int, int *, int *);
char *ecvt(double, int, int *, int *);

20
libc/fmt/fmts.h Normal file
View file

@ -0,0 +1,20 @@
#ifndef COSMOPOLITAN_LIBC_FMT_FMTS_H_
#define COSMOPOLITAN_LIBC_FMT_FMTS_H_
#define PRINTF_NTOA_BUFFER_SIZE 144
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
int __fmt_pad(int (*)(long, void *), void *, unsigned long) hidden;
int __fmt_stoa(int (*)(long, void *), void *, void *, unsigned long,
unsigned long, unsigned long, unsigned char,
unsigned char) hidden;
int __fmt_ntoa(int (*)(long, void *), void *, va_list, unsigned char,
unsigned long, unsigned long, unsigned long, unsigned char,
const char *) hidden;
char *__fmt_dtoa(double, int, int, int *, int *, char **) hidden;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_FMT_FMTS_H_ */

16
libc/fmt/internal.h Normal file
View file

@ -0,0 +1,16 @@
#ifndef COSMOPOLITAN_LIBC_FMT_INTERNAL_H_
#define COSMOPOLITAN_LIBC_FMT_INTERNAL_H_
#define FLAGS_ZEROPAD 0x01
#define FLAGS_LEFT 0x02
#define FLAGS_PLUS 0x04
#define FLAGS_SPACE 0x08
#define FLAGS_HASH 0x10
#define FLAGS_PRECISION 0x20
#define FLAGS_ISSIGNED 0x40
#define FLAGS_NOQUOTE 0x80
#define FLAGS_QUOTE FLAGS_SPACE
#define FLAGS_GROUPING FLAGS_NOQUOTE
#define FLAGS_REPR FLAGS_PLUS
#endif /* COSMOPOLITAN_LIBC_FMT_INTERNAL_H_ */

View file

@ -19,13 +19,12 @@
#include "libc/fmt/conv.h"
#include "libc/fmt/fmt.h"
STATIC_YOINK("ntoa");
compatfn char *itoa(int value, char *str, int radix) {
(sprintf)(
str,
VEIL("r",
radix == 16 ? "%x" : radix == 8 ? "%d" : radix == 2 ? "%b" : "%d"),
(sprintf)(str,
VEIL("r", radix == 16 ? "%x"
: radix == 8 ? "%d"
: radix == 2 ? "%b"
: "%d"),
value);
return str;
}

174
libc/fmt/ntoa.c Normal file
View file

@ -0,0 +1,174 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2021 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/fmt/conv.h"
#include "libc/fmt/fmts.h"
#include "libc/fmt/internal.h"
#define BUFFER_SIZE 144
uintmax_t __udivmodti4(uintmax_t, uintmax_t, uintmax_t *);
static int __fmt_ntoa_format(int out(long, void *), void *arg, char *buf,
unsigned len, bool negative, unsigned log2base,
unsigned prec, unsigned width,
unsigned char flags) {
unsigned i, idx;
idx = 0;
/* pad leading zeros */
if (!(flags & FLAGS_LEFT)) {
if (width && (flags & FLAGS_ZEROPAD) &&
(negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {
width--;
}
while ((len < prec) && (len < BUFFER_SIZE)) {
buf[len++] = '0';
}
while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < BUFFER_SIZE)) {
buf[len++] = '0';
}
}
/* handle hash */
if (flags & FLAGS_HASH) {
if (!(flags & FLAGS_PRECISION) && len &&
((len == prec) || (len == width)) && buf[len - 1] == '0') {
len--;
if (len && (log2base == 4 || log2base == 1) && buf[len - 1] == '0') {
len--;
}
}
if (log2base == 4 && len < BUFFER_SIZE) {
buf[len++] = 'x';
} else if (log2base == 1 && len < BUFFER_SIZE) {
buf[len++] = 'b';
}
if (len < BUFFER_SIZE) {
buf[len++] = '0';
}
}
if (len < BUFFER_SIZE) {
if (negative) {
buf[len++] = '-';
} else if (flags & FLAGS_PLUS) {
buf[len++] = '+'; /* ignore the space if the '+' exists */
} else if (flags & FLAGS_SPACE) {
buf[len++] = ' ';
}
}
/* pad spaces up to given width */
if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) {
if (len < width) {
if (__fmt_pad(out, arg, width - len) == -1) return -1;
}
}
/* reverse string */
for (i = 0U; i < len; i++) {
if (out(buf[len - i - 1], arg) == -1) return -1;
idx++;
}
/* append pad spaces up to given width */
if (flags & FLAGS_LEFT) {
if (idx < width) {
if (__fmt_pad(out, arg, width - idx) == -1) return -1;
}
}
return 0;
}
int __fmt_ntoa2(int out(long, void *), void *arg, uintmax_t value, bool neg,
unsigned log2base, unsigned prec, unsigned width,
unsigned flags, const char *alphabet) {
uintmax_t remainder;
unsigned len, count, digit;
char buf[BUFFER_SIZE];
len = 0;
if (!value) flags &= ~FLAGS_HASH;
if (value || !(flags & FLAGS_PRECISION)) {
count = 0;
do {
assert(len < BUFFER_SIZE);
if (!log2base) {
value = __udivmodti4(value, 10, &remainder);
digit = remainder;
} else {
digit = value;
digit &= (1u << log2base) - 1;
value >>= log2base;
}
if ((flags & FLAGS_GROUPING) && count == 3) {
buf[len++] = ',';
count = 1;
} else {
count++;
}
buf[len++] = alphabet[digit];
} while (value);
}
return __fmt_ntoa_format(out, arg, buf, len, neg, log2base, prec, width,
flags);
}
int __fmt_ntoa(int out(long, void *), void *arg, va_list va,
unsigned char signbit, unsigned long log2base,
unsigned long prec, unsigned long width, unsigned char flags,
const char *lang) {
bool neg;
uintmax_t value, sign;
/* ignore '0' flag when prec is given */
if (flags & FLAGS_PRECISION) {
flags &= ~FLAGS_ZEROPAD;
}
/* no plus / space flag for u, x, X, o, b */
if (!(flags & FLAGS_ISSIGNED)) {
flags &= ~(FLAGS_PLUS | FLAGS_SPACE);
}
if (signbit > 63) {
value = va_arg(va, uint128_t);
} else {
value = va_arg(va, uint64_t);
}
neg = 0;
sign = 1;
sign <<= signbit;
value &= sign | (sign - 1);
if (flags & FLAGS_ISSIGNED) {
if (value != sign) {
if (value & sign) {
value = ~value + 1;
value &= sign | (sign - 1);
neg = 1;
}
value &= sign - 1;
} else {
neg = 1;
}
}
return __fmt_ntoa2(out, arg, value, neg, log2base, prec, width, flags, lang);
}

View file

@ -16,9 +16,9 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/fmt/palandprintf.h"
#include "libc/fmt/fmts.h"
int spacepad(int out(long, void *), void *arg, unsigned long n) {
int __fmt_pad(int out(long, void *), void *arg, unsigned long n) {
int i, rc;
for (rc = i = 0; i < n; ++i) rc |= out(' ', arg);
return rc;

View file

@ -1,48 +0,0 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-
│vi: set net ft=c ts=8 sts=2 sw=2 fenc=utf-8 :vi│
╚══════════════════════════════════════════════════════════════════════════════╝
@author (c) Marco Paland (info@paland.com)
2014-2019, PALANDesign Hannover, Germany
@license The MIT License (MIT)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal│
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
└─────────────────────────────────────────────────────────────────────────────*/
asm(".ident\t\"\\n\\n\
Paland Printf (MIT License)\\n\
Copyright 2014-2019 Marco Paland\\n\
PALANDesign Hannover, Germany\\n\
info@paland.com\"");
#include "libc/mem/mem.h"
#include "libc/str/internal.h"
#include "libc/sysv/errfuns.h"
#define FLAGS_ZEROPAD (1U << 0U)
#define FLAGS_LEFT (1U << 1U)
#define FLAGS_PLUS (1U << 2U)
#define FLAGS_SPACE (1U << 3U)
#define FLAGS_HASH (1U << 4U)
#define FLAGS_PRECISION (1U << 5U)
#define FLAGS_ISSIGNED (1U << 6U)
#define FLAGS_NOQUOTE (1U << 7U)
#define FLAGS_QUOTE FLAGS_SPACE
#define FLAGS_GROUPING FLAGS_NOQUOTE
#define FLAGS_REPR FLAGS_PLUS

View file

@ -1,170 +0,0 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=8 sts=2 sw=2 fenc=utf-8 :vi
@author (c) Marco Paland (info@paland.com)
2014-2019, PALANDesign Hannover, Germany
@license The MIT License (MIT)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
@brief Tiny printf, sprintf and (v)snprintf implementation, optimized for
embedded systems with a very limited resources. These routines are
thread safe and reentrant! Use this instead of the bloated
standard/newlib printf cause these use malloc for printf (and may not
be thread safe).
*/
#include "libc/fmt/paland.inc"
#include "libc/fmt/palandprintf.h"
#include "libc/math.h"
/**
* Formats floating point number.
*
* @see xdtoa() for higher precision at the cost of bloat
* @see palandprintf() which is intended caller
*/
int ftoa(int out(long, void *), void *arg, long double value, int prec,
unsigned long width, unsigned long flags) {
long whole, frac;
long double tmp, diff;
unsigned i, len, count, idx;
char buf[PRINTF_FTOA_BUFFER_SIZE];
len = 0;
diff = 0;
if (isnan(value)) {
buf[0] = 'n';
buf[1] = 'a';
buf[2] = 'n';
buf[3] = '\0';
len += 3;
} else if (isinf(value) || (value && ilogbl(fabsl(value)) > 63)) {
buf[0] = 'f';
buf[1] = 'n';
buf[2] = 'i';
buf[3] = '\0';
len += 3;
} else {
/* set default precision to 6, if not set explicitly */
if (!(flags & FLAGS_PRECISION)) {
prec = 6;
}
while (len < PRINTF_FTOA_BUFFER_SIZE && prec > 14) {
buf[len++] = '0';
prec--;
}
whole = truncl(fabsl(value));
tmp = (fabsl(value) - whole) * exp10l(prec);
frac = tmp;
diff = tmp - frac;
if (diff > .5) {
++frac; /* handle rollover, e.g. case 0.99 with prec 1 is 1.0 */
if (frac >= exp10l(prec)) {
frac = 0;
++whole;
}
} else if (diff < .5) {
} else if (!frac || (frac & 1)) {
++frac; /* if halfway, round up if odd OR if last digit is 0 */
}
if (!prec) {
diff = fabsl(value) - whole;
if ((!(diff < .5) || (diff > .5)) && (whole & 1)) {
/* exactly .5 and ODD, then round up */
/* 1.5 -> 2, but 2.5 -> 2 */
++whole;
}
} else {
count = prec;
/* now do fractional part, as an unsigned number */
while (len < PRINTF_FTOA_BUFFER_SIZE) {
--count;
buf[len++] = 48 + (frac % 10);
if (!(frac /= 10)) {
break;
}
}
/* add extra 0s */
while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0)) {
buf[len++] = '0';
}
if (len < PRINTF_FTOA_BUFFER_SIZE) {
/* add decimal */
buf[len++] = '.';
}
}
/* do whole part, number is reversed */
while (len < PRINTF_FTOA_BUFFER_SIZE) {
buf[len++] = (char)(48 + (whole % 10));
if (!(whole /= 10)) {
break;
}
}
/* pad leading zeros */
if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) {
if (width && (signbit(value) || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {
width--;
}
while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) {
buf[len++] = '0';
}
}
}
if (len < PRINTF_FTOA_BUFFER_SIZE) {
if (signbit(value)) {
buf[len++] = '-';
} else if (flags & FLAGS_PLUS) {
buf[len++] = '+'; /* ignore the space if the '+' exists */
} else if (flags & FLAGS_SPACE) {
buf[len++] = ' ';
}
}
/* pad spaces up to given width */
if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) {
if (len < width) {
if (spacepad(out, arg, width - len) == -1) return -1;
}
}
/* reverse string */
for (idx = i = 0; i < len; i++) {
if (out(buf[len - i - 1U], arg) == -1) return -1;
idx++;
}
/* append pad spaces up to given width */
if (flags & FLAGS_LEFT) {
if (len < width) {
if (spacepad(out, arg, width - len) == -1) return -1;
}
}
return 0;
}

View file

@ -1,178 +0,0 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=8 sts=2 sw=2 fenc=utf-8 :vi
@author (c) Marco Paland (info@paland.com)
2014-2019, PALANDesign Hannover, Germany
@license The MIT License (MIT)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/fmt/conv.h"
#include "libc/fmt/paland.inc"
#include "libc/fmt/palandprintf.h"
uintmax_t __udivmodti4(uintmax_t, uintmax_t, uintmax_t *);
static int ntoaformat(int out(long, void *), void *arg, char *buf, unsigned len,
bool negative, unsigned log2base, unsigned prec,
unsigned width, unsigned char flags) {
unsigned i, idx;
idx = 0;
/* pad leading zeros */
if (!(flags & FLAGS_LEFT)) {
if (width && (flags & FLAGS_ZEROPAD) &&
(negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {
width--;
}
while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
buf[len++] = '0';
}
while ((flags & FLAGS_ZEROPAD) && (len < width) &&
(len < PRINTF_NTOA_BUFFER_SIZE)) {
buf[len++] = '0';
}
}
/* handle hash */
if (flags & FLAGS_HASH) {
if (!(flags & FLAGS_PRECISION) && len &&
((len == prec) || (len == width)) && buf[len - 1] == '0') {
len--;
if (len && (log2base == 4 || log2base == 1) && buf[len - 1] == '0') {
len--;
}
}
if (log2base == 4 && len < PRINTF_NTOA_BUFFER_SIZE) {
buf[len++] = 'x';
} else if (log2base == 1 && len < PRINTF_NTOA_BUFFER_SIZE) {
buf[len++] = 'b';
}
if (len < PRINTF_NTOA_BUFFER_SIZE) {
buf[len++] = '0';
}
}
if (len < PRINTF_NTOA_BUFFER_SIZE) {
if (negative) {
buf[len++] = '-';
} else if (flags & FLAGS_PLUS) {
buf[len++] = '+'; /* ignore the space if the '+' exists */
} else if (flags & FLAGS_SPACE) {
buf[len++] = ' ';
}
}
/* pad spaces up to given width */
if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) {
if (len < width) {
if (spacepad(out, arg, width - len) == -1) return -1;
}
}
/* reverse string */
for (i = 0U; i < len; i++) {
if (out(buf[len - i - 1], arg) == -1) return -1;
idx++;
}
/* append pad spaces up to given width */
if (flags & FLAGS_LEFT) {
if (idx < width) {
if (spacepad(out, arg, width - idx) == -1) return -1;
}
}
return 0;
}
int ntoa2(int out(long, void *), void *arg, uintmax_t value, bool neg,
unsigned log2base, unsigned prec, unsigned width, unsigned flags,
const char *alphabet) {
uintmax_t remainder;
unsigned len, count, digit;
char buf[PRINTF_NTOA_BUFFER_SIZE];
len = 0;
if (!value) flags &= ~FLAGS_HASH;
if (value || !(flags & FLAGS_PRECISION)) {
count = 0;
do {
assert(len < PRINTF_NTOA_BUFFER_SIZE);
if (!log2base) {
value = __udivmodti4(value, 10, &remainder);
digit = remainder;
} else {
digit = value;
digit &= (1u << log2base) - 1;
value >>= log2base;
}
if ((flags & FLAGS_GROUPING) && count == 3) {
buf[len++] = ',';
count = 1;
} else {
count++;
}
buf[len++] = alphabet[digit];
} while (value);
}
return ntoaformat(out, arg, buf, len, neg, log2base, prec, width, flags);
}
int ntoa(int out(long, void *), void *arg, va_list va, unsigned char signbit,
unsigned long log2base, unsigned long prec, unsigned long width,
unsigned char flags, const char *lang) {
bool neg;
uintmax_t value, sign;
/* ignore '0' flag when prec is given */
if (flags & FLAGS_PRECISION) {
flags &= ~FLAGS_ZEROPAD;
}
/* no plus / space flag for u, x, X, o, b */
if (!(flags & FLAGS_ISSIGNED)) {
flags &= ~(FLAGS_PLUS | FLAGS_SPACE);
}
if (signbit > 63) {
value = va_arg(va, uint128_t);
} else {
value = va_arg(va, uint64_t);
}
neg = 0;
sign = 1;
sign <<= signbit;
value &= sign | (sign - 1);
if (flags & FLAGS_ISSIGNED) {
if (value != sign) {
if (value & sign) {
value = ~value + 1;
value &= sign | (sign - 1);
neg = 1;
}
value &= sign - 1;
} else {
neg = 1;
}
}
return ntoa2(out, arg, value, neg, log2base, prec, width, flags, lang);
}

View file

@ -1,337 +0,0 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=8 sts=2 sw=2 fenc=utf-8 :vi
@author (c) Marco Paland (info@paland.com)
2014-2019, PALANDesign Hannover, Germany
@license The MIT License (MIT)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
@brief Tiny printf, sprintf and (v)snprintf implementation, optimized for
embedded systems with a very limited resources. These routines are
thread safe and reentrant! Use this instead of the bloated
standard/newlib printf cause these use malloc for printf (and may not
be thread safe).
@brief Modified by Justine Tunney to support three different types of
UNICODE, 128-bit arithmetic, binary conversion, string escaping,
AVX2 character scanning, and possibly a tinier footprint too, so
long as extremely wild linker hacks aren't considered cheating.
*/
#include "libc/assert.h"
#include "libc/bits/bits.h"
#include "libc/bits/weaken.h"
#include "libc/fmt/conv.h"
#include "libc/fmt/fmt.h"
#include "libc/fmt/paland.inc"
#include "libc/fmt/palandprintf.h"
#include "libc/mem/mem.h"
#include "libc/runtime/internal.h"
#include "libc/str/str.h"
#include "libc/sysv/errfuns.h"
static int ppatoi(const char **str) {
int i;
for (i = 0; '0' <= **str && **str <= '9'; ++*str) {
i *= 10;
i += **str - '0';
}
return i;
}
/**
* Implements {,v}{,s{,n},{,{,x}as},f,d}printf domain-specific language.
*
* Type Specifiers
*
* - `%s` char * (thompson-pike unicode)
* - `%ls` wchar_t * (32-bit unicode thompson-pike unicode)
* - `%hs` char16_t * (16-bit unicode thompson-pike unicode)
* - `%b` int (radix 2 binary)
* - `%o` int (radix 8 octal)
* - `%d` int (radix 10 decimal)
* - `%x` int (radix 16 hexadecimal)
* - `%X` int (radix 16 hexadecimal uppercase)
* - `%u` unsigned
* - `%f` double
* - `%Lf` long double
* - `%p` pointer (48-bit hexadecimal)
*
* Size Modifiers
*
* - `%hhd` char (8-bit)
* - `%hd` short (16-bit)
* - `%ld` long (64-bit)
* - `%lu` unsigned long (64-bit)
* - `%lx` unsigned long (64-bit hexadecimal)
* - `%jd` intmax_t (128-bit)
*
* Width Modifiers
*
* - `%08d` fixed columns w/ zero leftpadding
* - `%8d` fixed columns w/ space leftpadding
* - `%*s` variable column string (thompson-pike)
*
* Precision Modifiers
*
* - `%.8s` supplied byte length (obeys nul terminator)
* - `%.*s` supplied byte length argument (obeys nul terminator)
* - ``%`.*s`` supplied byte length argument c escaped (ignores nul term)
* - `%#.*s` supplied byte length argument visualized (ignores nul term)
* - `%.*hs` supplied char16_t length argument (obeys nul terminator)
* - `%.*ls` supplied wchar_t length argument (obeys nul terminator)
*
* Formatting Modifiers
*
* - `%,d` thousands separators
* - `%'s` escaped c string literal
* - ``%`c`` c escaped character
* - ``%`'c`` c escaped character quoted
* - ``%`s`` c escaped string
* - ``%`'s`` c escaped string quoted
* - ``%`s`` escaped double quoted c string literal
* - ``%`c`` escaped double quoted c character literal
* - `%+d` plus leftpad if positive (aligns w/ negatives)
* - `% d` space leftpad if positive (aligns w/ negatives)
* - `%#s` datum (radix 256 null-terminated ibm cp437)
* - `%#x` int (radix 16 hexadecimal w/ 0x prefix if not zero)
*
* @note implementation detail of printf(), snprintf(), etc.
* @see printf() for wordier documentation
* @asyncsignalsafe
* @vforksafe
*/
hidden int palandprintf(void *fn, void *arg, const char *format, va_list va) {
void *p;
char qchar;
bool longdouble;
long double ldbl;
wchar_t charbuf[1];
const char *alphabet;
int (*out)(long, void *);
unsigned char signbit, log2base;
int w, flags, width, lasterr, precision;
lasterr = errno;
out = fn ? fn : (void *)missingno;
while (*format) {
/* %[flags][width][.precision][length] */
if (*format != '%') {
/* no */
if (out(*format, arg) == -1) return -1;
format++;
continue;
} else {
/* yes, evaluate it */
format++;
}
/* evaluate flags */
flags = 0;
getflag:
switch (*format++) {
case '0':
flags |= FLAGS_ZEROPAD;
goto getflag;
case '-':
flags |= FLAGS_LEFT;
goto getflag;
case '+':
flags |= FLAGS_PLUS;
goto getflag;
case ' ':
flags |= FLAGS_SPACE;
goto getflag;
case '#':
flags |= FLAGS_HASH;
goto getflag;
case ',':
flags |= FLAGS_GROUPING;
goto getflag;
case '`':
flags |= FLAGS_REPR;
/* fallthrough */
case '\'':
flags |= FLAGS_QUOTE;
goto getflag;
default:
format--;
break;
}
/* evaluate width field */
width = 0;
if (isdigit(*format)) {
width = ppatoi(&format);
} else if (*format == '*') {
w = va_arg(va, int);
if (w < 0) {
flags |= FLAGS_LEFT; /* reverse padding */
width = -w;
} else {
width = w;
}
format++;
}
/* evaluate precision field */
precision = 0;
if (*format == '.') {
flags |= FLAGS_PRECISION;
format++;
if (isdigit(*format)) {
precision = ppatoi(&format);
} else if (*format == '*') {
precision = va_arg(va, int);
format++;
}
}
if (precision < 0) {
precision = 0;
}
/* evaluate length field */
signbit = 31;
longdouble = false;
switch (*format) {
case 'j': /* intmax_t */
format++;
signbit = sizeof(intmax_t) * 8 - 1;
break;
case 'l':
if (format[1] == 'f' || format[1] == 'F') {
format++;
break;
}
if (format[1] == 'l') format++;
/* fallthrough */
case 't': /* ptrdiff_t */
case 'z': /* size_t */
case 'Z': /* size_t */
format++;
signbit = 63;
break;
case 'L': /* long double */
format++;
longdouble = true;
break;
case 'h':
format++;
if (*format == 'h') {
format++;
signbit = 7;
} else {
signbit = 15;
}
break;
default:
break;
}
/* evaluate specifier */
alphabet = "0123456789abcdef";
log2base = 0;
qchar = '"';
switch (*format++) {
case 'p':
flags |= FLAGS_ZEROPAD;
width = POINTER_XDIGITS;
log2base = 4;
signbit = 47;
goto DoNumber;
case 'X':
alphabet = "0123456789ABCDEF";
/* fallthrough */
case 'x':
log2base = 4;
goto DoNumber;
case 'b':
log2base = 1;
goto DoNumber;
case 'o':
log2base = 3;
goto DoNumber;
case 'd':
case 'i':
flags |= FLAGS_ISSIGNED;
/* fallthrough */
case 'u': {
flags &= ~FLAGS_HASH; /* no hash for dec format */
DoNumber:
if (ntoa(out, arg, va, signbit, log2base, precision, width, flags,
alphabet) == -1) {
return -1;
}
break;
}
case 'f':
case 'F':
if (longdouble) {
ldbl = va_arg(va, long double);
} else {
ldbl = va_arg(va, double);
}
if (ftoa(out, arg, ldbl, precision, width, flags) == -1) {
return -1;
}
break;
case 'c':
precision = 1;
flags |= FLAGS_PRECISION;
qchar = '\'';
p = charbuf;
charbuf[0] = va_arg(va, int); /* assume little endian */
goto showstr;
case 'm':
p = weaken(strerror) ? weaken(strerror)(lasterr) : "?";
signbit = 0;
goto showstr;
case 'r':
flags |= FLAGS_REPR;
/* fallthrough */
case 'q':
flags |= FLAGS_QUOTE;
/* fallthrough */
case 's':
p = va_arg(va, void *);
showstr:
if (stoa(out, arg, p, flags, precision, width, signbit, qchar) == -1) {
return -1;
}
break;
case '%':
if (out('%', arg) == -1) return -1;
break;
default:
if (out(format[-1], arg) == -1) return -1;
break;
}
}
return 0;
}

View file

@ -1,20 +0,0 @@
#ifndef COSMOPOLITAN_LIBC_FMT_PALANDPRINTF_H_
#define COSMOPOLITAN_LIBC_FMT_PALANDPRINTF_H_
#define PRINTF_NTOA_BUFFER_SIZE 144
#define PRINTF_FTOA_BUFFER_SIZE 64
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
int spacepad(int (*)(long, void *), void *, unsigned long) hidden;
int ftoa(int (*)(long, void *), void *, long double, int, unsigned long,
unsigned long) hidden;
int stoa(int (*)(long, void *), void *, void *, unsigned long, unsigned long,
unsigned long, unsigned char, unsigned char) hidden;
int ntoa(int (*)(long, void *), void *, va_list, unsigned char, unsigned long,
unsigned long, unsigned long, unsigned char, const char *) hidden;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_FMT_PALANDPRINTF_H_ */

View file

@ -1,6 +1,7 @@
#ifndef COSMOPOLITAN_LIBC_FMT_PFLINK_H_
#define COSMOPOLITAN_LIBC_FMT_PFLINK_H_
#include "libc/dce.h"
#include "libc/fmt/fmts.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
@ -19,6 +20,7 @@
#define PFLINK(FMT) \
({ \
if (___PFLINK(FMT, strpbrk, "faAeEgG")) STATIC_YOINK("__fmt_dtoa"); \
if (___PFLINK(FMT, strpbrk, "cmrqs")) { \
if (___PFLINK(FMT, strchr, '#')) STATIC_YOINK("kCp437"); \
if (___PFLINK(FMT, strstr, "%m")) STATIC_YOINK("strerror"); \
@ -67,6 +69,7 @@
#define SFLINK(FMT) FMT
#ifdef __GNUC__
__asm__(".section .yoink\n\t"
"nopl\t__fmt_dtoa(%rip)\n\t"
"nopl\tkCp437(%rip)\n\t"
"nopl\tstrerror(%rip)\n\t"
"nopl\tstrnwidth(%rip)\n\t"
@ -79,6 +82,7 @@ __asm__(".section .yoink\n\t"
#else
static long __pflink(long x) {
x |= kCp437[0];
x |= __fmt_dtoa(0, 0, 0, 0, 0, 0);
x |= strnwidth(0, 0, 0);
x |= strnwidth16(0, 0, 0);
x |= wcsnwidth(0, 0, 0);

View file

@ -24,7 +24,7 @@
* @return number of bytes written, excluding the NUL terminator; or,
* if the output buffer wasn't passed, or was too short, then the
* number of characters that *would* have been written is returned
* @see palandprintf() and printf() for detailed documentation
* @see __fmt() and printf() for detailed documentation
* @asyncsignalsafe
* @vforksafe
*/

View file

@ -22,7 +22,7 @@
/**
* Formats string to buffer that's hopefully large enough.
*
* @see palandprintf() and printf() for detailed documentation
* @see __fmt() and printf() for detailed documentation
* @see snprintf() for same w/ buf size param
* @asyncsignalsafe
* @vforksafe

View file

@ -17,8 +17,8 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/bits/weaken.h"
#include "libc/fmt/paland.inc"
#include "libc/fmt/palandprintf.h"
#include "libc/fmt/fmts.h"
#include "libc/fmt/internal.h"
#include "libc/nexgen32e/tinystrlen.internal.h"
#include "libc/str/str.h"
#include "libc/str/thompike.h"
@ -28,11 +28,12 @@
typedef int (*emit_f)(int (*)(long, void *), void *, wint_t);
static noinstrument int StoaEmitByte(int f(long, void *), void *a, wint_t c) {
static noinstrument int __fmt_stoa_byte(int f(long, void *), void *a,
wint_t c) {
return f(c, a);
}
static noinstrument int StoaEmitWordEncodedString(int f(long, void *), void *a,
static noinstrument int __fmt_stoa_word(int f(long, void *), void *a,
uint64_t w) {
do {
if (f(w & 0xff, a) == -1) {
@ -42,29 +43,30 @@ static noinstrument int StoaEmitWordEncodedString(int f(long, void *), void *a,
return 0;
}
static noinstrument int StoaEmitUnicode(int f(long, void *), void *a,
static noinstrument int __fmt_stoa_wide(int f(long, void *), void *a,
wint_t c) {
if (isascii(c)) {
return f(c, a);
} else {
return StoaEmitWordEncodedString(f, a, tpenc(c));
return __fmt_stoa_word(f, a, tpenc(c));
}
}
static noinstrument int StoaEmitQuoted(int f(long, void *), void *a, wint_t c) {
if (isascii(c)) {
return StoaEmitWordEncodedString(f, a, cescapec(c));
} else {
return StoaEmitWordEncodedString(f, a, tpenc(c));
}
}
static noinstrument int StoaEmitVisualized(int f(long, void *), void *a,
static noinstrument int __fmt_stoa_bing(int f(long, void *), void *a,
wint_t c) {
return StoaEmitUnicode(f, a, (*weaken(kCp437))[c]);
return __fmt_stoa_wide(f, a, (*weaken(kCp437))[c]);
}
static noinstrument int StoaEmitQuote(int out(long, void *), void *arg,
static noinstrument int __fmt_stoa_quoted(int f(long, void *), void *a,
wint_t c) {
if (isascii(c)) {
return __fmt_stoa_word(f, a, cescapec(c));
} else {
return __fmt_stoa_word(f, a, tpenc(c));
}
}
static noinstrument int __fmt_stoa_quote(int out(long, void *), void *arg,
unsigned flags, char ch,
unsigned char signbit) {
if (flags & FLAGS_REPR) {
@ -81,14 +83,15 @@ static noinstrument int StoaEmitQuote(int out(long, void *), void *arg,
/**
* Converts string to array.
*
* This function is used by palandprintf() to implement the %s and %c
* directives. The content outputted to the array is always UTF-8, but
* the input may be UTF-16 or UTF-32.
* This is used by __fmt() to implement the %s and %c directives. The
* content outputted to the array is always UTF-8, but the input may be
* UTF-16 or UTF-32.
*
* @see palandprintf()
* @see __fmt()
*/
int stoa(int out(long, void *), void *arg, void *data, unsigned long flags,
unsigned long precision, unsigned long width, unsigned char signbit,
int __fmt_stoa(int out(long, void *), void *arg, void *data,
unsigned long flags, unsigned long precision,
unsigned long width, unsigned char signbit,
unsigned char qchar) {
char *p;
wint_t wc;
@ -104,28 +107,28 @@ int stoa(int out(long, void *), void *arg, void *data, unsigned long flags,
flags |= FLAGS_NOQUOTE;
signbit = 0;
} else {
if (StoaEmitQuote(out, arg, flags, qchar, signbit) == -1) return -1;
if (__fmt_stoa_quote(out, arg, flags, qchar, signbit) == -1) return -1;
}
ignorenul = false;
justdobytes = false;
if (signbit == 15 || signbit == 63) {
if (flags & FLAGS_QUOTE) {
emit = StoaEmitQuoted;
emit = __fmt_stoa_quoted;
ignorenul = flags & FLAGS_PRECISION;
} else {
emit = StoaEmitUnicode;
emit = __fmt_stoa_wide;
}
} else if ((flags & FLAGS_HASH) && weaken(kCp437)) {
justdobytes = true;
emit = StoaEmitVisualized;
emit = __fmt_stoa_bing;
ignorenul = flags & FLAGS_PRECISION;
} else if (flags & FLAGS_QUOTE) {
emit = StoaEmitQuoted;
emit = __fmt_stoa_quoted;
ignorenul = flags & FLAGS_PRECISION;
} else {
justdobytes = true;
emit = StoaEmitByte;
emit = __fmt_stoa_byte;
}
if (!(flags & FLAGS_PRECISION)) precision = -1;
@ -159,7 +162,7 @@ int stoa(int out(long, void *), void *arg, void *data, unsigned long flags,
}
if (pad && !(flags & FLAGS_LEFT)) {
if (spacepad(out, arg, pad) == -1) return -1;
if (__fmt_pad(out, arg, pad) == -1) return -1;
}
if (justdobytes) {
@ -208,7 +211,7 @@ int stoa(int out(long, void *), void *arg, void *data, unsigned long flags,
}
if (pad && (flags & FLAGS_LEFT)) {
if (spacepad(out, arg, pad) == -1) return -1;
if (__fmt_pad(out, arg, pad) == -1) return -1;
}
if (!(flags & FLAGS_NOQUOTE) && (flags & FLAGS_REPR)) {

View file

@ -26,9 +26,6 @@
#include "libc/nt/runtime.h"
#include "libc/str/str.h"
STATIC_YOINK("ntoa");
STATIC_YOINK("stoa");
STATIC_YOINK("E2BIG");
STATIC_YOINK("EACCES");
STATIC_YOINK("EADDRINUSE");

View file

@ -25,9 +25,5 @@
* @param optional_base is recommended as 0 for flexidecimal
*/
long strtol(const char *s, char **opt_out_end, int optional_base) {
long res;
res = strtoimax(s, opt_out_end, optional_base);
if (res < LONG_MIN) return LONG_MIN;
if (res > LONG_MAX) return LONG_MAX;
return res;
return strtoimax(s, opt_out_end, optional_base);
}

View file

@ -44,13 +44,13 @@ static noinstrument int vsnprintfputchar(unsigned char c,
* @return number of bytes written, excluding the NUL terminator; or,
* if the output buffer wasn't passed, or was too short, then the
* number of characters that *would* have been written is returned
* @see palandprintf() and printf() for detailed documentation
* @see __fmt() and printf() for detailed documentation
* @asyncsignalsafe
* @vforksafe
*/
int(vsnprintf)(char *buf, size_t size, const char *fmt, va_list va) {
struct SprintfStr str = {buf, 0, size};
palandprintf(vsnprintfputchar, &str, fmt, va);
__fmt(vsnprintfputchar, &str, fmt, va);
if (str.n) str.p[min(str.i, str.n - 1)] = '\0';
return str.i;
}

View file

@ -22,7 +22,7 @@
/**
* Formats string to buffer hopefully large enough w/ vararg state.
*
* @see palandprintf() and printf() for detailed documentation
* @see __fmt() and printf() for detailed documentation
* @see vsnprintf() for modern alternative w/ buf size param
*/
int(vsprintf)(char *buf, const char *fmt, va_list va) {

View file

@ -1,16 +0,0 @@
#ifndef COSMOPOLITAN_LIBC_FMT_WCSLOL_H_
#define COSMOPOLITAN_LIBC_FMT_WCSLOL_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
#define WCSLOL(STR, ENDPTR, OPTIONAL_BASE, MIN, MAX) \
({ \
intmax_t res = wcstoimax(STR, ENDPTR, OPTIONAL_BASE); \
if (res < MIN) return MIN; \
if (res > MAX) return MAX; \
res; \
})
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_FMT_WCSLOL_H_ */

View file

@ -17,10 +17,8 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/fmt/conv.h"
#include "libc/fmt/wcslol.internal.h"
#include "libc/limits.h"
#include "libc/str/str.h"
long wcstol(const wchar_t *s, wchar_t **end, int opt_base) {
return WCSLOL(s, end, opt_base, LONG_MIN, LONG_MAX);
return wcstoimax(s, end, opt_base);
}

View file

@ -22,9 +22,6 @@
#include "libc/log/log.h"
#include "libc/stdio/stdio.h"
STATIC_YOINK("ntoa");
STATIC_YOINK("stoa");
void __check_fail_aligned(unsigned bytes, uint64_t ptr) {
fflush(stderr);
if (!IsTiny()) memsummary(fileno(stderr));

View file

@ -33,10 +33,6 @@
#include "libc/sysv/consts/auxv.h"
#include "libc/sysv/consts/fileno.h"
STATIC_YOINK("ntoa");
STATIC_YOINK("stoa");
STATIC_YOINK("ftoa");
/**
* Handles failure of CHECK_xx() macros.
*/

View file

@ -44,7 +44,8 @@ LIBC_LOG_A_DIRECTDEPS = \
LIBC_TIME \
LIBC_TINYMATH \
LIBC_UNICODE \
THIRD_PARTY_DLMALLOC
THIRD_PARTY_DLMALLOC \
THIRD_PARTY_GDTOA
LIBC_LOG_A_DEPS := \
$(call uniq,$(foreach x,$(LIBC_LOG_A_DIRECTDEPS),$($(x))))

View file

@ -20,8 +20,6 @@
#include "libc/stdio/stdio.h"
#include "third_party/dlmalloc/dlmalloc.internal.h"
STATIC_YOINK("ntoa");
void malloc_stats(void) {
struct MallocStats res = dlmalloc_stats(g_dlmalloc);
(fprintf)(stderr, "max system bytes = %'10zu\r\n", res.maxfp);

View file

@ -21,9 +21,6 @@
#include "libc/log/log.h"
#include "libc/mem/mem.h"
STATIC_YOINK("ntoa");
STATIC_YOINK("stoa");
static void onmemchunk(void *start, void *end, size_t used_bytes, void *arg) {
(dprintf)(*(int *)arg, "%p - %p : %08zx / %08lx\r\n", start, end, used_bytes,
(intptr_t)end - (intptr_t)start);

View file

@ -20,8 +20,6 @@
#include "libc/log/log.h"
#include "libc/mem/mem.h"
STATIC_YOINK("ntoa");
void memsummary(int fd) {
struct mallinfo mi;
mi = mallinfo();

View file

@ -37,10 +37,6 @@
#define kNontrivialSize (8 * 1000 * 1000)
STATIC_YOINK("ntoa");
STATIC_YOINK("stoa");
STATIC_YOINK("ftoa");
static struct timespec vflogf_ts;
static int vflogf_loglevel2char(unsigned level) {

View file

@ -20,9 +20,6 @@
#include "libc/log/log.h"
#include "libc/runtime/memtrack.h"
STATIC_YOINK("ntoa");
STATIC_YOINK("stoa");
void PrintMemoryIntervals(int fd, const struct MemoryIntervals *mm) {
int i, frames, maptally, gaptally;
maptally = 0;

View file

@ -1,7 +1,7 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
Copyright 2021 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
@ -16,13 +16,10 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/fmt/conv.h"
#include "libc/math.h"
#include "libc/fmt/fmts.h"
#include "third_party/gdtoa/gdtoa.h"
double RoundDecimalPlaces(double f, double digs, double rounder(double)) {
if (!(0 <= digs && digs < 15)) {
return f;
} else {
return rounder(f * exp10(digs)) / exp10(digs);
}
char *__fmt_dtoa(double d0, int mode, int ndigits, int *decpt, int *sign,
char **rve) {
return dtoa(d0, mode, ndigits, decpt, sign, rve);
}

View file

@ -58,7 +58,7 @@
* This means it implies the quoting modifier, wraps the value with
* {,u,L}['"] quotes, displays NULL as "NULL" rather than "(null)".
*
* @see palandprintf() for intuitive reference documentation
* @see __fmt() for intuitive reference documentation
* @see {,v}{,s{,n},{,{,x}as},f,d}printf
*/
int(printf)(const char* fmt, ...) {

View file

@ -8,6 +8,9 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
int Printf(const char *, ...);
int Sprintf(char *, const char *, ...);
/*───────────────────────────────────────────────────────────────────────────│─╗
cosmopolitan § standard i/o
*/

View file

@ -37,7 +37,8 @@ LIBC_STDIO_A_DIRECTDEPS = \
LIBC_STR \
LIBC_STUBS \
LIBC_SYSV \
LIBC_SYSV_CALLS
LIBC_SYSV_CALLS \
THIRD_PARTY_GDTOA
LIBC_STDIO_A_DEPS := \
$(call uniq,$(foreach x,$(LIBC_STDIO_A_DIRECTDEPS),$($(x))))

View file

@ -33,7 +33,7 @@ static noinstrument int vfprintfputchar(int c, struct state *st) {
int(vfprintf)(FILE *f, const char *fmt, va_list va) {
struct state st[1] = {{f, 0}};
if (palandprintf(vfprintfputchar, st, fmt, va) != -1) {
if (__fmt(vfprintfputchar, st, fmt, va) != -1) {
return st->n;
} else {
return -1;

View file

@ -21,8 +21,6 @@
#include "libc/testlib/testlib.h"
#include "libc/time/time.h"
STATIC_YOINK("ntoa");
STATIC_YOINK("stoa");
STATIC_YOINK("strnwidth");
void __testlib_ezbenchreport(const char *form, uint64_t c1, uint64_t c2) {

View file

@ -20,9 +20,6 @@
#include "libc/time/struct/tm.h"
#include "libc/time/time.h"
STATIC_YOINK("ntoa");
STATIC_YOINK("stoa");
static unsigned clip(unsigned index, unsigned count) {
return index < count ? index : 0;
}

View file

@ -25,8 +25,6 @@
#include "libc/time/time.h"
#include "libc/time/tzfile.internal.h"
STATIC_YOINK("ntoa");
asm(".ident\t\"\\n\\n\
strftime (BSD-3)\\n\
Copyright 1989 The Regents of the University of California\"");

View file

@ -27,9 +27,6 @@
#include "libc/time/time.h"
#include "libc/x/x.h"
STATIC_YOINK("stoa");
STATIC_YOINK("ntoa");
/**
* @fileoverview Timestamps in One True Format w/o toil.
*/

View file

@ -433,8 +433,7 @@ TEST(sprintf, test_float) {
EXPECT_STREQ("3.5", Format("%.1f", 3.49));
EXPECT_STREQ("a0.5 ", Format("a%-5.1f", 0.5));
EXPECT_STREQ("a0.5 end", Format("a%-5.1fend", 0.5));
/* out of range in the moment, need to be fixed by someone */
EXPECT_STREQ("inf", Format("%.1f", 1E20));
EXPECT_STREQ("100000000000000000000.0", Format("%.1f", 1E20));
}
TEST(sprintf, test_types) {

View file

@ -0,0 +1,68 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2021 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/fmt/fmt.h"
#include "libc/math.h"
#include "libc/runtime/gc.h"
#include "libc/testlib/testlib.h"
#include "libc/x/x.h"
TEST(RealFormatting, g) {
EXPECT_STREQ("nan", gc(xasprintf("%g", NAN)));
EXPECT_STREQ("-nan", gc(xasprintf("%g", -NAN)));
EXPECT_STREQ("inf", gc(xasprintf("%g", INFINITY)));
EXPECT_STREQ("-inf", gc(xasprintf("%g", -INFINITY)));
EXPECT_STREQ("0", gc(xasprintf("%g", 0.)));
EXPECT_STREQ("-0", gc(xasprintf("%g", -0.)));
EXPECT_STREQ("1", gc(xasprintf("%g", 1.)));
EXPECT_STREQ("-1", gc(xasprintf("%g", -1.)));
EXPECT_STREQ("10", gc(xasprintf("%g", 10.)));
EXPECT_STREQ("10", gc(xasprintf("%.0g", 10.)));
EXPECT_STREQ("-10", gc(xasprintf("%g", -10.)));
EXPECT_STREQ("-10", gc(xasprintf("%.0g", -10.)));
EXPECT_STREQ(" -10", gc(xasprintf("%10g", -10.)));
EXPECT_STREQ(" -10", gc(xasprintf("%*g", 10, -10.)));
EXPECT_STREQ("1e+100", gc(xasprintf("%g", 1e100)));
EXPECT_STREQ("1e-100", gc(xasprintf("%g", 1e-100)));
EXPECT_STREQ("-1e-100", gc(xasprintf("%g", -1e-100)));
EXPECT_STREQ("3.14159", gc(xasprintf("%g", 0x1.921fb54442d1846ap+1)));
}
TEST(RealFormatting, f) {
EXPECT_STREQ("3.141593", gc(xasprintf("%f", 0x1.921fb54442d1846ap+1)));
EXPECT_STREQ("3.1415926535897931",
gc(xasprintf("%.16f", 0x1.921fb54442d1846ap+1)));
EXPECT_STREQ("100000000000000001590289110975991804683608085639452813"
"89781327557747838772170381060813469985856815104.000000",
gc(xasprintf("%f", 1e100)));
}
TEST(RealFormatting, e) {
EXPECT_STREQ("3.14159", gc(xasprintf("%g", 0x1.921fb54442d1846ap+1)));
EXPECT_STREQ("3.141592653589793",
gc(xasprintf("%.16g", 0x1.921fb54442d1846ap+1)));
EXPECT_STREQ("1.000000e+100", gc(xasprintf("%e", 1e100)));
EXPECT_STREQ("1.000000E+100", gc(xasprintf("%E", 1e100)));
}
TEST(RealFormatting, a) {
EXPECT_STREQ("0x1.921fb54442d18p+1",
gc(xasprintf("%a", 0x1.921fb54442d1846ap+1)));
EXPECT_STREQ("0X1.921FB54442D18P+1",
gc(xasprintf("%A", 0x1.921fb54442d1846ap+1)));
}

View file

@ -29,6 +29,7 @@ TEST_LIBC_FMT_DIRECTDEPS = \
LIBC_STR \
LIBC_STUBS \
LIBC_SYSV \
LIBC_TINYMATH \
LIBC_TESTLIB \
LIBC_UNICODE \
LIBC_X \

View file

@ -31,13 +31,14 @@ THIRD_PARTY_DUKTAPE_A_DIRECTDEPS = \
LIBC_FMT \
LIBC_INTRIN \
LIBC_MEM \
LIBC_NEXGEN32E \
LIBC_RUNTIME \
LIBC_STDIO \
LIBC_STR \
LIBC_STUBS \
LIBC_TIME \
LIBC_RUNTIME \
LIBC_TINYMATH \
LIBC_UNICODE \
LIBC_NEXGEN32E
LIBC_UNICODE
THIRD_PARTY_DUKTAPE_A_DEPS := \
$(call uniq,$(foreach x,$(THIRD_PARTY_DUKTAPE_A_DIRECTDEPS),$($(x))))

View file

@ -168,9 +168,9 @@ dtoa(double d0, int mode, int ndigits, int *decpt, int *sign, char **rve)
*decpt = 9999;
#ifdef IEEE_Arith
if (!word1(&d) && !(word0(&d) & 0xfffff))
return nrv_alloc("Infinity", rve, 8 MTb);
return nrv_alloc("inf", rve, 8 MTb);
#endif
return nrv_alloc("NaN", rve, 3 MTb);
return nrv_alloc("nan", rve, 3 MTb);
}
#endif
#ifdef IBM

View file

@ -135,8 +135,8 @@ int getopt(int nargc, char *const nargv[], const char *ostr) {
if (optopt == ':' || (oli = strchr(ostr, optopt)) == NULL) {
if (*getopt_place == 0) ++optind;
if (opterr && *ostr != ':') {
fprintf(stderr, "%s: illegal option -- %c\n", program_invocation_name,
optopt);
fprintf(stderr, "%s%s%c\n", program_invocation_name,
": illegal option -- ", optopt);
}
return (BADCH);
}
@ -157,8 +157,8 @@ int getopt(int nargc, char *const nargv[], const char *ostr) {
getopt_place = kGetoptEmsg;
if (*ostr == ':') return (BADARG);
if (opterr)
fprintf(stderr, "%s: option requires an argument -- %c\n",
program_invocation_name, optopt);
fprintf(stderr, "%s%s%c\n", program_invocation_name,
": option requires an argument -- ", optopt);
return (BADCH);
}
getopt_place = kGetoptEmsg;