diff options
Diffstat (limited to 'lib/libc/stdio/vfwprintf.c')
-rw-r--r-- | lib/libc/stdio/vfwprintf.c | 178 |
1 files changed, 116 insertions, 62 deletions
diff --git a/lib/libc/stdio/vfwprintf.c b/lib/libc/stdio/vfwprintf.c index 28a87f1..054299a 100644 --- a/lib/libc/stdio/vfwprintf.c +++ b/lib/libc/stdio/vfwprintf.c @@ -74,6 +74,14 @@ static wchar_t *__mbsconv(char *, int); #define CHAR wchar_t #include "printfcommon.h" +struct grouping_state { + wchar_t thousands_sep; /* locale-specific thousands separator */ + const char *grouping; /* locale-specific numeric grouping rules */ + int lead; /* sig figs before decimal or group sep */ + int nseps; /* number of group separators with ' */ + int nrepeats; /* number of repeats of the last group */ +}; + static const mbstate_t initial_mbs; static inline wchar_t @@ -90,6 +98,79 @@ get_decpt(void) return (decpt); } +static inline wchar_t +get_thousep(void) +{ + mbstate_t mbs; + wchar_t thousep; + int nconv; + + mbs = initial_mbs; + nconv = mbrtowc(&thousep, localeconv()->thousands_sep, + MB_CUR_MAX, &mbs); + if (nconv == (size_t)-1 || nconv == (size_t)-2) + thousep = '\0'; /* failsafe */ + return (thousep); +} + +/* + * Initialize the thousands' grouping state in preparation to print a + * number with ndigits digits. This routine returns the total number + * of wide characters that will be printed. + */ +static int +grouping_init(struct grouping_state *gs, int ndigits) +{ + + gs->grouping = localeconv()->grouping; + gs->thousands_sep = get_thousep(); + + gs->nseps = gs->nrepeats = 0; + gs->lead = ndigits; + while (*gs->grouping != CHAR_MAX) { + if (gs->lead <= *gs->grouping) + break; + gs->lead -= *gs->grouping; + if (*(gs->grouping+1)) { + gs->nseps++; + gs->grouping++; + } else + gs->nrepeats++; + } + return (gs->nseps + gs->nrepeats); +} + +/* + * Print a number with thousands' separators. + */ +static int +grouping_print(struct grouping_state *gs, struct io_state *iop, + const CHAR *cp, const CHAR *ep) +{ + const CHAR *cp0 = cp; + + if (io_printandpad(iop, cp, ep, gs->lead, zeroes)) + return (-1); + cp += gs->lead; + while (gs->nseps > 0 || gs->nrepeats > 0) { + if (gs->nrepeats > 0) + gs->nrepeats--; + else { + gs->grouping--; + gs->nseps--; + } + if (io_print(iop, &gs->thousands_sep, 1)) + return (-1); + if (io_printandpad(iop, cp, ep, *gs->grouping, zeroes)) + return (-1); + cp += *gs->grouping; + } + if (cp > ep) + cp = ep; + return (cp - cp0); +} + + /* * Flush out all the vectors defined by the given uio, * then reset it so that it can be reused. @@ -280,12 +361,14 @@ vfwprintf(FILE * __restrict fp, const wchar_t * __restrict fmt0, va_list ap) /* * The size of the buffer we use as scratch space for integer - * conversions, among other things. Technically, we would need the - * most space for base 10 conversions with thousands' grouping - * characters between each pair of digits. 100 bytes is a - * conservative overestimate even for a 128-bit uintmax_t. + * conversions, among other things. We need enough space to + * write a uintmax_t in octal (plus one byte). */ -#define BUF 100 +#if UINTMAX_MAX <= UINT64_MAX +#define BUF 32 +#else +#error "BUF must be large enough to format a uintmax_t" +#endif /* * Non-MT-safe version @@ -302,8 +385,7 @@ __vfwprintf(FILE *fp, const wchar_t *fmt0, va_list ap) int width; /* width from format (%8d), or 0 */ int prec; /* precision from format; <0 for N/A */ wchar_t sign; /* sign prefix (' ', '+', '-', or \0) */ - wchar_t thousands_sep; /* locale specific thousands separator */ - const char *grouping; /* locale specific numeric grouping rules */ + struct grouping_state gs; /* thousands' grouping info */ #ifndef NO_FLOATING_POINT /* * We can decompose the printed representation of floating @@ -329,12 +411,9 @@ __vfwprintf(FILE *fp, const wchar_t *fmt0, va_list ap) char expchar; /* exponent character: [eEpP\0] */ char *dtoaend; /* pointer to end of converted digits */ int expsize; /* character count for expstr */ - int lead; /* sig figs before decimal or group sep */ int ndig; /* actual number of digits returned by dtoa */ wchar_t expstr[MAXEXPDIG+2]; /* buffer for exponent string: e+ZZZ */ char *dtoaresult; /* buffer allocated by dtoa */ - int nseps; /* number of group separators with ' */ - int nrepeats; /* number of repeats of the last group */ #endif u_long ulval; /* integer arguments %[diouxX] */ uintmax_t ujval; /* %j, %ll, %q, %t, %z integers */ @@ -442,8 +521,6 @@ __vfwprintf(FILE *fp, const wchar_t *fmt0, va_list ap) if (prepwrite(fp) != 0) return (EOF); - thousands_sep = '\0'; - grouping = NULL; convbuf = NULL; fmt = (wchar_t *)fmt0; argtable = NULL; @@ -477,6 +554,7 @@ __vfwprintf(FILE *fp, const wchar_t *fmt0, va_list ap) dprec = 0; width = 0; prec = -1; + gs.grouping = NULL; sign = '\0'; ox[1] = '\0'; @@ -514,8 +592,6 @@ reswitch: switch (ch) { goto rflag; case '\'': flags |= GROUPING; - thousands_sep = *(localeconv()->thousands_sep); - grouping = localeconv()->grouping; goto rflag; case '.': if ((ch = *fmt++) == '*') { @@ -739,23 +815,8 @@ fp_common: /* space for decimal pt and following digits */ if (prec || flags & ALT) size += prec + 1; - if (grouping && expt > 0) { - /* space for thousands' grouping */ - nseps = nrepeats = 0; - lead = expt; - while (*grouping != CHAR_MAX) { - if (lead <= *grouping) - break; - lead -= *grouping; - if (*(grouping+1)) { - nseps++; - grouping++; - } else - nrepeats++; - } - size += nseps + nrepeats; - } else - lead = expt; + if ((flags & GROUPING) && expt > 0) + size += grouping_init(&gs, expt); } break; #endif /* !NO_FLOATING_POINT */ @@ -899,20 +960,18 @@ number: if ((dprec = prec) >= 0) if (ujval != 0 || prec != 0 || (flags & ALT && base == 8)) cp = __ujtoa(ujval, cp, base, - flags & ALT, xdigs, - flags & GROUPING, thousands_sep, - grouping); + flags & ALT, xdigs); } else { if (ulval != 0 || prec != 0 || (flags & ALT && base == 8)) cp = __ultoa(ulval, cp, base, - flags & ALT, xdigs, - flags & GROUPING, thousands_sep, - grouping); + flags & ALT, xdigs); } size = buf + BUF - cp; if (size > BUF) /* should never happen */ abort(); + if ((flags & GROUPING) && size != 0) + size += grouping_init(&gs, size); break; default: /* "%?" prints ?, unless ? is NUL */ if (ch == '\0') @@ -968,13 +1027,19 @@ number: if ((dprec = prec) >= 0) if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) PAD(width - realsz, zeroes); - /* leading zeroes from decimal precision */ - PAD(dprec - size, zeroes); - /* the string or number proper */ #ifndef NO_FLOATING_POINT if ((flags & FPT) == 0) { - PRINT(cp, size); +#endif + /* leading zeroes from decimal precision */ + PAD(dprec - size, zeroes); + if (gs.grouping) { + if (grouping_print(&gs, &io, cp, buf+BUF) < 0) + goto error; + } else { + PRINT(cp, size); + } +#ifndef NO_FLOATING_POINT } else { /* glue together f_p fragments */ if (!expchar) { /* %[fF] or sufficiently short %[gG] */ if (expt <= 0) { @@ -985,25 +1050,16 @@ number: if ((dprec = prec) >= 0) /* already handled initial 0's */ prec += expt; } else { - PRINTANDPAD(cp, convbuf + ndig, lead, zeroes); - cp += lead; - if (grouping) { - while (nseps>0 || nrepeats>0) { - if (nrepeats > 0) - nrepeats--; - else { - grouping--; - nseps--; - } - PRINT(&thousands_sep, - 1); - PRINTANDPAD(cp, - convbuf + ndig, - *grouping, zeroes); - cp += *grouping; - } - if (cp > convbuf + ndig) - cp = convbuf + ndig; + if (gs.grouping) { + n = grouping_print(&gs, &io, + cp, convbuf + ndig); + if (n < 0) + goto error; + cp += n; + } else { + PRINTANDPAD(cp, convbuf + ndig, + expt, zeroes); + cp += expt; } if (prec || flags & ALT) PRINT(&decimal_point, 1); @@ -1021,8 +1077,6 @@ number: if ((dprec = prec) >= 0) PRINT(expstr, expsize); } } -#else - PRINT(cp, size); #endif /* left-adjusting padding (always blank) */ if (flags & LADJUST) |