From bc2d70ee57c62739cb15d5d06aad6982306d05ff Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Fri, 14 Feb 2025 15:24:54 +0100 Subject: [PATCH] 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 . (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 + + 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 . + (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 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 @@ FreeBSD 9.0, NetBSD 6.1, OpenBSD 6.1, Minix 3.1.8, AIX 6.1, HP-UX 11, Solaris 11 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 #include -#include -#include -#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 +# include + +# 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: */ _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: */ _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 ]]) + 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 +#if HAVE_XLOCALE_H +# include +#endif +#include +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 ]]) 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 +#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.39.5