diff options
Diffstat (limited to 'lib/vasnprintf.c')
| -rw-r--r-- | lib/vasnprintf.c | 1508 |
1 files changed, 1026 insertions, 482 deletions
diff --git a/lib/vasnprintf.c b/lib/vasnprintf.c index 6ae95ca5..12bdd45b 100644 --- a/lib/vasnprintf.c +++ b/lib/vasnprintf.c @@ -1,5 +1,5 @@ /* vsprintf with automatic memory allocation. - Copyright (C) 1999, 2002-2024 Free Software Foundation, Inc. + Copyright (C) 1999, 2002-2026 Free Software Foundation, Inc. This file is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as @@ -29,6 +29,7 @@ Depends on FCHAR_T. DCHAR_CPY memcpy like function for DCHAR_T[] arrays. DCHAR_SET memset like function for DCHAR_T[] arrays. + DCHAR_STRLEN strlen like function for DCHAR_T[] arrays. DCHAR_MBSNLEN mbsnlen like function for DCHAR_T[] arrays. SNPRINTF The system's snprintf (or similar) function. This may be either snprintf or swprintf. @@ -88,7 +89,7 @@ #include <errno.h> /* errno */ #include <limits.h> /* CHAR_BIT, INT_MAX, INT_WIDTH, LONG_WIDTH */ #include <float.h> /* DBL_MAX_EXP, LDBL_MAX_EXP, LDBL_MANT_DIG */ -#if HAVE_NL_LANGINFO +#if HAVE_NL_LANGINFO || __GLIBC__ >= 2 || defined __CYGWIN__ # include <langinfo.h> #endif #ifndef VASNPRINTF @@ -133,6 +134,16 @@ # include "fpucw.h" #endif +#if HAVE_SAME_LONG_DOUBLE_AS_DOUBLE +/* Override macros from fpucw.h. */ +# undef DECL_LONG_DOUBLE_ROUNDING +# define DECL_LONG_DOUBLE_ROUNDING +# undef BEGIN_LONG_DOUBLE_ROUNDING +# define BEGIN_LONG_DOUBLE_ROUNDING() +# undef END_LONG_DOUBLE_ROUNDING +# define END_LONG_DOUBLE_ROUNDING() +#endif + /* Default parameters. */ #ifndef VASNPRINTF # if WIDE_CHAR_VERSION @@ -179,10 +190,24 @@ # define SNPRINTF swprintf # endif # else - /* Old platforms such as NetBSD 3.0, OpenBSD 3.8, HP-UX 11.00, IRIX 6.5. */ + /* Old platforms such as NetBSD 3.0, OpenBSD 3.8, HP-UX 11.00. */ # define TCHAR_T char # endif #endif +#ifndef DCHAR_STRLEN +# if WIDE_CHAR_VERSION +# define DCHAR_STRLEN local_wcslen +# else +# define DCHAR_STRLEN strlen +# endif +#endif +#ifndef DCHAR_MBSNLEN +# if WIDE_CHAR_VERSION +# define DCHAR_MBSNLEN wcsnlen +# else +# define DCHAR_MBSNLEN mbsnlen +# endif +#endif #if !WIDE_CHAR_VERSION || !DCHAR_IS_TCHAR /* TCHAR_T is char. */ /* Use snprintf if it exists under the name 'snprintf' or '_snprintf'. @@ -217,6 +242,12 @@ /* Here we need to call the native sprintf, not rpl_sprintf. */ #undef sprintf +/* macOS 12's "warning: 'sprintf' is deprecated" is pointless, + as sprintf is used safely here. */ +#if defined __APPLE__ && defined __MACH__ && _GL_GNUC_PREREQ (4, 2) +# pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + /* GCC >= 4.0 with -Wall emits unjustified "... may be used uninitialized" warnings in this file. Use -Dlint to suppress them. */ #if defined GCC_LINT || defined lint @@ -225,6 +256,11 @@ # define IF_LINT(Code) /* empty */ #endif +/* Here we need only the most basic fields of 'struct lconv', and can + therefore use the system's localeconv() function, without needing a + dependency on module 'localeconv'. */ +#undef localeconv + /* Avoid some warnings from "gcc -Wshadow". This file doesn't use the exp() and remainder() functions. */ #undef exp @@ -358,7 +394,7 @@ local_wctomb (char *s, wchar_t wc) # endif #endif -#if NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_INFINITE_LONG_DOUBLE || NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_DOUBLE || (NEED_WPRINTF_DIRECTIVE_LA && WIDE_CHAR_VERSION) +#if NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_INFINITE_LONG_DOUBLE || NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_DOUBLE || (NEED_WPRINTF_DIRECTIVE_LA && WIDE_CHAR_VERSION) || (NEED_PRINTF_FLAG_ALT_PRECISION_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION || NEED_PRINTF_FLAG_GROUPING || NEED_PRINTF_FLAG_GROUPING_INT) /* Determine the decimal-point character according to the current locale. */ # ifndef decimal_point_char_defined # define decimal_point_char_defined 1 @@ -385,6 +421,217 @@ decimal_point_char (void) # endif #endif +#if (!WIDE_CHAR_VERSION && (NEED_PRINTF_DOUBLE || NEED_PRINTF_LONG_DOUBLE)) || ((!WIDE_CHAR_VERSION || !DCHAR_IS_TCHAR) && (NEED_PRINTF_FLAG_ALT_PRECISION_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION || NEED_PRINTF_FLAG_GROUPING || NEED_PRINTF_FLAG_GROUPING_INT)) +/* Determine the thousands-separator character according to the current + locale. + It is a single multibyte character. + In glibc: 35x ".", 90x ",", 23x U+202F, 1x U+2019, 1x U+066C, on other + systems also U+00A0. */ +# ifndef thousands_separator_char_defined +# define thousands_separator_char_defined 1 +static const char * +thousands_separator_char (char stackbuf[10]) +{ + /* Determine it in a multithread-safe way. + We know nl_langinfo is multithread-safe on glibc systems, on Mac OS X + systems, and on NetBSD, but is not required to be multithread-safe by + POSIX. + localeconv() is not guaranteed to be multithread-safe by POSIX either; + however, on native Windows it is (cf. test-localeconv-mt). + sprintf(), however, is multithread-safe. */ +# if HAVE_NL_LANGINFO && (__GLIBC__ || defined __UCLIBC__ || (defined __APPLE__ && defined __MACH__) || defined __NetBSD__) + return nl_langinfo (THOUSEP); +# elif defined _WIN32 && !defined __CYGWIN__ + return localeconv () -> thousands_sep; +# else + sprintf (stackbuf, "%'.0f", 1000.0); + /* Now stackbuf = "1<thousep>000". */ + stackbuf[strlen (stackbuf) - 3] = '\0'; +# if defined __sun + /* Solaris specific hack: Replace wrong result (0xC2 means U+00A0). */ + if (streq (&stackbuf[1], "\302")) + strcpy (&stackbuf[1], MB_CUR_MAX > 1 ? "\302\240" : "\240"); +# endif + return &stackbuf[1]; +# endif +} +# endif +#endif +#if !WIDE_CHAR_VERSION && defined DCHAR_CONV_FROM_ENCODING && (NEED_PRINTF_DOUBLE || NEED_PRINTF_LONG_DOUBLE) +/* Determine the thousands-separator character, as a DCHAR_T[] array, + according to the current locale. + It is a single Unicode character. */ +# ifndef thousands_separator_DCHAR_defined +# define thousands_separator_DCHAR_defined 1 +static const DCHAR_T * +thousands_separator_DCHAR (DCHAR_T stackbuf[10]) +{ + /* Determine it in a multithread-safe way. */ + char tmpbuf[10]; + const char *tmp = thousands_separator_char (tmpbuf); + if (*tmp != '\0') + { + /* Convert it from char[] to DCHAR_T[]. */ + size_t converted_len = 10; + DCHAR_T *converted = + DCHAR_CONV_FROM_ENCODING (locale_charset (), + iconveh_question_mark, + tmp, strlen (tmp) + 1, + NULL, + stackbuf, &converted_len); + if (converted != NULL) + { + if (converted != stackbuf) + /* It should not be so long. */ + abort (); + return stackbuf; + } + } + stackbuf[0] = 0; + return stackbuf; +} +# endif +#endif +/* Maximum number of 'char' in the char[] or DCHAR_T[] representation of the + thousands separator. */ +#define THOUSEP_CHAR_MAXLEN 3 + +#if WIDE_CHAR_VERSION && ((NEED_PRINTF_DOUBLE || NEED_PRINTF_LONG_DOUBLE) || ((NEED_PRINTF_FLAG_ALT_PRECISION_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION || NEED_PRINTF_FLAG_GROUPING || NEED_PRINTF_FLAG_GROUPING_INT) && DCHAR_IS_TCHAR)) +/* Determine the thousands-separator character, as a wide character, according + to the current locale. + It is a single wide character. */ +# ifndef thousands_separator_wchar_defined +# define thousands_separator_wchar_defined 1 +static const wchar_t * +thousands_separator_wchar (wchar_t stackbuf[10]) +{ +# if __GLIBC__ >= 2 || defined __CYGWIN__ + /* On glibc, in the unibyte locale fr_FR, the *wprintf routines use U+202F + as separator, which cannot be represented in the locale encoding. */ + stackbuf[0] = + (wchar_t) (unsigned long) nl_langinfo (_NL_NUMERIC_THOUSANDS_SEP_WC); + stackbuf[1] = L'\0'; + return stackbuf; +# elif defined _WIN32 && !defined __CYGWIN__ + const char *tmp = localeconv () -> thousands_sep; + if (*tmp != '\0') + { + mbstate_t state; + mbszero (&state); + if ((int) mbrtowc (&stackbuf[0], tmp, strlen (tmp), &state) > 0) + stackbuf[1] = L'\0'; + else + stackbuf[0] = L'\0'; + } + else + stackbuf[0] = L'\0'; + return stackbuf; +# elif defined __sun + /* Use sprintf, because swprintf retrieves a wrong value for the + thousands-separator wide character (e.g. (wchar_t) 0xffffffa0). */ + char tmp[10]; + sprintf (tmp, "%'.0f", 1000.0); + /* Now tmp = L"1<thousep>000". */ + tmp[strlen (tmp) - 3] = '\0'; + /* Solaris specific hack: Replace wrong result (0xC2 means U+00A0). */ + if (streq (&tmp[1], "\302")) + strcpy (&tmp[1], MB_CUR_MAX > 1 ? "\302\240" : "\240"); + if (tmp[1] != '\0') + { + mbstate_t state; + mbszero (&state); + if ((int) mbrtowc (&stackbuf[0], &tmp[1], strlen (&tmp[1]), &state) > 0) + stackbuf[1] = L'\0'; + else + stackbuf[0] = L'\0'; + } + else + stackbuf[0] = L'\0'; + return stackbuf; +# else + swprintf (stackbuf, 10, L"%'.0f", 1000.0); + /* Now stackbuf = L"1<thousep>000". */ + stackbuf[local_wcslen (stackbuf) - 3] = '\0'; + return &stackbuf[1]; +# endif +} +# endif +#endif +/* Maximum number of 'wchar_t' in the wchar_t[] representation of the thousands + separator. */ +#define THOUSEP_WCHAR_MAXLEN 1 + +#if (NEED_PRINTF_DOUBLE || NEED_PRINTF_LONG_DOUBLE) || (NEED_PRINTF_FLAG_ALT_PRECISION_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION || NEED_PRINTF_FLAG_GROUPING || NEED_PRINTF_FLAG_GROUPING_INT) +# ifndef grouping_rule_defined +# define grouping_rule_defined 1 +/* Determine the grouping rule. + * As specified in POSIX + * <https://pubs.opengroup.org/onlinepubs/9799919799/functions/localeconv.html> + * <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap07.html#tag_07_03_04> + * it is a string whose elements are 'signed char' values, where + * "Each integer specifies the number of digits in each group, with the initial + * integer defining the size of the group immediately preceding the decimal + * delimiter, and the following integers defining the preceding groups. If + * the last integer is not -1, then the size of the previous group (if any) + * shall be repeatedly used for the remainder of the digits. If the last + * integer is -1, then no further grouping shall be performed." + * Platforms that have locales with grouping: + * glibc, FreeBSD, NetBSD, AIX, Solaris, Cygwin, Haiku. + * Platforms that don't: + * musl libc, macOS, OpenBSD, Android, mingw, MSVC. + * Typical grouping rules on glibc: + * 136x 3 (fr_FR etc.) + * 4x 4 (cmn_TW etc.) + * 9x 3;2 (ta_IN etc.) + * 1x 2;2;2;3 (umn_US) + * 21x -1 (C etc.) + */ +static const signed char * +grouping_rule (void) +{ + /* We know nl_langinfo is multithread-safe on glibc systems and on Cygwin, + but is not required to be multithread-safe by POSIX. + localeconv() is not guaranteed to be multithread-safe by POSIX either; + however, on all known systems it is (cf. test-localeconv-mt). */ +# if __GLIBC__ >= 2 + return (const signed char *) nl_langinfo (GROUPING); +# elif defined __CYGWIN__ + return (const signed char *) nl_langinfo (_NL_NUMERIC_GROUPING); +# else + return (const signed char *) localeconv () -> grouping; +# endif +} +/* Determines the number of thousands-separators to be inserted in a digit + sequence with ndigits digits (before the decimal point). */ +static size_t +num_thousands_separators (const signed char *grouping, size_t ndigits) +{ + const signed char *g = grouping; + int h = *g; + if (h <= 0 || ndigits == 0) + return 0; + size_t insert = 0; + for (;;) + { + /* Invariant: here h == *g, h > 0, ndigits > 0. */ + if (g[1] == 0) + /* h repeats endlessly. */ + return insert + (ndigits - 1) / h; + /* h does not repeat. */ + if (ndigits <= h) + return insert; + ndigits -= h; + insert++; + g++; + h = *g; + if (h < 0) + /* No further grouping. */ + return insert; + } +} +# endif +#endif + #if NEED_PRINTF_INFINITE_DOUBLE && !NEED_PRINTF_DOUBLE /* Equivalent to !isfinite(x) || x == 0, but does not require libm. */ @@ -502,19 +749,18 @@ multiply (mpn_t src1, mpn_t src2, mpn_t *dest) /* Here 1 <= len1 <= len2. */ size_t dlen; mp_limb_t *dp; - size_t k, i, j; dlen = len1 + len2; dp = (mp_limb_t *) malloc (dlen * sizeof (mp_limb_t)); if (dp == NULL) return NOMEM_PTR; - for (k = len2; k > 0; ) + for (size_t k = len2; k > 0; ) dp[--k] = 0; - for (i = 0; i < len1; i++) + for (size_t i = 0; i < len1; i++) { mp_limb_t digit1 = p1[i]; mp_twolimb_t carry = 0; - for (j = 0; j < len2; j++) + for (size_t j = 0; j < len2; j++) { mp_limb_t digit2 = p2[j]; carry += (mp_twolimb_t) digit1 * (mp_twolimb_t) digit2; @@ -598,7 +844,6 @@ divide (mpn_t a, mpn_t b, mpn_t *q) size_t a_len = a.nlimbs; const mp_limb_t *b_ptr = b.limbs; size_t b_len = b.nlimbs; - mp_limb_t *roomptr; mp_limb_t *tmp_roomptr = NULL; mp_limb_t *q_ptr; size_t q_len; @@ -608,7 +853,7 @@ divide (mpn_t a, mpn_t b, mpn_t *q) /* Allocate room for a_len+2 digits. (Need a_len+1 digits for the real division and 1 more digit for the final rounding of q.) */ - roomptr = (mp_limb_t *) malloc ((a_len + 2) * sizeof (mp_limb_t)); + mp_limb_t *roomptr = (mp_limb_t *) malloc ((a_len + 2) * sizeof (mp_limb_t)); if (roomptr == NULL) return NOMEM_PTR; @@ -650,8 +895,7 @@ divide (mpn_t a, mpn_t b, mpn_t *q) mp_limb_t remainder = 0; const mp_limb_t *sourceptr = a_ptr + a_len; mp_limb_t *destptr = q_ptr + a_len; - size_t count; - for (count = a_len; count > 0; count--) + for (size_t count = a_len; count > 0; count--) { mp_twolimb_t num = ((mp_twolimb_t) remainder << GMP_LIMB_BITS) | *--sourceptr; @@ -752,8 +996,7 @@ divide (mpn_t a, mpn_t b, mpn_t *q) const mp_limb_t *sourceptr = b_ptr; mp_limb_t *destptr = tmp_roomptr; mp_twolimb_t accu = 0; - size_t count; - for (count = b_len; count > 0; count--) + for (size_t count = b_len; count > 0; count--) { accu += (mp_twolimb_t) *sourceptr++ << s; *destptr++ = (mp_limb_t) accu; @@ -780,8 +1023,7 @@ divide (mpn_t a, mpn_t b, mpn_t *q) const mp_limb_t *sourceptr = a_ptr; mp_limb_t *destptr = r_ptr; mp_twolimb_t accu = 0; - size_t count; - for (count = a_len; count > 0; count--) + for (size_t count = a_len; count > 0; count--) { accu += (mp_twolimb_t) *sourceptr++ << s; *destptr++ = (mp_limb_t) accu; @@ -857,8 +1099,7 @@ divide (mpn_t a, mpn_t b, mpn_t *q) const mp_limb_t *sourceptr = b_ptr; mp_limb_t *destptr = r_ptr + j; mp_twolimb_t carry = 0; - size_t count; - for (count = b_len; count > 0; count--) + for (size_t count = b_len; count > 0; count--) { /* Here 0 <= carry <= q*. */ carry = @@ -882,8 +1123,7 @@ divide (mpn_t a, mpn_t b, mpn_t *q) const mp_limb_t *sourceptr = b_ptr; mp_limb_t *destptr = r_ptr + j; mp_limb_t carry = 0; - size_t count; - for (count = b_len; count > 0; count--) + for (size_t count = b_len; count > 0; count--) { mp_limb_t source1 = *sourceptr++; mp_limb_t source2 = *destptr; @@ -915,8 +1155,7 @@ divide (mpn_t a, mpn_t b, mpn_t *q) { mp_limb_t ptr = r_ptr + r_len; mp_twolimb_t accu = 0; - size_t count; - for (count = r_len; count > 0; count--) + for (size_t count = r_len; count > 0; count--) { accu = (mp_twolimb_t) (mp_limb_t) accu << GMP_LIMB_BITS; accu += (mp_twolimb_t) *--ptr << (GMP_LIMB_BITS - s); @@ -931,29 +1170,25 @@ divide (mpn_t a, mpn_t b, mpn_t *q) /* Compare r << 1 with b. */ if (r_len > b_len) goto increment_q; - { - size_t i; - for (i = b_len;;) - { - mp_limb_t r_i = - (i <= r_len && i > 0 ? r_ptr[i - 1] >> (GMP_LIMB_BITS - 1) : 0) - | (i < r_len ? r_ptr[i] << 1 : 0); - mp_limb_t b_i = (i < b_len ? b_ptr[i] : 0); - if (r_i > b_i) - goto increment_q; - if (r_i < b_i) - goto keep_q; - if (i == 0) - break; - i--; - } - } + for (size_t i = b_len;;) + { + mp_limb_t r_i = + (i <= r_len && i > 0 ? r_ptr[i - 1] >> (GMP_LIMB_BITS - 1) : 0) + | (i < r_len ? r_ptr[i] << 1 : 0); + mp_limb_t b_i = (i < b_len ? b_ptr[i] : 0); + if (r_i > b_i) + goto increment_q; + if (r_i < b_i) + goto keep_q; + if (i == 0) + break; + i--; + } if (q_len > 0 && ((q_ptr[0] & 1) != 0)) /* q is odd. */ increment_q: { - size_t i; - for (i = 0; i < q_len; i++) + for (size_t i = 0; i < q_len; i++) if (++(q_ptr[i]) != 0) goto keep_q; q_ptr[q_len++] = 1; @@ -999,8 +1234,7 @@ convert_to_decimal (mpn_t a, size_t extra_zeroes) /* Divide a by 10^9, in-place. */ mp_limb_t remainder = 0; mp_limb_t *ptr = a_ptr + a_len; - size_t count; - for (count = a_len; count > 0; count--) + for (size_t count = a_len; count > 0; count--) { mp_twolimb_t num = ((mp_twolimb_t) remainder << GMP_LIMB_BITS) | *--ptr; @@ -1008,7 +1242,7 @@ convert_to_decimal (mpn_t a, size_t extra_zeroes) remainder = num % 1000000000; } /* Store the remainder as 9 decimal digits. */ - for (count = 9; count > 0; count--) + for (size_t count = 9; count > 0; count--) { *d_ptr++ = '0' + (remainder % 10); remainder = remainder / 10; @@ -1042,18 +1276,15 @@ convert_to_decimal (mpn_t a, size_t extra_zeroes) static void * decode_long_double (long double x, int *ep, mpn_t *mp) { - mpn_t m; - int exp; - long double y; - size_t i; - /* Allocate memory for result. */ + mpn_t m; m.nlimbs = (LDBL_MANT_BIT + GMP_LIMB_BITS - 1) / GMP_LIMB_BITS; m.limbs = (mp_limb_t *) malloc (m.nlimbs * sizeof (mp_limb_t)); if (m.limbs == NULL) return NULL; /* Split into exponential part and mantissa. */ - y = safe_frexpl (x, &exp); + int exp; + long double y = safe_frexpl (x, &exp); if (!(y >= 0.0L && y < 1.0L)) abort (); /* x = 2^exp * y = 2^(exp - LDBL_MANT_BIT) * (y * 2^LDBL_MANT_BIT), and the @@ -1091,7 +1322,7 @@ decode_long_double (long double x, int *ep, mpn_t *mp) } # endif # endif - for (i = LDBL_MANT_BIT / GMP_LIMB_BITS; i > 0; ) + for (size_t i = LDBL_MANT_BIT / GMP_LIMB_BITS; i > 0; ) { mp_limb_t hi, lo; y *= (mp_limb_t) 1 << (GMP_LIMB_BITS / 2); @@ -1130,18 +1361,15 @@ decode_long_double (long double x, int *ep, mpn_t *mp) static void * decode_double (double x, int *ep, mpn_t *mp) { - mpn_t m; - int exp; - double y; - size_t i; - /* Allocate memory for result. */ + mpn_t m; m.nlimbs = (DBL_MANT_BIT + GMP_LIMB_BITS - 1) / GMP_LIMB_BITS; m.limbs = (mp_limb_t *) malloc (m.nlimbs * sizeof (mp_limb_t)); if (m.limbs == NULL) return NULL; /* Split into exponential part and mantissa. */ - y = frexp (x, &exp); + int exp; + double y = frexp (x, &exp); if (!(y >= 0.0 && y < 1.0)) abort (); /* x = 2^exp * y = 2^(exp - DBL_MANT_BIT) * (y * 2^DBL_MANT_BIT), and the @@ -1179,7 +1407,7 @@ decode_double (double x, int *ep, mpn_t *mp) } # endif # endif - for (i = DBL_MANT_BIT / GMP_LIMB_BITS; i > 0; ) + for (size_t i = DBL_MANT_BIT / GMP_LIMB_BITS; i > 0; ) { mp_limb_t hi, lo; y *= (mp_limb_t) 1 << (GMP_LIMB_BITS / 2); @@ -1214,24 +1442,11 @@ decode_double (double x, int *ep, mpn_t *mp) static char * scale10_round_decimal_decoded (int e, mpn_t m, void *memory, int n) { - int s; - size_t extra_zeroes; - unsigned int abs_n; - unsigned int abs_s; - mp_limb_t *pow5_ptr; - size_t pow5_len; - unsigned int s_limbs; - unsigned int s_bits; - mpn_t pow5; - mpn_t z; - void *z_memory; - char *digits; - /* x = 2^e * m, hence y = round (2^e * 10^n * m) = round (2^(e+n) * 5^n * m) = round (2^s * 5^n * m). */ - s = e + n; - extra_zeroes = 0; + int s = e + n; + size_t extra_zeroes = 0; /* Factor out a common power of 10 if possible. */ if (s > 0 && n > 0) { @@ -1244,11 +1459,12 @@ scale10_round_decimal_decoded (int e, mpn_t m, void *memory, int n) z = round (2^s * 5^n * m). */ /* Compute 5^|n|, possibly shifted by |s| bits if n and s have the same sign. 2.322 is slightly larger than log(5)/log(2). */ - abs_n = (n >= 0 ? n : -n); - abs_s = (s >= 0 ? s : -s); - pow5_ptr = (mp_limb_t *) malloc (((int)(abs_n * (2.322f / GMP_LIMB_BITS)) + 1 - + abs_s / GMP_LIMB_BITS + 1) - * sizeof (mp_limb_t)); + unsigned int abs_n = (n >= 0 ? n : -n); + unsigned int abs_s = (s >= 0 ? s : -s); + mp_limb_t *pow5_ptr = + (mp_limb_t *) malloc (((int)(abs_n * (2.322f / GMP_LIMB_BITS)) + 1 + + abs_s / GMP_LIMB_BITS + 1) + * sizeof (mp_limb_t)); if (pow5_ptr == NULL) { free (memory); @@ -1256,7 +1472,7 @@ scale10_round_decimal_decoded (int e, mpn_t m, void *memory, int n) } /* Initialize with 1. */ pow5_ptr[0] = 1; - pow5_len = 1; + size_t pow5_len = 1; /* Multiply with 5^|n|. */ if (abs_n > 0) { @@ -1265,13 +1481,11 @@ scale10_round_decimal_decoded (int e, mpn_t m, void *memory, int n) 1, 5, 25, 125, 625, 3125, 15625, 78125, 390625, 1953125, 9765625, 48828125, 244140625, 1220703125 }; - unsigned int n13; - for (n13 = 0; n13 <= abs_n; n13 += 13) + for (unsigned int n13 = 0; n13 <= abs_n; n13 += 13) { mp_limb_t digit1 = small_pow5[n13 + 13 <= abs_n ? 13 : abs_n - n13]; - size_t j; mp_twolimb_t carry = 0; - for (j = 0; j < pow5_len; j++) + for (size_t j = 0; j < pow5_len; j++) { mp_limb_t digit2 = pow5_ptr[j]; carry += (mp_twolimb_t) digit1 * (mp_twolimb_t) digit2; @@ -1282,8 +1496,11 @@ scale10_round_decimal_decoded (int e, mpn_t m, void *memory, int n) pow5_ptr[pow5_len++] = (mp_limb_t) carry; } } - s_limbs = abs_s / GMP_LIMB_BITS; - s_bits = abs_s % GMP_LIMB_BITS; + unsigned int s_limbs = abs_s / GMP_LIMB_BITS; + unsigned int s_bits = abs_s % GMP_LIMB_BITS; + mpn_t pow5; + mpn_t z; + void *z_memory; if (n >= 0 ? s >= 0 : s <= 0) { /* Multiply with 2^|s|. */ @@ -1291,8 +1508,7 @@ scale10_round_decimal_decoded (int e, mpn_t m, void *memory, int n) { mp_limb_t *ptr = pow5_ptr; mp_twolimb_t accu = 0; - size_t count; - for (count = pow5_len; count > 0; count--) + for (size_t count = pow5_len; count > 0; count--) { accu += (mp_twolimb_t) *ptr << s_bits; *ptr++ = (mp_limb_t) accu; @@ -1306,13 +1522,12 @@ scale10_round_decimal_decoded (int e, mpn_t m, void *memory, int n) } if (s_limbs > 0) { - size_t count; - for (count = pow5_len; count > 0;) + for (size_t count = pow5_len; count > 0;) { count--; pow5_ptr[s_limbs + count] = pow5_ptr[count]; } - for (count = s_limbs; count > 0;) + for (size_t count = s_limbs; count > 0;) { count--; pow5_ptr[count] = 0; @@ -1353,8 +1568,7 @@ scale10_round_decimal_decoded (int e, mpn_t m, void *memory, int n) /* Construct 2^|s|. */ { mp_limb_t *ptr = pow5_ptr + pow5_len; - size_t i; - for (i = 0; i < s_limbs; i++) + for (size_t i = 0; i < s_limbs; i++) ptr[i] = 0; ptr[s_limbs] = (mp_limb_t) 1 << s_bits; denominator.limbs = ptr; @@ -1379,17 +1593,13 @@ scale10_round_decimal_decoded (int e, mpn_t m, void *memory, int n) } { mp_limb_t *destptr = num_ptr; - { - size_t i; - for (i = 0; i < s_limbs; i++) - *destptr++ = 0; - } + for (size_t i = 0; i < s_limbs; i++) + *destptr++ = 0; if (s_bits > 0) { const mp_limb_t *sourceptr = m.limbs; mp_twolimb_t accu = 0; - size_t count; - for (count = m.nlimbs; count > 0; count--) + for (size_t count = m.nlimbs; count > 0; count--) { accu += (mp_twolimb_t) *sourceptr++ << s_bits; *destptr++ = (mp_limb_t) accu; @@ -1401,8 +1611,7 @@ scale10_round_decimal_decoded (int e, mpn_t m, void *memory, int n) else { const mp_limb_t *sourceptr = m.limbs; - size_t count; - for (count = m.nlimbs; count > 0; count--) + for (size_t count = m.nlimbs; count > 0; count--) *destptr++ = *sourceptr++; } numerator.limbs = num_ptr; @@ -1419,7 +1628,7 @@ scale10_round_decimal_decoded (int e, mpn_t m, void *memory, int n) if (z_memory == NOMEM_PTR) return NULL; - digits = convert_to_decimal (z, extra_zeroes); + char *digits = convert_to_decimal (z, extra_zeroes); free (z_memory); return digits; } @@ -1474,13 +1683,9 @@ scale10_round_decimal_double (double x, int n) static int floorlog10l (long double x) { - int exp; - long double y; - double z; - double l; - /* Split into exponential part and mantissa. */ - y = safe_frexpl (x, &exp); + int exp; + long double y = safe_frexpl (x, &exp); if (!(y >= 0.0L && y < 1.0L)) abort (); if (y == 0.0L) @@ -1521,8 +1726,8 @@ floorlog10l (long double x) if (!(y >= 0.5L && y < 1.0L)) abort (); /* Compute an approximation for l = log2(x) = exp + log2(y). */ - l = exp; - z = y; + double l = exp; + double z = y; if (z < 0.70710678118654752444) { z *= 1.4142135623730950488; @@ -1565,13 +1770,9 @@ floorlog10l (long double x) static int floorlog10 (double x) { - int exp; - double y; - double z; - double l; - /* Split into exponential part and mantissa. */ - y = frexp (x, &exp); + int exp; + double y = frexp (x, &exp); if (!(y >= 0.0 && y < 1.0)) abort (); if (y == 0.0) @@ -1612,8 +1813,8 @@ floorlog10 (double x) if (!(y >= 0.5 && y < 1.0)) abort (); /* Compute an approximation for l = log2(x) = exp + log2(y). */ - l = exp; - z = y; + double l = exp; + double z = y; if (z < 0.70710678118654752444) { z *= 1.4142135623730950488; @@ -1839,8 +2040,17 @@ MAX_ROOM_NEEDED (const arguments *ap, size_t arg_index, FCHAR_T conversion, } if (tmp_length < precision) tmp_length = precision; - /* Multiply by 2, as an estimate for FLAG_GROUP. */ - tmp_length = xsum (tmp_length, tmp_length); + /* Account for thousands separators. */ + if (flags & FLAG_GROUP) + { + /* A thousands separator needs to be inserted at most every 2 digits. + This is the case in the ta_IN locale. */ +# if WIDE_CHAR_VERSION + tmp_length = xsum (tmp_length, tmp_length / 2 * THOUSEP_WCHAR_MAXLEN); +# else + tmp_length = xsum (tmp_length, tmp_length / 2 * THOUSEP_CHAR_MAXLEN); +# endif + } /* Add 1, to account for a leading sign. */ tmp_length = xsum (tmp_length, 1); break; @@ -2088,12 +2298,18 @@ MAX_ROOM_NEEDED (const arguments *ap, size_t arg_index, FCHAR_T conversion, tmp_length = xsum (tmp_length, 2); break; + case 'e': case 'E': + tmp_length = + 12; /* sign, decimal point, exponent etc. */ + tmp_length = xsum (tmp_length, precision); + break; + case 'f': case 'F': if (type == TYPE_LONGDOUBLE) tmp_length = (unsigned int) (LDBL_MAX_EXP * 0.30103 /* binary -> decimal */ - * 2 /* estimate for FLAG_GROUP */ + * 0.5 * 3 /* estimate for FLAG_GROUP */ ) + 1 /* turn floor into ceil */ + 10; /* sign, decimal point etc. */ @@ -2101,17 +2317,20 @@ MAX_ROOM_NEEDED (const arguments *ap, size_t arg_index, FCHAR_T conversion, tmp_length = (unsigned int) (DBL_MAX_EXP * 0.30103 /* binary -> decimal */ - * 2 /* estimate for FLAG_GROUP */ + * 0.5 * 3 /* estimate for FLAG_GROUP */ ) + 1 /* turn floor into ceil */ + 10; /* sign, decimal point etc. */ tmp_length = xsum (tmp_length, precision); break; - case 'e': case 'E': case 'g': case 'G': + case 'g': case 'G': tmp_length = 12; /* sign, decimal point, exponent etc. */ - tmp_length = xsum (tmp_length, precision); + tmp_length = xsum (tmp_length, + precision + * 0.5 * 3 /* estimate for FLAG_GROUP */ + ); break; case 'a': case 'A': @@ -2258,21 +2477,12 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, goto fail_1_with_EINVAL; { - size_t buf_neededlength; - TCHAR_T *buf; - TCHAR_T *buf_malloced; - const FCHAR_T *cp; - size_t i; - DIRECTIVE *dp; - /* Output string accumulator. */ - DCHAR_T *result; - size_t allocated; - size_t length; - /* Allocate a small buffer that will hold a directive passed to sprintf or snprintf. */ - buf_neededlength = + size_t buf_neededlength = xsum4 (7, d.max_width_length, d.max_precision_length, 6); + TCHAR_T *buf; + TCHAR_T *buf_malloced; #if HAVE_ALLOCA if (buf_neededlength < 4000 / sizeof (TCHAR_T)) { @@ -2291,9 +2501,11 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, buf_malloced = buf; } - result = resultbuf; - allocated = (resultbuf != NULL ? *lengthp : 0); - length = 0; + /* Output string accumulator. */ + DCHAR_T *result = resultbuf; + size_t allocated = (resultbuf != NULL ? *lengthp : 0); + size_t length = 0; + /* Invariants: result is either == resultbuf or malloc-allocated. If result == NULL, resultbuf is == NULL as well. @@ -2304,15 +2516,13 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, #define ENSURE_ALLOCATION_ELSE(needed, oom_statement) \ if ((needed) > allocated) \ { \ - size_t memory_size; \ - DCHAR_T *memory; \ - \ allocated = (allocated > 0 ? xtimes (allocated, 2) : 12); \ if ((needed) > allocated) \ allocated = (needed); \ - memory_size = xtimes (allocated, sizeof (DCHAR_T)); \ + size_t memory_size = xtimes (allocated, sizeof (DCHAR_T)); \ if (size_overflow_p (memory_size)) \ oom_statement \ + DCHAR_T *memory; \ if (result == resultbuf) \ memory = (DCHAR_T *) malloc (memory_size); \ else \ @@ -2326,7 +2536,10 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, #define ENSURE_ALLOCATION(needed) \ ENSURE_ALLOCATION_ELSE((needed), goto out_of_memory; ) - for (cp = format, i = 0, dp = &d.dir[0]; ; cp = dp->dir_end, i++, dp++) + const FCHAR_T *cp; + size_t di; + DIRECTIVE *dp; + for (cp = format, di = 0, dp = &d.dir[0]; ; cp = dp->dir_end, di++, dp++) { if (cp != dp->dir_start) { @@ -2349,7 +2562,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, while (--n > 0); } } - if (i == d.count) + if (di == d.count) break; /* Execute a single directive. */ @@ -2426,22 +2639,16 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, { arg_type type = a.arg[dp->arg_index].type; int flags = dp->flags; - int has_width; - size_t width; - int has_precision; - size_t precision; - has_width = 0; - width = 0; + int has_width = 0; + size_t width = 0; if (dp->width_start != dp->width_end) { if (dp->width_arg_index != ARG_NONE) { - int arg; - if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) abort (); - arg = a.arg[dp->width_arg_index].a.a_int; + int arg = a.arg[dp->width_arg_index].a.a_int; width = arg; if (arg < 0) { @@ -2464,17 +2671,15 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, has_width = 1; } - has_precision = 0; - precision = 0; + int has_precision = 0; + size_t precision = 0; if (dp->precision_start != dp->precision_end) { if (dp->precision_arg_index != ARG_NONE) { - int arg; - if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) abort (); - arg = a.arg[dp->precision_arg_index].a.a_int; + int arg = a.arg[dp->precision_arg_index].a.a_int; /* "A negative precision is taken as if the precision were omitted." */ if (arg >= 0) @@ -2819,22 +3024,16 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, than INT_MAX characters, whence snprintf or sprintf would fail to process it. */ int flags = dp->flags; - int has_width; - size_t width; - int has_precision; - size_t precision; - has_width = 0; - width = 0; + int has_width = 0; + size_t width = 0; if (dp->width_start != dp->width_end) { if (dp->width_arg_index != ARG_NONE) { - int arg; - if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) abort (); - arg = a.arg[dp->width_arg_index].a.a_int; + int arg = a.arg[dp->width_arg_index].a.a_int; width = arg; if (arg < 0) { @@ -2857,17 +3056,15 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, has_width = 1; } - has_precision = 0; - precision = 6; + int has_precision = 0; + size_t precision = 6; if (dp->precision_start != dp->precision_end) { if (dp->precision_arg_index != ARG_NONE) { - int arg; - if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) abort (); - arg = a.arg[dp->precision_arg_index].a.a_int; + int arg = a.arg[dp->precision_arg_index].a.a_int; /* "A negative precision is taken as if the precision were omitted." */ if (arg >= 0) @@ -2889,18 +3086,8 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, { const char *arg = a.arg[dp->arg_index].a.a_string; - size_t bytes; -# if ENABLE_UNISTDIO && DCHAR_IS_TCHAR - size_t characters; -# endif -# if !DCHAR_IS_TCHAR - /* This code assumes that TCHAR_T is 'char'. */ - static_assert (sizeof (TCHAR_T) == 1); - DCHAR_T *tmpdst; - size_t tmpdst_len; -# endif - size_t w; + size_t bytes; if (has_precision) { /* Use only at most PRECISION bytes, from the left. */ @@ -2914,6 +3101,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, } # if ENABLE_UNISTDIO && DCHAR_IS_TCHAR + size_t characters; if (has_width) characters = mbsnlen (arg, bytes); else @@ -2925,6 +3113,10 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, # endif # if !DCHAR_IS_TCHAR + /* This code assumes that TCHAR_T is 'char'. */ + static_assert (sizeof (TCHAR_T) == 1); + DCHAR_T *tmpdst; + size_t tmpdst_len; /* Convert from TCHAR_T[] to DCHAR_T[]. */ tmpdst = DCHAR_CONV_FROM_ENCODING (locale_charset (), @@ -2936,6 +3128,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, goto fail_with_errno; # endif + size_t w; if (has_width) { # if ENABLE_UNISTDIO @@ -3005,18 +3198,15 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, convert back the result from a char[] to a wchar_t[]. Instead, just copy the argument wchar_t[] to the result. */ int flags = dp->flags; - size_t width; - width = 0; + size_t width = 0; if (dp->width_start != dp->width_end) { if (dp->width_arg_index != ARG_NONE) { - int arg; - if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) abort (); - arg = a.arg[dp->width_arg_index].a.a_int; + int arg = a.arg[dp->width_arg_index].a.a_int; width = arg; if (arg < 0) { @@ -3045,20 +3235,15 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, if (dp->conversion == 's') { - int has_precision; - size_t precision; - - has_precision = 0; - precision = 6; + int has_precision = 0; + size_t precision = 6; if (dp->precision_start != dp->precision_end) { if (dp->precision_arg_index != ARG_NONE) { - int arg; - if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) abort (); - arg = a.arg[dp->precision_arg_index].a.a_int; + int arg = a.arg[dp->precision_arg_index].a.a_int; /* "A negative precision is taken as if the precision were omitted." */ if (arg >= 0) @@ -3084,9 +3269,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, { /* Use only at most PRECISION wide characters, from the left. */ - const wchar_t *ls_arg_end; - - ls_arg_end = ls_arg; + const wchar_t *ls_arg_end = ls_arg; characters = 0; for (; precision > 0; precision--) { @@ -3158,22 +3341,16 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, such bugs, we implement the entire processing of the 's' directive ourselves. */ int flags = dp->flags; - int has_width; - size_t width; - int has_precision; - size_t precision; - has_width = 0; - width = 0; + int has_width = 0; + size_t width = 0; if (dp->width_start != dp->width_end) { if (dp->width_arg_index != ARG_NONE) { - int arg; - if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) abort (); - arg = a.arg[dp->width_arg_index].a.a_int; + int arg = a.arg[dp->width_arg_index].a.a_int; width = arg; if (arg < 0) { @@ -3196,17 +3373,15 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, has_width = 1; } - has_precision = 0; - precision = 6; + int has_precision = 0; + size_t precision = 6; if (dp->precision_start != dp->precision_end) { if (dp->precision_arg_index != ARG_NONE) { - int arg; - if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) abort (); - arg = a.arg[dp->precision_arg_index].a.a_int; + int arg = a.arg[dp->precision_arg_index].a.a_int; /* "A negative precision is taken as if the precision were omitted." */ if (arg >= 0) @@ -3308,13 +3483,12 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, if (has_precision || has_width) { /* We know the number of wide characters in advance. */ - size_t remaining; # if HAVE_MBRTOWC mbstate_t state; mbszero (&state); # endif ENSURE_ALLOCATION (xsum (length, characters)); - for (remaining = characters; remaining > 0; remaining--) + for (size_t remaining = characters; remaining > 0; remaining--) { wchar_t wc; int count; @@ -3400,13 +3574,11 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, # endif while (precision > 0) { - char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */ - int count; - if (*arg_end == 0) /* Found the terminating null wide character. */ break; - count = local_wcrtomb (cbuf, *arg_end, &state); + char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */ + int count = local_wcrtomb (cbuf, *arg_end, &state); if (count < 0) /* Cannot convert. */ goto fail_with_EILSEQ; @@ -3439,13 +3611,11 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, # endif for (;;) { - char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */ - int count; - if (*arg_end == 0) /* Found the terminating null wide character. */ break; - count = local_wcrtomb (cbuf, *arg_end, &state); + char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */ + int count = local_wcrtomb (cbuf, *arg_end, &state); if (count < 0) /* Cannot convert. */ goto fail_with_EILSEQ; @@ -3472,27 +3642,22 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, # if !DCHAR_IS_TCHAR { - TCHAR_T *tmpsrc; - /* Convert the string into a piece of temporary memory. */ - tmpsrc = (TCHAR_T *) malloc (bytes * sizeof (TCHAR_T)); + TCHAR_T *tmpsrc = (TCHAR_T *) malloc (bytes * sizeof (TCHAR_T)); if (tmpsrc == NULL) goto out_of_memory; { TCHAR_T *tmpptr = tmpsrc; - size_t remaining; # if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t mbstate_t state; mbszero (&state); # endif - for (remaining = bytes; remaining > 0; ) + for (size_t remaining = bytes; remaining > 0; ) { - char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */ - int count; - if (*arg == 0) abort (); - count = local_wcrtomb (cbuf, *arg, &state); + char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */ + int count = local_wcrtomb (cbuf, *arg, &state); if (count <= 0) /* Inconsistency. */ abort (); @@ -3559,20 +3724,17 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, if (has_precision || has_width) { /* We know the number of bytes in advance. */ - size_t remaining; # if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t mbstate_t state; mbszero (&state); # endif ENSURE_ALLOCATION (xsum (length, bytes)); - for (remaining = bytes; remaining > 0; ) + for (size_t remaining = bytes; remaining > 0; ) { - char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */ - int count; - if (*arg == 0) abort (); - count = local_wcrtomb (cbuf, *arg, &state); + char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */ + int count = local_wcrtomb (cbuf, *arg, &state); if (count <= 0) /* Inconsistency. */ abort (); @@ -3592,12 +3754,10 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, # endif while (arg < arg_end) { - char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */ - int count; - if (*arg == 0) abort (); - count = local_wcrtomb (cbuf, *arg, &state); + char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */ + int count = local_wcrtomb (cbuf, *arg, &state); if (count <= 0) /* Cannot convert. */ goto fail_with_EILSEQ; @@ -3634,20 +3794,16 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, a correct behaviour for the null wint_t argument and/or the fallback that avoids EILSEQ. */ int flags = dp->flags; - int has_width; - size_t width; - has_width = 0; - width = 0; + int has_width = 0; + size_t width = 0; if (dp->width_start != dp->width_end) { if (dp->width_arg_index != ARG_NONE) { - int arg; - if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) abort (); - arg = a.arg[dp->width_arg_index].a.a_int; + int arg = a.arg[dp->width_arg_index].a.a_int; width = arg; if (arg < 0) { @@ -3690,14 +3846,13 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, # endif { /* Count the number of bytes. */ - char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */ - int count; # if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t mbstate_t state; mbszero (&state); # endif - count = local_wcrtomb (cbuf, arg, &state); + char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */ + int count = local_wcrtomb (cbuf, arg, &state); if (count < 0) /* Cannot convert. */ goto fail_with_EILSEQ; @@ -3725,14 +3880,13 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, /* Convert the string into a piece of temporary memory. */ if (bytes > 0) { - char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */ - int count; # if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t mbstate_t state; mbszero (&state); # endif - count = local_wcrtomb (cbuf, arg, &state); + char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */ + int count = local_wcrtomb (cbuf, arg, &state); if (count <= 0) /* Inconsistency. */ abort (); @@ -3792,13 +3946,12 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, ENSURE_ALLOCATION (xsum (length, bytes)); if (bytes > 0) { - int count; # if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t mbstate_t state; mbszero (&state); # endif - count = local_wcrtomb (result + length, arg, &state); + int count = local_wcrtomb (result + length, arg, &state); if (count <= 0) /* Inconsistency. */ abort (); @@ -3807,14 +3960,13 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, } else { - char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */ - int count; # if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t mbstate_t state; mbszero (&state); # endif - count = local_wcrtomb (cbuf, arg, &state); + char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */ + int count = local_wcrtomb (cbuf, arg, &state); if (count < 0) /* Cannot convert. */ goto fail_with_EILSEQ; @@ -3847,18 +3999,15 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, /* Implement the 'c' directive ourselves, in order to avoid EILSEQ in the "C" locale. */ int flags = dp->flags; - size_t width; - width = 0; + size_t width = 0; if (dp->width_start != dp->width_end) { if (dp->width_arg_index != ARG_NONE) { - int arg; - if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) abort (); - arg = a.arg[dp->width_arg_index].a.a_int; + int arg = a.arg[dp->width_arg_index].a.a_int; width = arg; if (arg < 0) { @@ -3884,9 +4033,9 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, { char arg = (char) a.arg[dp->arg_index].a.a_char; mbstate_t state; - wchar_t wc; - mbszero (&state); + + wchar_t wc; int count = mbrtowc (&wc, &arg, 1, &state); if (count < 0) /* Invalid or incomplete multibyte character. */ @@ -3927,30 +4076,16 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, { arg_type type = a.arg[dp->arg_index].type; int flags = dp->flags; - int has_width; - size_t width; - int has_precision; - size_t precision; - size_t tmp_length; - size_t count; - DCHAR_T tmpbuf[700]; - DCHAR_T *tmp; - DCHAR_T *tmp_end; - DCHAR_T *tmp_start; - DCHAR_T *pad_ptr; - DCHAR_T *p; - has_width = 0; - width = 0; + int has_width = 0; + size_t width = 0; if (dp->width_start != dp->width_end) { if (dp->width_arg_index != ARG_NONE) { - int arg; - if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) abort (); - arg = a.arg[dp->width_arg_index].a.a_int; + int arg = a.arg[dp->width_arg_index].a.a_int; width = arg; if (arg < 0) { @@ -3973,17 +4108,15 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, has_width = 1; } - has_precision = 0; - precision = 1; + int has_precision = 0; + size_t precision = 1; if (dp->precision_start != dp->precision_end) { if (dp->precision_arg_index != ARG_NONE) { - int arg; - if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) abort (); - arg = a.arg[dp->precision_arg_index].a.a_int; + int arg = a.arg[dp->precision_arg_index].a.a_int; /* "A negative precision is taken as if the precision were omitted." */ if (arg >= 0) @@ -4004,6 +4137,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, } /* Allocate a temporary buffer of sufficient size. */ + size_t tmp_length; switch (type) { default: @@ -4070,6 +4204,8 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, if (tmp_length < width) tmp_length = width; + DCHAR_T tmpbuf[700]; + DCHAR_T *tmp; if (tmp_length <= sizeof (tmpbuf) / sizeof (DCHAR_T)) tmp = tmpbuf; else @@ -4085,7 +4221,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, goto out_of_memory; } - tmp_end = tmp + tmp_length; + DCHAR_T *tmp_end = tmp + tmp_length; unsigned long long arg; switch (type) @@ -4134,7 +4270,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, } int need_prefix = ((flags & FLAG_ALT) && arg != 0); - p = tmp_end; + DCHAR_T *p = tmp_end; /* "The result of converting a zero value with a precision of zero is no characters." */ if (!(has_precision && precision == 0 && arg == 0)) @@ -4154,7 +4290,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, *--p = '0'; } - pad_ptr = p; + DCHAR_T *pad_ptr = p; if (need_prefix) { @@ -4167,12 +4303,12 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, # endif *--p = '0'; } - tmp_start = p; + DCHAR_T *tmp_start = p; /* The generated string now extends from tmp_start to tmp_end, with the zero padding insertion point being at pad_ptr, tmp_start <= pad_ptr <= tmp_end. */ - count = tmp_end - tmp_start; + size_t count = tmp_end - tmp_start; if (count < width) { @@ -4248,26 +4384,15 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, { arg_type type = a.arg[dp->arg_index].type; int flags = dp->flags; - size_t width; - int has_precision; - size_t precision; - size_t tmp_length; - size_t count; - DCHAR_T tmpbuf[700]; - DCHAR_T *tmp; - DCHAR_T *pad_ptr; - DCHAR_T *p; - width = 0; + size_t width = 0; if (dp->width_start != dp->width_end) { if (dp->width_arg_index != ARG_NONE) { - int arg; - if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) abort (); - arg = a.arg[dp->width_arg_index].a.a_int; + int arg = a.arg[dp->width_arg_index].a.a_int; width = arg; if (arg < 0) { @@ -4289,17 +4414,15 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, goto overflow; } - has_precision = 0; - precision = 0; + int has_precision = 0; + size_t precision = 0; if (dp->precision_start != dp->precision_end) { if (dp->precision_arg_index != ARG_NONE) { - int arg; - if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) abort (); - arg = a.arg[dp->precision_arg_index].a.a_int; + int arg = a.arg[dp->precision_arg_index].a.a_int; /* "A negative precision is taken as if the precision were omitted." */ if (arg >= 0) @@ -4320,6 +4443,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, } /* Allocate a temporary buffer of sufficient size. */ + size_t tmp_length; if (type == TYPE_LONGDOUBLE) tmp_length = (unsigned int) ((LDBL_DIG + 1) @@ -4342,6 +4466,8 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, tmp_length = xsum (tmp_length, 1); /* account for trailing NUL */ + DCHAR_T tmpbuf[700]; + DCHAR_T *tmp; if (tmp_length <= sizeof (tmpbuf) / sizeof (DCHAR_T)) tmp = tmpbuf; else @@ -4357,8 +4483,8 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, goto out_of_memory; } - pad_ptr = NULL; - p = tmp; + DCHAR_T *pad_ptr = NULL; + DCHAR_T *p = tmp; if (type == TYPE_LONGDOUBLE) { # if NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_LONG_DOUBLE || (NEED_WPRINTF_DIRECTIVE_LA && WIDE_CHAR_VERSION) @@ -4377,11 +4503,11 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, } else { - int sign = 0; DECL_LONG_DOUBLE_ROUNDING BEGIN_LONG_DOUBLE_ROUNDING (); + int sign = 0; if (signbit (arg)) /* arg < 0.0L or negative zero */ { sign = -1; @@ -4410,7 +4536,6 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, { int exponent; long double mantissa; - if (arg > 0.0L) mantissa = printf_frexpl (arg, &exponent); else @@ -4424,9 +4549,8 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, { /* Round the mantissa. */ long double tail = mantissa; - size_t q; - for (q = precision; ; q--) + for (size_t q = precision; ; q--) { int digit = (int) tail; tail -= digit; @@ -4441,7 +4565,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, tail *= 16.0L; } if (tail != 0.0L) - for (q = precision; q > 0; q--) + for (size_t q = precision; q > 0; q--) tail *= 0.0625L; mantissa += tail; } @@ -4450,9 +4574,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, *p++ = dp->conversion - 'A' + 'X'; pad_ptr = p; { - int digit; - - digit = (int) mantissa; + int digit = (int) mantissa; mantissa -= digit; *p++ = '0' + digit; if ((flags & FLAG_ALT) @@ -4499,9 +4621,8 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, else { char expbuf[6 + 1]; - const char *ep; sprintf (expbuf, "%+d", exponent); - for (ep = expbuf; (*p = *ep) != '\0'; ep++) + for (const char *ep = expbuf; (*p = *ep) != '\0'; ep++) p++; } # endif @@ -4532,7 +4653,6 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, else { int sign = 0; - if (signbit (arg)) /* arg < 0.0 or negative zero */ { sign = -1; @@ -4561,7 +4681,6 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, { int exponent; double mantissa; - if (arg > 0.0) mantissa = printf_frexp (arg, &exponent); else @@ -4575,9 +4694,8 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, { /* Round the mantissa. */ double tail = mantissa; - size_t q; - for (q = precision; ; q--) + for (size_t q = precision; ; q--) { int digit = (int) tail; tail -= digit; @@ -4592,7 +4710,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, tail *= 16.0; } if (tail != 0.0) - for (q = precision; q > 0; q--) + for (size_t q = precision; q > 0; q--) tail *= 0.0625; mantissa += tail; } @@ -4601,9 +4719,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, *p++ = dp->conversion - 'A' + 'X'; pad_ptr = p; { - int digit; - - digit = (int) mantissa; + int digit = (int) mantissa; mantissa -= digit; *p++ = '0' + digit; if ((flags & FLAG_ALT) @@ -4650,9 +4766,8 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, else { char expbuf[6 + 1]; - const char *ep; sprintf (expbuf, "%+d", exponent); - for (ep = expbuf; (*p = *ep) != '\0'; ep++) + for (const char *ep = expbuf; (*p = *ep) != '\0'; ep++) p++; } # endif @@ -4665,7 +4780,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, /* The generated string now extends from tmp to p, with the zero padding insertion point being at pad_ptr. */ - count = p - tmp; + size_t count = p - tmp; if (count < width) { @@ -4746,8 +4861,8 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, || (a.arg[dp->arg_index].type == TYPE_LONGDOUBLE /* Some systems produce wrong output for Inf, -Inf, and NaN. Some systems in this category - (IRIX 5.3) also do so for -0.0. Therefore we - treat this case here as well. */ + also do so for -0.0. Therefore we treat this + case here as well. */ && is_infinite_or_zerol (a.arg[dp->arg_index].a.a_longdouble)) # endif )) @@ -4756,26 +4871,15 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, arg_type type = a.arg[dp->arg_index].type; # endif int flags = dp->flags; - size_t width; - size_t count; - int has_precision; - size_t precision; - size_t tmp_length; - DCHAR_T tmpbuf[700]; - DCHAR_T *tmp; - DCHAR_T *pad_ptr; - DCHAR_T *p; - width = 0; + size_t width = 0; if (dp->width_start != dp->width_end) { if (dp->width_arg_index != ARG_NONE) { - int arg; - if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) abort (); - arg = a.arg[dp->width_arg_index].a.a_int; + int arg = a.arg[dp->width_arg_index].a.a_int; width = arg; if (arg < 0) { @@ -4797,17 +4901,15 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, goto overflow; } - has_precision = 0; - precision = 0; + int has_precision = 0; + size_t precision = 0; if (dp->precision_start != dp->precision_end) { if (dp->precision_arg_index != ARG_NONE) { - int arg; - if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) abort (); - arg = a.arg[dp->precision_arg_index].a.a_int; + int arg = a.arg[dp->precision_arg_index].a.a_int; /* "A negative precision is taken as if the precision were omitted." */ if (arg >= 0) @@ -4836,6 +4938,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, precision = 6; /* Allocate a temporary buffer of sufficient size. */ + size_t tmp_length; # if NEED_PRINTF_DOUBLE && NEED_PRINTF_LONG_DOUBLE tmp_length = (type == TYPE_LONGDOUBLE ? LDBL_DIG + 1 : DBL_DIG + 1); # elif NEED_PRINTF_INFINITE_DOUBLE && NEED_PRINTF_LONG_DOUBLE @@ -4881,6 +4984,17 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, } } # endif + /* Account for thousands separators. */ + if (flags & FLAG_GROUP) + { + /* A thousands separator needs to be inserted at most every 2 digits. + This is the case in the ta_IN locale. */ +# if WIDE_CHAR_VERSION + tmp_length = xsum (tmp_length, tmp_length / 2 * THOUSEP_WCHAR_MAXLEN); +# else + tmp_length = xsum (tmp_length, tmp_length / 2 * THOUSEP_CHAR_MAXLEN); +# endif + } /* Account for sign, decimal point etc. */ tmp_length = xsum (tmp_length, 12); @@ -4889,6 +5003,8 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, tmp_length = xsum (tmp_length, 1); /* account for trailing NUL */ + DCHAR_T tmpbuf[700]; + DCHAR_T *tmp; if (tmp_length <= sizeof (tmpbuf) / sizeof (DCHAR_T)) tmp = tmpbuf; else @@ -4904,8 +5020,8 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, goto out_of_memory; } - pad_ptr = NULL; - p = tmp; + DCHAR_T *pad_ptr = NULL; + DCHAR_T *p = tmp; # if NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_INFINITE_LONG_DOUBLE # if NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_DOUBLE @@ -4927,11 +5043,11 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, } else { - int sign = 0; DECL_LONG_DOUBLE_ROUNDING BEGIN_LONG_DOUBLE_ROUNDING (); + int sign = 0; if (signbit (arg)) /* arg < 0.0L or negative zero */ { sign = -1; @@ -4963,25 +5079,94 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, if (dp->conversion == 'f' || dp->conversion == 'F') { - char *digits; - size_t ndigits; - - digits = + char *digits = scale10_round_decimal_long_double (arg, precision); if (digits == NULL) { END_LONG_DOUBLE_ROUNDING (); goto out_of_memory; } - ndigits = strlen (digits); + size_t ndigits = strlen (digits); if (ndigits > precision) - do - { - --ndigits; - *p++ = digits[ndigits]; - } - while (ndigits > precision); + { + /* Number of digits before the decimal point. */ + size_t intpart_digits = ndigits - precision; + + const DCHAR_T *thousep = NULL; + DCHAR_T thousep_buf[10]; +# if !WIDE_CHAR_VERSION + size_t thousep_len = 0; +# endif + const signed char *grouping; + size_t insert = 0; + + if ((flags & FLAG_GROUP) && (intpart_digits > 1)) + { + /* Determine the thousands separator and + the grouping rule of the current locale. */ +# if WIDE_CHAR_VERSION + /* DCHAR_T is wchar_t. */ + thousep = thousands_separator_wchar (thousep_buf); +# define thousep_len 1 +# elif defined DCHAR_CONV_FROM_ENCODING + /* DCHAR_T is uintN_t. */ + thousep = thousands_separator_DCHAR (thousep_buf); + thousep_len = DCHAR_STRLEN (thousep); +# else + /* DCHAR_T is char. */ + thousep = thousands_separator_char (thousep_buf); + thousep_len = strlen (thousep); +# endif + if (*thousep == 0) + thousep = NULL; + if (thousep != NULL) + { + grouping = grouping_rule (); + insert = + num_thousands_separators (grouping, intpart_digits); + } + } + + const char *digitp = digits + precision; + DCHAR_T *p_before_intpart = p; + p += intpart_digits + insert * thousep_len; + DCHAR_T *p_after_intpart = p; + if (insert > 0) /* implies (flag & FLAG_GROUP) && (thousep != NULL) */ + { + const signed char *g = grouping; + for (;;) + { + int h = *g; + if (h <= 0) + abort (); + int i = h; + do + *--p = *digitp++; + while (--i > 0); +# if WIDE_CHAR_VERSION + *--p = thousep[0]; +# else + p -= thousep_len; + DCHAR_CPY (p, thousep, thousep_len); +# endif + insert--; + if (insert == 0) + break; + if (g[1] != 0) + g++; + } + } + for (;;) + { + *--p = *digitp++; + if (p == p_before_intpart) + break; + } + p = p_after_intpart; + ndigits = precision; +# undef thousep_len + } else *p++ = '0'; /* Here ndigits <= precision. */ @@ -5017,12 +5202,10 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, else { /* arg > 0.0L. */ - int adjusted; + exponent = floorlog10l (arg); + int adjusted = 0; char *digits; size_t ndigits; - - exponent = floorlog10l (arg); - adjusted = 0; for (;;) { digits = @@ -5113,9 +5296,8 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, else { char expbuf[6 + 1]; - const char *ep; sprintf (expbuf, "%+.2d", exponent); - for (ep = expbuf; (*p = *ep) != '\0'; ep++) + for (const char *ep = expbuf; (*p = *ep) != '\0'; ep++) p++; } # endif @@ -5151,14 +5333,10 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, else { /* arg > 0.0L. */ - int exponent; - int adjusted; + int exponent = floorlog10l (arg); + int adjusted = 0; char *digits; size_t ndigits; - size_t nzeroes; - - exponent = floorlog10l (arg); - adjusted = 0; for (;;) { digits = @@ -5218,7 +5396,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, /* Determine the number of trailing zeroes that have to be dropped. */ - nzeroes = 0; + size_t nzeroes = 0; if ((flags & FLAG_ALT) == 0) while (nzeroes < ndigits && digits[nzeroes] == '0') @@ -5234,10 +5412,84 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, digits without trailing zeroes. */ if (exponent >= 0) { - size_t ecount = exponent + 1; - /* Note: count <= precision = ndigits. */ - for (; ecount > 0; ecount--) - *p++ = digits[--ndigits]; + /* Number of digits before the decimal point. */ + size_t intpart_digits = exponent + 1; + /* Note: intpart_digits <= precision = ndigits. */ + + const DCHAR_T *thousep = NULL; + DCHAR_T thousep_buf[10]; +# if !WIDE_CHAR_VERSION + size_t thousep_len = 0; +# endif + const signed char *grouping; + size_t insert = 0; + + if ((flags & FLAG_GROUP) && (intpart_digits > 1)) + { + /* Determine the thousands separator and + the grouping rule of the current locale. */ +# if WIDE_CHAR_VERSION + /* DCHAR_T is wchar_t. */ + thousep = thousands_separator_wchar (thousep_buf); +# define thousep_len 1 +# elif defined DCHAR_CONV_FROM_ENCODING + /* DCHAR_T is uintN_t. */ + thousep = thousands_separator_DCHAR (thousep_buf); + thousep_len = DCHAR_STRLEN (thousep); +# else + /* DCHAR_T is char. */ + thousep = thousands_separator_char (thousep_buf); + thousep_len = strlen (thousep); +# endif + if (*thousep == 0) + thousep = NULL; + if (thousep != NULL) + { + grouping = grouping_rule (); + insert = + num_thousands_separators (grouping, intpart_digits); + } + } + + const char *digitp = digits + ndigits - intpart_digits; + DCHAR_T *p_before_intpart = p; + p += intpart_digits + insert * thousep_len; + DCHAR_T *p_after_intpart = p; + if (insert > 0) /* implies (flag & FLAG_GROUP) && (thousep != NULL) */ + { + const signed char *g = grouping; + for (;;) + { + int h = *g; + if (h <= 0) + abort (); + int i = h; + do + *--p = *digitp++; + while (--i > 0); +# if WIDE_CHAR_VERSION + *--p = thousep[0]; +# else + p -= thousep_len; + DCHAR_CPY (p, thousep, thousep_len); +# endif + insert--; + if (insert == 0) + break; + if (g[1] != 0) + g++; + } + } + for (;;) + { + *--p = *digitp++; + if (p == p_before_intpart) + break; + } + p = p_after_intpart; + ndigits -= intpart_digits; +# undef thousep_len + if ((flags & FLAG_ALT) || ndigits > nzeroes) { *p++ = decimal_point_char (); @@ -5294,9 +5546,8 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, else { char expbuf[6 + 1]; - const char *ep; sprintf (expbuf, "%+.2d", exponent); - for (ep = expbuf; (*p = *ep) != '\0'; ep++) + for (const char *ep = expbuf; (*p = *ep) != '\0'; ep++) p++; } # endif @@ -5396,7 +5647,6 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, else { int sign = 0; - if (signbit (arg)) /* arg < 0.0 or negative zero */ { sign = -1; @@ -5428,22 +5678,91 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, if (dp->conversion == 'f' || dp->conversion == 'F') { - char *digits; - size_t ndigits; - - digits = + char *digits = scale10_round_decimal_double (arg, precision); if (digits == NULL) goto out_of_memory; - ndigits = strlen (digits); + size_t ndigits = strlen (digits); if (ndigits > precision) - do - { - --ndigits; - *p++ = digits[ndigits]; - } - while (ndigits > precision); + { + /* Number of digits before the decimal point. */ + size_t intpart_digits = ndigits - precision; + + const DCHAR_T *thousep = NULL; + DCHAR_T thousep_buf[10]; +# if !WIDE_CHAR_VERSION + size_t thousep_len = 0; +# endif + const signed char *grouping; + size_t insert = 0; + + if ((flags & FLAG_GROUP) && (intpart_digits > 1)) + { + /* Determine the thousands separator and + the grouping rule of the current locale. */ +# if WIDE_CHAR_VERSION + /* DCHAR_T is wchar_t. */ + thousep = thousands_separator_wchar (thousep_buf); +# define thousep_len 1 +# elif defined DCHAR_CONV_FROM_ENCODING + /* DCHAR_T is uintN_t. */ + thousep = thousands_separator_DCHAR (thousep_buf); + thousep_len = DCHAR_STRLEN (thousep); +# else + /* DCHAR_T is char. */ + thousep = thousands_separator_char (thousep_buf); + thousep_len = strlen (thousep); +# endif + if (*thousep == 0) + thousep = NULL; + if (thousep != NULL) + { + grouping = grouping_rule (); + insert = + num_thousands_separators (grouping, intpart_digits); + } + } + + const char *digitp = digits + precision; + DCHAR_T *p_before_intpart = p; + p += intpart_digits + insert * thousep_len; + DCHAR_T *p_after_intpart = p; + if (insert > 0) /* implies (flag & FLAG_GROUP) && (thousep != NULL) */ + { + const signed char *g = grouping; + for (;;) + { + int h = *g; + if (h <= 0) + abort (); + int i = h; + do + *--p = *digitp++; + while (--i > 0); +# if WIDE_CHAR_VERSION + *--p = thousep[0]; +# else + p -= thousep_len; + DCHAR_CPY (p, thousep, thousep_len); +# endif + insert--; + if (insert == 0) + break; + if (g[1] != 0) + g++; + } + } + for (;;) + { + *--p = *digitp++; + if (p == p_before_intpart) + break; + } + p = p_after_intpart; + ndigits = precision; +# undef thousep_len + } else *p++ = '0'; /* Here ndigits <= precision. */ @@ -5479,12 +5798,10 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, else { /* arg > 0.0. */ - int adjusted; + exponent = floorlog10 (arg); + int adjusted = 0; char *digits; size_t ndigits; - - exponent = floorlog10 (arg); - adjusted = 0; for (;;) { digits = @@ -5586,9 +5903,8 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, else { char expbuf[6 + 1]; - const char *ep; sprintf (expbuf, decimal_format, exponent); - for (ep = expbuf; (*p = *ep) != '\0'; ep++) + for (const char *ep = expbuf; (*p = *ep) != '\0'; ep++) p++; } } @@ -5625,14 +5941,10 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, else { /* arg > 0.0. */ - int exponent; - int adjusted; + int exponent = floorlog10 (arg); + int adjusted = 0; char *digits; size_t ndigits; - size_t nzeroes; - - exponent = floorlog10 (arg); - adjusted = 0; for (;;) { digits = @@ -5688,7 +6000,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, /* Determine the number of trailing zeroes that have to be dropped. */ - nzeroes = 0; + size_t nzeroes = 0; if ((flags & FLAG_ALT) == 0) while (nzeroes < ndigits && digits[nzeroes] == '0') @@ -5704,10 +6016,84 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, digits without trailing zeroes. */ if (exponent >= 0) { - size_t ecount = exponent + 1; - /* Note: ecount <= precision = ndigits. */ - for (; ecount > 0; ecount--) - *p++ = digits[--ndigits]; + /* Number of digits before the decimal point. */ + size_t intpart_digits = exponent + 1; + /* Note: intpart_digits <= precision = ndigits. */ + + const DCHAR_T *thousep = NULL; + DCHAR_T thousep_buf[10]; +# if !WIDE_CHAR_VERSION + size_t thousep_len = 0; +# endif + const signed char *grouping; + size_t insert = 0; + + if ((flags & FLAG_GROUP) && (intpart_digits > 1)) + { + /* Determine the thousands separator and + the grouping rule of the current locale. */ +# if WIDE_CHAR_VERSION + /* DCHAR_T is wchar_t. */ + thousep = thousands_separator_wchar (thousep_buf); +# define thousep_len 1 +# elif defined DCHAR_CONV_FROM_ENCODING + /* DCHAR_T is uintN_t. */ + thousep = thousands_separator_DCHAR (thousep_buf); + thousep_len = DCHAR_STRLEN (thousep); +# else + /* DCHAR_T is char. */ + thousep = thousands_separator_char (thousep_buf); + thousep_len = strlen (thousep); +# endif + if (*thousep == 0) + thousep = NULL; + if (thousep != NULL) + { + grouping = grouping_rule (); + insert = + num_thousands_separators (grouping, intpart_digits); + } + } + + const char *digitp = digits + ndigits - intpart_digits; + DCHAR_T *p_before_intpart = p; + p += intpart_digits + insert * thousep_len; + DCHAR_T *p_after_intpart = p; + if (insert > 0) /* implies (flag & FLAG_GROUP) && (thousep != NULL) */ + { + const signed char *g = grouping; + for (;;) + { + int h = *g; + if (h <= 0) + abort (); + int i = h; + do + *--p = *digitp++; + while (--i > 0); +# if WIDE_CHAR_VERSION + *--p = thousep[0]; +# else + p -= thousep_len; + DCHAR_CPY (p, thousep, thousep_len); +# endif + insert--; + if (insert == 0) + break; + if (g[1] != 0) + g++; + } + } + for (;;) + { + *--p = *digitp++; + if (p == p_before_intpart) + break; + } + p = p_after_intpart; + ndigits -= intpart_digits; +# undef thousep_len + if ((flags & FLAG_ALT) || ndigits > nzeroes) { *p++ = decimal_point_char (); @@ -5779,9 +6165,8 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, else { char expbuf[6 + 1]; - const char *ep; sprintf (expbuf, decimal_format, exponent); - for (ep = expbuf; (*p = *ep) != '\0'; ep++) + for (const char *ep = expbuf; (*p = *ep) != '\0'; ep++) p++; } } @@ -5851,7 +6236,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, /* The generated string now extends from tmp to p, with the zero padding insertion point being at pad_ptr. */ - count = p - tmp; + size_t count = p - tmp; if (count < width) { @@ -5915,24 +6300,19 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, { arg_type type = a.arg[dp->arg_index].type; int flags = dp->flags; -#if (WIDE_CHAR_VERSION && MUSL_LIBC) || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_FLAG_ALT_PRECISION_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION - int has_width; -#endif -#if !USE_SNPRINTF || WIDE_CHAR_VERSION || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_FLAG_ALT_PRECISION_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION - size_t width; -#endif -#if !USE_SNPRINTF || (WIDE_CHAR_VERSION && DCHAR_IS_TCHAR) || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || (WIDE_CHAR_VERSION && MUSL_LIBC) || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_FLAG_ALT_PRECISION_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION - int has_precision; - size_t precision; -#endif #if NEED_PRINTF_FLAG_ALT_PRECISION_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION int prec_ourselves; #else # define prec_ourselves 0 #endif +#if NEED_PRINTF_FLAG_ALT_PRECISION_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION || NEED_PRINTF_FLAG_GROUPING || NEED_PRINTF_FLAG_GROUPING_INT + int group_ourselves; +#else +# define group_ourselves 0 +#endif #if (WIDE_CHAR_VERSION && MUSL_LIBC) || NEED_PRINTF_FLAG_LEFTADJUST # define pad_ourselves 1 -#elif !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_FLAG_ALT_PRECISION_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION +#elif !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_FLAG_ALT_PRECISION_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION || NEED_PRINTF_FLAG_GROUPING || NEED_PRINTF_FLAG_GROUPING_INT int pad_ourselves; #else # define pad_ourselves 0 @@ -5947,20 +6327,18 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, TCHAR_T *tmp; #endif -#if (WIDE_CHAR_VERSION && MUSL_LIBC) || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_FLAG_ALT_PRECISION_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION - has_width = 0; +#if (WIDE_CHAR_VERSION && MUSL_LIBC) || NEED_PRINTF_FLAG_LEFTADJUST || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_FLAG_ALT_PRECISION_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION || NEED_PRINTF_FLAG_GROUPING || NEED_PRINTF_FLAG_GROUPING_INT + int has_width = 0; #endif -#if !USE_SNPRINTF || WIDE_CHAR_VERSION || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_FLAG_ALT_PRECISION_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION - width = 0; +#if !USE_SNPRINTF || WIDE_CHAR_VERSION || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || NEED_PRINTF_FLAG_LEFTADJUST || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_FLAG_ALT_PRECISION_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION || NEED_PRINTF_FLAG_GROUPING || NEED_PRINTF_FLAG_GROUPING_INT + size_t width = 0; if (dp->width_start != dp->width_end) { if (dp->width_arg_index != ARG_NONE) { - int arg; - if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) abort (); - arg = a.arg[dp->width_arg_index].a.a_int; + int arg = a.arg[dp->width_arg_index].a.a_int; width = arg; if (arg < 0) { @@ -5981,24 +6359,22 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, if (width > (size_t) INT_MAX) goto overflow; # define WIDTH_IS_CHECKED 1 -# if (WIDE_CHAR_VERSION && MUSL_LIBC) || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_FLAG_ALT_PRECISION_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION +# if (WIDE_CHAR_VERSION && MUSL_LIBC) || NEED_PRINTF_FLAG_LEFTADJUST || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_FLAG_ALT_PRECISION_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION || NEED_PRINTF_FLAG_GROUPING || NEED_PRINTF_FLAG_GROUPING_INT has_width = 1; # endif } #endif -#if !USE_SNPRINTF || (WIDE_CHAR_VERSION && DCHAR_IS_TCHAR) || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || (WIDE_CHAR_VERSION && MUSL_LIBC) || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_FLAG_ALT_PRECISION_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION - has_precision = 0; - precision = 6; +#if !USE_SNPRINTF || (WIDE_CHAR_VERSION && DCHAR_IS_TCHAR) || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || (WIDE_CHAR_VERSION && MUSL_LIBC) || NEED_PRINTF_FLAG_LEFTADJUST || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_FLAG_ALT_PRECISION_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION || NEED_PRINTF_FLAG_GROUPING || NEED_PRINTF_FLAG_GROUPING_INT + int has_precision = 0; + size_t precision = 6; if (dp->precision_start != dp->precision_end) { if (dp->precision_arg_index != ARG_NONE) { - int arg; - if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) abort (); - arg = a.arg[dp->precision_arg_index].a.a_int; + int arg = a.arg[dp->precision_arg_index].a.a_int; /* "A negative precision is taken as if the precision were omitted." */ if (arg >= 0) @@ -6052,25 +6428,56 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, } #endif + /* Decide whether to add the thousands separators ourselves. */ +#if NEED_PRINTF_FLAG_ALT_PRECISION_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION || NEED_PRINTF_FLAG_GROUPING || NEED_PRINTF_FLAG_GROUPING_INT + if (flags & FLAG_GROUP) + { + switch (dp->conversion) + { + case 'd': case 'i': case 'u': +# if NEED_PRINTF_FLAG_GROUPING || NEED_PRINTF_FLAG_GROUPING_INT + group_ourselves = 1; +# else + group_ourselves = prec_ourselves; +# endif + break; + case 'f': case 'F': case 'g': case 'G': +# if NEED_PRINTF_FLAG_GROUPING + group_ourselves = 1; +# else + group_ourselves = prec_ourselves; +# endif + break; + default: + group_ourselves = 0; + break; + } + } + else + group_ourselves = 0; +#endif + /* Decide whether to perform the padding ourselves. */ -#if !((WIDE_CHAR_VERSION && MUSL_LIBC) || NEED_PRINTF_FLAG_LEFTADJUST) && (!DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_FLAG_ALT_PRECISION_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION) +#if !((WIDE_CHAR_VERSION && MUSL_LIBC) || NEED_PRINTF_FLAG_LEFTADJUST) && (!DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_FLAG_ALT_PRECISION_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION || NEED_PRINTF_FLAG_GROUPING || NEED_PRINTF_FLAG_GROUPING_INT) switch (dp->conversion) { -# if !DCHAR_IS_TCHAR || ENABLE_UNISTDIO +# if !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_ZERO +# if !DCHAR_IS_TCHAR || ENABLE_UNISTDIO /* If we need conversion from TCHAR_T[] to DCHAR_T[], we need to perform the padding after this conversion. Functions with unistdio extensions perform the padding based on character count rather than element count. */ case 'c': case 's': -# endif -# if NEED_PRINTF_FLAG_ZERO +# endif +# if NEED_PRINTF_FLAG_ZERO case 'f': case 'F': case 'e': case 'E': case 'g': case 'G': case 'a': case 'A': -# endif +# endif pad_ourselves = 1; break; +# endif default: - pad_ourselves = prec_ourselves; + pad_ourselves = prec_ourselves | group_ourselves; break; } #endif @@ -6103,14 +6510,8 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, sprintf. */ fbp = buf; *fbp++ = '%'; -#if NEED_PRINTF_FLAG_GROUPING - /* The underlying implementation doesn't support the ' flag. - Produce no grouping characters in this case; this is - acceptable because the grouping is locale dependent. */ -#else - if (flags & FLAG_GROUP) + if ((flags & FLAG_GROUP) && !group_ourselves) *fbp++ = '\''; -#endif if (flags & FLAG_LEFT) *fbp++ = '-'; if (flags & FLAG_SHOWSIGN) @@ -6142,11 +6543,9 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, quick check anyway. */ if (dp->width_arg_index != ARG_NONE) { - int arg; - if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) abort (); - arg = a.arg[dp->width_arg_index].a.a_int; + int arg = a.arg[dp->width_arg_index].a.a_int; width = arg; if (arg < 0) { @@ -6205,7 +6604,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, } } - switch (type) + switch (+type) { case TYPE_LONGLONGINT: case TYPE_ULONGLONGINT: @@ -6371,7 +6770,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, /* The following platforms forbid %n: - On glibc2 systems from 2004-10-18 or newer, the use of %n in format strings in writable memory may crash the - program (if compiled with _FORTIFY_SOURCE=2). + program (if compiled with _FORTIFY_SOURCE >= 2). - On macOS 10.13 or newer, the use of %n in format strings in writable memory by default crashes the program. @@ -6404,7 +6803,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, on musl libc because we would run into an swprintf() bug. See <https://www.openwall.com/lists/musl/2023/03/19/1>. */ fbp[1] = '\0'; -# else /* AIX <= 5.1, HP-UX, IRIX, OSF/1, Solaris <= 9, BeOS */ +# else /* AIX <= 5.1, HP-UX, Solaris <= 9, BeOS */ fbp[1] = '%'; fbp[2] = 'n'; fbp[3] = '\0'; @@ -6501,7 +6900,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, #endif errno = 0; - switch (type) + switch (+type) { case TYPE_SCHAR: { @@ -6870,10 +7269,13 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, || *prec_ptr == ' ')) prefix_count = 1; /* Put the additional zeroes after the 0x prefix if - (flags & FLAG_ALT) || (dp->conversion == 'p'). */ + (flags & FLAG_ALT) || (dp->conversion == 'p'), or + after the 0b prefix if (flags & FLAG_ALT). */ else if (count >= 2 && prec_ptr[0] == '0' - && (prec_ptr[1] == 'x' || prec_ptr[1] == 'X')) + && (prec_ptr[1] == 'x' || prec_ptr[1] == 'X' + || prec_ptr[1] == 'b' + || prec_ptr[1] == 'B')) prefix_count = 2; move = count - prefix_count; @@ -6922,6 +7324,135 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, } #endif +#if NEED_PRINTF_FLAG_ALT_PRECISION_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION || NEED_PRINTF_FLAG_GROUPING || NEED_PRINTF_FLAG_GROUPING_INT + if (group_ourselves) /* implies (flags & FLAG_GROUP) */ + /* Handle the grouping. */ + switch (dp->conversion) + { + /* These are the only conversion to which grouping + applies. */ + case 'd': case 'i': case 'u': + case 'f': case 'F': case 'g': case 'G': + { + /* Determine the thousands separator of the current + locale. */ + const TCHAR_T *thousep; + TCHAR_T thousep_buf[10]; + +# if WIDE_CHAR_VERSION && DCHAR_IS_TCHAR + /* TCHAR_T is wchar_t. */ + thousep = thousands_separator_wchar (thousep_buf); +# else + /* TCHAR_T is char. */ + thousep = thousands_separator_char (thousep_buf); +# endif + + /* Nothing to do in locales where thousep is the empty + string. */ + if (*thousep != 0) + { + /* Since FLAG_LOCALIZED is only supported on glibc + systems, here we can assume that all digits are + the ASCII digits '0'..'9'. */ + TCHAR_T *number_ptr = +# if USE_SNPRINTF + (TCHAR_T *) (result + length); +# else + tmp; +# endif + TCHAR_T *end_ptr = number_ptr + count; + + /* Find where the leading digits start. */ + TCHAR_T *digits_ptr = number_ptr; + if (count >= 1 + && (*digits_ptr == '-' || *digits_ptr == '+' + || *digits_ptr == ' ')) + digits_ptr++; + + /* Find where the leading digits end. */ + TCHAR_T *digits_end_ptr; + switch (dp->conversion) + { + case 'd': case 'i': case 'u': + digits_end_ptr = end_ptr; + break; + case 'f': case 'F': case 'g': case 'G': + { + TCHAR_T decimal_point = decimal_point_char (); + for (digits_end_ptr = digits_ptr; + digits_end_ptr < end_ptr; + digits_end_ptr++) + if (*digits_end_ptr == decimal_point + || *digits_end_ptr == 'e') + break; + } + break; + } + + /* Determine the number of thousands separators + to insert. */ + const signed char *grouping = grouping_rule (); + size_t insert = + num_thousands_separators (grouping, digits_end_ptr - digits_ptr); + if (insert > 0) + { +# if WIDE_CHAR_VERSION && DCHAR_IS_TCHAR +# define thousep_len 1 +# else + size_t thousep_len = strlen (thousep); +# endif +# if USE_SNPRINTF + size_t digits_offset = digits_ptr - number_ptr; + size_t digits_end_offset = digits_end_ptr - number_ptr; + size_t n = + xsum (length, + (count + insert * thousep_len + TCHARS_PER_DCHAR - 1) + / TCHARS_PER_DCHAR); + length += (count + TCHARS_PER_DCHAR - 1) / TCHARS_PER_DCHAR; + ENSURE_ALLOCATION (n); + length -= (count + TCHARS_PER_DCHAR - 1) / TCHARS_PER_DCHAR; + number_ptr = (TCHAR_T *) (result + length); + end_ptr = number_ptr + count; + digits_ptr = number_ptr + digits_offset; + digits_end_ptr = number_ptr + digits_end_offset; +# endif + + count += insert * thousep_len; + + const TCHAR_T *p = end_ptr; + TCHAR_T *q = end_ptr + insert * thousep_len; + while (p > digits_end_ptr) + *--q = *--p; + const signed char *g = grouping; + for (;;) + { + int h = *g; + if (h <= 0) + abort (); + int i = h; + do + *--q = *--p; + while (--i > 0); +# if WIDE_CHAR_VERSION && DCHAR_IS_TCHAR + *--q = *thousep; +# else + q -= thousep_len; + memcpy (q, thousep, thousep_len); +# endif + insert--; + if (insert == 0) + break; + if (g[1] != 0) + g++; + } + /* Here q == p. Done with the insertions. */ + } + } + } + break; + } +#endif + #if !USE_SNPRINTF if (count >= tmp_length) /* tmp_length was incorrectly calculated - fix the @@ -6932,22 +7463,23 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, #if !DCHAR_IS_TCHAR /* Convert from TCHAR_T[] to DCHAR_T[]. */ if (dp->conversion == 'c' || dp->conversion == 's' + || (flags & FLAG_GROUP) # if __GLIBC__ >= 2 && !defined __UCLIBC__ || (flags & FLAG_LOCALIZED) # endif ) { /* The result string is not guaranteed to be ASCII. */ - const TCHAR_T *tmpsrc; - DCHAR_T *tmpdst; - size_t tmpdst_len; /* This code assumes that TCHAR_T is 'char'. */ static_assert (sizeof (TCHAR_T) == 1); + const TCHAR_T *tmpsrc; # if USE_SNPRINTF tmpsrc = (TCHAR_T *) (result + length); # else tmpsrc = tmp; # endif + DCHAR_T *tmpdst; + size_t tmpdst_len; # if WIDE_CHAR_VERSION /* Convert tmpsrc[0..count-1] to a freshly allocated wide character array. */ @@ -7025,9 +7557,6 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, # endif { const TCHAR_T *tmpsrc; - DCHAR_T *tmpdst; - size_t n; - # if USE_SNPRINTF if (result == resultbuf) { @@ -7047,11 +7576,11 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, tmpsrc = tmp; ENSURE_ALLOCATION (xsum (length, count)); # endif - tmpdst = result + length; + DCHAR_T *tmpdst = result + length; /* Copy backwards, because of overlapping. */ tmpsrc += count; tmpdst += count; - for (n = count; n > 0; n--) + for (size_t n = count; n > 0; n--) *--tmpdst = *--tmpsrc; } } @@ -7073,7 +7602,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, /* Here count <= allocated - length. */ /* Perform padding. */ -#if (WIDE_CHAR_VERSION && MUSL_LIBC) || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_FLAG_ALT_PRECISION_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION +#if (WIDE_CHAR_VERSION && MUSL_LIBC) || NEED_PRINTF_FLAG_LEFTADJUST || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_FLAG_ALT_PRECISION_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION || NEED_PRINTF_FLAG_GROUPING || NEED_PRINTF_FLAG_GROUPING_INT if (pad_ourselves && has_width) { size_t w; @@ -7082,6 +7611,23 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, against the number of _characters_ of the converted value. */ w = DCHAR_MBSNLEN (result + length, count); +# elif __GLIBC__ >= 2 + /* glibc prefers to compare the width against the number + of characters as well, but only for numeric conversion + specifiers. See + <https://sourceware.org/PR28943> + <https://sourceware.org/PR30883> + <https://sourceware.org/PR31542> */ + switch (dp->conversion) + { + case 'd': case 'i': case 'u': + case 'f': case 'F': case 'g': case 'G': + w = DCHAR_MBSNLEN (result + length, count); + break; + default: + w = count; + break; + } # else /* The width is compared against the number of _bytes_ of the converted value, says POSIX. */ @@ -7219,8 +7765,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, { /* Convert the %f result to upper case for %F. */ DCHAR_T *rp = result + length; - size_t rc; - for (rc = count; rc > 0; rc--, rp++) + for (size_t rc = count; rc > 0; rc--, rp++) if (*rp >= 'a' && *rp <= 'z') *rp = *rp - 'a' + 'A'; } @@ -7243,9 +7788,8 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, if (result != resultbuf && length + 1 < allocated) { /* Shrink the allocated memory if possible. */ - DCHAR_T *memory; - - memory = (DCHAR_T *) realloc (result, (length + 1) * sizeof (DCHAR_T)); + DCHAR_T *memory = + (DCHAR_T *) realloc (result, (length + 1) * sizeof (DCHAR_T)); if (memory != NULL) result = memory; } |
