]> Savannah Git Hosting - gnulib.git/commitdiff
vasnwprintf-posix: Fix behaviour in the C locale.
authorBruno Haible <bruno@clisp.org>
Sat, 1 Apr 2023 12:51:22 +0000 (14:51 +0200)
committerBruno Haible <bruno@clisp.org>
Sat, 1 Apr 2023 12:51:22 +0000 (14:51 +0200)
* lib/vasnprintf.c (VASNPRINTF): If NEED_WPRINTF_DIRECTIVE_C is set,
implement the 'c' directive here.
* m4/vasnprintf.m4 (gl_PREREQ_VASNWPRINTF): Invoke gl_MBRTOWC_C_LOCALE.
If mbrtowc is buggy in the C locale, define NEED_WPRINTF_DIRECTIVE_C.
* modules/vasnwprintf (Files): Add m4/mbrtowc.m4.
* tests/test-vasnwprintf-posix.c (test_function): Add tests of %s and %c
in the C locale.
* doc/posix-functions/fwprintf.texi: Mention the C locale behaviour bug.
* doc/posix-functions/swprintf.texi: Likewise.
* doc/posix-functions/vfwprintf.texi: Likewise.
* doc/posix-functions/vswprintf.texi: Likewise.
* doc/posix-functions/vwprintf.texi: Likewise.
* doc/posix-functions/wprintf.texi: Likewise.

ChangeLog
doc/posix-functions/fwprintf.texi
doc/posix-functions/swprintf.texi
doc/posix-functions/vfwprintf.texi
doc/posix-functions/vswprintf.texi
doc/posix-functions/vwprintf.texi
doc/posix-functions/wprintf.texi
lib/vasnprintf.c
m4/vasnprintf.m4
modules/vasnwprintf
tests/test-vasnwprintf-posix.c

index e696a7eba6b4375dbe6fd7da6412fcc7b7c0a942..ca6f2d54f40a17c64978279db06681a18b295eb3 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,20 @@
+2023-04-01  Bruno Haible  <bruno@clisp.org>
+
+       vasnwprintf-posix: Fix behaviour in the C locale.
+       * lib/vasnprintf.c (VASNPRINTF): If NEED_WPRINTF_DIRECTIVE_C is set,
+       implement the 'c' directive here.
+       * m4/vasnprintf.m4 (gl_PREREQ_VASNWPRINTF): Invoke gl_MBRTOWC_C_LOCALE.
+       If mbrtowc is buggy in the C locale, define NEED_WPRINTF_DIRECTIVE_C.
+       * modules/vasnwprintf (Files): Add m4/mbrtowc.m4.
+       * tests/test-vasnwprintf-posix.c (test_function): Add tests of %s and %c
+       in the C locale.
+       * doc/posix-functions/fwprintf.texi: Mention the C locale behaviour bug.
+       * doc/posix-functions/swprintf.texi: Likewise.
+       * doc/posix-functions/vfwprintf.texi: Likewise.
+       * doc/posix-functions/vswprintf.texi: Likewise.
+       * doc/posix-functions/vwprintf.texi: Likewise.
+       * doc/posix-functions/wprintf.texi: Likewise.
+
 2023-04-01  Bruno Haible  <bruno@clisp.org>
 
        vasnprintf-posix: Fix crash in narrow %lc directive (regr. 2023-03-21).
index 2b4600de11f097fb0c892a6b6ce549a9ad2bb9b7..db42363cfaccb32944980ff72e3ee23f75ea1695 100644 (file)
@@ -34,6 +34,10 @@ The @code{%m} directive is not portable, use @code{%s} mapped to an
 argument of @code{strerror(errno)} (or a version of @code{strerror_r})
 instead.
 @item
+In the C or POSIX locales, the @code{%c} and @code{%s} conversions may fail
+on some platforms:
+glibc 2.35.
+@item
 When formatting an integer with grouping flag, this function inserts thousands
 separators even in the "C" locale on some platforms:
 NetBSD 5.1.
index b53c96c6d702bc3dfe6da4329f7ddb191a522f8d..cd20d0e70218ac8135615c737fda9873652e53cd 100644 (file)
@@ -63,6 +63,10 @@ The @code{%m} directive is not portable, use @code{%s} mapped to an
 argument of @code{strerror(errno)} (or a version of @code{strerror_r})
 instead.
 @item
+In the C or POSIX locales, the @code{%c} and @code{%s} conversions may fail
+on some platforms:
+glibc 2.35.
+@item
 When formatting an integer with grouping flag, this function inserts thousands
 separators even in the "C" locale on some platforms:
 NetBSD 5.1.
index 22e4a5921247192c28179903fac8fd6667189dd3..0712269aa4681769d826786b77b582ea0f6ea7cb 100644 (file)
@@ -34,6 +34,10 @@ The @code{%m} directive is not portable, use @code{%s} mapped to an
 argument of @code{strerror(errno)} (or a version of @code{strerror_r})
 instead.
 @item
+In the C or POSIX locales, the @code{%c} and @code{%s} conversions may fail
+on some platforms:
+glibc 2.35.
+@item
 When formatting an integer with grouping flag, this function inserts thousands
 separators even in the "C" locale on some platforms:
 NetBSD 5.1.
index 4083cfd2a1cb67a71a883d730a150bca51b2f8a2..7512a2d0237a39788db3b7f95917f2b8ce2dfb13 100644 (file)
@@ -36,6 +36,10 @@ The @code{%m} directive is not portable, use @code{%s} mapped to an
 argument of @code{strerror(errno)} (or a version of @code{strerror_r})
 instead.
 @item
+In the C or POSIX locales, the @code{%c} and @code{%s} conversions may fail
+on some platforms:
+glibc 2.35.
+@item
 When formatting an integer with grouping flag, this function inserts thousands
 separators even in the "C" locale on some platforms:
 NetBSD 5.1.
index 7b029c9ceb75a6281d50c977eec2be2b6ff88d9e..1e364288f62254c6b506b3217572e11fe98dfa44 100644 (file)
@@ -37,6 +37,10 @@ The @code{%m} directive is not portable, use @code{%s} mapped to an
 argument of @code{strerror(errno)} (or a version of @code{strerror_r})
 instead.
 @item
+In the C or POSIX locales, the @code{%c} and @code{%s} conversions may fail
+on some platforms:
+glibc 2.35.
+@item
 When formatting an integer with grouping flag, this function inserts thousands
 separators even in the "C" locale on some platforms:
 NetBSD 5.1.
index 56106b309e2a5114cac51d3a4ab26b277126b193..c4d58fab2e158aa199f95830091f212dfc65fca1 100644 (file)
@@ -37,6 +37,10 @@ The @code{%m} directive is not portable, use @code{%s} mapped to an
 argument of @code{strerror(errno)} (or a version of @code{strerror_r})
 instead.
 @item
+In the C or POSIX locales, the @code{%c} and @code{%s} conversions may fail
+on some platforms:
+glibc 2.35.
+@item
 When formatting an integer with grouping flag, this function inserts thousands
 separators even in the "C" locale on some platforms:
 NetBSD 5.1.
index 90e52d8a96b3066eb9a6b9995c165e3da1a1d1fd..111d898a94de23d91201d2a7d012b30dcd4cd961 100644 (file)
@@ -3543,6 +3543,77 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
                 }
               }
 #endif
+#if NEED_WPRINTF_DIRECTIVE_C && WIDE_CHAR_VERSION
+            else if (dp->conversion == 'c'
+                     && a.arg[dp->arg_index].type != TYPE_WIDE_CHAR)
+              {
+                /* Implement the 'c' directive ourselves, in order to avoid
+                   EILSEQ in the "C" locale.  */
+                int flags = dp->flags;
+                size_t width;
+
+                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;
+                        width = arg;
+                        if (arg < 0)
+                          {
+                            /* "A negative field width is taken as a '-' flag
+                                followed by a positive field width."  */
+                            flags |= FLAG_LEFT;
+                            width = -width;
+                          }
+                      }
+                    else
+                      {
+                        const FCHAR_T *digitp = dp->width_start;
+
+                        do
+                          width = xsum (xtimes (width, 10), *digitp++ - '0');
+                        while (digitp != dp->width_end);
+                      }
+                  }
+
+                /* %c in vasnwprintf.  See the specification of fwprintf.  */
+                {
+                  char arg = (char) a.arg[dp->arg_index].a.a_char;
+                  mbstate_t state;
+                  wchar_t wc;
+
+                  memset (&state, '\0', sizeof (mbstate_t));
+                  int count = mbrtowc (&wc, &arg, 1, &state);
+                  if (count < 0)
+                    /* Invalid or incomplete multibyte character.  */
+                    goto fail_with_EILSEQ;
+
+                  if (1 < width && !(flags & FLAG_LEFT))
+                    {
+                      size_t n = width - 1;
+                      ENSURE_ALLOCATION (xsum (length, n));
+                      DCHAR_SET (result + length, ' ', n);
+                      length += n;
+                    }
+
+                  ENSURE_ALLOCATION (xsum (length, 1));
+                  result[length++] = wc;
+
+                  if (1 < width && (flags & FLAG_LEFT))
+                    {
+                      size_t n = width - 1;
+                      ENSURE_ALLOCATION (xsum (length, n));
+                      DCHAR_SET (result + length, ' ', n);
+                      length += n;
+                    }
+                }
+              }
+#endif
 #if NEED_PRINTF_DIRECTIVE_B || NEED_PRINTF_DIRECTIVE_UPPERCASE_B
             else if (0
 # if NEED_PRINTF_DIRECTIVE_B
@@ -6816,7 +6887,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
     errno = ENOMEM;
     goto fail_with_errno;
 
-#if ENABLE_UNISTDIO || ((!USE_SNPRINTF || WIDE_CHAR_VERSION || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || NEED_PRINTF_DIRECTIVE_LS || ENABLE_WCHAR_FALLBACK) && HAVE_WCHAR_T) || ((NEED_PRINTF_DIRECTIVE_LC || ENABLE_WCHAR_FALLBACK) && HAVE_WINT_T && !WIDE_CHAR_VERSION)
+#if ENABLE_UNISTDIO || ((!USE_SNPRINTF || WIDE_CHAR_VERSION || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || NEED_PRINTF_DIRECTIVE_LS || ENABLE_WCHAR_FALLBACK) && HAVE_WCHAR_T) || ((NEED_PRINTF_DIRECTIVE_LC || ENABLE_WCHAR_FALLBACK) && HAVE_WINT_T && !WIDE_CHAR_VERSION) || (NEED_WPRINTF_DIRECTIVE_C && WIDE_CHAR_VERSION)
   fail_with_EILSEQ:
     errno = EILSEQ;
     goto fail_with_errno;
index 929f3d80af790f348ad12002125870b6cb4e753d..86133b30b178481feee9c5566b4f8300c45093f7 100644 (file)
@@ -1,4 +1,4 @@
-# vasnprintf.m4 serial 47
+# vasnprintf.m4 serial 48
 dnl Copyright (C) 2002-2004, 2006-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,
@@ -105,6 +105,15 @@ AC_DEFUN_ONCE([gl_PREREQ_VASNWPRINTF],
          that contains null wide characters.])
       ;;
   esac
+  gl_MBRTOWC_C_LOCALE
+  case "$gl_cv_func_mbrtowc_C_locale_sans_EILSEQ" in
+    *yes) ;;
+    *)
+      AC_DEFINE([NEED_WPRINTF_DIRECTIVE_C], [1],
+        [Define if the vasnwprintf implementation needs special code for
+         the 'c' directive.])
+      ;;
+  esac
   gl_MUSL_LIBC
   gl_PREREQ_VASNXPRINTF
 ])
index 82d4aa4668b48fcacfaa761fc7976dc241f595a2..70f3fb2c9511b8ca37ef8a8e9bbd081ac2a8856f 100644 (file)
@@ -19,6 +19,7 @@ m4/stdint_h.m4
 m4/inttypes_h.m4
 m4/vasnprintf.m4
 m4/printf.m4
+m4/mbrtowc.m4
 m4/math_h.m4
 m4/exponentd.m4
 m4/musl.m4
index 74a26079d324454c061f70693aeb77230380a736..125b9386a489d12f86243eaeffd8e53f45e15212 100644 (file)
@@ -3757,6 +3757,28 @@ test_function (wchar_t * (*my_asnwprintf) (wchar_t *, size_t *, const wchar_t *,
     free (result);
   }
 
+  /* On Android ≥ 5.0, the default locale is the "C.UTF-8" locale, not the
+     "C" locale.  Furthermore, when you attempt to set the "C" or "POSIX"
+     locale via setlocale(), what you get is a "C" locale with UTF-8 encoding,
+     that is, effectively the "C.UTF-8" locale.  */
+#ifndef __ANDROID__
+  { /* The conversion of %s to wide characters is done as if through repeated
+       invocations of mbrtowc(), and in the "C" and "POSIX" locales, "an
+       [EILSEQ] error cannot occur since all byte values are valid characters",
+       says POSIX:2018.  */
+    int c;
+
+    for (c = 0; c < 0x100; c++)
+      {
+        char s[2] = { c, '\0' };
+        size_t length;
+        wchar_t *result = my_asnwprintf (NULL, &length, L"%s", s);
+        ASSERT (result != NULL);
+        free (result);
+      }
+  }
+#endif
+
 #if HAVE_WCHAR_T
   static wchar_t L_xyz[4] = { 'x', 'y', 'z', 0 };
 
@@ -3960,6 +3982,26 @@ test_function (wchar_t * (*my_asnwprintf) (wchar_t *, size_t *, const wchar_t *,
     free (result);
   }
 
+  /* On Android ≥ 5.0, the default locale is the "C.UTF-8" locale, not the
+     "C" locale.  Furthermore, when you attempt to set the "C" or "POSIX"
+     locale via setlocale(), what you get is a "C" locale with UTF-8 encoding,
+     that is, effectively the "C.UTF-8" locale.  */
+#ifndef __ANDROID__
+  { /* The conversion of %c to wide character is done as if through btowc(),
+       and in the "C" and "POSIX" locales, "btowc() shall not return WEOF if
+       c has a value in the range 0 to 255 inclusive", says POSIX:2018.  */
+    int c;
+
+    for (c = 0; c < 0x100; c++)
+      {
+        size_t length;
+        wchar_t *result = my_asnwprintf (NULL, &length, L"%c", c);
+        ASSERT (result != NULL);
+        free (result);
+      }
+  }
+#endif
+
 #if HAVE_WCHAR_T
   static wint_t L_x = (wchar_t) 'x';