diff options
author | Corinna Vinschen <corinna@vinschen.de> | 2010-02-05 08:35:35 +0000 |
---|---|---|
committer | Corinna Vinschen <corinna@vinschen.de> | 2010-02-05 08:35:35 +0000 |
commit | b0776ebd41456fba102190420bc64b74f0baa029 (patch) | |
tree | 8da6ec7485d73ad163e18ea3151ff0af0b1d7bbc /newlib/libc/stdio/vfprintf.c | |
parent | 6e3f2c626d965a8de228372b819fc65f0ba6da64 (diff) | |
download | newlib-b0776ebd41456fba102190420bc64b74f0baa029.zip newlib-b0776ebd41456fba102190420bc64b74f0baa029.tar.gz newlib-b0776ebd41456fba102190420bc64b74f0baa029.tar.bz2 |
* libc/stdio/vfprintf.c: Include locale.h also if _WANT_IO_C99_FORMATS
is defined. Raise conversion buffer size to make sure it has enough
room for numbers plus grouping character. Define GROUPING flag.
(_VFPRINTF_R): Add PRINTANDPAD macro. Handle grouping flag character.
Handle grouping for decimal integer and float values.
* libc/stdio/vfwprintf.c: Ditto.
Diffstat (limited to 'newlib/libc/stdio/vfprintf.c')
-rw-r--r-- | newlib/libc/stdio/vfprintf.c | 187 |
1 files changed, 150 insertions, 37 deletions
diff --git a/newlib/libc/stdio/vfprintf.c b/newlib/libc/stdio/vfprintf.c index fc33c30..be4170c 100644 --- a/newlib/libc/stdio/vfprintf.c +++ b/newlib/libc/stdio/vfprintf.c @@ -375,8 +375,10 @@ _DEFUN(__sbprintf, (rptr, fp, fmt, ap), #endif /* !STRING_ONLY */ -#ifdef FLOATING_POINT +#if defined (FLOATING_POINT) || defined (_WANT_IO_C99_FORMATS) # include <locale.h> +#endif +#ifdef FLOATING_POINT # include <math.h> /* For %La, an exponent of 15 bits occupies the exponent character, a @@ -423,8 +425,16 @@ static int exponent(char *, int, int); reentrant storage shared with mprec. All other formats that use buf get by with fewer characters. Making BUF slightly bigger reduces the need for malloc in %.*a and %S, when large precision or - long strings are processed. */ + long strings are processed. + The bigger size of 100 bytes is used on systems which allow number + strings using the locale's grouping character. Since that's a multibyte + value, we should use a conservative value. + */ +#ifdef _WANT_IO_C99_FORMATS +#define BUF 100 +#else #define BUF 40 +#endif #if defined _MB_CAPABLE && MB_LEN_MAX > BUF # undef BUF # define BUF MB_LEN_MAX @@ -508,6 +518,9 @@ _EXFUN(get_arg, (struct _reent *data, int n, char *fmt, #else /* define as 0, to make SARG and UARG occupy fewer instructions */ # define CHARINT 0 #endif +#ifdef _WANT_IO_C99_FORMATS +# define GROUPING 0x400 /* use grouping ("'" flag) */ +#endif int _EXFUN(_VFPRINTF_R, (struct _reent *, FILE *, _CONST char *, va_list)); @@ -552,6 +565,12 @@ _DEFUN(_VFPRINTF_R, (data, fp, fmt0, ap), int width; /* width from format (%8d), or 0 */ int prec; /* precision from format (%.3d), or -1 */ char sign; /* sign prefix (' ', '+', '-', or \0) */ +#ifdef _WANT_IO_C99_FORMATS + /* locale specific numeric grouping */ + char *thousands_sep; + size_t thsnd_len; + const char *grouping; +#endif #ifdef FLOATING_POINT char *decimal_point = _localeconv_r (data)->decimal_point; size_t decp_len = strlen (decimal_point); @@ -560,9 +579,16 @@ _DEFUN(_VFPRINTF_R, (data, fp, fmt0, ap), # define _fpvalue (_double_.fp) int expt; /* integer value of exponent */ int expsize = 0; /* character count for expstr */ - int ndig = 0; /* actual number of digits returned by cvt */ char expstr[MAXEXPLEN]; /* buffer for exponent string */ + int lead; /* sig figs before decimal or group sep */ #endif /* FLOATING_POINT */ +#if defined (FLOATING_POINT) || defined (_WANT_IO_C99_FORMATS) + int ndig = 0; /* actual number of digits returned by cvt */ +#endif +#ifdef _WANT_IO_C99_FORMATS + int nseps; /* number of group separators with ' */ + int nrepeats; /* number of repeats of the last group */ +#endif u_quad_t _uquad; /* integer arguments %[diouxX] */ enum { OCT, DEC, HEX } base;/* base for [diouxX] conversion */ int dprec; /* a copy of prec if [diouxX], 0 otherwise */ @@ -617,6 +643,14 @@ _DEFUN(_VFPRINTF_R, (data, fp, fmt0, ap), PRINT (with, n); \ } \ } +#define PRINTANDPAD(p, ep, len, with) { \ + int n = (ep) - (p); \ + if (n > (len)) \ + n = (len); \ + if (n > 0) \ + PRINT((p), n); \ + PAD((len) - (n > 0 ? n : 0), (with)); \ +} #define FLUSH() { \ if (uio.uio_resid && __SPRINT(data, fp, &uio)) \ goto error; \ @@ -757,6 +791,12 @@ _DEFUN(_VFPRINTF_R, (data, fp, fmt0, ap), width = 0; prec = -1; sign = '\0'; +#ifdef FLOATING_POINT + lead = 0; +#endif +#ifdef _WANT_IO_C99_FORMATS + nseps = nrepeats = 0; +#endif #ifndef _NO_POS_ARGS N = arg_index; is_pos_arg = 0; @@ -766,12 +806,12 @@ rflag: ch = *fmt++; reswitch: switch (ch) { #ifdef _WANT_IO_C99_FORMATS case '\'': - /* The ' flag is required by POSIX, but not C99. - In the C locale, LC_NUMERIC requires - thousands_sep to be the empty string. And since - no other locales are supported (yet), this flag - is currently a no-op. */ - goto rflag; + thousands_sep = _localeconv_r (data)->thousands_sep; + thsnd_len = strlen (thousands_sep); + grouping = _localeconv_r (data)->grouping; + if (thsnd_len > 0 && grouping && *grouping) + flags |= GROUPING; + goto rflag; #endif case ' ': /* @@ -1140,22 +1180,46 @@ reswitch: switch (ch) { size = expsize + ndig; if (ndig > 1 || flags & ALT) ++size; - } else if (ch == 'f') { /* f fmt */ - if (expt > 0) { +# ifdef _WANT_IO_C99_FORMATS + flags &= ~GROUPING; +# endif + } else { + if (ch == 'f') { /* f fmt */ + if (expt > 0) { + size = expt; + if (prec || flags & ALT) + size += prec + 1; + } else /* "0.X" */ + size = (prec || flags & ALT) + ? prec + 2 + : 1; + } else if (expt >= ndig) { /* fixed g fmt */ size = expt; - if (prec || flags & ALT) - size += prec + 1; - } else /* "0.X" */ - size = (prec || flags & ALT) - ? prec + 2 - : 1; - } else if (expt >= ndig) { /* fixed g fmt */ - size = expt; - if (flags & ALT) - ++size; - } else - size = ndig + (expt > 0 ? - 1 : 2 - expt); + if (flags & ALT) + ++size; + } else + size = ndig + (expt > 0 ? + 1 : 2 - expt); +# ifdef _WANT_IO_C99_FORMATS + if ((flags & 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) * thsnd_len; + } else +# endif + lead = expt; + } if (softsign) sign = '-'; @@ -1184,6 +1248,9 @@ reswitch: switch (ch) { case 'o': _uquad = UARG (); base = OCT; +#ifdef _WANT_IO_C99_FORMATS + flags &= ~GROUPING; +#endif goto nosign; case 'p': /* @@ -1320,6 +1387,9 @@ hex: _uquad = UARG (); flags |= HEXPREFIX; } +#ifdef _WANT_IO_C99_FORMATS + flags &= ~GROUPING; +#endif /* unsigned conversions */ nosign: sign = '\0'; /* @@ -1355,11 +1425,37 @@ number: if ((dprec = prec) >= 0) case DEC: /* many numbers are 1 digit */ - while (_uquad >= 10) { - *--cp = to_char (_uquad % 10); - _uquad /= 10; + if (_uquad < 10) { + *--cp = to_char(_uquad); + break; } - *--cp = to_char (_uquad); +#ifdef _WANT_IO_C99_FORMATS + ndig = 0; +#endif + do { + *--cp = to_char (_uquad % 10); +#ifdef _WANT_IO_C99_FORMATS + ndig++; + /* If (*grouping == CHAR_MAX) then no + more grouping */ + if ((flags & GROUPING) + && ndig == *grouping + && *grouping != CHAR_MAX + && _uquad > 9) { + cp -= thsnd_len; + strncpy (cp, thousands_sep, + thsnd_len); + ndig = 0; + /* If (grouping[1] == '\0') then we + have to use *grouping character + (last grouping rule) for all + next cases. */ + if (grouping[1] != '\0') + grouping++; + } +#endif + _uquad /= 10; + } while (_uquad != 0); break; case HEX: @@ -1459,16 +1555,33 @@ number: if ((dprec = prec) >= 0) PAD (-expt, zeroes); PRINT (cp, ndig); } - } else if (expt >= ndig) { - PRINT (cp, ndig); - PAD (expt - ndig, zeroes); - if (flags & ALT) - PRINT (decimal_point, decp_len); } else { - PRINT (cp, expt); - cp += expt; - PRINT (decimal_point, decp_len); - PRINT (cp, ndig - expt); + char *convbuf = cp; + PRINTANDPAD(cp, convbuf + ndig, + lead, zeroes); + cp += lead; +#ifdef _WANT_IO_C99_FORMATS + if (flags & GROUPING) { + while (nseps > 0 || nrepeats > 0) { + if (nrepeats > 0) + nrepeats--; + else { + grouping--; + nseps--; + } + PRINT(thousands_sep, thsnd_len); + PRINTANDPAD (cp, convbuf + ndig, + *grouping, zeroes); + cp += *grouping; + } + if (cp > convbuf + ndig) + cp = convbuf + ndig; + } +#endif + if (prec || flags & ALT) + PRINT (decimal_point, decp_len); + PRINTANDPAD (cp, convbuf + ndig, + ndig - expt, zeroes); } } else { /* 'a', 'A', 'e', or 'E' */ if (ndig > 1 || flags & ALT) { |