On 2023-03-21 I noticed a bug with %lc in most libcs: <https://lists.gnu.org/archive/html/bug-gnulib/2023-03/msg00080.html>.
On 2023-03-28 Eric Blake opened a defect with POSIX, with the intent that both ISO C and POSIX make the four *printf cases consistent: <https://austingroupbugs.net/view.php?id=1647> This issue was then submitted in the ISO C 23 ballot as GB-141, and in the meeting from 2023-06-20 to 2023-06-23 it was decided upon: <https://www.open-std.org/JTC1/sc22/wg14/www/docs/n3167.pdf> page 23, 24. The decision ("option 1") is detailed in <https://www.open-std.org/JTC1/sc22/wg14/www/docs/n3148.doc>: "Option 1 (require a NUL) - change the text to: If an l length modifier is present, the wint_t argument is converted as if by a call to the wcrtomb function with a pointer to storage of at least MB_CUR_MAX bytes, the wint_t argument converted to wchar_t, and an initial shift state." So, ISO C changed, and POSIX will follow suit. The bug in most libcs is thus no longer a bug. musl libc, which had it correct, now has a bug. And Gnulib can stop overriding printf for this reason on most platforms, and should override it on musl libc instead. 2023-11-14 Bruno Haible <br...@clisp.org> *printf-posix: Revert expectations of %lc of 0. * m4/printf.m4 (gl_PRINTF_DIRECTIVE_LC): Expect a NUL byte in the output. * lib/vasnprintf.c (VASNPRINTF): In the %lc implementation, don't special-case the NUL wide character. * tests/test-vasnprintf-posix.c (test_function): Change expected result for %lc of L'\0'. * tests/test-vasprintf-posix.c (test_function): Likewise. * tests/test-snprintf-posix.h (test_function): Likewise. * tests/test-sprintf-posix.h (test_function): Likewise. * doc/posix-functions/dprintf.texi: Update platform list regarding %lc of 0 bug. * doc/posix-functions/fprintf.texi: Likewise. * doc/posix-functions/printf.texi: Likewise. * doc/posix-functions/snprintf.texi: Likewise. * doc/posix-functions/sprintf.texi: Likewise. * doc/posix-functions/vdprintf.texi: Likewise. * doc/posix-functions/vfprintf.texi: Likewise. * doc/posix-functions/vprintf.texi: Likewise. * doc/posix-functions/vsnprintf.texi: Likewise. * doc/posix-functions/vsprintf.texi: Likewise. * doc/glibc-functions/asprintf.texi: Likewise. * doc/glibc-functions/vasprintf.texi: Likewise. * doc/glibc-functions/obstack_printf.texi: Likewise. * doc/glibc-functions/obstack_vprintf.texi: Likewise. diff --git a/doc/glibc-functions/asprintf.texi b/doc/glibc-functions/asprintf.texi index 19602929cc..5e0cc116c9 100644 --- a/doc/glibc-functions/asprintf.texi +++ b/doc/glibc-functions/asprintf.texi @@ -77,7 +77,7 @@ @item This function produces wrong output for the @samp{lc} directive with a NUL wide character argument on some platforms: -glibc 2.35, FreeBSD 13.1, NetBSD 9.0, OpenBSD 7.2, macOS 12.5, AIX 7.2, Solaris 11.4, and others. +musl libc 1.2.4. @item This function can crash in out-of-memory conditions on some platforms: FreeBSD 13.0, NetBSD 5.0. diff --git a/doc/glibc-functions/obstack_printf.texi b/doc/glibc-functions/obstack_printf.texi index 975b255274..5d6dae0f84 100644 --- a/doc/glibc-functions/obstack_printf.texi +++ b/doc/glibc-functions/obstack_printf.texi @@ -83,7 +83,7 @@ @item This function produces wrong output for the @samp{lc} directive with a NUL wide character argument on some platforms: -glibc 2.35, FreeBSD 13.1, NetBSD 9.0, OpenBSD 7.2, macOS 12.5, AIX 7.2, Solaris 11.4, and others. +musl libc 1.2.4. @item This function can crash in out-of-memory conditions on some platforms: FreeBSD 13.0, NetBSD 5.0. diff --git a/doc/glibc-functions/obstack_vprintf.texi b/doc/glibc-functions/obstack_vprintf.texi index a3ec3d364b..da8ed20491 100644 --- a/doc/glibc-functions/obstack_vprintf.texi +++ b/doc/glibc-functions/obstack_vprintf.texi @@ -83,7 +83,7 @@ @item This function produces wrong output for the @samp{lc} directive with a NUL wide character argument on some platforms: -glibc 2.35, FreeBSD 13.1, NetBSD 9.0, OpenBSD 7.2, macOS 12.5, AIX 7.2, Solaris 11.4, and others. +musl libc 1.2.4. @item This function can crash in out-of-memory conditions on some platforms: FreeBSD 13.0, NetBSD 5.0. diff --git a/doc/glibc-functions/vasprintf.texi b/doc/glibc-functions/vasprintf.texi index b33ad91642..e3940baac9 100644 --- a/doc/glibc-functions/vasprintf.texi +++ b/doc/glibc-functions/vasprintf.texi @@ -77,7 +77,7 @@ @item This function produces wrong output for the @samp{lc} directive with a NUL wide character argument on some platforms: -glibc 2.35, FreeBSD 13.1, NetBSD 9.0, OpenBSD 7.2, macOS 12.5, AIX 7.2, Solaris 11.4, and others. +musl libc 1.2.4. @item This function can crash in out-of-memory conditions on some platforms: FreeBSD 13.0, NetBSD 5.0. diff --git a/doc/posix-functions/dprintf.texi b/doc/posix-functions/dprintf.texi index b109398793..7e787399e7 100644 --- a/doc/posix-functions/dprintf.texi +++ b/doc/posix-functions/dprintf.texi @@ -57,7 +57,7 @@ @item This function produces wrong output for the @samp{lc} directive with a NUL wide character argument on some platforms: -glibc 2.35, FreeBSD 13.1, NetBSD 9.0, OpenBSD 7.2, macOS 12.5, AIX 7.2, Solaris 11.4, and others. +musl libc 1.2.4. @end itemize Portability problems fixed by Gnulib module @code{dprintf-gnu}: diff --git a/doc/posix-functions/fprintf.texi b/doc/posix-functions/fprintf.texi index 7bd9ff50d8..1b881e0785 100644 --- a/doc/posix-functions/fprintf.texi +++ b/doc/posix-functions/fprintf.texi @@ -81,7 +81,7 @@ @item This function produces wrong output for the @samp{lc} directive with a NUL wide character argument on some platforms: -glibc 2.35, FreeBSD 13.1, NetBSD 9.0, OpenBSD 7.2, macOS 12.5, AIX 7.2, Solaris 11.4, and others. +musl libc 1.2.4. @item This function can crash in out-of-memory conditions on some platforms: FreeBSD 13.0, NetBSD 5.0. diff --git a/doc/posix-functions/printf.texi b/doc/posix-functions/printf.texi index 12ba645734..ea08c0f07e 100644 --- a/doc/posix-functions/printf.texi +++ b/doc/posix-functions/printf.texi @@ -81,7 +81,7 @@ @item This function produces wrong output for the @samp{lc} directive with a NUL wide character argument on some platforms: -glibc 2.35, FreeBSD 13.1, NetBSD 9.0, OpenBSD 7.2, macOS 12.5, AIX 7.2, Solaris 11.4, and others. +musl libc 1.2.4. @item This function can crash in out-of-memory conditions on some platforms: FreeBSD 13.0, NetBSD 5.0. diff --git a/doc/posix-functions/snprintf.texi b/doc/posix-functions/snprintf.texi index f907e9cc7f..aa74b9e288 100644 --- a/doc/posix-functions/snprintf.texi +++ b/doc/posix-functions/snprintf.texi @@ -92,7 +92,7 @@ @item This function produces wrong output for the @samp{lc} directive with a NUL wide character argument on some platforms: -glibc 2.35, FreeBSD 13.1, NetBSD 9.0, OpenBSD 7.2, macOS 12.5, AIX 7.2, Solaris 11.4, and others. +musl libc 1.2.4. @item This function can crash in out-of-memory conditions on some platforms: FreeBSD 13.0, NetBSD 5.0. diff --git a/doc/posix-functions/sprintf.texi b/doc/posix-functions/sprintf.texi index 2652acdebc..676190bbcf 100644 --- a/doc/posix-functions/sprintf.texi +++ b/doc/posix-functions/sprintf.texi @@ -81,7 +81,7 @@ @item This function produces wrong output for the @samp{lc} directive with a NUL wide character argument on some platforms: -glibc 2.35, FreeBSD 13.1, NetBSD 9.0, OpenBSD 7.2, macOS 12.5, AIX 7.2, Solaris 11.4, and others. +musl libc 1.2.4. @item This function can crash in out-of-memory conditions on some platforms: FreeBSD 13.0, NetBSD 5.0. diff --git a/doc/posix-functions/vdprintf.texi b/doc/posix-functions/vdprintf.texi index 22ac3d17b4..5c95dc77ec 100644 --- a/doc/posix-functions/vdprintf.texi +++ b/doc/posix-functions/vdprintf.texi @@ -57,7 +57,7 @@ @item This function produces wrong output for the @samp{lc} directive with a NUL wide character argument on some platforms: -glibc 2.35, FreeBSD 13.1, NetBSD 9.0, OpenBSD 7.2, macOS 12.5, AIX 7.2, Solaris 11.4, and others. +musl libc 1.2.4. @end itemize Portability problems fixed by Gnulib module @code{vdprintf-gnu}: diff --git a/doc/posix-functions/vfprintf.texi b/doc/posix-functions/vfprintf.texi index 94bd3a75d4..bdf9f81795 100644 --- a/doc/posix-functions/vfprintf.texi +++ b/doc/posix-functions/vfprintf.texi @@ -81,7 +81,7 @@ @item This function produces wrong output for the @samp{lc} directive with a NUL wide character argument on some platforms: -glibc 2.35, FreeBSD 13.1, NetBSD 9.0, OpenBSD 7.2, macOS 12.5, AIX 7.2, Solaris 11.4, and others. +musl libc 1.2.4. @item This function can crash in out-of-memory conditions on some platforms: FreeBSD 13.0, NetBSD 5.0. diff --git a/doc/posix-functions/vprintf.texi b/doc/posix-functions/vprintf.texi index 8877376b91..7f39cca067 100644 --- a/doc/posix-functions/vprintf.texi +++ b/doc/posix-functions/vprintf.texi @@ -81,7 +81,7 @@ @item This function produces wrong output for the @samp{lc} directive with a NUL wide character argument on some platforms: -glibc 2.35, FreeBSD 13.1, NetBSD 9.0, OpenBSD 7.2, macOS 12.5, AIX 7.2, Solaris 11.4, and others. +musl libc 1.2.4. @item This function can crash in out-of-memory conditions on some platforms: FreeBSD 13.0, NetBSD 5.0. diff --git a/doc/posix-functions/vsnprintf.texi b/doc/posix-functions/vsnprintf.texi index 5bc943fde3..b4b1ce82b5 100644 --- a/doc/posix-functions/vsnprintf.texi +++ b/doc/posix-functions/vsnprintf.texi @@ -85,7 +85,7 @@ @item This function produces wrong output for the @samp{lc} directive with a NUL wide character argument on some platforms: -glibc 2.35, FreeBSD 13.1, NetBSD 9.0, OpenBSD 7.2, macOS 12.5, AIX 7.2, Solaris 11.4, and others. +musl libc 1.2.4. @item This function can crash in out-of-memory conditions on some platforms: FreeBSD 13.0, NetBSD 5.0. diff --git a/doc/posix-functions/vsprintf.texi b/doc/posix-functions/vsprintf.texi index 4e726f96b0..0160862df4 100644 --- a/doc/posix-functions/vsprintf.texi +++ b/doc/posix-functions/vsprintf.texi @@ -81,7 +81,7 @@ @item This function produces wrong output for the @samp{lc} directive with a NUL wide character argument on some platforms: -glibc 2.35, FreeBSD 13.1, NetBSD 9.0, OpenBSD 7.2, macOS 12.5, AIX 7.2, Solaris 11.4, and others. +musl libc 1.2.4. @item This function can crash in out-of-memory conditions on some platforms: FreeBSD 13.0, NetBSD 5.0. diff --git a/lib/vasnprintf.c b/lib/vasnprintf.c index 2cfdbe97bd..06abb5afe2 100644 --- a/lib/vasnprintf.c +++ b/lib/vasnprintf.c @@ -3424,21 +3424,18 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, { /* Count the number of bytes. */ characters = 0; - if (arg != 0) - { - char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */ - int count; + char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */ + int count; # if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t - mbstate_t state; - mbszero (&state); + mbstate_t state; + mbszero (&state); # endif - count = local_wcrtomb (cbuf, arg, &state); - if (count < 0) - /* Cannot convert. */ - goto fail_with_EILSEQ; - characters = count; - } + count = local_wcrtomb (cbuf, arg, &state); + if (count < 0) + /* Cannot convert. */ + goto fail_with_EILSEQ; + characters = count; } # if DCHAR_IS_TCHAR else @@ -3450,7 +3447,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, # if !DCHAR_IS_TCHAR /* Convert the string into a piece of temporary memory. */ - if (characters > 0) /* implies arg != 0 */ + if (characters > 0) { char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */ int count; @@ -3507,7 +3504,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, { /* We know the number of bytes in advance. */ ENSURE_ALLOCATION (xsum (length, characters)); - if (characters > 0) /* implies arg != 0 */ + if (characters > 0) { int count; # if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t @@ -3524,23 +3521,20 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, } else { - if (arg != 0) - { - char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */ - int count; + char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */ + int count; # if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t - mbstate_t state; - mbszero (&state); + mbstate_t state; + mbszero (&state); # endif - count = local_wcrtomb (cbuf, arg, &state); - if (count < 0) - /* Cannot convert. */ - goto fail_with_EILSEQ; - ENSURE_ALLOCATION (xsum (length, count)); - memcpy (result + length, cbuf, count); - length += count; - } + count = local_wcrtomb (cbuf, arg, &state); + if (count < 0) + /* Cannot convert. */ + goto fail_with_EILSEQ; + ENSURE_ALLOCATION (xsum (length, count)); + memcpy (result + length, cbuf, count); + length += count; } # else ENSURE_ALLOCATION_ELSE (xsum (length, tmpdst_len), diff --git a/m4/printf.m4 b/m4/printf.m4 index 729847b0aa..bf0f7ecab4 100644 --- a/m4/printf.m4 +++ b/m4/printf.m4 @@ -1,4 +1,4 @@ -# printf.m4 serial 87 +# printf.m4 serial 88 dnl Copyright (C) 2003, 2007-2023 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -1032,7 +1032,8 @@ AC_DEFUN([gl_PRINTF_DIRECTIVE_LS] dnl Test whether the *printf family of functions supports the %lc format dnl directive and in particular, when the argument is a null wide character, -dnl whether the functions don't produce a NUL byte. +dnl whether the functions produce a NUL byte, as specified in ISO C 23 +dnl after the issue GB-141 was fixed. dnl Result is gl_cv_func_printf_directive_lc. AC_DEFUN([gl_PRINTF_DIRECTIVE_LC], @@ -1051,13 +1052,11 @@ AC_DEFUN([gl_PRINTF_DIRECTIVE_LC] { int result = 0; char buf[100]; - /* This test fails on glibc 2.35, FreeBSD 13.1, NetBSD 9.0, OpenBSD 7.2, - macOS 12.5, AIX 7.2, Solaris 11.4. - glibc 2.35 bug: <https://sourceware.org/bugzilla/show_bug.cgi?id=30257> */ + /* This test fails on musl libc 1.2.4. */ { buf[0] = '\0'; if (sprintf (buf, "%lc%lc%lc", (wint_t) 'a', (wint_t) 0, (wint_t) 'z') < 0 - || strcmp (buf, "az") != 0) + || memcmp (buf, "a\0z", 4) != 0) result |= 1; } return result; @@ -1067,10 +1066,10 @@ AC_DEFUN([gl_PRINTF_DIRECTIVE_LC] [ changequote(,)dnl case "$host_os" in - # Guess yes on musl libc. - *-musl* | midipix*) gl_cv_func_printf_directive_lc="guessing yes";; - # Guess no otherwise. - *) gl_cv_func_printf_directive_lc="guessing no";; + # Guess no on musl libc. + *-musl* | midipix*) gl_cv_func_printf_directive_lc="guessing no";; + # Guess yes otherwise. + *) gl_cv_func_printf_directive_lc="guessing yes";; esac changequote([,])dnl ]) @@ -2172,39 +2171,39 @@ AC_DEFUN([gl_SWPRINTF_DIRECTIVE_LC] dnl . = yes, # = no. dnl dnl 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 -dnl musl libc 1.2.3 . # . . . . # # . . . . . . . . ? . . . . . . . . # . # -dnl glibc 2.35 . # . . . . . . . . . # . . . . . . . . . . . . . . . . -dnl glibc 2.5 . # . . . . # # . . . # . . . . . . . . . . . . . . # . -dnl glibc 2.3.6 . # . . . # # # . . . # . . . . . . . . . . . . . . # . -dnl FreeBSD 13.0 . # . . . # # # . . . # . . . . . . # . . . . . . # . # -dnl FreeBSD 5.4, 6.1 . # . . . # # # . . . # . . . # ? . # . . . . . . # ? ? -dnl Mac OS X 10.13.5 . # . . # # # # . # . # . . . . . . . . . . # . . # ? ? -dnl Mac OS X 10.5.8 . # . . # # # # . . . # . . . # # . . . . . . . . # ? ? -dnl Mac OS X 10.3.9 . # . . . # # # . . . # . . . # # . # . . . . . . # ? ? -dnl OpenBSD 6.0, 6.7 . # . . . # # # . . . # . . . . . . # . . . . . . # . # -dnl OpenBSD 3.9, 4.0 . # . # # # # # # . # # . # . # ? . # . . . . . . # ? ? +dnl musl libc 1.2.3 . # . . . . # # . . . # . . . . ? . . . . . . . . # . # +dnl glibc 2.35 . # . . . . . . . . . . . . . . . . . . . . . . . . . . +dnl glibc 2.5 . # . . . . # # . . . . . . . . . . . . . . . . . . # . +dnl glibc 2.3.6 . # . . . # # # . . . . . . . . . . . . . . . . . . # . +dnl FreeBSD 13.0 . # . . . # # # . . . . . . . . . . # . . . . . . # . # +dnl FreeBSD 5.4, 6.1 . # . . . # # # . . . . . . . # ? . # . . . . . . # ? ? +dnl Mac OS X 10.13.5 . # . . # # # # . # . . . . . . . . . . . . # . . # ? ? +dnl Mac OS X 10.5.8 . # . . # # # # . . . . . . . # # . . . . . . . . # ? ? +dnl Mac OS X 10.3.9 . # . . . # # # . . . . . . . # # . # . . . . . . # ? ? +dnl OpenBSD 6.0, 6.7 . # . . . # # # . . . . . . . . . . # . . . . . . # . # +dnl OpenBSD 3.9, 4.0 . # . # # # # # # . # . . # . # ? . # . . . . . . # ? ? dnl Cygwin 1.7.0 (2009) . # . . # . # # . . ? ? . . . . ? . ? . . . . . . ? ? ? dnl Cygwin 1.5.25 (2008) . # . . # # # # . . # ? . . . . ? . # . . . . . . ? ? ? dnl Cygwin 1.5.19 (2006) # # . . # # # # # . # ? . # . # ? # # . . . . . . ? ? ? -dnl Solaris 11.4 . # . # # # # # . . # # . . . # . . . . . . . . . . # . -dnl Solaris 11.3 . # . . . # # # . . # # . . . . . . . . . . . . . . # . -dnl Solaris 11.0 . # . # # # # # . . # # . . . # . . . . . . . . . ? ? ? -dnl Solaris 10 . # . # # # # # . . # # . . . # . # . . . . . . . . # . -dnl Solaris 2.6 ... 9 # # . # # # # # # . # # . . . # ? # . . . # . . . ? ? ? -dnl Solaris 2.5.1 # # . # # # # # # . # # . . . # ? . . # # # # # # ? ? ? -dnl AIX 7.1 . # . # # # # # . . . # . . . # . # . . . . . . . # . . -dnl AIX 5.2 . # . # # # # # . . . # . . . # ? . . . . . . . . # ? ? -dnl AIX 4.3.2, 5.1 # # . # # # # # # . . # . . . # ? . . . . # . . . # ? ? +dnl Solaris 11.4 . # . # # # # # . . # . . . . # . . . . . . . . . . # . +dnl Solaris 11.3 . # . . . # # # . . # . . . . . . . . . . . . . . . # . +dnl Solaris 11.0 . # . # # # # # . . # . . . . # . . . . . . . . . ? ? ? +dnl Solaris 10 . # . # # # # # . . # . . . . # . # . . . . . . . . # . +dnl Solaris 2.6 ... 9 # # . # # # # # # . # . . . . # ? # . . . # . . . ? ? ? +dnl Solaris 2.5.1 # # . # # # # # # . # . . . . # ? . . # # # # # # ? ? ? +dnl AIX 7.1 . # . # # # # # . . . . . . . # . # . . . . . . . # . . +dnl AIX 5.2 . # . # # # # # . . . . . . . # ? . . . . . . . . # ? ? +dnl AIX 4.3.2, 5.1 # # . # # # # # # . . . . . . # ? . . . . # . . . # ? ? dnl HP-UX 11.31 . # . . . # # # . . . ? . . . # ? . . . . # # . . ? ? ? dnl HP-UX 11.{00,11,23} # # . . . # # # # . . ? . . . # ? . . . . # # . # ? ? ? dnl HP-UX 10.20 # # . # . # # # # . ? ? . . # # ? . . . . # # ? # ? ? ? -dnl IRIX 6.5 # # . # # # # # # . # # . . . # ? . . . . # . . . # ? ? +dnl IRIX 6.5 # # . # # # # # # . # . . . . # ? . . . . # . . . # ? ? dnl OSF/1 5.1 # # . # # # # # # . . ? . . . # ? . . . . # . . # ? ? ? dnl OSF/1 4.0d # # . # # # # # # . . ? . . . # ? . . # # # # # # ? ? ? -dnl NetBSD 9.0 . # . . . # # # . . . # . . . . . . . . . . . . . # . # -dnl NetBSD 5.0 . # . . # # # # . . . # . . . # ? . # . . . . . . # ? ? -dnl NetBSD 4.0 . # ? ? ? ? # # ? . ? # . ? ? ? ? ? ? . . . ? ? ? # ? ? -dnl NetBSD 3.0 . # . . . # # # # . ? # # # ? # ? . # . . . . . . # ? ? +dnl NetBSD 9.0 . # . . . # # # . . . . . . . . . . . . . . . . . # . # +dnl NetBSD 5.0 . # . . # # # # . . . . . . . # ? . # . . . . . . # ? ? +dnl NetBSD 4.0 . # ? ? ? ? # # ? . ? . . ? ? ? ? ? ? . . . ? ? ? # ? ? +dnl NetBSD 3.0 . # . . . # # # # . ? . # # ? # ? . # . . . . . . # ? ? dnl Haiku . # . . # # # # # . # ? . . . . ? . ? . . ? . . . . # . dnl BeOS # # # . # # # # # . ? ? # . ? . ? # ? . . ? . . . ? ? ? dnl Android 4.3 . # . # # # # # # # # ? . # . # ? . # . . . # . . ? ? ? diff --git a/tests/test-snprintf-posix.h b/tests/test-snprintf-posix.h index 748b2775a6..3c15f11d4a 100644 --- a/tests/test-snprintf-posix.h +++ b/tests/test-snprintf-posix.h @@ -3184,11 +3184,14 @@ test_function (int (*my_snprintf) (char *, size_t, const char *, ...)) int retval = my_snprintf (result, sizeof (result), "a%lcz %d", (wint_t) L'\0', 33, 44, 55); - /* No NUL byte between 'a' and 'z'. This is surprising, but is a - consequence of how POSIX:2018 and ISO C 23 specify the handling - of %lc. */ - ASSERT (memcmp (result, "az 33\0", 5 + 1) == 0); - ASSERT (retval == 5); + /* ISO C had this wrong for decades. ISO C 23 now corrects it, through + this wording: + "If an l length modifier is present, the wint_t argument is converted + as if by a call to the wcrtomb function with a pointer to storage of + at least MB_CUR_MAX bytes, the wint_t argument converted to wchar_t, + and an initial shift state." */ + ASSERT (memcmp (result, "a\0z 33\0", 6 + 1) == 0); + ASSERT (retval == 6); } static wint_t L_invalid = (wchar_t) 0x76543210; diff --git a/tests/test-sprintf-posix.h b/tests/test-sprintf-posix.h index aabd182a8b..9a1e0c380d 100644 --- a/tests/test-sprintf-posix.h +++ b/tests/test-sprintf-posix.h @@ -3162,11 +3162,14 @@ test_function (int (*my_sprintf) (char *, const char *, ...)) { /* NUL character. */ int retval = my_sprintf (result, "a%lcz %d", (wint_t) L'\0', 33, 44, 55); - /* No NUL byte between 'a' and 'z'. This is surprising, but is a - consequence of how POSIX:2018 and ISO C 23 specify the handling - of %lc. */ - ASSERT (memcmp (result, "az 33\0", 5 + 1) == 0); - ASSERT (retval == 5); + /* ISO C had this wrong for decades. ISO C 23 now corrects it, through + this wording: + "If an l length modifier is present, the wint_t argument is converted + as if by a call to the wcrtomb function with a pointer to storage of + at least MB_CUR_MAX bytes, the wint_t argument converted to wchar_t, + and an initial shift state." */ + ASSERT (memcmp (result, "a\0z 33\0", 6 + 1) == 0); + ASSERT (retval == 6); } static wint_t L_invalid = (wchar_t) 0x76543210; diff --git a/tests/test-vasnprintf-posix.c b/tests/test-vasnprintf-posix.c index 92b61b1329..5124138c56 100644 --- a/tests/test-vasnprintf-posix.c +++ b/tests/test-vasnprintf-posix.c @@ -4139,11 +4139,14 @@ test_function (char * (*my_asnprintf) (char *, size_t *, const char *, ...)) size_t length; char *result = my_asnprintf (NULL, &length, "a%lcz %d", (wint_t) L'\0', 33, 44, 55); - /* No NUL byte between 'a' and 'z'. This is surprising, but is a - consequence of how POSIX:2018 and ISO C 23 specify the handling - of %lc. */ - ASSERT (memcmp (result, "az 33\0", 5 + 1) == 0); - ASSERT (length == 5); + /* ISO C had this wrong for decades. ISO C 23 now corrects it, through + this wording: + "If an l length modifier is present, the wint_t argument is converted + as if by a call to the wcrtomb function with a pointer to storage of + at least MB_CUR_MAX bytes, the wint_t argument converted to wchar_t, + and an initial shift state." */ + ASSERT (memcmp (result, "a\0z 33\0", 6 + 1) == 0); + ASSERT (length == 6); free (result); } diff --git a/tests/test-vasprintf-posix.c b/tests/test-vasprintf-posix.c index 3499451745..00a3b047c3 100644 --- a/tests/test-vasprintf-posix.c +++ b/tests/test-vasprintf-posix.c @@ -4078,11 +4078,14 @@ test_function (int (*my_asprintf) (char **, const char *, ...)) char *result; int retval = my_asprintf (&result, "a%lcz %d", (wint_t) L'\0', 33, 44, 55); - /* No NUL byte between 'a' and 'z'. This is surprising, but is a - consequence of how POSIX:2018 and ISO C 23 specify the handling - of %lc. */ - ASSERT (memcmp (result, "az 33\0", 5 + 1) == 0); - ASSERT (retval == 5); + /* ISO C had this wrong for decades. ISO C 23 now corrects it, through + this wording: + "If an l length modifier is present, the wint_t argument is converted + as if by a call to the wcrtomb function with a pointer to storage of + at least MB_CUR_MAX bytes, the wint_t argument converted to wchar_t, + and an initial shift state." */ + ASSERT (memcmp (result, "a\0z 33\0", 6 + 1) == 0); + ASSERT (retval == 6); free (result); }