These patches work around two platform-specific bugs of newlocale().
2025-02-14 Bruno Haible <br...@clisp.org> newlocale: Work around NetBSD bug. * lib/newlocale.c (newlocale) [NetBSD]: Test whether the locale name is valid; fail with error ENOENT if not. * doc/posix-functions/newlocale.texi: Mention the NetBSD bug. 2025-02-14 Bruno Haible <br...@clisp.org> newlocale: Work around macOS, NetBSD, Solaris 11 OpenIndiana bug. * m4/newlocale.m4 (gl_FUNC_NEWLOCALE): Test for the "null base" bug. Set REPLACE_NEWLOCALE to 1 if it has the bug. * lib/newlocale.c (newlocale): Add alternative implementation that uses the system's newlocale(). * modules/newlocale (configure.ac): Consider REPLACE_NEWLOCALE. * tests/test-newlocale.c: Include <langinfo.h>. (main): Verify fix for the "null base" bug. * modules/newlocale-tests (configure.ac): Test for nl_langinfo_l. * doc/posix-functions/newlocale.texi: Mention the "null base" bug. 2025-02-14 Bruno Haible <br...@clisp.org> newlocale, freelocale: Tweak configuration. * m4/newlocale.m4 (gl_FUNC_NEWLOCALE): Make consistent with m4/localename.m4. * m4/freelocale.m4 (gl_FUNC_FREELOCALE): Likewise.
>From e8618f66fa25932f1121632b52044abb27a6ecec Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Fri, 14 Feb 2025 14:32:30 +0100 Subject: [PATCH 1/6] newlocale, freelocale: Tweak configuration. * m4/newlocale.m4 (gl_FUNC_NEWLOCALE): Make consistent with m4/localename.m4. * m4/freelocale.m4 (gl_FUNC_FREELOCALE): Likewise. --- ChangeLog | 7 +++++++ m4/freelocale.m4 | 18 +++++++++++++++--- m4/newlocale.m4 | 18 +++++++++++++++--- 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/ChangeLog b/ChangeLog index 50b6a1daf3..03cc2df88b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2025-02-14 Bruno Haible <br...@clisp.org> + + newlocale, freelocale: Tweak configuration. + * m4/newlocale.m4 (gl_FUNC_NEWLOCALE): Make consistent with + m4/localename.m4. + * m4/freelocale.m4 (gl_FUNC_FREELOCALE): Likewise. + 2025-02-14 Bruno Haible <br...@clisp.org> duplocale: Support all platforms. diff --git a/m4/freelocale.m4 b/m4/freelocale.m4 index 61fc0a93bd..5c6a144a74 100644 --- a/m4/freelocale.m4 +++ b/m4/freelocale.m4 @@ -1,5 +1,5 @@ # freelocale.m4 -# serial 1 +# serial 2 dnl Copyright (C) 2025 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -9,9 +9,21 @@ AC_DEFUN([gl_FUNC_FREELOCALE], [ AC_REQUIRE([gl_LOCALE_H_DEFAULTS]) - gl_CHECK_FUNCS_ANDROID([freelocale], [[#include <locale.h>]]) - if test $ac_cv_func_freelocale = no; then + AC_REQUIRE([gl_LOCALE_T]) + if test $HAVE_LOCALE_T = 1; then + gl_CHECK_FUNCS_ANDROID([freelocale], [[#include <locale.h>]]) + gl_func_freelocale="$ac_cv_func_freelocale" + else + dnl In 2019, some versions of z/OS lack the locale_t type and have broken + dnl newlocale, duplocale, freelocale functions. + gl_cv_onwards_func_freelocale='future OS version' + gl_func_freelocale=no + fi + if test $gl_func_freelocale != yes; then HAVE_FREELOCALE=0 + case "$gl_cv_onwards_func_freelocale" in + future*) REPLACE_FREELOCALE=1 ;; + esac fi ]) diff --git a/m4/newlocale.m4 b/m4/newlocale.m4 index b2475eb16f..13df13808e 100644 --- a/m4/newlocale.m4 +++ b/m4/newlocale.m4 @@ -1,5 +1,5 @@ # newlocale.m4 -# serial 1 +# serial 2 dnl Copyright (C) 2025 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -9,9 +9,21 @@ AC_DEFUN([gl_FUNC_NEWLOCALE], [ AC_REQUIRE([gl_LOCALE_H_DEFAULTS]) - gl_CHECK_FUNCS_ANDROID([newlocale], [[#include <locale.h>]]) - if test $ac_cv_func_newlocale = no; then + AC_REQUIRE([gl_LOCALE_T]) + if test $HAVE_LOCALE_T = 1; then + gl_CHECK_FUNCS_ANDROID([newlocale], [[#include <locale.h>]]) + gl_func_newlocale="$ac_cv_func_newlocale" + else + dnl In 2019, some versions of z/OS lack the locale_t type and have broken + dnl newlocale, duplocale, freelocale functions. + gl_cv_onwards_func_newlocale='future OS version' + gl_func_newlocale=no + fi + if test $gl_func_newlocale != yes; then HAVE_NEWLOCALE=0 + case "$gl_cv_onwards_func_newlocale" in + future*) REPLACE_NEWLOCALE=1 ;; + esac fi ]) -- 2.43.0
>From bc2d70ee57c62739cb15d5d06aad6982306d05ff Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Fri, 14 Feb 2025 15:24:54 +0100 Subject: [PATCH 2/6] newlocale: Work around macOS, NetBSD, Solaris 11 OpenIndiana bug. * m4/newlocale.m4 (gl_FUNC_NEWLOCALE): Test for the "null base" bug. Set REPLACE_NEWLOCALE to 1 if it has the bug. * lib/newlocale.c (newlocale): Add alternative implementation that uses the system's newlocale(). * modules/newlocale (configure.ac): Consider REPLACE_NEWLOCALE. * tests/test-newlocale.c: Include <langinfo.h>. (main): Verify fix for the "null base" bug. * modules/newlocale-tests (configure.ac): Test for nl_langinfo_l. * doc/posix-functions/newlocale.texi: Mention the "null base" bug. --- ChangeLog | 13 +++++++ doc/posix-functions/newlocale.texi | 4 +++ lib/newlocale.c | 55 ++++++++++++++++++++++-------- m4/newlocale.m4 | 54 +++++++++++++++++++++++++++-- modules/newlocale | 3 +- modules/newlocale-tests | 1 + tests/test-newlocale.c | 35 ++++++++++++++++--- 7 files changed, 143 insertions(+), 22 deletions(-) diff --git a/ChangeLog b/ChangeLog index 03cc2df88b..5c52985015 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +2025-02-14 Bruno Haible <br...@clisp.org> + + newlocale: Work around macOS, NetBSD, Solaris 11 OpenIndiana bug. + * m4/newlocale.m4 (gl_FUNC_NEWLOCALE): Test for the "null base" bug. + Set REPLACE_NEWLOCALE to 1 if it has the bug. + * lib/newlocale.c (newlocale): Add alternative implementation that uses + the system's newlocale(). + * modules/newlocale (configure.ac): Consider REPLACE_NEWLOCALE. + * tests/test-newlocale.c: Include <langinfo.h>. + (main): Verify fix for the "null base" bug. + * modules/newlocale-tests (configure.ac): Test for nl_langinfo_l. + * doc/posix-functions/newlocale.texi: Mention the "null base" bug. + 2025-02-14 Bruno Haible <br...@clisp.org> newlocale, freelocale: Tweak configuration. diff --git a/doc/posix-functions/newlocale.texi b/doc/posix-functions/newlocale.texi index a305c3821b..b4dc3661bd 100644 --- a/doc/posix-functions/newlocale.texi +++ b/doc/posix-functions/newlocale.texi @@ -16,6 +16,10 @@ This function is useless because the @code{locale_t} type is not defined on some platforms: z/OS. +@item +When the third argument is NULL, this function uses locale category data +from the current locale instead of from the "C" locale on some platforms: +macOS, NetBSD 10.0, Solaris 11 OpenIndiana. @end itemize Portability problems not fixed by Gnulib: diff --git a/lib/newlocale.c b/lib/newlocale.c index 01bc2f3953..d5f902b8fb 100644 --- a/lib/newlocale.c +++ b/lib/newlocale.c @@ -22,10 +22,33 @@ #include <locale.h> #include <errno.h> -#include <stdlib.h> -#include <string.h> -#include "localename.h" +#if HAVE_NEWLOCALE +/* Only provide workarounds. */ + +locale_t +newlocale (int category_mask, const char *name, locale_t base) +# undef newlocale +{ + if ((category_mask & ~LC_ALL_MASK) != 0) + { + errno = EINVAL; + return NULL; + } + + if (category_mask != LC_ALL_MASK && base == NULL) + base = newlocale (LC_ALL_MASK, "C", NULL); + + return newlocale (category_mask, name, base); +} + +#else +/* Implement from scratch. */ + +# include <stdlib.h> +# include <string.h> + +# include "localename.h" locale_t newlocale (int category_mask, const char *name, locale_t base) @@ -57,7 +80,7 @@ newlocale (int category_mask, const char *name, locale_t base) if (strcmp (name, "POSIX") == 0) name = "C"; -#if !HAVE_WINDOWS_LOCALE_T +# if !HAVE_WINDOWS_LOCALE_T /* In this case, the only NAMEs that we support are "C" and (equivalently) "POSIX". */ if (category_mask != 0 && strcmp (name, "C") != 0) @@ -65,7 +88,7 @@ newlocale (int category_mask, const char *name, locale_t base) errno = ENOENT; return NULL; } -#endif +# endif int i; int err; @@ -111,15 +134,15 @@ newlocale (int category_mask, const char *name, locale_t base) if (strcmp (lcname, "C") == 0) { result->category[i].is_c_locale = true; -#if HAVE_WINDOWS_LOCALE_T +# if HAVE_WINDOWS_LOCALE_T /* Just to initialize it. */ result->category[i].system_locale = NULL; -#endif +# endif } else { result->category[i].is_c_locale = false; -#if HAVE_WINDOWS_LOCALE_T +# if HAVE_WINDOWS_LOCALE_T if (log2_lcmask == gl_log2_lc_mask (LC_MESSAGES)) result->category[i].system_locale = NULL; else @@ -137,7 +160,7 @@ newlocale (int category_mask, const char *name, locale_t base) goto fail_with_err; } } -#endif +# endif } } else @@ -151,10 +174,10 @@ newlocale (int category_mask, const char *name, locale_t base) goto fail_with_err; } result->category[i].is_c_locale = true; -#if HAVE_WINDOWS_LOCALE_T +# if HAVE_WINDOWS_LOCALE_T /* Just to initialize it. */ result->category[i].system_locale = NULL; -#endif +# endif } } } @@ -168,13 +191,13 @@ newlocale (int category_mask, const char *name, locale_t base) int log2_lcmask = gl_index_to_log2_lcmask (i); if ((category_mask & (1 << log2_lcmask)) != 0) { -#if HAVE_WINDOWS_LOCALE_T +# if HAVE_WINDOWS_LOCALE_T if (!(i == gl_log2_lcmask_to_index (gl_log2_lc_mask (LC_MESSAGES)) || base->category[i].is_c_locale)) /* Documentation: <https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/free-locale> */ _free_locale (base->category[i].system_locale); -#endif +# endif free (base->category[i].name); base->category[i] = result->category[i]; @@ -188,13 +211,13 @@ newlocale (int category_mask, const char *name, locale_t base) fail_with_err: while (--i >= 0) { -#if HAVE_WINDOWS_LOCALE_T +# if HAVE_WINDOWS_LOCALE_T if (!(i == gl_log2_lcmask_to_index (gl_log2_lc_mask (LC_MESSAGES)) || result->category[i].is_c_locale)) /* Documentation: <https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/free-locale> */ _free_locale (result->category[i].system_locale); -#endif +# endif free (result->category[i].name); } if (base == NULL) @@ -202,3 +225,5 @@ newlocale (int category_mask, const char *name, locale_t base) errno = err; return NULL; } + +#endif diff --git a/m4/newlocale.m4 b/m4/newlocale.m4 index 13df13808e..2775609ee6 100644 --- a/m4/newlocale.m4 +++ b/m4/newlocale.m4 @@ -1,5 +1,5 @@ # newlocale.m4 -# serial 2 +# serial 3 dnl Copyright (C) 2025 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -19,7 +19,57 @@ AC_DEFUN([gl_FUNC_NEWLOCALE] gl_cv_onwards_func_newlocale='future OS version' gl_func_newlocale=no fi - if test $gl_func_newlocale != yes; then + if test $gl_func_newlocale = yes; then + dnl Check against the macOS, NetBSD, Solaris 11 OpenIndiana bug: + dnl When the third argument is NULL, newlocale() uses locale category data + dnl from the current locale instead of from the "C" locale. + gl_CHECK_FUNCS_ANDROID([nl_langinfo_l], [[#include <langinfo.h>]]) + if test $ac_cv_func_nl_langinfo_l = yes; then + AC_REQUIRE([AC_CANONICAL_HOST]) + AC_CACHE_CHECK([whether newlocale with a null base works], + [gl_cv_func_newlocale_works], + [dnl Prepare a guess, used when cross-compiling or when specific locales + dnl are not available. + case "$host_os" in + darwin* | netbsd* | solaris*) + gl_cv_func_newlocale_works="guessing no" ;; + *) + gl_cv_func_newlocale_works="guessing yes" ;; + esac + AC_RUN_IFELSE( + [AC_LANG_SOURCE([[ +#include <locale.h> +#if HAVE_XLOCALE_H +# include <xlocale.h> +#endif +#include <langinfo.h> +int main () +{ + locale_t l1 = newlocale (LC_TIME_MASK, "en_US.UTF-8", NULL); + if (l1 != NULL) + { + if (setlocale (LC_ALL, "fr_FR.UTF-8") != NULL) + { + locale_t l1a = newlocale (LC_TIME_MASK, "en_US.UTF-8", NULL); + const char *radixchar1a = nl_langinfo_l (RADIXCHAR, l1a); + return (*radixchar1a != '.'); + } + } + return 2; +}]])], + [gl_cv_func_newlocale_works=yes], + [if test $? = 1; then + gl_cv_func_newlocale_works=no + fi + ], + []) + ]) + case "$gl_cv_func_newlocale_works" in + *yes) ;; + *) REPLACE_NEWLOCALE=1 ;; + esac + fi + else HAVE_NEWLOCALE=0 case "$gl_cv_onwards_func_newlocale" in future*) REPLACE_NEWLOCALE=1 ;; diff --git a/modules/newlocale b/modules/newlocale index 78a10436fa..04f6df28f9 100644 --- a/modules/newlocale +++ b/modules/newlocale @@ -11,7 +11,8 @@ localename-environ configure.ac: gl_FUNC_NEWLOCALE -gl_CONDITIONAL([GL_COND_OBJ_NEWLOCALE], [test $HAVE_LOCALE_T = 0]) +gl_CONDITIONAL([GL_COND_OBJ_NEWLOCALE], + [test $HAVE_LOCALE_T = 0 || { test $REPLACE_NEWLOCALE = 1 && test "$gt_localename_enhances_locale_funcs" != yes; }]) AM_COND_IF([GL_COND_OBJ_NEWLOCALE], [ gl_PREREQ_NEWLOCALE ]) diff --git a/modules/newlocale-tests b/modules/newlocale-tests index 706003b4f2..6876456784 100644 --- a/modules/newlocale-tests +++ b/modules/newlocale-tests @@ -6,6 +6,7 @@ tests/macros.h Depends-on: configure.ac: +gl_CHECK_FUNCS_ANDROID([nl_langinfo_l], [[#include <langinfo.h>]]) Makefile.am: TESTS += test-newlocale diff --git a/tests/test-newlocale.c b/tests/test-newlocale.c index fa21f8b697..a6829d7154 100644 --- a/tests/test-newlocale.c +++ b/tests/test-newlocale.c @@ -23,6 +23,10 @@ #include "signature.h" SIGNATURE_CHECK (newlocale, locale_t, (int, const char *, locale_t)); +#if HAVE_NL_LANGINFO_L +# include <langinfo.h> +#endif + #include "macros.h" #if defined _WIN32 && !defined __CYGWIN__ @@ -47,10 +51,33 @@ SIGNATURE_CHECK (newlocale, locale_t, (int, const char *, locale_t)); int main () { - locale_t l1 = newlocale (LC_TIME_MASK, LOCALE1, NULL); - locale_t l2 = newlocale (LC_MESSAGES_MASK, LOCALE2, l1); - locale_t l3 = newlocale (LC_TIME_MASK, LOCALE3, l2); - (void) l3; + { + locale_t l1 = newlocale (LC_TIME_MASK, LOCALE1, NULL); + locale_t l2 = newlocale (LC_MESSAGES_MASK, LOCALE2, l1); + locale_t l3 = newlocale (LC_TIME_MASK, LOCALE3, l2); + (void) l3; + } + +#if HAVE_NL_LANGINFO_L + /* Verify that when the base argument is NULL, "the data for all sections + not requested by category_mask shall be taken from the POSIX locale". */ + { + locale_t l1 = newlocale (LC_TIME_MASK, LOCALE1, NULL); + if (l1 != NULL) + { + const char *radixchar1 = nl_langinfo_l (RADIXCHAR, l1); + ASSERT (*radixchar1 == '.'); + if (setlocale (LC_ALL, LOCALE2) != NULL) + { + radixchar1 = nl_langinfo_l (RADIXCHAR, l1); + ASSERT (*radixchar1 == '.'); + locale_t l1a = newlocale (LC_TIME_MASK, LOCALE1, NULL); + const char *radixchar1a = nl_langinfo_l (RADIXCHAR, l1a); + ASSERT (*radixchar1a == '.'); + } + } + } +#endif return test_exit_status; } -- 2.43.0
>From 49564bc5410d4480a847a772628eec176fc68582 Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Fri, 14 Feb 2025 15:42:22 +0100 Subject: [PATCH 3/6] newlocale: Work around NetBSD bug. * lib/newlocale.c (newlocale) [NetBSD]: Test whether the locale name is valid; fail with error ENOENT if not. * doc/posix-functions/newlocale.texi: Mention the NetBSD bug. --- ChangeLog | 7 +++++++ doc/posix-functions/newlocale.texi | 4 ++++ lib/newlocale.c | 30 ++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/ChangeLog b/ChangeLog index 5c52985015..3d8d3bb3f0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2025-02-14 Bruno Haible <br...@clisp.org> + + newlocale: Work around NetBSD bug. + * lib/newlocale.c (newlocale) [NetBSD]: Test whether the locale name is + valid; fail with error ENOENT if not. + * doc/posix-functions/newlocale.texi: Mention the NetBSD bug. + 2025-02-14 Bruno Haible <br...@clisp.org> newlocale: Work around macOS, NetBSD, Solaris 11 OpenIndiana bug. diff --git a/doc/posix-functions/newlocale.texi b/doc/posix-functions/newlocale.texi index b4dc3661bd..54af40a76f 100644 --- a/doc/posix-functions/newlocale.texi +++ b/doc/posix-functions/newlocale.texi @@ -20,6 +20,10 @@ When the third argument is NULL, this function uses locale category data from the current locale instead of from the "C" locale on some platforms: macOS, NetBSD 10.0, Solaris 11 OpenIndiana. +@item +When the second argument is an invalid or unsupported locale name, +this function uses the "C" locale instead of failing on some platforms: +NetBSD 10.0. @end itemize Portability problems not fixed by Gnulib: diff --git a/lib/newlocale.c b/lib/newlocale.c index d5f902b8fb..ccce715852 100644 --- a/lib/newlocale.c +++ b/lib/newlocale.c @@ -26,6 +26,9 @@ #if HAVE_NEWLOCALE /* Only provide workarounds. */ +# include <sys/types.h> +# include <sys/stat.h> + locale_t newlocale (int category_mask, const char *name, locale_t base) # undef newlocale @@ -36,6 +39,33 @@ newlocale (int category_mask, const char *name, locale_t base) return NULL; } +# if defined __NetBSD__ + /* Work around a NetBSD bug: newlocale does not fail (unlike setlocale) + when NAME is an invalid locale name. */ + if (category_mask != 0) + { + /* Test whether NAME is valid. */ + if (!(strcmp (name, "C") == 0 || strcmp (name, "POSIX") == 0)) + { + char *filename = (char *) malloc (18 + strlen (name) + 9 + 1); + if (filename == NULL) + { + errno = ENOMEM; + return NULL; + } + sprintf (filename, "/usr/share/locale/%s/LC_CTYPE", name); + struct stat statbuf; + if (stat (filename, &statbuf) < 0) + { + free (filename); + errno = ENOENT; + return NULL; + } + free (filename); + } + } +# endif + if (category_mask != LC_ALL_MASK && base == NULL) base = newlocale (LC_ALL_MASK, "C", NULL); -- 2.43.0