Many *printf implementations have bugs in the implementation of the "'" flag,
that is supposed to add thousands separators between digits.

One of these bugs became apparent when testing coreutils-9.7 across platforms.
The other one is much more insidious, and affects even relatively recent
glibc releases.

These two patches here add support for correct handling of the "'" flag,
with associated unit tests.

With these patches, the misc/numfmt test failures in coreutils-9.7 are
fixed:
  - The bug found on Solaris 11 OpenIndiana
    <https://lists.gnu.org/archive/html/bug-coreutils/2025-04/msg00024.html>,
  - The bug found on NetBSD 10.0
    <https://lists.gnu.org/archive/html/coreutils/2025-04/msg00024.html>.


2025-04-11  Bruno Haible  <br...@clisp.org>

        vasnprintf: Work around two grouping bugs on many platforms.
        * m4/printf.m4 (gl_PRINTF_FLAG_GROUPING_INT_PRECISION): New macro.
        (gl_PRINTF_FLAG_GROUPING_MULTIBYTE): New macro.
        * m4/fprintf-posix.m4 (gl_FUNC_FPRINTF_IS_POSIX): Require
        gl_PRINTF_FLAG_GROUPING_INT_PRECISION,
        gl_PRINTF_FLAG_GROUPING_MULTIBYTE. Test
        gl_cv_func_printf_flag_grouping_int_precision,
        gl_cv_func_printf_flag_grouping_multibyte.
        * m4/dprintf-posix.m4 (gl_FUNC_DPRINTF_IS_POSIX): Likewise.
        * m4/snprintf-posix.m4 (gl_FUNC_SNPRINTF_IS_POSIX): Likewise.
        * m4/sprintf-posix.m4 (gl_FUNC_SPRINTF_IS_POSIX): Likewise.
        * m4/vasnprintf-posix.m4 (gl_FUNC_VASNPRINTF_IS_POSIX): Likewise.
        * m4/vasprintf-posix.m4 (gl_FUNC_VASPRINTF_IS_POSIX): Likewise.
        * m4/vdprintf-posix.m4 (gl_FUNC_VDPRINTF_IS_POSIX): Likewise.
        * m4/vfprintf-posix.m4 (gl_FUNC_VFPRINTF_IS_POSIX): Likewise.
        * m4/vsnprintf-posix.m4 (gl_FUNC_VSNPRINTF_IS_POSIX): Likewise.
        * m4/vsprintf-posix.m4 (gl_FUNC_VSPRINTF_IS_POSIX): Likewise.
        * m4/obstack-printf-posix.m4 (gl_FUNC_OBSTACK_PRINTF_IS_POSIX):
        Likewise.
        * m4/vasnprintf.m4 (gl_PREREQ_VASNPRINTF_FLAG_GROUPING): Require
        gl_PRINTF_FLAG_GROUPING_INT_PRECISION,
        gl_PRINTF_FLAG_GROUPING_MULTIBYTE. Test
        gl_cv_func_printf_flag_grouping_int_precision,
        gl_cv_func_printf_flag_grouping_multibyte. Define the C macro
        NEED_PRINTF_FLAG_GROUPING_INT.
        * lib/vasnprintf.c (thousands_separator_char): New function.
        (THOUSEP_CHAR_MAXLEN): New macro.
        (thousands_separator_wchar): New function.
        (THOUSEP_WCHAR_MAXLEN): New macro.
        (MAX_ROOM_NEEDED): Improve the room estimate for FLAG_GROUP.
        (VASNPRINTF): Add code for emitting thousands separators in the %f, %F,
        %g, %G implementations and in the code relies the system's
        sprintf/snprintf/swprintf function. Test NEED_PRINTF_FLAG_GROUPING and
        NEED_PRINTF_FLAG_GROUPING_INT where needed.
        * tests/test-vasnprintf-posix2.c (main): Add tests of the grouping flag.
        * tests/test-vasnwprintf-posix2.c (main): Likewise.
        * tests/test-vasnprintf-posix2.sh: Test in both French locales, not only
        in one of them.
        * tests/test-vasnwprintf-posix2.sh: Likewise.
        * modules/vasnprintf-posix-tests (Depends-on): Add localeconv.
        * modules/vasnwprintf-posix-tests (Depends-on): Likewise.
        * doc/posix-functions/fprintf.texi: Mention the grouping bug with
        precision on integers and the grouping bug with a multibyte thousands
        separator.
        * doc/posix-functions/vfprintf.texi: Likewise.
        * doc/posix-functions/printf.texi: Likewise.
        * doc/posix-functions/vprintf.texi: Likewise.
        * doc/posix-functions/sprintf.texi: Likewise.
        * doc/posix-functions/vsprintf.texi: Likewise.
        * doc/posix-functions/snprintf.texi: Likewise.
        * doc/posix-functions/vsnprintf.texi: Likewise.
        * doc/posix-functions/dprintf.texi: Likewise.
        * doc/posix-functions/vdprintf.texi: Likewise.
        * doc/posix-functions/fwprintf.texi: Likewise.
        * doc/posix-functions/vfwprintf.texi: Likewise.
        * doc/posix-functions/wprintf.texi: Likewise.
        * doc/posix-functions/vwprintf.texi: Likewise.
        * doc/posix-functions/swprintf.texi: Likewise.
        * doc/posix-functions/vswprintf.texi: Likewise.
        * doc/posix-functions/asprintf.texi: Likewise.
        * doc/posix-functions/vasprintf.texi: Likewise.
        * doc/glibc-functions/obstack_printf.texi: Likewise.
        * doc/glibc-functions/obstack_vprintf.texi: Likewise.

2025-04-11  Bruno Haible  <br...@clisp.org>

        vasnprintf: Follow glibc's behaviour on glibc systems.
        * lib/vasnprintf.c (DCHAR_MBSNLEN): Define fallback.
        (VASNPRINTF): Use it on for width handling on glibc.
        * modules/vasnprintf (Depends-on): Add mbsnlen.
        * modules/c-vasnprintf (Depends-on): Likewise.

>From aef99bf93993103c35e967020385138a672f0f8d Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Fri, 11 Apr 2025 16:59:24 +0200
Subject: [PATCH 1/2] vasnprintf: Follow glibc's behaviour on glibc systems.

* lib/vasnprintf.c (DCHAR_MBSNLEN): Define fallback.
(VASNPRINTF): Use it on for width handling on glibc.
* modules/vasnprintf (Depends-on): Add mbsnlen.
* modules/c-vasnprintf (Depends-on): Likewise.
---
 ChangeLog            |  8 ++++++++
 lib/vasnprintf.c     | 24 ++++++++++++++++++++++++
 modules/c-vasnprintf |  1 +
 modules/vasnprintf   |  1 +
 4 files changed, 34 insertions(+)

diff --git a/ChangeLog b/ChangeLog
index adad6ce81b..dedd3ca34e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2025-04-11  Bruno Haible  <br...@clisp.org>
+
+	vasnprintf: Follow glibc's behaviour on glibc systems.
+	* lib/vasnprintf.c (DCHAR_MBSNLEN): Define fallback.
+	(VASNPRINTF): Use it on for width handling on glibc.
+	* modules/vasnprintf (Depends-on): Add mbsnlen.
+	* modules/c-vasnprintf (Depends-on): Likewise.
+
 2025-04-11  Bruno Haible  <br...@clisp.org>
 
 	vasnprintf: Fix memory size bound for %g with grouping and precision.
diff --git a/lib/vasnprintf.c b/lib/vasnprintf.c
index 559b3c7043..f91300cbd1 100644
--- a/lib/vasnprintf.c
+++ b/lib/vasnprintf.c
@@ -183,6 +183,13 @@
 #   define TCHAR_T char
 # 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'.
@@ -7100,6 +7107,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/bugzilla/show_bug.cgi?id=28943>
+                           <https://sourceware.org/bugzilla/show_bug.cgi?id=30883>
+                           <https://sourceware.org/bugzilla/show_bug.cgi?id=31542>  */
+                        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.  */
diff --git a/modules/c-vasnprintf b/modules/c-vasnprintf
index eebaf3aed8..aaa6011809 100644
--- a/modules/c-vasnprintf
+++ b/modules/c-vasnprintf
@@ -36,6 +36,7 @@ errno-h
 memchr
 multiarch
 mbszero
+mbsnlen
 
 configure.ac:
 AC_REQUIRE([AC_C_RESTRICT])
diff --git a/modules/vasnprintf b/modules/vasnprintf
index 96fb208891..aa12d79139 100644
--- a/modules/vasnprintf
+++ b/modules/vasnprintf
@@ -33,6 +33,7 @@ memchr
 assert-h
 wchar-h
 mbszero
+mbsnlen
 
 configure.ac:
 AC_REQUIRE([AC_C_RESTRICT])
-- 
2.43.0

Attachment: 0002-vasnprintf-Work-around-two-grouping-bugs-on-many-pla.patch.gz
Description: application/gzip

Reply via email to