I wrote: > Since the *c32* functions are built on the *wc* functions, it is worth > to assert the same consistency — mbrtowc and wcrtomb — in the C locale.
And similarly, we need consistency for the btowc and wctob functions. 2026-09-20 Bruno Haible <[email protected]> wctob: Add tests. * tests/test-wctob.c: New file. * tests/test-wctob.sh: New file. * modules/wctob-tests: New file. wctob: Make consistent with wcrtomb. * m4/wctob.m4 (gl_FUNC_WCTOB): Require gl_MBRTOWC_C_LOCALE and, if needed, set REPLACE_WCTOB. * modules/wctob (Files): Add mbrtowc.m4. wctob: Make thread-safe if possible. * m4/wctob.m4 (gl_PREREQ_WCTOB): Test for wcrtomb. * lib/wctob.c (wctob): Use wcrtomb instead of wctomb if possible. * modules/wctob (Depends-on): Add mbszero, wcrtomb. 2026-09-20 Bruno Haible <[email protected]> btowc: Enhance tests. * tests/test-btowc.c (main): Check that btowc is consistent with mbrtowc. * modules/btowc-tests (Depends-on): Add mbrtowc.
From 159d00520ecd01ebfc92397fd9b470a7627974f0 Mon Sep 17 00:00:00 2001 From: Bruno Haible <[email protected]> Date: Sun, 20 Sep 2026 16:50:14 +0200 Subject: [PATCH 1/4] btowc: Enhance tests. * tests/test-btowc.c (main): Check that btowc is consistent with mbrtowc. * modules/btowc-tests (Depends-on): Add mbrtowc. --- ChangeLog | 7 +++++++ modules/btowc-tests | 1 + tests/test-btowc.c | 15 +++++++++++++++ 3 files changed, 23 insertions(+) diff --git a/ChangeLog b/ChangeLog index aca5a13e45..7f9e95abdd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2026-09-20 Bruno Haible <[email protected]> + + btowc: Enhance tests. + * tests/test-btowc.c (main): Check that btowc is consistent with + mbrtowc. + * modules/btowc-tests (Depends-on): Add mbrtowc. + 2026-09-20 Bruno Haible <[email protected]> wcrtomb: Make consistent with mbrtowc (regression 2016-04-09). diff --git a/modules/btowc-tests b/modules/btowc-tests index 5b5ce4aa42..2f9fbee441 100644 --- a/modules/btowc-tests +++ b/modules/btowc-tests @@ -11,6 +11,7 @@ m4/codeset.m4 Depends-on: setlocale +mbrtowc streq configure.ac: diff --git a/tests/test-btowc.c b/tests/test-btowc.c index e4ca8418ad..a3ab096638 100644 --- a/tests/test-btowc.c +++ b/tests/test-btowc.c @@ -37,6 +37,21 @@ main (int argc, char *argv[]) ASSERT (btowc (EOF) == WEOF); + /* Verify that btowc is consistent with mbrtowc. */ + for (int c = 0; c < 0x100; c++) + if (c != 0) + { + mbstate_t state; + mbszero (&state); + char s[1]; + s[0] = (unsigned char) c; + wchar_t wc; + if (mbrtowc (&wc, s, 1, &state) <= 1) + ASSERT (btowc (c) == (wint_t) wc); + else + ASSERT (btowc (c) == WEOF); + } + #ifdef __ANDROID__ /* 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" -- 2.53.0
>From 4258ec3e8362b815c3da749e32fa4818bde7f306 Mon Sep 17 00:00:00 2001 From: Bruno Haible <[email protected]> Date: Sun, 20 Sep 2026 17:13:27 +0200 Subject: [PATCH 2/4] wctob: Make thread-safe if possible. * m4/wctob.m4 (gl_PREREQ_WCTOB): Test for wcrtomb. * lib/wctob.c (wctob): Use wcrtomb instead of wctomb if possible. * modules/wctob (Depends-on): Add mbszero, wcrtomb. --- ChangeLog | 7 +++++++ lib/wctob.c | 12 ++++++++++-- m4/wctob.m4 | 3 ++- modules/wctob | 2 ++ 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index 7f9e95abdd..d8b7ff6559 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2026-09-20 Bruno Haible <[email protected]> + + wctob: Make thread-safe if possible. + * m4/wctob.m4 (gl_PREREQ_WCTOB): Test for wcrtomb. + * lib/wctob.c (wctob): Use wcrtomb instead of wctomb if possible. + * modules/wctob (Depends-on): Add mbszero, wcrtomb. + 2026-09-20 Bruno Haible <[email protected]> btowc: Enhance tests. diff --git a/lib/wctob.c b/lib/wctob.c index 1a569a88b9..3b3ab82b1a 100644 --- a/lib/wctob.c +++ b/lib/wctob.c @@ -32,7 +32,15 @@ wctob (wint_t wc) abort (); /* Handle the case where WEOF is a value that does not fit in a wchar_t. */ if (wc == (wchar_t)wc) - if (wctomb (buf, (wchar_t)wc) == 1) - return (unsigned char) buf[0]; + { +#if HAVE_WCRTOMB + mbstate_t state; mbszero (&state); + if (wcrtomb (buf, (wchar_t)wc, &state) == 1) + return (unsigned char) buf[0]; +#else + if (wctomb (buf, (wchar_t)wc) == 1) + return (unsigned char) buf[0]; +#endif + } return EOF; } diff --git a/m4/wctob.m4 b/m4/wctob.m4 index 76acd28929..0fcb560aaf 100644 --- a/m4/wctob.m4 +++ b/m4/wctob.m4 @@ -1,5 +1,5 @@ # wctob.m4 -# serial 15 +# serial 16 dnl Copyright (C) 2008-2026 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -96,5 +96,6 @@ AC_DEFUN([gl_FUNC_WCTOB] # Prerequisites of lib/wctob.c. AC_DEFUN([gl_PREREQ_WCTOB], [ + AC_CHECK_FUNCS_ONCE([wcrtomb]) : ]) diff --git a/modules/wctob b/modules/wctob index ef50bf5b5c..35e6e8be0d 100644 --- a/modules/wctob +++ b/modules/wctob @@ -10,6 +10,8 @@ m4/codeset.m4 Depends-on: wchar-h +mbszero [test $HAVE_WCTOB = 0 || test $REPLACE_WCTOB = 1] +wcrtomb [test $HAVE_WCTOB = 0 || test $REPLACE_WCTOB = 1] wctomb [test $HAVE_WCTOB = 0 || test $REPLACE_WCTOB = 1] configure.ac: -- 2.53.0
>From b09ea200306c5df2f897f032c3d0e9f76e00a913 Mon Sep 17 00:00:00 2001 From: Bruno Haible <[email protected]> Date: Sun, 20 Sep 2026 17:21:26 +0200 Subject: [PATCH 3/4] wctob: Make consistent with wcrtomb. * m4/wctob.m4 (gl_FUNC_WCTOB): Require gl_MBRTOWC_C_LOCALE and, if needed, set REPLACE_WCTOB. * modules/wctob (Files): Add mbrtowc.m4. --- ChangeLog | 5 +++++ m4/wctob.m4 | 10 +++++++++- modules/wctob | 1 + 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index d8b7ff6559..6145b67156 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,10 @@ 2026-09-20 Bruno Haible <[email protected]> + wctob: Make consistent with wcrtomb. + * m4/wctob.m4 (gl_FUNC_WCTOB): Require gl_MBRTOWC_C_LOCALE and, if + needed, set REPLACE_WCTOB. + * modules/wctob (Files): Add mbrtowc.m4. + wctob: Make thread-safe if possible. * m4/wctob.m4 (gl_PREREQ_WCTOB): Test for wcrtomb. * lib/wctob.c (wctob): Use wcrtomb instead of wctomb if possible. diff --git a/m4/wctob.m4 b/m4/wctob.m4 index 0fcb560aaf..127fd18dc5 100644 --- a/m4/wctob.m4 +++ b/m4/wctob.m4 @@ -1,5 +1,5 @@ # wctob.m4 -# serial 16 +# serial 17 dnl Copyright (C) 2008-2026 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -91,6 +91,14 @@ AC_DEFUN([gl_FUNC_WCTOB] *yes) ;; *) REPLACE_WCTOB=1 ;; esac + dnl When we adjust mbrtowc in the C locale, we need to adjust wctob as well, + dnl for consistency. + AC_REQUIRE([gl_MBRTOWC_C_LOCALE]) + case "$gl_cv_func_mbrtowc_C_locale_sans_EILSEQ" in + *yes) ;; + *) REPLACE_WCTOB=1 + ;; + esac fi ]) diff --git a/modules/wctob b/modules/wctob index 35e6e8be0d..cfd3b898d1 100644 --- a/modules/wctob +++ b/modules/wctob @@ -4,6 +4,7 @@ wctob() function: convert wide character to unibyte character. Files: lib/wctob.c m4/wctob.m4 +m4/mbrtowc.m4 m4/locale-en.m4 m4/locale-fr.m4 m4/codeset.m4 -- 2.53.0
From 1052c15fa387a35c9548dae5ac5c77f93773bd9e Mon Sep 17 00:00:00 2001 From: Bruno Haible <[email protected]> Date: Sun, 20 Sep 2026 17:23:31 +0200 Subject: [PATCH 4/4] wctob: Add tests. * tests/test-wctob.c: New file. * tests/test-wctob.sh: New file. * modules/wctob-tests: New file. --- ChangeLog | 5 ++ modules/wctob-tests | 33 ++++++++++++ tests/test-wctob.c | 121 ++++++++++++++++++++++++++++++++++++++++++++ tests/test-wctob.sh | 49 ++++++++++++++++++ 4 files changed, 208 insertions(+) create mode 100644 modules/wctob-tests create mode 100644 tests/test-wctob.c create mode 100755 tests/test-wctob.sh diff --git a/ChangeLog b/ChangeLog index 6145b67156..5bc453e4c4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,10 @@ 2026-09-20 Bruno Haible <[email protected]> + wctob: Add tests. + * tests/test-wctob.c: New file. + * tests/test-wctob.sh: New file. + * modules/wctob-tests: New file. + wctob: Make consistent with wcrtomb. * m4/wctob.m4 (gl_FUNC_WCTOB): Require gl_MBRTOWC_C_LOCALE and, if needed, set REPLACE_WCTOB. diff --git a/modules/wctob-tests b/modules/wctob-tests new file mode 100644 index 0000000000..2eaec88855 --- /dev/null +++ b/modules/wctob-tests @@ -0,0 +1,33 @@ +Files: +tests/test-wctob.sh +tests/test-wctob.c +tests/signature.h +tests/macros.h +m4/locale-en.m4 +m4/locale-fr.m4 +m4/locale-ja.m4 +m4/locale-zh.m4 +m4/codeset.m4 + +Depends-on: +btowc +setlocale + +configure.ac: +gt_LOCALE_EN_UTF8 +gt_LOCALE_FR +gt_LOCALE_FR_UTF8 +gt_LOCALE_JA +gt_LOCALE_ZH_CN + +Makefile.am: +TESTS += \ + test-wctob.sh +TESTS_ENVIRONMENT += \ + LOCALE_EN_UTF8='@LOCALE_EN_UTF8@' \ + LOCALE_FR='@LOCALE_FR@' \ + LOCALE_FR_UTF8='@LOCALE_FR_UTF8@' \ + LOCALE_JA='@LOCALE_JA@' \ + LOCALE_ZH_CN='@LOCALE_ZH_CN@' +check_PROGRAMS += test-wctob +test_wctob_LDADD = $(LDADD) $(SETLOCALE_LIB) diff --git a/tests/test-wctob.c b/tests/test-wctob.c new file mode 100644 index 0000000000..a3acfb3212 --- /dev/null +++ b/tests/test-wctob.c @@ -0,0 +1,121 @@ +/* Test of conversion of wide character to unibyte character. + Copyright (C) 2008-2026 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. */ + +/* Written by Bruno Haible <[email protected]>, 2026. */ + +#include <config.h> + +#include <wchar.h> + +#include "signature.h" +SIGNATURE_CHECK (wctob, int, (wint_t)); + +#include <locale.h> +#include <stdlib.h> +#include <string.h> + +#include "macros.h" + +/* Check the unibyte character c. */ +static void +check_character (unsigned char c) +{ + wint_t wc = btowc (c); + ASSERT (wc != WEOF); + int cc = wctob (wc); + ASSERT (cc == c); +} + +int +main (int argc, char *argv[]) +{ + /* configure should already have checked that the locale is supported. */ + if (setlocale (LC_ALL, "") == NULL) + return 1; + + /* Test NUL character. */ + ASSERT (wctob (0) == 0); + + /* Test single bytes. */ + for (int c = 0; c < 0x100; c++) + switch (c) + { + case '\t': case '\v': case '\f': + case ' ': case '!': case '"': case '#': case '%': + case '&': case '\'': case '(': case ')': case '*': + case '+': case ',': case '-': case '.': case '/': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + case ':': case ';': case '<': case '=': case '>': + case '?': + case 'A': case 'B': case 'C': case 'D': case 'E': + case 'F': case 'G': case 'H': case 'I': case 'J': + case 'K': case 'L': case 'M': case 'N': case 'O': + case 'P': case 'Q': case 'R': case 'S': case 'T': + case 'U': case 'V': case 'W': case 'X': case 'Y': + case 'Z': + case '[': case '\\': case ']': case '^': case '_': + case 'a': case 'b': case 'c': case 'd': case 'e': + case 'f': case 'g': case 'h': case 'i': case 'j': + case 'k': case 'l': case 'm': case 'n': case 'o': + case 'p': case 'q': case 'r': case 's': case 't': + case 'u': case 'v': case 'w': case 'x': case 'y': + case 'z': case '{': case '|': case '}': case '~': + /* c is in the ISO C "basic character set". */ + check_character (c); + break; + } + + if (argc > 1) + switch (argv[1][0]) + { + case '1': + /* C locale; tested above. */ +#if !defined __ANDROID__ + /* 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. */ + /* Check that wctob does the inverse of btowc, in the C locale. + Above we have only tested the ISO C "basic character set". */ + for (int c = 0; c < 0x100; c++) + check_character (c); +#endif + return test_exit_status; + + case '2': + /* Locale encoding is ISO-8859-1 or ISO-8859-15. */ + return test_exit_status; + + case '3': + /* Locale encoding is UTF-8. */ + ASSERT (wctob (0x0091) == EOF); + ASSERT (wctob (0x00DF) == EOF); + ASSERT (wctob (0x00FC) == EOF); + ASSERT (wctob (0x1F60B) == EOF); + return test_exit_status; + + case '4': + /* Locale encoding is EUC-JP. */ + return test_exit_status; + + case '5': + /* Locale encoding is GB18030. */ + return test_exit_status; + } + + return 1; +} diff --git a/tests/test-wctob.sh b/tests/test-wctob.sh new file mode 100755 index 0000000000..3c897d5660 --- /dev/null +++ b/tests/test-wctob.sh @@ -0,0 +1,49 @@ +#!/bin/sh + +# Test in the POSIX locale. +LC_ALL=C ${CHECKER} ./test-wctob${EXEEXT} 1 || exit 1 +LC_ALL=POSIX ${CHECKER} ./test-wctob${EXEEXT} 1 || exit 1 + +# Test in an ISO-8859-1 or ISO-8859-15 locale. +: "${LOCALE_FR=fr_FR}" +if test $LOCALE_FR != none; then + LC_ALL=$LOCALE_FR \ + ${CHECKER} ./test-wctob${EXEEXT} 2 \ + || exit 1 +fi + +# Test whether a specific UTF-8 locale is installed. +: "${LOCALE_EN_UTF8=en_US.UTF-8}" +: "${LOCALE_FR_UTF8=fr_FR.UTF-8}" +if test "$LOCALE_EN_UTF8" != none || test $LOCALE_FR_UTF8 != none; then + # It's sufficient to test in one of the two locales. + if test $LOCALE_FR_UTF8 != none; then + testlocale=$LOCALE_FR_UTF8 + else + testlocale="$LOCALE_EN_UTF8" + fi + LC_ALL="$testlocale" \ + ${CHECKER} ./test-wctob${EXEEXT} 3 \ + || exit 1 +fi + +# Test whether a specific EUC-JP locale is installed. +: "${LOCALE_JA=ja_JP}" +if test $LOCALE_JA != none; then + LC_ALL=$LOCALE_JA \ + ${CHECKER} ./test-wctob${EXEEXT} 4 \ + || exit 1 +fi + +# Test whether a specific GB18030 locale is installed. +: "${LOCALE_ZH_CN=zh_CN.GB18030}" +if test $LOCALE_ZH_CN != none; then + LC_ALL=$LOCALE_ZH_CN \ + ${CHECKER} ./test-wctob${EXEEXT} 5 + case $? in + 0 | 77) ;; + *) exit 1 ;; + esac +fi + +exit 0 -- 2.53.0
