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"),
value);
(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"
@ -17,19 +18,20 @@
* format strings are constexprs that only contain directives.
*/
#define PFLINK(FMT) \
({ \
if (___PFLINK(FMT, strpbrk, "cmrqs")) { \
if (___PFLINK(FMT, strchr, '#')) STATIC_YOINK("kCp437"); \
if (___PFLINK(FMT, strstr, "%m")) STATIC_YOINK("strerror"); \
if (!IsTiny() && (___PFLINK(FMT, strstr, "%*") || \
___PFLINK(FMT, strpbrk, "0123456789"))) { \
STATIC_YOINK("strnwidth"); \
STATIC_YOINK("strnwidth16"); \
STATIC_YOINK("wcsnwidth"); \
} \
} \
FMT; \
#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"); \
if (!IsTiny() && (___PFLINK(FMT, strstr, "%*") || \
___PFLINK(FMT, strpbrk, "0123456789"))) { \
STATIC_YOINK("strnwidth"); \
STATIC_YOINK("strnwidth16"); \
STATIC_YOINK("wcsnwidth"); \
} \
} \
FMT; \
})
#define SFLINK(FMT) \
@ -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,12 +28,13 @@
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,
uint64_t w) {
static noinstrument int __fmt_stoa_word(int f(long, void *), void *a,
uint64_t w) {
do {
if (f(w & 0xff, a) == -1) {
return -1;
@ -42,31 +43,32 @@ 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) {
static noinstrument int __fmt_stoa_bing(int f(long, void *), void *a,
wint_t c) {
return __fmt_stoa_wide(f, a, (*weaken(kCp437))[c]);
}
static noinstrument int __fmt_stoa_quoted(int f(long, void *), void *a,
wint_t c) {
if (isascii(c)) {
return StoaEmitWordEncodedString(f, a, cescapec(c));
return __fmt_stoa_word(f, a, cescapec(c));
} else {
return StoaEmitWordEncodedString(f, a, tpenc(c));
return __fmt_stoa_word(f, a, tpenc(c));
}
}
static noinstrument int StoaEmitVisualized(int f(long, void *), void *a,
wint_t c) {
return StoaEmitUnicode(f, a, (*weaken(kCp437))[c]);
}
static noinstrument int StoaEmitQuote(int out(long, void *), void *arg,
unsigned flags, char ch,
unsigned char signbit) {
static noinstrument int __fmt_stoa_quote(int out(long, void *), void *arg,
unsigned flags, char ch,
unsigned char signbit) {
if (flags & FLAGS_REPR) {
if (signbit == 63) {
if (out('L', arg) == -1) return -1;
@ -81,15 +83,16 @@ 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,
unsigned char qchar) {
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;
unsigned n;
@ -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\"");
@ -330,13 +328,13 @@ static char *strftime_timefmt(char *p, const char *pe, const char *format,
if (t->tm_isdst == 0)
#ifdef USG_COMPAT
diff = -timezone;
#else /* !defined USG_COMPAT */
#else /* !defined USG_COMPAT */
continue;
#endif /* !defined USG_COMPAT */
else
#ifdef ALTZONE
diff = -altzone;
#else /* !defined ALTZONE */
#else /* !defined ALTZONE */
continue;
#endif /* !defined ALTZONE */
#endif /* !defined TM_GMTOFF */

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;