From b0776ebd41456fba102190420bc64b74f0baa029 Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Fri, 5 Feb 2010 08:35:35 +0000 Subject: [PATCH] * 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. --- newlib/ChangeLog | 9 ++ newlib/libc/stdio/vfprintf.c | 187 ++++++++++++++++++++++++------ newlib/libc/stdio/vfwprintf.c | 210 +++++++++++++++++++++++++++------- 3 files changed, 330 insertions(+), 76 deletions(-) diff --git a/newlib/ChangeLog b/newlib/ChangeLog index e83fdcecd..cfac5d128 100644 --- a/newlib/ChangeLog +++ b/newlib/ChangeLog @@ -1,3 +1,12 @@ +2010-02-05 Corinna Vinschen + + * 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. + 2010-02-01 Christopher Faylor * libc/stdlib/__atexit.c (__atexit_lock): Define a global lock for diff --git a/newlib/libc/stdio/vfprintf.c b/newlib/libc/stdio/vfprintf.c index fc33c3027..be4170cc2 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 +#endif +#ifdef FLOATING_POINT # include /* 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) { diff --git a/newlib/libc/stdio/vfwprintf.c b/newlib/libc/stdio/vfwprintf.c index 68e92a8a6..9cf9c3ec7 100644 --- a/newlib/libc/stdio/vfwprintf.c +++ b/newlib/libc/stdio/vfwprintf.c @@ -201,8 +201,10 @@ _DEFUN(__sbwprintf, (rptr, fp, fmt, ap), #endif /* !STRING_ONLY */ -#ifdef FLOATING_POINT +#if defined (FLOATING_POINT) || defined (_WANT_IO_C99_FORMATS) # include +#endif +#ifdef FLOATING_POINT # include /* For %La, an exponent of 15 bits occupies the exponent character, a @@ -249,8 +251,16 @@ static int wexponent(wchar_t *, 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 %ls/%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 @@ -336,6 +346,9 @@ _EXFUN(get_arg, (struct _reent *data, int n, wchar_t *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 #ifndef STRING_ONLY int @@ -378,19 +391,31 @@ _DEFUN(_VFWPRINTF_R, (data, fp, fmt0, ap), int width; /* width from format (%8d), or 0 */ int prec; /* precision from format (%.3d), or -1 */ wchar_t sign; /* sign prefix (' ', '+', '-', or \0) */ -#ifdef FLOATING_POINT - wchar_t decimal_point; +#ifdef _WANT_IO_C99_FORMATS + /* locale specific numeric grouping */ + wchar_t thousands_sep; + const char *grouping; +#endif #ifdef _MB_CAPABLE mbstate_t state; /* mbtowc calls from library must not change state */ #endif +#ifdef FLOATING_POINT + wchar_t decimal_point; wchar_t softsign; /* temporary negative sign for floats */ union { int i; _PRINTF_FLOAT_TYPE fp; } _double_ = {0}; # 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 wcvt */ wchar_t 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 */ @@ -419,9 +444,16 @@ _DEFUN(_VFWPRINTF_R, (data, fp, fmt0, ap), #ifdef FLOATING_POINT #ifdef _MB_CAPABLE - memset (&state, '\0', sizeof (state)); - _mbrtowc_r (data, &decimal_point, _localeconv_r (data)->decimal_point, - MB_CUR_MAX, &state); + { + size_t nconv; + + memset (&state, '\0', sizeof (state)); + nconv = _mbrtowc_r (data, &decimal_point, + _localeconv_r (data)->decimal_point, + MB_CUR_MAX, &state); + if (nconv == (size_t) -1 || nconv == (size_t) -2) + decimal_point = L'.'; + } #else decimal_point = (wchar_t) *_localeconv_r (data)->decimal_point; #endif @@ -449,6 +481,14 @@ _DEFUN(_VFWPRINTF_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; \ @@ -570,6 +610,12 @@ _DEFUN(_VFWPRINTF_R, (data, fp, fmt0, ap), width = 0; prec = -1; sign = L'\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; @@ -579,8 +625,23 @@ rflag: ch = *fmt++; reswitch: switch (ch) { #ifdef _WANT_IO_C99_FORMATS case L'\'': - /* The ' flag is required by POSIX, but not C99. - FIXME: this flag is currently a no-op. */ +#ifdef _MB_CAPABLE + { + size_t nconv; + + memset (&state, '\0', sizeof (state)); + nconv = _mbrtowc_r (data, &thousands_sep, + _localeconv_r (data)->thousands_sep, + MB_CUR_MAX, &state); + if (nconv == (size_t) -1 || nconv == (size_t) -2) + thousands_sep = L'\0'; + } +#else + thousands_sep = (wchar_t) *_localeconv_r(data)->thousands_sep; +#endif + grouping = _localeconv_r (data)->grouping; + if (thousands_sep && grouping && *grouping) + flags |= GROUPING; goto rflag; #endif case L' ': @@ -942,23 +1003,46 @@ reswitch: switch (ch) { size = expsize + ndig; if (ndig > 1 || flags & ALT) ++size; - } else if (ch == L'f') { /* f fmt */ - if (expt > 0) { +# ifdef _WANT_IO_C99_FORMATS + flags &= ~GROUPING; +# endif + } else { + if (ch == L'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; + } else +# endif + lead = expt; + } if (softsign) sign = L'-'; break; @@ -983,6 +1067,9 @@ reswitch: switch (ch) { case L'o': _uquad = UARG (); base = OCT; +#ifdef _WANT_IO_C99_FORMATS + flags &= ~GROUPING; +#endif goto nosign; case L'p': /* @@ -1106,6 +1193,9 @@ hex: _uquad = UARG (); flags |= HEXPREFIX; } +#ifdef _WANT_IO_C99_FORMATS + flags &= ~GROUPING; +#endif /* unsigned conversions */ nosign: sign = L'\0'; /* @@ -1141,11 +1231,35 @@ 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 = thousands_sep; + 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: @@ -1245,17 +1359,35 @@ 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, 1); } else { - PRINT (cp, expt); - cp += expt; - PRINT (&decimal_point, 1); - PRINT (cp, ndig - expt); + wchar_t *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, 1); + PRINTANDPAD (cp, convbuf + ndig, + *grouping, zeroes); + cp += *grouping; + } + if (cp > convbuf + ndig) + cp = convbuf + ndig; + } +#endif + if (prec || flags & ALT) + PRINT (&decimal_point, 1); + PRINTANDPAD (cp, convbuf + ndig, + ndig - expt, zeroes); } + } else { /* 'a', 'A', 'e', or 'E' */ if (ndig > 1 || flags & ALT) { PRINT (cp, 1); -- 2.43.5