This patch implements a portable duplocale() function, that matches the just-added newlocale() function. Part of the code was already there, for old glibc and old AIX platforms.
2025-02-14 Bruno Haible <br...@clisp.org> duplocale: Support all platforms. * lib/locale.in.h (duplocale): Declare also on platforms that don't already have a duplocale function. Don't define HAVE_WORKING_DUPLOCALE. * lib/duplocale.c: Include <stdlib.h>. (duplocale): Renamed from rpl_duplocale. Add implementation for platforms without native locale_t. * modules/duplocale (Depends-on): Add newlocale, freelocale. (configure.ac): Compile also on platforms without native locale_t. * tests/test-duplocale.c: Ignore HAVE_WORKING_DUPLOCALE. * tests/test-locale-h-c++.cc: Likewise. * doc/posix-functions/duplocale.texi: Mention the change.
>From a5ce928de09dad7d429f96d5e23ce59c93bc2039 Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Fri, 14 Feb 2025 05:56:55 +0100 Subject: [PATCH] duplocale: Support all platforms. * lib/locale.in.h (duplocale): Declare also on platforms that don't already have a duplocale function. Don't define HAVE_WORKING_DUPLOCALE. * lib/duplocale.c: Include <stdlib.h>. (duplocale): Renamed from rpl_duplocale. Add implementation for platforms without native locale_t. * modules/duplocale (Depends-on): Add newlocale, freelocale. (configure.ac): Compile also on platforms without native locale_t. * tests/test-duplocale.c: Ignore HAVE_WORKING_DUPLOCALE. * tests/test-locale-h-c++.cc: Likewise. * doc/posix-functions/duplocale.texi: Mention the change. --- ChangeLog | 14 ++++++ doc/posix-functions/duplocale.texi | 14 +++--- lib/duplocale.c | 79 ++++++++++++++++++++++++++++-- lib/locale.in.h | 28 +++++------ modules/duplocale | 6 ++- tests/test-duplocale.c | 15 ------ tests/test-locale-h-c++.cc | 2 +- 7 files changed, 113 insertions(+), 45 deletions(-) diff --git a/ChangeLog b/ChangeLog index 712f90be04..50b6a1daf3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +2025-02-14 Bruno Haible <br...@clisp.org> + + duplocale: Support all platforms. + * lib/locale.in.h (duplocale): Declare also on platforms that don't + already have a duplocale function. Don't define HAVE_WORKING_DUPLOCALE. + * lib/duplocale.c: Include <stdlib.h>. + (duplocale): Renamed from rpl_duplocale. Add implementation for + platforms without native locale_t. + * modules/duplocale (Depends-on): Add newlocale, freelocale. + (configure.ac): Compile also on platforms without native locale_t. + * tests/test-duplocale.c: Ignore HAVE_WORKING_DUPLOCALE. + * tests/test-locale-h-c++.cc: Likewise. + * doc/posix-functions/duplocale.texi: Mention the change. + 2025-02-14 Bruno Haible <br...@clisp.org> freelocale: Fix typo. diff --git a/doc/posix-functions/duplocale.texi b/doc/posix-functions/duplocale.texi index 4d5e919b52..efcb847ca6 100644 --- a/doc/posix-functions/duplocale.texi +++ b/doc/posix-functions/duplocale.texi @@ -10,6 +10,13 @@ Portability problems fixed by Gnulib: @itemize @item +This function is missing on many platforms: +FreeBSD 9.0, NetBSD 6.1, OpenBSD 6.1, Minix 3.1.8, AIX 6.1, HP-UX 11, Solaris 11.3, Cygwin 2.5.x, mingw, MSVC 14, Android 4.4. +@item +This function is useless because the @code{locale_t} type is not defined +on some platforms: +z/OS. +@item The argument @code{LC_GLOBAL_LOCALE} is not supported on some platforms: glibc 2.11, AIX 7.1. @item @@ -21,13 +28,6 @@ Portability problems not fixed by Gnulib: @itemize @item -This function is missing on many platforms: -FreeBSD 9.0, NetBSD 5.0, OpenBSD 6.1, Minix 3.1.8, AIX 6.1, HP-UX 11, Solaris 11.3, Cygwin 2.5.x, mingw, MSVC 14, Android 4.4. -@item -This function is useless because the @code{locale_t} type is not defined -on some platforms: -z/OS. -@item With the argument @code{LC_GLOBAL_LOCALE}, this function returns a wrong result on some platforms: @c https://dev.haiku-os.org/ticket/18345 diff --git a/lib/duplocale.c b/lib/duplocale.c index 933c4bbf38..47a626e1e6 100644 --- a/lib/duplocale.c +++ b/lib/duplocale.c @@ -22,16 +22,17 @@ #include <locale.h> #include <errno.h> +#include <stdlib.h> #include <string.h> #define SIZEOF(a) (sizeof(a) / sizeof(a[0])) -#undef duplocale - locale_t -rpl_duplocale (locale_t locale) +duplocale (locale_t locale) +#undef duplocale { - /* Work around crash in the duplocale function in glibc < 2.12. + /* Implement duplocale(LC_GLOBAL_LOCALE) on platforms without locale_t. + Also, work around crash in the duplocale function in glibc < 2.12. See <https://sourceware.org/bugzilla/show_bug.cgi?id=10969>. Also, on AIX 7.1, duplocale(LC_GLOBAL_LOCALE) returns (locale_t)0 with errno set to EINVAL. @@ -113,5 +114,75 @@ rpl_duplocale (locale_t locale) return base_copy; } +#if GNULIB_defined_locale_t + + locale_t result = (struct gl_locale_t *) malloc (sizeof (struct gl_locale_t)); + if (result == NULL) + { + errno = ENOMEM; + return NULL; + } + + int i; + int err; + for (i = 0; i < 6; i++) + { + int log2_lcmask = gl_index_to_log2_lcmask (i); + + result->category[i].name = strdup (locale->category[i].name); + if (result->category[i].name == NULL) + { + err = ENOMEM; + goto fail_with_err; + } + result->category[i].is_c_locale = locale->category[i].is_c_locale; +# if HAVE_WINDOWS_LOCALE_T + if (log2_lcmask == gl_log2_lc_mask (LC_MESSAGES) + || result->category[i].is_c_locale) + { + /* Just to initialize it. */ + result->category[i].system_locale = NULL; + } + else + { + int cat = log2_lcmask; + (void) cat; + /* Documentation: + <https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/create-locale-wcreate-locale> */ + result->category[i].system_locale = + _create_locale (LC_ALL /* or cat */, result->category[i].name); + if (result->category[i].system_locale == NULL) + { + free (result->category[i].name); + err = ENOENT; + goto fail_with_err; + } + } +# endif + } + + /* Success. */ + return result; + + fail_with_err: + while (--i >= 0) + { +# 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 + free (result->category[i].name); + } + free (result); + errno = err; + return NULL; + +#else + return duplocale (locale); + +#endif } diff --git a/lib/locale.in.h b/lib/locale.in.h index 3d4f34f579..7c35b283de 100644 --- a/lib/locale.in.h +++ b/lib/locale.in.h @@ -332,31 +332,27 @@ _GL_WARN_ON_USE (newlocale, "newlocale is not portable"); #endif #if @GNULIB_DUPLOCALE@ || (@GNULIB_LOCALENAME_UNSAFE@ && @LOCALENAME_ENHANCE_LOCALE_FUNCS@ && @HAVE_DUPLOCALE@) -# if @HAVE_DUPLOCALE@ /* locale_t may be undefined if !@HAVE_DUPLOCALE@. */ -# if @REPLACE_DUPLOCALE@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef duplocale -# define duplocale rpl_duplocale -# define GNULIB_defined_duplocale 1 -# endif +# if @REPLACE_DUPLOCALE@ +# if !(defined __cplusplus && defined GNULIB_NAMESPACE) +# undef duplocale +# define duplocale rpl_duplocale +# define GNULIB_defined_duplocale 1 +# endif _GL_FUNCDECL_RPL (duplocale, locale_t, (locale_t locale), _GL_ARG_NONNULL ((1))); _GL_CXXALIAS_RPL (duplocale, locale_t, (locale_t locale)); -# else -_GL_CXXALIAS_SYS (duplocale, locale_t, (locale_t locale)); +# else +# if !@HAVE_DUPLOCALE@ +_GL_FUNCDECL_SYS (duplocale, locale_t, (locale_t locale), _GL_ARG_NONNULL ((1))); # endif +_GL_CXXALIAS_SYS (duplocale, locale_t, (locale_t locale)); # endif -# if __GLIBC__ >= 2 && @HAVE_DUPLOCALE@ +# if __GLIBC__ >= 2 _GL_CXXALIASWARN (duplocale); # endif -# if @HAVE_DUPLOCALE@ -# ifndef HAVE_WORKING_DUPLOCALE -# define HAVE_WORKING_DUPLOCALE 1 -# endif -# endif #elif defined GNULIB_POSIXCHECK # undef duplocale # if HAVE_RAW_DECL_DUPLOCALE -_GL_WARN_ON_USE (duplocale, "duplocale is buggy on some glibc systems - " +_GL_WARN_ON_USE (duplocale, "duplocale is unportable and buggy on some glibc systems - " "use gnulib module duplocale for portability"); # endif #endif diff --git a/modules/duplocale b/modules/duplocale index 8ccc964a43..8d34e9476a 100644 --- a/modules/duplocale +++ b/modules/duplocale @@ -7,12 +7,14 @@ m4/duplocale.m4 Depends-on: locale-h -setlocale-null [test $HAVE_DUPLOCALE = 1 && test $REPLACE_DUPLOCALE = 1] +freelocale [test $HAVE_LOCALE_T = 0 || { test $HAVE_DUPLOCALE = 1 && test $REPLACE_DUPLOCALE = 1; }] +newlocale [test $HAVE_LOCALE_T = 0 || { test $HAVE_DUPLOCALE = 1 && test $REPLACE_DUPLOCALE = 1; }] +setlocale-null [test $HAVE_LOCALE_T = 0 || { test $HAVE_DUPLOCALE = 1 && test $REPLACE_DUPLOCALE = 1; }] configure.ac: gl_FUNC_DUPLOCALE gl_CONDITIONAL([GL_COND_OBJ_DUPLOCALE], - [test $HAVE_DUPLOCALE = 1 && test $REPLACE_DUPLOCALE = 1]) + [test $HAVE_LOCALE_T = 0 || { test $HAVE_DUPLOCALE = 1 && test $REPLACE_DUPLOCALE = 1; }]) AM_COND_IF([GL_COND_OBJ_DUPLOCALE], [ gl_PREREQ_DUPLOCALE ]) diff --git a/tests/test-duplocale.c b/tests/test-duplocale.c index bce2491c0d..cc348441e4 100644 --- a/tests/test-duplocale.c +++ b/tests/test-duplocale.c @@ -20,8 +20,6 @@ #include <locale.h> -#if HAVE_WORKING_DUPLOCALE - #include "signature.h" SIGNATURE_CHECK (duplocale, locale_t, (locale_t)); @@ -234,16 +232,3 @@ main () return test_exit_status; } - -#else - -#include <stdio.h> - -int -main () -{ - fprintf (stderr, "Skipping test: function duplocale not available\n"); - return 77; -} - -#endif diff --git a/tests/test-locale-h-c++.cc b/tests/test-locale-h-c++.cc index f2bcfaa71d..5a6a2a195b 100644 --- a/tests/test-locale-h-c++.cc +++ b/tests/test-locale-h-c++.cc @@ -36,7 +36,7 @@ SIGNATURE_CHECK (GNULIB_NAMESPACE::setlocale, char *, (int, const char *)); SIGNATURE_CHECK (GNULIB_NAMESPACE::newlocale, locale_t, (int, const char *, locale_t)); #endif -#if GNULIB_TEST_DUPLOCALE && HAVE_WORKING_DUPLOCALE +#if GNULIB_TEST_DUPLOCALE SIGNATURE_CHECK (GNULIB_NAMESPACE::duplocale, locale_t, (locale_t)); #endif -- 2.43.0