summaryrefslogtreecommitdiff
path: root/lib/vasnprintf.c
diff options
context:
space:
mode:
authorJörg Frings-Fürst <debian@jff.email>2025-10-18 19:06:52 +0200
committerJörg Frings-Fürst <debian@jff.email>2025-10-18 19:06:52 +0200
commit27dae84ed92f1ef0300263091972338d12e78348 (patch)
tree7c52931f474fafb8a4bd4fd15ca3461c77cdecc2 /lib/vasnprintf.c
parent4682deeb62247d34de87f8e777f99e2d337fd377 (diff)
New upstream version 1.4.1upstream/1.4.1upstream
Diffstat (limited to 'lib/vasnprintf.c')
-rw-r--r--lib/vasnprintf.c862
1 files changed, 800 insertions, 62 deletions
diff --git a/lib/vasnprintf.c b/lib/vasnprintf.c
index 6ae95ca5..614da318 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-2025 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
@@ -179,10 +180,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 +232,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 +246,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 +384,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 +411,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. */
@@ -1839,8 +2076,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 +2334,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 +2353,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':
@@ -2262,7 +2517,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
TCHAR_T *buf;
TCHAR_T *buf_malloced;
const FCHAR_T *cp;
- size_t i;
+ size_t di;
DIRECTIVE *dp;
/* Output string accumulator. */
DCHAR_T *result;
@@ -2326,7 +2581,7 @@ 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++)
+ for (cp = format, di = 0, dp = &d.dir[0]; ; cp = dp->dir_end, di++, dp++)
{
if (cp != dp->dir_start)
{
@@ -2349,7 +2604,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
while (--n > 0);
}
}
- if (i == d.count)
+ if (di == d.count)
break;
/* Execute a single directive. */
@@ -4746,8 +5001,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
))
@@ -4881,6 +5136,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);
@@ -4976,12 +5242,84 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
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. */
@@ -5234,10 +5572,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 ();
@@ -5438,12 +5850,84 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
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. */
@@ -5704,10 +6188,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 ();
@@ -5915,13 +6473,13 @@ 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
+#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;
#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
+#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;
#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
+#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;
size_t precision;
#endif
@@ -5930,9 +6488,14 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
#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,10 +6510,10 @@ 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
+#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 = 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
+#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
width = 0;
if (dp->width_start != dp->width_end)
{
@@ -5981,13 +6544,13 @@ 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
+#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
has_precision = 0;
precision = 6;
if (dp->precision_start != dp->precision_end)
@@ -6052,25 +6615,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 +6697,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)
@@ -6205,7 +6793,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
}
}
- switch (type)
+ switch (+type)
{
case TYPE_LONGLONGINT:
case TYPE_ULONGLONGINT:
@@ -6404,7 +6992,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 +7089,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
#endif
errno = 0;
- switch (type)
+ switch (+type)
{
case TYPE_SCHAR:
{
@@ -6870,10 +7458,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 +7513,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,6 +7652,7 @@ 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
@@ -7073,7 +7794,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 +7803,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. */