+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).
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.
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.
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.
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.
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.
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.
}
}
#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
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;
-# 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,
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
])
m4/inttypes_h.m4
m4/vasnprintf.m4
m4/printf.m4
+m4/mbrtowc.m4
m4/math_h.m4
m4/exponentd.m4
m4/musl.m4
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 };
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';