2023-06-17 17:13:50 +00:00
|
|
|
/*-*- 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. │
|
|
|
|
│ │
|
|
|
|
│ Copyright (C) 1997, 1999, 2001 Lucent Technologies │
|
|
|
|
│ All Rights Reserved │
|
|
|
|
│ │
|
|
|
|
│ Permission to use, copy, modify, and distribute this software and │
|
|
|
|
│ its documentation for any purpose and without fee is hereby │
|
|
|
|
│ granted, provided that the above copyright notice appear in all │
|
|
|
|
│ copies and that both that the copyright notice and this │
|
|
|
|
│ permission notice and warranty disclaimer appear in supporting │
|
|
|
|
│ documentation, and that the name of Lucent or any of its entities │
|
|
|
|
│ not be used in advertising or publicity pertaining to │
|
|
|
|
│ distribution of the software without specific, written prior │
|
|
|
|
│ permission. │
|
|
|
|
│ │
|
|
|
|
│ LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, │
|
|
|
|
│ INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. │
|
|
|
|
│ IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY │
|
|
|
|
│ SPECIAL, 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/assert.h"
|
|
|
|
#include "libc/errno.h"
|
|
|
|
#include "libc/fmt/conv.h"
|
|
|
|
#include "libc/fmt/divmod10.internal.h"
|
|
|
|
#include "libc/fmt/itoa.h"
|
|
|
|
#include "libc/intrin/bits.h"
|
|
|
|
#include "libc/intrin/bsr.h"
|
|
|
|
#include "libc/intrin/nomultics.internal.h"
|
|
|
|
#include "libc/intrin/safemacros.internal.h"
|
|
|
|
#include "libc/limits.h"
|
|
|
|
#include "libc/macros.internal.h"
|
|
|
|
#include "libc/math.h"
|
|
|
|
#include "libc/mem/mem.h"
|
|
|
|
#include "libc/mem/reverse.internal.h"
|
|
|
|
#include "libc/runtime/internal.h"
|
|
|
|
#include "libc/str/str.h"
|
|
|
|
#include "libc/str/strwidth.h"
|
|
|
|
#include "libc/str/tab.internal.h"
|
|
|
|
#include "libc/str/thompike.h"
|
|
|
|
#include "libc/str/unicode.h"
|
|
|
|
#include "libc/str/utf16.h"
|
|
|
|
#include "libc/sysv/errfuns.h"
|
|
|
|
#include "third_party/gdtoa/gdtoa.h"
|
|
|
|
|
|
|
|
#define BUFFER_SIZE 144
|
|
|
|
|
|
|
|
#define PRINTF_NTOA_BUFFER_SIZE 144
|
|
|
|
|
|
|
|
#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
|
|
|
|
|
|
|
|
#define __FMT_PUT(C) \
|
|
|
|
do { \
|
|
|
|
char Buf[1] = {C}; \
|
|
|
|
if (out(Buf, arg, 1) == -1) { \
|
|
|
|
return -1; \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
struct FPBits {
|
|
|
|
uint32_t bits[4];
|
|
|
|
const FPI *fpi;
|
|
|
|
int sign;
|
|
|
|
int ex; // exponent
|
|
|
|
int kind;
|
|
|
|
};
|
|
|
|
|
|
|
|
union U {
|
|
|
|
double d;
|
|
|
|
uint64_t q;
|
|
|
|
long double ld;
|
|
|
|
uint32_t ui[4];
|
|
|
|
uint16_t us[5];
|
|
|
|
};
|
|
|
|
|
|
|
|
static const FPI kFpiDbl = {
|
|
|
|
.nbits = DBL_MANT_DIG,
|
|
|
|
.emin = 3 - DBL_MAX_EXP - DBL_MANT_DIG,
|
|
|
|
.emax = DBL_MAX_EXP - DBL_MANT_DIG,
|
|
|
|
.rounding = FPI_Round_near,
|
|
|
|
.sudden_underflow = 0,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const FPI kFpiLdbl = {
|
|
|
|
.nbits = LDBL_MANT_DIG,
|
|
|
|
.emin = 3 - LDBL_MAX_EXP - LDBL_MANT_DIG,
|
|
|
|
.emax = LDBL_MAX_EXP - LDBL_MANT_DIG,
|
|
|
|
.rounding = FPI_Round_near,
|
|
|
|
.sudden_underflow = 0,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const char kSpecialFloats[2][2][4] = {
|
|
|
|
{"INF", "inf"},
|
|
|
|
{"NAN", "nan"},
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef int (*out_f)(const char *, void *, size_t);
|
|
|
|
typedef int (*emit_f)(out_f, void *, uint64_t);
|
|
|
|
|
|
|
|
uint128_t __udivmodti4(uint128_t, uint128_t, uint128_t *);
|
|
|
|
|
|
|
|
static int __fmt_atoi(const char **str) {
|
|
|
|
int i;
|
|
|
|
for (i = 0; '0' <= **str && **str <= '9'; ++*str) {
|
|
|
|
i *= 10;
|
|
|
|
i += **str - '0';
|
|
|
|
}
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int __fmt_pad(int out(const char *, void *, size_t), void *arg,
|
|
|
|
unsigned long n) {
|
|
|
|
int i, rc;
|
|
|
|
for (rc = i = 0; i < n; ++i) rc |= out(" ", arg, 1);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int __fmt_ntoa_format(int out(const char *, void *, size_t), void *arg,
|
|
|
|
char *buf, unsigned len, bool negative,
|
|
|
|
unsigned log2base, unsigned prec, unsigned width,
|
|
|
|
unsigned char flags, const char *alphabet) {
|
|
|
|
unsigned i, prec_width_zeros;
|
|
|
|
char alternate_form_middle_char, sign_character;
|
|
|
|
unsigned actual_buf_len;
|
|
|
|
actual_buf_len = len;
|
|
|
|
prec_width_zeros = 0;
|
|
|
|
/* pad leading zeros */
|
|
|
|
if (width && (flags & FLAGS_ZEROPAD) &&
|
|
|
|
(negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {
|
|
|
|
width--;
|
|
|
|
}
|
|
|
|
if (len < prec) {
|
|
|
|
prec_width_zeros += (prec - len);
|
|
|
|
len = prec;
|
|
|
|
}
|
|
|
|
if ((flags & FLAGS_ZEROPAD) && (len < width)) {
|
|
|
|
prec_width_zeros += (width - len);
|
|
|
|
len = width;
|
|
|
|
}
|
|
|
|
/* handle hash */
|
|
|
|
if (flags & FLAGS_HASH) {
|
|
|
|
if ((!(flags & FLAGS_PRECISION) || log2base == 3) && len &&
|
|
|
|
((len >= prec) || (len >= width)) &&
|
|
|
|
(prec_width_zeros || buf[len - 1] == '0')) {
|
|
|
|
if (prec_width_zeros) {
|
|
|
|
--prec_width_zeros;
|
|
|
|
}
|
|
|
|
--len;
|
|
|
|
if (len < actual_buf_len) {
|
|
|
|
actual_buf_len = len;
|
|
|
|
}
|
|
|
|
if (len && (log2base == 4 || log2base == 1) &&
|
|
|
|
(prec_width_zeros || buf[len - 1] == '0')) {
|
|
|
|
if (prec_width_zeros) {
|
|
|
|
--prec_width_zeros;
|
|
|
|
}
|
|
|
|
--len;
|
|
|
|
if (len < actual_buf_len) {
|
|
|
|
actual_buf_len = len;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
alternate_form_middle_char = '\0';
|
|
|
|
if ((log2base == 4 || log2base == 1)) {
|
|
|
|
++len;
|
|
|
|
alternate_form_middle_char =
|
|
|
|
alphabet[17]; // x, X or b (for the corresponding conversion
|
|
|
|
// specifiers)
|
|
|
|
}
|
|
|
|
++len;
|
|
|
|
}
|
|
|
|
sign_character = '\0';
|
|
|
|
if (negative) {
|
|
|
|
++len;
|
|
|
|
sign_character = '-';
|
|
|
|
} else if (flags & FLAGS_PLUS) {
|
|
|
|
++len;
|
|
|
|
sign_character = '+'; /* ignore the space if the '+' exists */
|
|
|
|
} else if (flags & FLAGS_SPACE) {
|
|
|
|
++len;
|
|
|
|
sign_character = ' ';
|
|
|
|
}
|
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (sign_character != '\0' && out(&sign_character, arg, 1) == -1) return -1;
|
|
|
|
if (flags & FLAGS_HASH) {
|
|
|
|
if (out("0", arg, 1) == -1) return -1;
|
|
|
|
if (alternate_form_middle_char != '\0' &&
|
|
|
|
out(&alternate_form_middle_char, arg, 1) == -1)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
for (i = 0; i < prec_width_zeros; ++i)
|
|
|
|
if (out("0", arg, 1) == -1) return -1;
|
|
|
|
reverse(buf, actual_buf_len);
|
|
|
|
if (out(buf, arg, actual_buf_len) == -1) return -1;
|
|
|
|
/* append pad spaces up to given width */
|
|
|
|
if (flags & FLAGS_LEFT) {
|
|
|
|
if (len < width) {
|
|
|
|
if (__fmt_pad(out, arg, width - len) == -1) return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-07-03 09:47:05 +00:00
|
|
|
static int __fmt_ntoa2(int out(const char *, void *, size_t), void *arg,
|
|
|
|
uint128_t value, bool neg, unsigned log2base,
|
|
|
|
unsigned prec, unsigned width, unsigned flags,
|
|
|
|
const char *alphabet) {
|
2023-06-17 17:13:50 +00:00
|
|
|
uint128_t remainder;
|
|
|
|
unsigned len, count, digit;
|
|
|
|
char buf[BUFFER_SIZE];
|
|
|
|
len = 0;
|
|
|
|
/* we check for log2base != 3 because otherwise we'll print nothing for a
|
|
|
|
* value of 0 with precision 0 when # mandates that one be printed */
|
|
|
|
if (!value && log2base != 3) flags &= ~FLAGS_HASH;
|
|
|
|
if (value || !(flags & FLAGS_PRECISION)) {
|
|
|
|
count = 0;
|
|
|
|
do {
|
|
|
|
if (!log2base) {
|
|
|
|
if (value <= UINT64_MAX) {
|
|
|
|
value = DivMod10(value, &digit);
|
|
|
|
} else {
|
|
|
|
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);
|
|
|
|
_npassert(count <= BUFFER_SIZE);
|
|
|
|
}
|
|
|
|
return __fmt_ntoa_format(out, arg, buf, len, neg, log2base, prec, width,
|
|
|
|
flags, alphabet);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int __fmt_ntoa(int out(const char *, void *, size_t), void *arg,
|
|
|
|
uint128_t value, unsigned char signbit,
|
|
|
|
unsigned long log2base, unsigned long prec,
|
|
|
|
unsigned long width, unsigned char flags,
|
|
|
|
const char *lang) {
|
|
|
|
bool neg;
|
|
|
|
uint128_t sign;
|
|
|
|
|
|
|
|
/* ignore '0' flag when prec or minus flag is given */
|
|
|
|
if (flags & (FLAGS_PRECISION | FLAGS_LEFT)) {
|
|
|
|
flags &= ~FLAGS_ZEROPAD;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* no plus / space flag for u, x, X, o, b */
|
|
|
|
if (!(flags & FLAGS_ISSIGNED)) {
|
|
|
|
flags &= ~(FLAGS_PLUS | FLAGS_SPACE);
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-03 09:47:05 +00:00
|
|
|
return __fmt_ntoa2(out, arg, value, neg, log2base, prec, width, flags, lang);
|
2023-06-17 17:13:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Converts byte to word-encoded C string literal representation.
|
|
|
|
*/
|
|
|
|
static int __fmt_cescapec(int c) {
|
|
|
|
switch ((c &= 255)) {
|
|
|
|
case '\a':
|
|
|
|
return '\\' | 'a' << 8;
|
|
|
|
case '\b':
|
|
|
|
return '\\' | 'b' << 8;
|
|
|
|
case '\t':
|
|
|
|
return '\\' | 't' << 8;
|
|
|
|
case '\n':
|
|
|
|
return '\\' | 'n' << 8;
|
|
|
|
case '\v':
|
|
|
|
return '\\' | 'v' << 8;
|
|
|
|
case '\f':
|
|
|
|
return '\\' | 'f' << 8;
|
|
|
|
case '\r':
|
|
|
|
return '\\' | 'r' << 8;
|
|
|
|
case '"':
|
|
|
|
return '\\' | '"' << 8;
|
|
|
|
case '\'':
|
|
|
|
return '\\' | '\'' << 8;
|
|
|
|
case '\\':
|
|
|
|
return '\\' | '\\' << 8;
|
|
|
|
default:
|
|
|
|
if (' ' <= c && c <= '~') {
|
|
|
|
return c;
|
|
|
|
} else {
|
|
|
|
return '\\' | //
|
|
|
|
('0' + (c >> 6)) << 8 | //
|
|
|
|
('0' + ((c >> 3) & 7)) << 16 | //
|
|
|
|
('0' + (c & 7)) << 24;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int __fmt_stoa_byte(out_f out, void *a, uint64_t c) {
|
|
|
|
char buf[1] = {c};
|
|
|
|
return out(buf, a, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int __fmt_stoa_wide(out_f out, void *a, uint64_t w) {
|
|
|
|
char buf[8];
|
|
|
|
if (!isascii(w)) w = _tpenc(w);
|
|
|
|
WRITE64LE(buf, w);
|
|
|
|
return out(buf, a, w ? (_bsr(w) >> 3) + 1 : 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int __fmt_stoa_bing(out_f out, void *a, uint64_t w) {
|
|
|
|
char buf[8];
|
|
|
|
w = _tpenc(kCp437[w & 0xFF]);
|
|
|
|
WRITE64LE(buf, w);
|
|
|
|
return out(buf, a, w ? (_bsr(w) >> 3) + 1 : 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int __fmt_stoa_quoted(out_f out, void *a, uint64_t w) {
|
|
|
|
char buf[8];
|
|
|
|
if (isascii(w)) {
|
|
|
|
w = __fmt_cescapec(w);
|
|
|
|
} else {
|
|
|
|
w = _tpenc(w);
|
|
|
|
}
|
|
|
|
WRITE64LE(buf, w);
|
|
|
|
return out(buf, a, w ? (_bsr(w) >> 3) + 1 : 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Converts string to array.
|
|
|
|
*
|
|
|
|
* 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 __fmt()
|
|
|
|
*/
|
|
|
|
static int __fmt_stoa(int out(const char *, void *, size_t), void *arg,
|
|
|
|
void *data, unsigned long flags, unsigned long precision,
|
|
|
|
unsigned long width, unsigned char signbit,
|
|
|
|
unsigned char qchar) {
|
|
|
|
wint_t wc;
|
|
|
|
unsigned n;
|
|
|
|
emit_f emit;
|
|
|
|
char *p, buf[1];
|
|
|
|
unsigned w, c, pad;
|
|
|
|
bool justdobytes, ignorenul;
|
|
|
|
|
|
|
|
p = data;
|
|
|
|
if (!p) {
|
|
|
|
p = ((flags & FLAGS_REPR) ? "NULL" : "(null)");
|
|
|
|
signbit = 0;
|
|
|
|
flags |= FLAGS_NOQUOTE;
|
|
|
|
if (flags & FLAGS_PRECISION) {
|
|
|
|
precision = min(strlen(p), precision);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ignorenul = false;
|
|
|
|
justdobytes = false;
|
|
|
|
if (signbit == 15 || signbit == 63) {
|
|
|
|
if (flags & FLAGS_QUOTE) {
|
|
|
|
emit = __fmt_stoa_quoted;
|
|
|
|
ignorenul = flags & FLAGS_PRECISION;
|
|
|
|
} else {
|
|
|
|
emit = __fmt_stoa_wide;
|
|
|
|
}
|
2023-07-10 11:29:46 +00:00
|
|
|
} else if (flags & FLAGS_HASH) {
|
2023-06-17 17:13:50 +00:00
|
|
|
justdobytes = true;
|
|
|
|
emit = __fmt_stoa_bing;
|
|
|
|
ignorenul = flags & FLAGS_PRECISION;
|
|
|
|
} else if (flags & FLAGS_QUOTE) {
|
|
|
|
emit = __fmt_stoa_quoted;
|
|
|
|
ignorenul = flags & FLAGS_PRECISION;
|
|
|
|
} else {
|
|
|
|
justdobytes = true;
|
|
|
|
emit = __fmt_stoa_byte;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(flags & FLAGS_PRECISION)) precision = -1;
|
|
|
|
if (!(flags & FLAGS_PRECISION) || !ignorenul) {
|
|
|
|
if (signbit == 63) {
|
|
|
|
precision = wcsnlen((const wchar_t *)p, precision);
|
|
|
|
} else if (signbit == 15) {
|
|
|
|
precision = strnlen16((const char16_t *)p, precision);
|
|
|
|
} else {
|
|
|
|
precision = strnlen(p, precision);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pad = 0;
|
|
|
|
if (width) {
|
|
|
|
w = precision;
|
|
|
|
if (signbit == 63) {
|
|
|
|
w = wcsnwidth((const wchar_t *)p, precision, 0);
|
|
|
|
} else if (signbit == 15) {
|
|
|
|
w = strnwidth16((const char16_t *)p, precision, 0);
|
|
|
|
} else {
|
|
|
|
w = strnwidth(p, precision, 0);
|
|
|
|
}
|
|
|
|
if (!(flags & FLAGS_NOQUOTE) && (flags & FLAGS_REPR)) {
|
|
|
|
w += 2 + (signbit == 63) + (signbit == 15);
|
|
|
|
}
|
|
|
|
if (w < width) {
|
|
|
|
pad = width - w;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pad && !(flags & FLAGS_LEFT)) {
|
|
|
|
if (__fmt_pad(out, arg, pad) == -1) return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(flags & FLAGS_NOQUOTE) && (flags & FLAGS_REPR)) {
|
|
|
|
if (signbit == 63) {
|
|
|
|
if (out("L", arg, 1) == -1) return -1;
|
|
|
|
} else if (signbit == 15) {
|
|
|
|
if (out("u", arg, 1) == -1) return -1;
|
|
|
|
}
|
|
|
|
buf[0] = qchar;
|
|
|
|
if (out(buf, arg, 1) == -1) return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (justdobytes) {
|
|
|
|
while (precision--) {
|
|
|
|
wc = *p++ & 0xff;
|
|
|
|
if (!wc && !ignorenul) break;
|
|
|
|
if (emit(out, arg, wc) == -1) return -1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
while (precision--) {
|
|
|
|
if (signbit == 15) {
|
|
|
|
wc = *(const char16_t *)p;
|
|
|
|
if (!wc && !ignorenul) break;
|
|
|
|
if (IsUcs2(wc)) {
|
|
|
|
p += sizeof(char16_t);
|
|
|
|
} else if (IsUtf16Cont(wc)) {
|
|
|
|
p += sizeof(char16_t);
|
|
|
|
continue;
|
|
|
|
} else if (!precision) {
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
--precision;
|
|
|
|
wc = MergeUtf16(wc, *(const char16_t *)p);
|
|
|
|
}
|
|
|
|
} else if (signbit == 63) {
|
|
|
|
wc = *(const wint_t *)p;
|
|
|
|
if (!wc && !ignorenul) break;
|
|
|
|
p += sizeof(wint_t);
|
|
|
|
if (!wc) break;
|
|
|
|
} else {
|
|
|
|
wc = *p++ & 0xff;
|
|
|
|
if (!wc && !ignorenul) break;
|
|
|
|
if (!isascii(wc)) {
|
|
|
|
if (ThomPikeCont(wc)) continue;
|
|
|
|
n = ThomPikeLen(wc) - 1;
|
|
|
|
wc = ThomPikeByte(wc);
|
|
|
|
if (n > precision) break;
|
|
|
|
precision -= n;
|
|
|
|
while (n--) {
|
|
|
|
wc = ThomPikeMerge(wc, *p++);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (emit(out, arg, wc) == -1) return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(flags & FLAGS_NOQUOTE) && (flags & FLAGS_REPR)) {
|
|
|
|
buf[0] = qchar;
|
|
|
|
if (out(buf, arg, 1) == -1) return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pad && (flags & FLAGS_LEFT)) {
|
|
|
|
if (__fmt_pad(out, arg, pad) == -1) return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __fmt_dfpbits(union U *u, struct FPBits *b) {
|
|
|
|
int ex, i;
|
|
|
|
b->fpi = &kFpiDbl;
|
|
|
|
b->sign = u->ui[1] & 0x80000000L;
|
|
|
|
b->bits[1] = u->ui[1] & 0xfffff;
|
|
|
|
b->bits[0] = u->ui[0];
|
|
|
|
if ((ex = (u->ui[1] & 0x7ff00000L) >> 20) != 0) {
|
|
|
|
if (ex != 0x7ff) {
|
|
|
|
i = STRTOG_Normal;
|
|
|
|
b->bits[1] |= 1 << (52 - 32); // set lowest exponent bit
|
|
|
|
} else if (b->bits[0] | b->bits[1]) {
|
|
|
|
i = STRTOG_NaN;
|
|
|
|
} else {
|
|
|
|
i = STRTOG_Infinite;
|
|
|
|
}
|
|
|
|
} else if (b->bits[0] | b->bits[1]) {
|
|
|
|
i = STRTOG_Denormal;
|
|
|
|
ex = 1;
|
|
|
|
} else {
|
|
|
|
i = STRTOG_Zero;
|
|
|
|
}
|
|
|
|
b->kind = i;
|
|
|
|
b->ex = ex - (0x3ff + 52);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __fmt_ldfpbits(union U *u, struct FPBits *b) {
|
|
|
|
#if LDBL_MANT_DIG == 53
|
|
|
|
__fmt_dfpbits(u, b);
|
|
|
|
#else
|
|
|
|
int ex, i;
|
|
|
|
uint16_t sex;
|
|
|
|
#if LDBL_MANT_DIG == 64
|
|
|
|
b->bits[3] = 0;
|
|
|
|
b->bits[2] = 0;
|
|
|
|
b->bits[1] = ((unsigned)u->us[3] << 16) | u->us[2];
|
|
|
|
b->bits[0] = ((unsigned)u->us[1] << 16) | u->us[0];
|
|
|
|
sex = u->us[4];
|
|
|
|
#elif LDBL_MANT_DIG == 113
|
|
|
|
b->bits[3] = u->ui[3] & 0xffff;
|
|
|
|
b->bits[2] = u->ui[2];
|
|
|
|
b->bits[1] = u->ui[1];
|
|
|
|
b->bits[0] = u->ui[0];
|
|
|
|
sex = u->ui[3] >> 16;
|
|
|
|
#else
|
|
|
|
#error "unsupported architecture"
|
|
|
|
#endif
|
|
|
|
b->fpi = &kFpiLdbl;
|
|
|
|
b->sign = sex & 0x8000;
|
|
|
|
if ((ex = sex & 0x7fff) != 0) {
|
|
|
|
if (ex != 0x7fff) {
|
|
|
|
i = STRTOG_Normal;
|
|
|
|
#if LDBL_MANT_DIG == 113
|
|
|
|
b->bits[3] |= 1 << (112 - 32 * 3); // set lowest exponent bit
|
|
|
|
#endif
|
|
|
|
} else if (b->bits[0] | b->bits[1] | b->bits[2] | b->bits[3]) {
|
|
|
|
i = STRTOG_NaN;
|
|
|
|
} else {
|
|
|
|
i = STRTOG_Infinite;
|
|
|
|
}
|
|
|
|
} else if (b->bits[0] | b->bits[1] | b->bits[2] | b->bits[3]) {
|
|
|
|
i = STRTOG_Denormal;
|
|
|
|
ex = 1;
|
|
|
|
} else {
|
|
|
|
i = STRTOG_Zero;
|
|
|
|
}
|
|
|
|
b->kind = i;
|
|
|
|
b->ex = ex - (0x3fff + (LDBL_MANT_DIG - 1));
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
// returns number of hex digits minus 1, or 0 for zero
|
|
|
|
static int __fmt_fpiprec(struct FPBits *b) {
|
|
|
|
FPI *fpi;
|
|
|
|
int i, j, k, m;
|
|
|
|
uint32_t *bits;
|
|
|
|
if (b->kind == STRTOG_Zero) return (b->ex = 0);
|
|
|
|
fpi = b->fpi;
|
|
|
|
bits = b->bits;
|
|
|
|
for (k = (fpi->nbits - 1) >> 2; k > 0; --k) {
|
|
|
|
if ((bits[k >> 3] >> 4 * (k & 7)) & 0xf) {
|
|
|
|
m = k >> 3;
|
|
|
|
for (i = 0; i <= m; ++i)
|
|
|
|
if (bits[i]) {
|
|
|
|
if (i > 0) {
|
|
|
|
k -= 8 * i;
|
|
|
|
b->ex += 32 * i;
|
|
|
|
for (j = i; j <= m; ++j) {
|
|
|
|
bits[j - i] = bits[j];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
for (i = 0; i < 28 && !((bits[0] >> i) & 0xf); i += 4) donothing;
|
|
|
|
if (i) {
|
|
|
|
b->ex += i;
|
|
|
|
m = k >> 3;
|
|
|
|
k -= (i >> 2);
|
|
|
|
for (j = 0;; ++j) {
|
|
|
|
bits[j] >>= i;
|
|
|
|
if (j == m) break;
|
|
|
|
bits[j] |= bits[j + 1] << (32 - i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return k;
|
|
|
|
}
|
|
|
|
|
|
|
|
// round to prec hex digits after the "."
|
|
|
|
// prec1 = incoming precision (after ".")
|
|
|
|
static int __fmt_bround(struct FPBits *b, int prec, int prec1) {
|
|
|
|
uint32_t *bits, t;
|
|
|
|
int i, inc, j, k, m, n;
|
|
|
|
m = prec1 - prec;
|
|
|
|
bits = b->bits;
|
|
|
|
inc = 0;
|
|
|
|
k = m - 1;
|
|
|
|
if ((t = bits[k >> 3] >> (j = (k & 7) * 4)) & 8) {
|
|
|
|
if (t & 7) goto inc1;
|
|
|
|
if (j && bits[k >> 3] << (32 - j)) goto inc1;
|
|
|
|
while (k >= 8) {
|
|
|
|
k -= 8;
|
|
|
|
if (bits[k >> 3]) {
|
|
|
|
inc1:
|
|
|
|
inc = 1;
|
|
|
|
goto haveinc;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
haveinc:
|
|
|
|
b->ex += m * 4;
|
|
|
|
i = m >> 3;
|
|
|
|
k = prec1 >> 3;
|
|
|
|
j = i;
|
|
|
|
if ((n = 4 * (m & 7)))
|
|
|
|
for (;; ++j) {
|
|
|
|
bits[j - i] = bits[j] >> n;
|
|
|
|
if (j == k) break;
|
|
|
|
bits[j - i] |= bits[j + 1] << (32 - n);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
for (;; ++j) {
|
|
|
|
bits[j - i] = bits[j];
|
|
|
|
if (j == k) break;
|
|
|
|
}
|
|
|
|
k = prec >> 3;
|
|
|
|
if (inc) {
|
|
|
|
for (j = 0; !(++bits[j] & 0xffffffff); ++j) donothing;
|
|
|
|
if (j > k) {
|
|
|
|
onebit:
|
|
|
|
bits[0] = 1;
|
|
|
|
b->ex += 4 * prec;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if ((j = prec & 7) < 7 && bits[k] >> (j + 1) * 4) goto onebit;
|
|
|
|
}
|
|
|
|
for (i = 0; !(bits[i >> 3] & (0xf << 4 * (i & 7))); ++i) donothing;
|
|
|
|
if (i) {
|
|
|
|
b->ex += 4 * i;
|
|
|
|
prec -= i;
|
|
|
|
j = i >> 3;
|
|
|
|
i &= 7;
|
|
|
|
i *= 4;
|
|
|
|
for (m = j;; ++m) {
|
|
|
|
bits[m - j] = bits[m] >> i;
|
|
|
|
if (m == k) break;
|
|
|
|
bits[m - j] |= bits[m + 1] << (32 - i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return prec;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
*
|
|
|
|
* 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 (64-bit)
|
|
|
|
* - `%jjd` int128_t (128-bit)
|
2023-07-09 12:11:25 +00:00
|
|
|
* - `%Lg` long double
|
2023-06-17 17:13:50 +00:00
|
|
|
*
|
|
|
|
* 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)
|
|
|
|
*
|
|
|
|
* This implements most of ANSI C's printf floating-point directives,
|
|
|
|
* omitting L, with %.0g and %.0G giving the shortest decimal string
|
|
|
|
* that rounds to the number being converted, and with negative
|
|
|
|
* precisions allowed for %f.
|
|
|
|
*
|
|
|
|
* @note implementation detail of printf(), snprintf(), etc.
|
|
|
|
* @see printf() for wordier documentation
|
|
|
|
* @note netlib.org is so helpful
|
|
|
|
* @asyncsignalsafe if floating point isn't used
|
|
|
|
* @vforksafe if floating point isn't used
|
|
|
|
*/
|
2023-07-24 15:31:54 +00:00
|
|
|
int __fmt(void *fn, void *arg, const char *format, va_list va) {
|
2023-06-17 17:13:50 +00:00
|
|
|
long ld;
|
|
|
|
void *p;
|
|
|
|
double x;
|
|
|
|
unsigned u;
|
|
|
|
union U un;
|
|
|
|
char ibuf[21];
|
|
|
|
bool longdouble;
|
|
|
|
unsigned long lu;
|
|
|
|
struct FPBits fpb;
|
|
|
|
wchar_t charbuf[1];
|
|
|
|
const char *alphabet;
|
|
|
|
unsigned char signbit, log2base;
|
|
|
|
int (*out)(const char *, void *, size_t);
|
|
|
|
char *se, *s0, *s, *q, qchar, special[8];
|
|
|
|
int d, w, n, sign, prec, flags, width, lasterr;
|
2023-07-10 11:29:46 +00:00
|
|
|
int c, k, i1, ui, bw, rc, bex, sgn, prec1, decpt;
|
2023-06-17 17:13:50 +00:00
|
|
|
|
|
|
|
x = 0;
|
|
|
|
lasterr = errno;
|
|
|
|
out = fn ? fn : (void *)_missingno;
|
|
|
|
|
|
|
|
while (*format) {
|
|
|
|
if (*format != '%') {
|
|
|
|
for (n = 1; format[n]; ++n) {
|
|
|
|
if (format[n] == '%') break;
|
|
|
|
}
|
|
|
|
if (out(format, arg, n) == -1) return -1;
|
|
|
|
format += n;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!IsTiny()) {
|
|
|
|
if (format[1] == 's') { // FAST PATH: PLAIN STRING
|
|
|
|
s = va_arg(va, char *);
|
|
|
|
if (!s) s = "(null)";
|
|
|
|
if (out(s, arg, strlen(s)) == -1) return -1;
|
|
|
|
format += 2;
|
|
|
|
continue;
|
|
|
|
} else if (format[1] == 'd') { // FAST PATH: PLAIN INTEGER
|
|
|
|
d = va_arg(va, int);
|
|
|
|
if (out(ibuf, arg, FormatInt32(ibuf, d) - ibuf) == -1) return -1;
|
|
|
|
format += 2;
|
|
|
|
continue;
|
|
|
|
} else if (format[1] == 'u') { // FAST PATH: PLAIN UNSIGNED
|
|
|
|
u = va_arg(va, unsigned);
|
|
|
|
if (out(ibuf, arg, FormatUint32(ibuf, u) - ibuf) == -1) return -1;
|
|
|
|
format += 2;
|
|
|
|
continue;
|
|
|
|
} else if (format[1] == 'x') { // FAST PATH: PLAIN HEX
|
|
|
|
u = va_arg(va, unsigned);
|
|
|
|
if (out(ibuf, arg, uint64toarray_radix16(u, ibuf)) == -1) return -1;
|
|
|
|
format += 2;
|
|
|
|
continue;
|
|
|
|
} else if (format[1] == 'l' && format[2] == 'x') {
|
|
|
|
lu = va_arg(va, unsigned long); // FAST PATH: PLAIN LONG HEX
|
|
|
|
if (out(ibuf, arg, uint64toarray_radix16(lu, ibuf)) == -1) return -1;
|
|
|
|
format += 3;
|
|
|
|
continue;
|
|
|
|
} else if (format[1] == 'l' && format[2] == 'd') {
|
|
|
|
ld = va_arg(va, long); // FAST PATH: PLAIN LONG
|
|
|
|
if (out(ibuf, arg, FormatInt64(ibuf, ld) - ibuf) == -1) return -1;
|
|
|
|
format += 3;
|
|
|
|
continue;
|
|
|
|
} else if (format[1] == 'l' && format[2] == 'u') {
|
|
|
|
lu = va_arg(va, unsigned long); // FAST PATH: PLAIN UNSIGNED LONG
|
|
|
|
if (out(ibuf, arg, FormatUint64(ibuf, lu) - ibuf) == -1) return -1;
|
|
|
|
format += 3;
|
|
|
|
continue;
|
|
|
|
} else if (format[1] == '.' && format[2] == '*' && format[3] == 's') {
|
|
|
|
n = va_arg(va, unsigned); // FAST PATH: PRECISION STRING
|
|
|
|
s = va_arg(va, const char *);
|
|
|
|
if (s) {
|
|
|
|
n = strnlen(s, n);
|
|
|
|
} else {
|
|
|
|
s = "(null)";
|
|
|
|
n = MIN(6, n);
|
|
|
|
}
|
|
|
|
if (out(s, arg, n) == -1) return -1;
|
|
|
|
format += 4;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// GENERAL PATH
|
|
|
|
format++;
|
|
|
|
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;
|
|
|
|
flags &= ~FLAGS_PRECISION;
|
|
|
|
}
|
|
|
|
|
|
|
|
// evaluate length field
|
|
|
|
signbit = 31;
|
|
|
|
longdouble = false;
|
|
|
|
switch (*format) {
|
|
|
|
case 'j': // intmax_t
|
|
|
|
format++;
|
|
|
|
signbit = sizeof(intmax_t) * 8 - 1;
|
|
|
|
if (*format == 'j') {
|
|
|
|
format++;
|
|
|
|
signbit = sizeof(int128_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++;
|
|
|
|
#if !(LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024)
|
|
|
|
longdouble = true;
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
case 'h':
|
|
|
|
format++;
|
|
|
|
if (*format == 'h') {
|
|
|
|
format++;
|
|
|
|
signbit = 7;
|
|
|
|
} else {
|
|
|
|
signbit = 15;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// evaluate specifier
|
|
|
|
qchar = '"';
|
|
|
|
log2base = 0;
|
|
|
|
alphabet = "0123456789abcdefpx";
|
|
|
|
switch ((d = *format++)) {
|
|
|
|
case 'p':
|
|
|
|
flags |= FLAGS_HASH;
|
|
|
|
log2base = 4;
|
|
|
|
signbit = 63;
|
|
|
|
goto FormatNumber;
|
|
|
|
case 'X':
|
|
|
|
alphabet = "0123456789ABCDEFPX";
|
|
|
|
// fallthrough
|
|
|
|
case 'x':
|
|
|
|
log2base = 4;
|
|
|
|
goto FormatNumber;
|
|
|
|
case 'b':
|
|
|
|
log2base = 1;
|
|
|
|
alphabet = "0123456789abcdefpb";
|
|
|
|
goto FormatNumber;
|
|
|
|
case 'o':
|
|
|
|
log2base = 3;
|
|
|
|
goto FormatNumber;
|
|
|
|
case 'd':
|
|
|
|
case 'i':
|
|
|
|
flags |= FLAGS_ISSIGNED;
|
|
|
|
// fallthrough
|
|
|
|
case 'u': {
|
|
|
|
uint128_t value;
|
|
|
|
flags &= ~FLAGS_HASH; // no hash for dec format
|
|
|
|
FormatNumber:
|
|
|
|
if (signbit > 63) {
|
|
|
|
value = va_arg(va, uint128_t);
|
|
|
|
} else {
|
|
|
|
value = va_arg(va, uint64_t);
|
|
|
|
}
|
|
|
|
if (__fmt_ntoa(out, arg, value, signbit, log2base, prec, width, flags,
|
|
|
|
alphabet) == -1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'c':
|
|
|
|
if ((charbuf[0] = va_arg(va, int))) {
|
|
|
|
p = charbuf;
|
|
|
|
qchar = '\'';
|
|
|
|
flags |= FLAGS_PRECISION;
|
|
|
|
prec = 1;
|
|
|
|
goto FormatString;
|
|
|
|
} else {
|
|
|
|
__FMT_PUT('\0');
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'm':
|
|
|
|
p = strerror(lasterr);
|
|
|
|
signbit = 0;
|
|
|
|
goto FormatString;
|
|
|
|
case 'r':
|
|
|
|
// undocumented %r specifier
|
|
|
|
// used for good carriage return
|
|
|
|
// helps integrate loggers with repls
|
|
|
|
if (!__replstderr) {
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
p = "\r\e[K";
|
|
|
|
goto FormatString;
|
|
|
|
}
|
|
|
|
case 'S':
|
|
|
|
// Specified by POSIX to be equivalent to %ls
|
|
|
|
signbit = 63;
|
|
|
|
goto FormatStringPNotFetchedYet;
|
|
|
|
case 'q':
|
|
|
|
flags |= FLAGS_QUOTE;
|
|
|
|
// fallthrough
|
|
|
|
case 's':
|
|
|
|
FormatStringPNotFetchedYet:
|
|
|
|
p = va_arg(va, void *);
|
|
|
|
FormatString:
|
|
|
|
if (__fmt_stoa(out, arg, p, flags, prec, width, signbit, qchar) == -1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'n':
|
|
|
|
__FMT_PUT('\n');
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'F':
|
|
|
|
case 'f':
|
|
|
|
if (!(flags & FLAGS_PRECISION)) prec = 6;
|
|
|
|
if (!longdouble) {
|
|
|
|
x = va_arg(va, double);
|
|
|
|
s = s0 = dtoa(x, 3, prec, &decpt, &fpb.sign, &se);
|
|
|
|
if (decpt == 9999) {
|
|
|
|
if (s && s[0] == 'N') {
|
|
|
|
fpb.kind = STRTOG_NaN;
|
|
|
|
} else {
|
|
|
|
fpb.kind = STRTOG_Infinite;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
un.ld = va_arg(va, long double);
|
|
|
|
__fmt_ldfpbits(&un, &fpb);
|
|
|
|
s = s0 =
|
|
|
|
gdtoa(fpb.fpi, fpb.ex, fpb.bits, &fpb.kind, 3, prec, &decpt, &se);
|
|
|
|
}
|
|
|
|
if (decpt == 9999) {
|
|
|
|
Format9999:
|
|
|
|
if (s0) freedtoa(s0);
|
|
|
|
bzero(special, sizeof(special));
|
|
|
|
s = q = special;
|
|
|
|
if (fpb.sign) {
|
|
|
|
*q++ = '-';
|
|
|
|
} else if (flags & FLAGS_PLUS) {
|
|
|
|
*q++ = '+';
|
|
|
|
} else if (flags & FLAGS_SPACE) {
|
|
|
|
*q++ = ' ';
|
|
|
|
}
|
|
|
|
memcpy(q, kSpecialFloats[fpb.kind == STRTOG_NaN][d >= 'a'], 4);
|
|
|
|
flags &= ~(FLAGS_PRECISION | FLAGS_PLUS | FLAGS_HASH | FLAGS_SPACE);
|
|
|
|
prec = 0;
|
|
|
|
rc = __fmt_stoa(out, arg, s, flags, prec, width, signbit, qchar);
|
|
|
|
if (rc == -1) return -1;
|
2023-07-10 11:29:46 +00:00
|
|
|
break;
|
2023-06-17 17:13:50 +00:00
|
|
|
}
|
|
|
|
FormatReal:
|
|
|
|
if (fpb.sign /* && (x || sign) */) 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 || (flags & FLAGS_HASH)) --width;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (width > 0 && !(flags & FLAGS_LEFT)) {
|
|
|
|
if ((flags & FLAGS_ZEROPAD)) {
|
|
|
|
if (sign) __FMT_PUT(sign);
|
|
|
|
sign = 0;
|
|
|
|
do __FMT_PUT('0');
|
|
|
|
while (--width > 0);
|
|
|
|
} else
|
|
|
|
do __FMT_PUT(' ');
|
|
|
|
while (--width > 0);
|
|
|
|
}
|
|
|
|
if (sign) __FMT_PUT(sign);
|
|
|
|
if (decpt <= 0) {
|
|
|
|
__FMT_PUT('0');
|
|
|
|
if (prec > 0 || (flags & FLAGS_HASH)) __FMT_PUT('.');
|
|
|
|
while (decpt < 0) {
|
|
|
|
__FMT_PUT('0');
|
|
|
|
prec--;
|
|
|
|
decpt++;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
do {
|
|
|
|
if ((c = *s)) {
|
|
|
|
s++;
|
|
|
|
} else {
|
|
|
|
c = '0';
|
|
|
|
}
|
|
|
|
__FMT_PUT(c);
|
|
|
|
} while (--decpt > 0);
|
|
|
|
if (prec > 0 || (flags & FLAGS_HASH)) __FMT_PUT('.');
|
|
|
|
}
|
|
|
|
while (--prec >= 0) {
|
|
|
|
if ((c = *s)) {
|
|
|
|
s++;
|
|
|
|
} else {
|
|
|
|
c = '0';
|
|
|
|
}
|
|
|
|
__FMT_PUT(c);
|
|
|
|
}
|
|
|
|
while (--width >= 0) __FMT_PUT(' ');
|
|
|
|
if (s0) freedtoa(s0);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'G':
|
|
|
|
case 'g':
|
|
|
|
if (!(flags & FLAGS_PRECISION)) prec = 6;
|
|
|
|
if (prec < 1) prec = 1;
|
|
|
|
if (!longdouble) {
|
|
|
|
x = va_arg(va, double);
|
|
|
|
s = s0 = dtoa(x, 2, prec, &decpt, &fpb.sign, &se);
|
|
|
|
if (decpt == 9999) {
|
|
|
|
if (s && s[0] == 'N') {
|
|
|
|
fpb.kind = STRTOG_NaN;
|
|
|
|
} else {
|
|
|
|
fpb.kind = STRTOG_Infinite;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
un.ld = va_arg(va, long double);
|
|
|
|
__fmt_ldfpbits(&un, &fpb);
|
|
|
|
s = s0 = gdtoa(fpb.fpi, fpb.ex, fpb.bits, &fpb.kind, prec ? 2 : 0,
|
|
|
|
prec, &decpt, &se);
|
|
|
|
}
|
|
|
|
if (decpt == 9999) goto Format9999;
|
|
|
|
c = se - s;
|
|
|
|
prec1 = prec;
|
|
|
|
if (!prec) {
|
|
|
|
prec = c;
|
|
|
|
prec1 = c + (s[1] || (flags & FLAGS_HASH) ? 5 : 4);
|
|
|
|
// %.0g gives 10 rather than 1e1
|
|
|
|
}
|
|
|
|
if (decpt > -4 && decpt <= prec1) {
|
|
|
|
if ((flags & FLAGS_HASH))
|
|
|
|
prec -= decpt;
|
|
|
|
else
|
|
|
|
prec = c - decpt;
|
|
|
|
if (prec < 0) prec = 0;
|
|
|
|
goto FormatReal;
|
|
|
|
}
|
|
|
|
d -= 2;
|
|
|
|
if (!(flags & FLAGS_HASH) && prec > c) prec = c;
|
|
|
|
--prec;
|
|
|
|
goto FormatExpo;
|
|
|
|
|
|
|
|
case 'e':
|
|
|
|
case 'E':
|
|
|
|
if (!(flags & FLAGS_PRECISION)) prec = 6;
|
|
|
|
if (prec < 0) prec = 0;
|
|
|
|
if (!longdouble) {
|
|
|
|
x = va_arg(va, double);
|
|
|
|
s = s0 = dtoa(x, 2, prec + 1, &decpt, &fpb.sign, &se);
|
|
|
|
if (decpt == 9999) {
|
|
|
|
if (s && s[0] == 'N') {
|
|
|
|
fpb.kind = STRTOG_NaN;
|
|
|
|
} else {
|
|
|
|
fpb.kind = STRTOG_Infinite;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
un.ld = va_arg(va, long double);
|
|
|
|
__fmt_ldfpbits(&un, &fpb);
|
|
|
|
s = s0 = gdtoa(fpb.fpi, fpb.ex, fpb.bits, &fpb.kind, prec ? 2 : 0,
|
|
|
|
prec, &decpt, &se);
|
|
|
|
}
|
|
|
|
if (decpt == 9999) goto Format9999;
|
|
|
|
FormatExpo:
|
|
|
|
if (fpb.sign /* && (x || sign) */) sign = '-';
|
|
|
|
if ((width -= prec + 5) > 0) {
|
|
|
|
if (sign) --width;
|
|
|
|
if (prec || (flags & FLAGS_HASH)) --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) __FMT_PUT(sign);
|
|
|
|
sign = 0;
|
|
|
|
do __FMT_PUT('0');
|
|
|
|
while (--width > 0);
|
|
|
|
} else
|
|
|
|
do __FMT_PUT(' ');
|
|
|
|
while (--width > 0);
|
|
|
|
}
|
|
|
|
if (sign) __FMT_PUT(sign);
|
|
|
|
__FMT_PUT(*s++);
|
|
|
|
if (prec || (flags & FLAGS_HASH)) __FMT_PUT('.');
|
|
|
|
while (--prec >= 0) {
|
2023-07-09 12:11:25 +00:00
|
|
|
if ((c = *s)) {
|
2023-06-17 17:13:50 +00:00
|
|
|
s++;
|
2023-07-09 12:11:25 +00:00
|
|
|
} else {
|
2023-06-17 17:13:50 +00:00
|
|
|
c = '0';
|
2023-07-09 12:11:25 +00:00
|
|
|
}
|
2023-06-17 17:13:50 +00:00
|
|
|
__FMT_PUT(c);
|
|
|
|
}
|
|
|
|
__FMT_PUT(d);
|
|
|
|
if (decpt < 0) {
|
|
|
|
__FMT_PUT('-');
|
|
|
|
decpt = -decpt;
|
2023-07-09 12:11:25 +00:00
|
|
|
} else {
|
2023-06-17 17:13:50 +00:00
|
|
|
__FMT_PUT('+');
|
2023-07-09 12:11:25 +00:00
|
|
|
}
|
|
|
|
for (c = 2, k = 10; 10 * k <= decpt; c++, k *= 10) {
|
|
|
|
donothing;
|
|
|
|
}
|
2023-06-17 17:13:50 +00:00
|
|
|
for (;;) {
|
|
|
|
i1 = decpt / k;
|
|
|
|
__FMT_PUT(i1 + '0');
|
|
|
|
if (--c <= 0) break;
|
|
|
|
decpt -= i1 * k;
|
|
|
|
decpt *= 10;
|
|
|
|
}
|
2023-07-09 12:11:25 +00:00
|
|
|
while (--width >= 0) {
|
|
|
|
__FMT_PUT(' ');
|
|
|
|
}
|
2023-06-17 17:13:50 +00:00
|
|
|
freedtoa(s0);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'A':
|
|
|
|
alphabet = "0123456789ABCDEFPX";
|
|
|
|
goto FormatBinary;
|
|
|
|
case 'a':
|
|
|
|
alphabet = "0123456789abcdefpx";
|
|
|
|
FormatBinary:
|
|
|
|
if (longdouble) {
|
|
|
|
un.ld = va_arg(va, long double);
|
|
|
|
__fmt_ldfpbits(&un, &fpb);
|
|
|
|
} else {
|
|
|
|
un.d = va_arg(va, double);
|
|
|
|
__fmt_dfpbits(&un, &fpb);
|
|
|
|
}
|
|
|
|
if (fpb.kind == STRTOG_Infinite || fpb.kind == STRTOG_NaN) {
|
|
|
|
s0 = 0;
|
|
|
|
goto Format9999;
|
|
|
|
}
|
|
|
|
prec1 = __fmt_fpiprec(&fpb);
|
|
|
|
if ((flags & FLAGS_PRECISION) && prec < prec1) {
|
|
|
|
prec1 = __fmt_bround(&fpb, prec, prec1);
|
|
|
|
}
|
|
|
|
bw = 1;
|
|
|
|
bex = fpb.ex + 4 * prec1;
|
|
|
|
if (bex) {
|
|
|
|
if ((i1 = bex) < 0) i1 = -i1;
|
|
|
|
while (i1 >= 10) {
|
|
|
|
++bw;
|
|
|
|
i1 /= 10;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (fpb.sign /* && (sign || fpb.kind != STRTOG_Zero) */) {
|
|
|
|
sign = '-';
|
|
|
|
}
|
|
|
|
if ((width -= bw + 5) > 0) {
|
|
|
|
if (sign) --width;
|
|
|
|
if (prec1 || (flags & FLAGS_HASH)) --width;
|
|
|
|
}
|
2023-07-10 11:29:46 +00:00
|
|
|
if ((width -= MAX(prec, prec1)) > 0 && !(flags & FLAGS_LEFT) &&
|
2023-06-17 17:13:50 +00:00
|
|
|
!(flags & FLAGS_ZEROPAD)) {
|
|
|
|
do __FMT_PUT(' ');
|
|
|
|
while (--width > 0);
|
|
|
|
}
|
|
|
|
if (sign) __FMT_PUT(sign);
|
|
|
|
__FMT_PUT('0');
|
2023-07-09 12:11:25 +00:00
|
|
|
__FMT_PUT(alphabet[17]); // x or X
|
2023-06-17 17:13:50 +00:00
|
|
|
if ((flags & FLAGS_ZEROPAD) && width > 0 && !(flags & FLAGS_LEFT)) {
|
|
|
|
do __FMT_PUT('0');
|
|
|
|
while (--width > 0);
|
|
|
|
}
|
|
|
|
i1 = prec1 & 7;
|
|
|
|
k = prec1 >> 3;
|
|
|
|
__FMT_PUT(alphabet[(fpb.bits[k] >> 4 * i1) & 0xf]);
|
2023-07-09 12:11:25 +00:00
|
|
|
if (prec1 > 0 || prec > 0) {
|
|
|
|
__FMT_PUT('.');
|
|
|
|
}
|
|
|
|
while (prec1 > 0) {
|
|
|
|
if (--i1 < 0) {
|
|
|
|
if (--k < 0) break;
|
|
|
|
i1 = 7;
|
2023-06-17 17:13:50 +00:00
|
|
|
}
|
2023-07-09 12:11:25 +00:00
|
|
|
__FMT_PUT(alphabet[(fpb.bits[k] >> 4 * i1) & 0xf]);
|
|
|
|
--prec1;
|
|
|
|
--prec;
|
|
|
|
}
|
|
|
|
while (prec-- > 0) {
|
|
|
|
__FMT_PUT('0');
|
2023-06-17 17:13:50 +00:00
|
|
|
}
|
2023-07-09 12:11:25 +00:00
|
|
|
__FMT_PUT(alphabet[16]); // p or P
|
2023-06-17 17:13:50 +00:00
|
|
|
if (bex < 0) {
|
|
|
|
__FMT_PUT('-');
|
|
|
|
bex = -bex;
|
2023-07-09 12:11:25 +00:00
|
|
|
} else {
|
2023-06-17 17:13:50 +00:00
|
|
|
__FMT_PUT('+');
|
2023-07-09 12:11:25 +00:00
|
|
|
}
|
|
|
|
for (c = 1; 10 * c <= bex; c *= 10) {
|
|
|
|
donothing;
|
|
|
|
}
|
2023-06-17 17:13:50 +00:00
|
|
|
for (;;) {
|
|
|
|
i1 = bex / c;
|
|
|
|
__FMT_PUT('0' + i1);
|
|
|
|
if (!--bw) break;
|
|
|
|
bex -= i1 * c;
|
|
|
|
bex *= 10;
|
|
|
|
}
|
2023-07-09 12:11:25 +00:00
|
|
|
while (--width >= 0) {
|
|
|
|
__FMT_PUT(' ');
|
|
|
|
}
|
2023-06-17 17:13:50 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case '%':
|
|
|
|
__FMT_PUT('%');
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
__FMT_PUT(format[-1]);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|