diff options
author | tjr <tjr@FreeBSD.org> | 2003-04-07 06:36:49 +0000 |
---|---|---|
committer | tjr <tjr@FreeBSD.org> | 2003-04-07 06:36:49 +0000 |
commit | 7714c81b08deffcc9474cbef172aa6762d58b11e (patch) | |
tree | e7d90196703c8690008b6d54f43a24a82242fd6b /lib/libc/stdio | |
parent | 160e84fba339ff29085f83dd2c3462ce17e461d6 (diff) | |
download | FreeBSD-src-7714c81b08deffcc9474cbef172aa6762d58b11e.zip FreeBSD-src-7714c81b08deffcc9474cbef172aa6762d58b11e.tar.gz |
Catch up with recent vfprintf.c changes.
Diffstat (limited to 'lib/libc/stdio')
-rw-r--r-- | lib/libc/stdio/vfwprintf.c | 390 |
1 files changed, 215 insertions, 175 deletions
diff --git a/lib/libc/stdio/vfwprintf.c b/lib/libc/stdio/vfwprintf.c index 658f879..3feacf2 100644 --- a/lib/libc/stdio/vfwprintf.c +++ b/lib/libc/stdio/vfwprintf.c @@ -39,7 +39,7 @@ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)vfprintf.c 8.1 (Berkeley) 6/4/93"; #endif /* LIBC_SCCS and not lint */ -__FBSDID("FreeBSD: src/lib/libc/stdio/vfprintf.c,v 1.52 2003/03/14 04:48:09 das Exp"); +__FBSDID("FreeBSD: src/lib/libc/stdio/vfprintf.c,v 1.57 2003/04/07 03:17:39 ache Exp"); #endif __FBSDID("$FreeBSD$"); @@ -114,10 +114,10 @@ enum typeid { }; static int __sbprintf(FILE *, const wchar_t *, va_list); -static wchar_t *__ujtoa(uintmax_t, wchar_t *, int, int, wchar_t *, int, char, - const char *); -static wchar_t *__ultoa(u_long, wchar_t *, int, int, wchar_t *, int, char, - const char *); +static wchar_t *__ujtoa(uintmax_t, wchar_t *, int, int, const wchar_t *, int, + char, const char *); +static wchar_t *__ultoa(u_long, wchar_t *, int, int, const wchar_t *, int, + char, const char *); static wchar_t *__mbsconv(char *, int); static void __find_arguments(const wchar_t *, va_list, union arg **); static void __grow_type_table(int, enum typeid **, int *); @@ -169,7 +169,7 @@ __sbprintf(FILE *fp, const wchar_t *fmt, va_list ap) * use the given digits. */ static wchar_t * -__ultoa(u_long val, wchar_t *endp, int base, int octzero, wchar_t *xdigs, +__ultoa(u_long val, wchar_t *endp, int base, int octzero, const wchar_t *xdigs, int needgrp, char thousep, const char *grp) { wchar_t *cp = endp; @@ -246,8 +246,8 @@ __ultoa(u_long val, wchar_t *endp, int base, int octzero, wchar_t *xdigs, /* Identical to __ultoa, but for intmax_t. */ static wchar_t * -__ujtoa(uintmax_t val, wchar_t *endp, int base, int octzero, wchar_t *xdigs, - int needgrp, char thousep, const char *grp) +__ujtoa(uintmax_t val, wchar_t *endp, int base, int octzero, + const wchar_t *xdigs, int needgrp, char thousep, const char *grp) { wchar_t *cp = endp; intmax_t sval; @@ -330,6 +330,9 @@ __mbsconv(char *mbsarg, int prec) size_t insize, nchars, nconv; mbstate_t mbs; + if (mbsarg == NULL) + return (NULL); + /* * Supplied argument is a multibyte string; convert it to wide * characters first. @@ -400,16 +403,17 @@ vfwprintf(FILE * __restrict fp, const wchar_t * __restrict fmt0, va_list ap) } #ifdef FLOATING_POINT + +#define dtoa __dtoa +#define freedtoa __freedtoa + #include <float.h> #include <math.h> #include "floatio.h" +#include "gdtoa.h" #define DEFPREC 6 -extern char *__dtoa(double, int, int, int *, int *, char **); -extern void __freedtoa(char *s); - -static wchar_t *cvt(double, int, int, char *, int *, wchar_t, int *); static int exponent(wchar_t *, int, wchar_t); #endif /* FLOATING_POINT */ @@ -429,7 +433,6 @@ static int exponent(wchar_t *, int, wchar_t); * Flags used during conversion. */ #define ALT 0x001 /* alternate form */ -#define HEXPREFIX 0x002 /* add 0x or 0X prefix */ #define LADJUST 0x004 /* left adjustment */ #define LONGDBL 0x008 /* long double */ #define LONGINT 0x010 /* long integer */ @@ -452,24 +455,46 @@ __vfwprintf(FILE *fp, const wchar_t *fmt0, va_list ap) { wchar_t *fmt; /* format string */ wchar_t ch; /* character from fmt */ - int n, n2; /* handy integer (short term usage) */ + int n, n2, n3; /* handy integer (short term usage) */ wchar_t *cp; /* handy char pointer (short term usage) */ int flags; /* flags as above */ int ret; /* return value accumulator */ int width; /* width from format (%8d), or 0 */ - int prec; /* precision from format (%.3d), or -1 */ + int prec; /* precision from format; <0 for N/A */ wchar_t sign; /* sign prefix (' ', '+', '-', or \0) */ char thousands_sep; /* locale specific thousands separator */ const char *grouping; /* locale specific numeric grouping rules */ #ifdef FLOATING_POINT + /* + * We can decompose the printed representation of floating + * point numbers into several parts, some of which may be empty: + * + * [+|-| ] [0x|0X] MMM . NNN [e|E|p|P] [+|-] ZZ + * A B ---C--- D E F + * + * A: 'sign' holds this value if present; '\0' otherwise + * B: ox[1] holds the 'x' or 'X'; '\0' if not hexadecimal + * C: cp points to the string MMMNNN. Leading and trailing + * zeros are not in the string and must be added. + * D: expchar holds this character; '\0' if no exponent, e.g. %f + * F: at least two digits for decimal, at least one digit for hex + */ char *decimal_point; /* locale specific decimal point */ - char softsign; /* temporary negative sign for floats */ - double _double; /* double precision arguments %[eEfgG] */ + int signflag; /* true if float is negative */ + union { /* floating point arguments %[aAeEfFgG] */ + double dbl; + long double ldbl; + } fparg; int expt; /* integer value of exponent */ + char expchar; /* exponent character: [eEpP\0] */ + char *dtoaend; /* pointer to end of converted digits */ int expsize; /* character count for expstr */ - int ndig; /* actual number of digits returned by cvt */ - wchar_t expstr[7]; /* buffer for exponent string */ - wchar_t *dtoaresult; /* buffer allocated by dtoa */ + 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 */ @@ -478,7 +503,7 @@ __vfwprintf(FILE *fp, const wchar_t *fmt0, va_list ap) int realsz; /* field size expanded by dprec, sign, etc */ int size; /* size of converted field or string */ int prsize; /* max size of printed field */ - wchar_t *xdigs; /* digits for [xX] conversion */ + const wchar_t *xdigs; /* digits for [xX] conversion */ wchar_t buf[BUF]; /* buffer with space for digits of uintmax_t */ wchar_t ox[2]; /* space for 0x hex-prefix */ union arg *argtable; /* args, built due to positional arg */ @@ -498,13 +523,16 @@ __vfwprintf(FILE *fp, const wchar_t *fmt0, va_list ap) static wchar_t zeroes[PADSIZE] = {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'}; + static const wchar_t xdigs_lower[16] = L"0123456789abcdef"; + static const wchar_t xdigs_upper[16] = L"0123456789ABCDEF"; + /* * BEWARE, these `goto error' on error, PRINT uses `n2' and * PAD uses `n'. */ #define PRINT(ptr, len) do { \ - for (n2 = 0; n2 < (len); n2++) \ - __fputwc((ptr)[n2], fp); \ + for (n3 = 0; n3 < (len); n3++) \ + __fputwc((ptr)[n3], fp); \ } while (0) #define PAD(howmany, with) do { \ if ((n = (howmany)) > 0) { \ @@ -515,6 +543,14 @@ __vfwprintf(FILE *fp, const wchar_t *fmt0, va_list ap) PRINT(with, n); \ } \ } while (0) +#define PRINTANDPAD(p, ep, len, with) do { \ + n2 = (ep) - (p); \ + if (n2 > (len)) \ + n2 = (len); \ + if (n2 > 0) \ + PRINT((p), n2); \ + PAD((len) - (n2 > 0 ? n2 : 0), (with)); \ +} while(0) /* * Get the argument indexed by nextarg. If the argument table is @@ -580,7 +616,6 @@ __vfwprintf(FILE *fp, const wchar_t *fmt0, va_list ap) thousands_sep = '\0'; grouping = NULL; #ifdef FLOATING_POINT - dtoaresult = NULL; decimal_point = localeconv()->decimal_point; #endif convbuf = NULL; @@ -622,6 +657,7 @@ __vfwprintf(FILE *fp, const wchar_t *fmt0, va_list ap) width = 0; prec = -1; sign = '\0'; + ox[1] = '\0'; rflag: ch = *fmt++; reswitch: switch (ch) { @@ -662,16 +698,14 @@ reswitch: switch (ch) { goto rflag; case '.': if ((ch = *fmt++) == '*') { - GETASTER (n); - prec = n < 0 ? -1 : n; + GETASTER (prec); goto rflag; } - n = 0; + prec = 0; while (is_digit(ch)) { - n = 10 * n + to_digit(ch); + prec = 10 * prec + to_digit(ch); ch = *fmt++; } - prec = n < 0 ? -1 : n; goto reswitch; case '0': /*- @@ -765,86 +799,128 @@ reswitch: switch (ch) { #ifdef HEXFLOAT case 'a': case 'A': + if (ch == 'a') { + ox[1] = 'x'; + xdigs = xdigs_lower; + expchar = 'p'; + } else { + ox[1] = 'X'; + xdigs = xdigs_upper; + expchar = 'P'; + } + /* + * XXX We don't actually have a conversion + * XXX routine for this yet. + */ + if (flags & LONGDBL) { + fparg.ldbl = (double)GETARG(long double); + dtoaresult = + __hldtoa(fparg.ldbl, xdigs, prec, + &expt, &signflag, &dtoaend); + } else { + fparg.dbl = GETARG(double); + dtoaresult = + __hdtoa(fparg.dbl, xdigs, prec, + &expt, &signflag, &dtoaend); + } + if (convbuf != NULL) + free(convbuf); + cp = convbuf = __mbsconv(dtoaresult, -1); + freedtoa(dtoaresult); + goto fp_begin; #endif case 'e': case 'E': - /*- - * Grouping apply to %i, %d, %u, %f, %F, %g, %G - * conversion specifiers only. For other conversions - * behavior is undefined. - * -- POSIX - */ - flags &= ~GROUPING; - /*FALLTHROUGH*/ + expchar = ch; + if (prec < 0) /* account for digit before decpt */ + prec = DEFPREC + 1; + else + prec++; + goto fp_begin; case 'f': case 'F': + expchar = '\0'; goto fp_begin; case 'g': case 'G': + expchar = ch - ('g' - 'e'); if (prec == 0) prec = 1; -fp_begin: if (prec == -1) +fp_begin: + if (prec < 0) prec = DEFPREC; - if (flags & LONGDBL) - /* XXX this loses precision. */ - _double = (double)GETARG(long double); - else - _double = GETARG(double); - /* do this before tricky precision changes */ - if (isinf(_double)) { - if (_double < 0) - sign = '-'; - if (iswupper(ch)) - cp = L"INF"; - else - cp = L"inf"; - size = 3; - break; + if (convbuf != NULL) + free(convbuf); + if (flags & LONGDBL) { + fparg.ldbl = GETARG(long double); + dtoaresult = + __ldtoa(&fparg.ldbl, expchar ? 2 : 3, prec, + &expt, &signflag, &dtoaend); + } else { + fparg.dbl = GETARG(double); + dtoaresult = + dtoa(fparg.dbl, expchar ? 2 : 3, prec, + &expt, &signflag, &dtoaend); + if (expt == 9999) + expt = INT_MAX; } - if (isnan(_double)) { - if (iswupper(ch)) - cp = L"NAN"; - else - cp = L"nan"; + ndig = dtoaend - dtoaresult; + cp = convbuf = __mbsconv(dtoaresult, -1); + freedtoa(dtoaresult); + if (signflag) + sign = '-'; + if (expt == INT_MAX) { /* inf or nan */ + if (*cp == 'N') { + cp = (ch >= 'a') ? L"nan" : L"NAN"; + sign = '\0'; + } else + cp = (ch >= 'a') ? L"inf" : L"INF"; size = 3; break; } flags |= FPT; - if (dtoaresult != NULL) { - free(dtoaresult); - dtoaresult = NULL; - } - dtoaresult = cp = cvt(_double, prec, flags, &softsign, - &expt, ch, &ndig); if (ch == 'g' || ch == 'G') { - if (expt <= -4 || expt > prec) - ch = (ch == 'g') ? 'e' : 'E'; - else - ch = 'g'; + if (expt > -4 && expt <= prec) { + /* Make %[gG] smell like %[fF] */ + expchar = '\0'; + if (flags & ALT) + prec -= expt; + else + prec = ndig - expt; + if (prec < 0) + prec = 0; + } } - if (ch == 'e' || ch == 'E') { - --expt; - expsize = exponent(expstr, expt, ch); - size = expsize + ndig; - if (ndig > 1 || flags & ALT) + if (expchar) { + expsize = exponent(expstr, expt - 1, expchar); + size = expsize + prec; + if (prec > 1 || flags & ALT) ++size; - } else if (ch == 'f' || ch == 'F') { + } else { if (expt > 0) { size = expt; if (prec || flags & ALT) size += prec + 1; } else /* "0.X" */ size = prec + 2; - } else if (expt >= ndig) { /* fixed g fmt */ - size = expt; - if (flags & ALT) - ++size; - } else - size = ndig + (expt > 0 ? - 1 : 2 - expt); - - if (softsign) - sign = '-'; + 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; + } break; #endif /* FLOATING_POINT */ case 'n': @@ -890,9 +966,9 @@ fp_begin: if (prec == -1) */ ujval = (uintmax_t)(uintptr_t)GETARG(void *); base = 16; - xdigs = L"0123456789abcdef"; - flags = flags | INTMAXT | HEXPREFIX; - ch = 'x'; + xdigs = xdigs_lower; + flags = flags | INTMAXT; + ox[1] = 'x'; goto nosign; case 'S': flags |= LONGINT; @@ -947,10 +1023,10 @@ fp_begin: if (prec == -1) base = 10; goto nosign; case 'X': - xdigs = L"0123456789ABCDEF"; + xdigs = xdigs_upper; goto hex; case 'x': - xdigs = L"0123456789abcdef"; + xdigs = xdigs_lower; hex: if (flags & INTMAX_SIZE) ujval = UJARG(); @@ -960,7 +1036,7 @@ hex: /* leading 0x/X only if non-zero */ if (flags & ALT && (flags & INTMAX_SIZE ? ujval != 0 : ulval != 0)) - flags |= HEXPREFIX; + ox[1] = ch; flags &= ~GROUPING; /* unsigned conversions */ @@ -1024,7 +1100,7 @@ number: if ((dprec = prec) >= 0) realsz = dprec > size ? dprec : size; if (sign) realsz++; - else if (flags & HEXPREFIX) + else if (ox[1]) realsz += 2; prsize = width > realsz ? width : realsz; @@ -1040,9 +1116,8 @@ number: if ((dprec = prec) >= 0) /* prefix */ if (sign) { PRINT(&sign, 1); - } else if (flags & HEXPREFIX) { + } else if (ox[1]) { /* ox[1] is either x, X, or \0 */ ox[0] = '0'; - ox[1] = ch; PRINT(ox, 2); } @@ -1058,40 +1133,48 @@ number: if ((dprec = prec) >= 0) if ((flags & FPT) == 0) { PRINT(cp, size); } else { /* glue together f_p fragments */ - if (ch >= 'f') { /* 'f' or 'g' */ - if (_double == 0) { - /* kludge for __dtoa irregularity */ - PRINT(L"0", 1); - if (expt < ndig || (flags & ALT) != 0) { - PRINT(decimal_point, 1); - PAD(ndig - 1, zeroes); - } - } else if (expt <= 0) { - PRINT(L"0", 1); - PRINT(decimal_point, 1); + if (!expchar) { /* %[fF] or sufficiently short %[gG] */ + if (expt <= 0) { + buf[0] = '0'; + buf[1] = *decimal_point; + PRINT(buf, 2); PAD(-expt, zeroes); - PRINT(cp, ndig); - } else if (expt >= ndig) { - PRINT(cp, ndig); - PAD(expt - ndig, zeroes); - if (flags & ALT) - PRINT(decimal_point, 1); + /* already handled initial 0's */ + prec += expt; } else { - PRINT(cp, expt); - cp += expt; - PRINT(decimal_point, 1); - PRINT(cp, ndig-expt); + 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 (prec || flags & ALT) { + buf[0] = *decimal_point; + PRINT(buf, 1); + } } - } else { /* 'e' or 'E' */ - if (ndig > 1 || flags & ALT) { - ox[0] = *cp++; - ox[1] = *decimal_point; - PRINT(ox, 2); - if (_double) { - PRINT(cp, ndig-1); - } else /* 0.[0..] */ - /* __dtoa irregularity */ - PAD(ndig - 1, zeroes); + PRINTANDPAD(cp, convbuf + ndig, prec, zeroes); + } else { /* %[eE] or sufficiently long %[gG] */ + if (prec > 1 || flags & ALT) { + buf[0] = *cp++; + buf[1] = *decimal_point; + PRINT(buf, 2); + PRINT(cp, ndig-1); + PAD(prec - ndig, zeroes); } else /* XeYYY */ PRINT(cp, 1); PRINT(expstr, expsize); @@ -1109,10 +1192,6 @@ number: if ((dprec = prec) >= 0) } done: error: -#ifdef FLOATING_POINT - if (dtoaresult != NULL) - free(dtoaresult); -#endif if (convbuf != NULL) free(convbuf); if (__sferror(fp)) @@ -1481,52 +1560,6 @@ __grow_type_table (int nextarg, enum typeid **typetable, int *tablesize) #ifdef FLOATING_POINT -static wchar_t * -cvt(double value, int ndigits, int flags, char *sign, int *decpt, - wchar_t ch, int *length) -{ - int i, mode, dsgn; - wchar_t *digits, *bp, *result, *rve; - char *tresult, *trve; - - if (ch == 'f') - mode = 3; /* ndigits after the decimal point */ - else { - /* - * To obtain ndigits after the decimal point for the 'e' - * and 'E' formats, round to ndigits + 1 significant - * figures. - */ - if (ch == 'e' || ch == 'E') - ndigits++; - mode = 2; /* ndigits significant digits */ - } - tresult = __dtoa(value, mode, ndigits, decpt, &dsgn, &trve); - if ((result = malloc((trve - tresult + 1) * sizeof(*result))) == NULL) - abort(); /* XXX handle better */ - for (i = 0; i < trve - tresult + 1; i++) - result[i] = (wchar_t)(unsigned char)tresult[i]; - rve = result + (trve - tresult); - __freedtoa(tresult); - digits = result; - *sign = dsgn != 0; - if ((ch != 'g' && ch != 'G') || flags & ALT) { - /* print trailing zeros */ - bp = digits + ndigits; - if (ch == 'f') { - if ((*digits == '0' || *digits == '\0') && value) - *decpt = -ndigits + 1; - bp += *decpt; - } - if (value == 0) /* kludge for __dtoa irregularity */ - rve = bp; - while (rve < bp) - *rve++ = '0'; - } - *length = rve - digits; - return (digits); -} - static int exponent(wchar_t *p0, int exp, wchar_t fmtch) { @@ -1550,7 +1583,14 @@ exponent(wchar_t *p0, int exp, wchar_t fmtch) for (; t < expbuf + MAXEXPDIG; *p++ = *t++); } else { - *p++ = '0'; + /* + * Exponents for decimal floating point conversions + * (%[eEgG]) must be at least two characters long, + * whereas exponents for hexadecimal conversions can + * be only one character long. + */ + if (fmtch == 'e' || fmtch == 'E') + *p++ = '0'; *p++ = to_char(exp); } return (p - p0); |