These patches introduce a module 'getlocalename_l-simple', that implement the simple case (category != LC_ALL) of the POSIX:2024 getlocalename_l function.
Most of the code was already present in the 'localename-unsafe' module and is merely moved to a different source file. New code is only added for glibc, NetBSD, OpenBSD, Android. 2025-02-21 Bruno Haible <br...@clisp.org> getlocalename_l-simple: Add tests. * tests/test-getlocalename_l.c: New file. * modules/getlocalename_l-simple-tests: New file. getlocalename_l-simple: New module. * lib/locale.in.h (newlocale, duplocale, freelocale): Now enabled by module 'getlocalename_l-simple'. (getlocalename_l): New declaration. * lib/getlocalename_l.c: New file, based on lib/localename-unsafe.c, with modifications for glibc systems, NetBSD, OpenBSD, Android. * lib/localename-table.h (LCMIN): New macro. * lib/struniq.h: Update a comment. * lib/localename-unsafe.c: Don't define the LOCALENAME_ENHANCE_LOCALE_FUNCS overrides here. Moved to lib/getlocalename_l.c. (gl_locale_name_thread_unsafe): Invoke getlocalename_l. Previous code moved to lib/getlocalename_l.c. * m4/intl-thread-locale.m4 (gt_INTL_THREAD_LOCALE_NAME): Improve support for OpenBSD: Set gt_nameless_locales to yes and gt_localename_enhances_locale_funcs to yes also when $gt_fake_locales is yes. * m4/getlocalename_l.m4: New file, based on m4/localename.m4. * m4/localename.m4 (gl_LOCALENAME_UNSAFE, gl_LOCALENAME_UNSAFE_LIMITED): Remove code that was moved to m4/getlocalename_l.m4. * m4/locale_h.m4 (gl_LOCALE_H): Test whether getlocalename_l is declared. (gl_LOCALE_H_REQUIRE_DEFAULTS): Initialize GNULIB_GETLOCALENAME_L. (gl_LOCALE_H_DEFAULTS): Initialize HAVE_GETLOCALENAME_L. * modules/locale-h (Makefile.am): Substitute GNULIB_GETLOCALENAME_L, HAVE_GETLOCALENAME_L. * modules/getlocalename_l-simple: New file. * modules/localename-unsafe (Files): Remove lib/localename-table.h, lib/localename-table.c, lib/struniq.h. (Depends-on): Add getlocalename_l-simple. Remove bool, flexmember, free-posix, langinfo-h, thread-optim. (Makefile.am): Don't compile localename-table.c. * modules/localename-unsafe-limited (Depends-on): Add getlocalename_l-simple. * modules/newlocale (Link): New section. * modules/duplocale (Link): Link with $(GETLOCALENAME_L_LIB). * modules/freelocale (Link): New section. * modules/newlocale-tests (Makefile.am): Link the test program with $(GETLOCALENAME_L_LIB). * modules/duplocale-tests (Makefile.am): Likewise. * modules/freelocale-tests (Makefile.am): Likewise. * modules/is*_l-tests (Makefile.am): Likewise. * modules/tolower_l-tests (Makefile.am): Likewise. * modules/toupper_l-tests (Makefile.am): Likewise. * modules/strcasecmp_l-tests (Makefile.am): Likewise. * modules/strncasecmp_l-tests (Makefile.am): Likewise. * modules/strerror_l-tests (Makefile.am): Likewise. * doc/posix-functions/getlocalename_l.texi: Mention the new module.
From 706178a93b611cc47d305c92c9844e56503e6d5a Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Fri, 21 Feb 2025 11:25:51 +0100 Subject: [PATCH 1/2] getlocalename_l-simple: New module. * lib/locale.in.h (newlocale, duplocale, freelocale): Now enabled by module 'getlocalename_l-simple'. (getlocalename_l): New declaration. * lib/getlocalename_l.c: New file, based on lib/localename-unsafe.c, with modifications for glibc systems, NetBSD, OpenBSD, Android. * lib/localename-table.h (LCMIN): New macro. * lib/struniq.h: Update a comment. * lib/localename-unsafe.c: Don't define the LOCALENAME_ENHANCE_LOCALE_FUNCS overrides here. Moved to lib/getlocalename_l.c. (gl_locale_name_thread_unsafe): Invoke getlocalename_l. Previous code moved to lib/getlocalename_l.c. * m4/intl-thread-locale.m4 (gt_INTL_THREAD_LOCALE_NAME): Improve support for OpenBSD: Set gt_nameless_locales to yes and gt_localename_enhances_locale_funcs to yes also when $gt_fake_locales is yes. * m4/getlocalename_l.m4: New file, based on m4/localename.m4. * m4/localename.m4 (gl_LOCALENAME_UNSAFE, gl_LOCALENAME_UNSAFE_LIMITED): Remove code that was moved to m4/getlocalename_l.m4. * m4/locale_h.m4 (gl_LOCALE_H): Test whether getlocalename_l is declared. (gl_LOCALE_H_REQUIRE_DEFAULTS): Initialize GNULIB_GETLOCALENAME_L. (gl_LOCALE_H_DEFAULTS): Initialize HAVE_GETLOCALENAME_L. * modules/locale-h (Makefile.am): Substitute GNULIB_GETLOCALENAME_L, HAVE_GETLOCALENAME_L. * modules/getlocalename_l-simple: New file. * modules/localename-unsafe (Files): Remove lib/localename-table.h, lib/localename-table.c, lib/struniq.h. (Depends-on): Add getlocalename_l-simple. Remove bool, flexmember, free-posix, langinfo-h, thread-optim. (Makefile.am): Don't compile localename-table.c. * modules/localename-unsafe-limited (Depends-on): Add getlocalename_l-simple. * modules/newlocale (Link): New section. * modules/duplocale (Link): Link with $(GETLOCALENAME_L_LIB). * modules/freelocale (Link): New section. * modules/newlocale-tests (Makefile.am): Link the test program with $(GETLOCALENAME_L_LIB). * modules/duplocale-tests (Makefile.am): Likewise. * modules/freelocale-tests (Makefile.am): Likewise. * modules/is*_l-tests (Makefile.am): Likewise. * modules/tolower_l-tests (Makefile.am): Likewise. * modules/toupper_l-tests (Makefile.am): Likewise. * modules/strcasecmp_l-tests (Makefile.am): Likewise. * modules/strncasecmp_l-tests (Makefile.am): Likewise. * modules/strerror_l-tests (Makefile.am): Likewise. * doc/posix-functions/getlocalename_l.texi: Mention the new module. --- ChangeLog | 51 ++ doc/posix-functions/getlocalename_l.texi | 11 +- lib/getlocalename_l.c | 660 +++++++++++++++++++++++ lib/locale.in.h | 24 +- lib/localename-table.h | 5 +- lib/localename-unsafe.c | 570 +------------------- lib/struniq.h | 2 +- m4/getlocalename_l.m4 | 76 +++ m4/intl-thread-locale.m4 | 23 +- m4/locale_h.m4 | 6 +- m4/localename.m4 | 46 +- modules/duplocale | 2 +- modules/duplocale-tests | 2 +- modules/freelocale | 3 + modules/freelocale-tests | 1 + modules/getlocalename_l-simple | 47 ++ modules/isalnum_l-tests | 1 + modules/isalpha_l-tests | 1 + modules/isblank_l-tests | 1 + modules/iscntrl_l-tests | 1 + modules/isdigit_l-tests | 1 + modules/isgraph_l-tests | 1 + modules/islower_l-tests | 1 + modules/isprint_l-tests | 1 + modules/ispunct_l-tests | 1 + modules/isspace_l-tests | 1 + modules/isupper_l-tests | 1 + modules/isxdigit_l-tests | 1 + modules/locale-h | 2 + modules/localename-unsafe | 11 +- modules/localename-unsafe-limited | 1 + modules/newlocale | 3 + modules/newlocale-tests | 1 + modules/strcasecmp_l-tests | 1 + modules/strerror_l-tests | 1 + modules/strncasecmp_l-tests | 1 + modules/tolower_l-tests | 1 + modules/toupper_l-tests | 1 + 38 files changed, 917 insertions(+), 647 deletions(-) create mode 100644 lib/getlocalename_l.c create mode 100644 m4/getlocalename_l.m4 create mode 100644 modules/getlocalename_l-simple diff --git a/ChangeLog b/ChangeLog index 14546b8227..cc692df338 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,54 @@ +2025-02-21 Bruno Haible <br...@clisp.org> + + getlocalename_l-simple: New module. + * lib/locale.in.h (newlocale, duplocale, freelocale): Now enabled by + module 'getlocalename_l-simple'. + (getlocalename_l): New declaration. + * lib/getlocalename_l.c: New file, based on lib/localename-unsafe.c, + with modifications for glibc systems, NetBSD, OpenBSD, Android. + * lib/localename-table.h (LCMIN): New macro. + * lib/struniq.h: Update a comment. + * lib/localename-unsafe.c: Don't define the + LOCALENAME_ENHANCE_LOCALE_FUNCS overrides here. Moved to + lib/getlocalename_l.c. + (gl_locale_name_thread_unsafe): Invoke getlocalename_l. Previous code + moved to lib/getlocalename_l.c. + * m4/intl-thread-locale.m4 (gt_INTL_THREAD_LOCALE_NAME): Improve support + for OpenBSD: Set gt_nameless_locales to yes and + gt_localename_enhances_locale_funcs to yes also when $gt_fake_locales is + yes. + * m4/getlocalename_l.m4: New file, based on m4/localename.m4. + * m4/localename.m4 (gl_LOCALENAME_UNSAFE, gl_LOCALENAME_UNSAFE_LIMITED): + Remove code that was moved to m4/getlocalename_l.m4. + * m4/locale_h.m4 (gl_LOCALE_H): Test whether getlocalename_l is + declared. + (gl_LOCALE_H_REQUIRE_DEFAULTS): Initialize GNULIB_GETLOCALENAME_L. + (gl_LOCALE_H_DEFAULTS): Initialize HAVE_GETLOCALENAME_L. + * modules/locale-h (Makefile.am): Substitute GNULIB_GETLOCALENAME_L, + HAVE_GETLOCALENAME_L. + * modules/getlocalename_l-simple: New file. + * modules/localename-unsafe (Files): Remove lib/localename-table.h, + lib/localename-table.c, lib/struniq.h. + (Depends-on): Add getlocalename_l-simple. Remove bool, flexmember, + free-posix, langinfo-h, thread-optim. + (Makefile.am): Don't compile localename-table.c. + * modules/localename-unsafe-limited (Depends-on): Add + getlocalename_l-simple. + * modules/newlocale (Link): New section. + * modules/duplocale (Link): Link with $(GETLOCALENAME_L_LIB). + * modules/freelocale (Link): New section. + * modules/newlocale-tests (Makefile.am): Link the test program with + $(GETLOCALENAME_L_LIB). + * modules/duplocale-tests (Makefile.am): Likewise. + * modules/freelocale-tests (Makefile.am): Likewise. + * modules/is*_l-tests (Makefile.am): Likewise. + * modules/tolower_l-tests (Makefile.am): Likewise. + * modules/toupper_l-tests (Makefile.am): Likewise. + * modules/strcasecmp_l-tests (Makefile.am): Likewise. + * modules/strncasecmp_l-tests (Makefile.am): Likewise. + * modules/strerror_l-tests (Makefile.am): Likewise. + * doc/posix-functions/getlocalename_l.texi: Mention the new module. + 2025-02-21 Bruno Haible <br...@clisp.org> setlocale-messages: New module. diff --git a/doc/posix-functions/getlocalename_l.texi b/doc/posix-functions/getlocalename_l.texi index 26f8f2ab42..dc7b3ed94c 100644 --- a/doc/posix-functions/getlocalename_l.texi +++ b/doc/posix-functions/getlocalename_l.texi @@ -4,15 +4,18 @@ POSIX specification:@* @url{https://pubs.opengroup.org/onlinepubs/9799919799/functions/getlocalename_l.html} -Gnulib module: --- +Gnulib module: getlocalename_l-simple +@mindex getlocalename_l-simple Portability problems fixed by Gnulib: @itemize +@item +This function is missing on many platforms: +glibc 2.41, macOS 14, FreeBSD 14.0, NetBSD 10.0, OpenBSD 7.5, Minix 3.3.0, AIX 7.3.1, HP-UX 11.31, Solaris 11.4, Cygwin 3.5.x, mingw, MSVC 14, Android 9.0. +But the gnulib replacement supports only a single locale category: +the argument @code{LC_ALL} is unsupported. @end itemize Portability problems not fixed by Gnulib: @itemize -@item -This function is missing on many platforms: -glibc 2.41, macOS 14, FreeBSD 14.0, NetBSD 10.0, OpenBSD 7.5, Minix 3.3.0, AIX 7.3.1, HP-UX 11.31, Solaris 11.4, Cygwin 3.5.x, mingw, MSVC 14, Android 9.0. @end itemize diff --git a/lib/getlocalename_l.c b/lib/getlocalename_l.c new file mode 100644 index 0000000000..4cc63a5a94 --- /dev/null +++ b/lib/getlocalename_l.c @@ -0,0 +1,660 @@ +/* Return name of a single locale category. + Copyright (C) 1995-2025 Free Software Foundation, Inc. + + This file is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + This file 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. */ + +#include <config.h> + +/* Specification. */ +#include <locale.h> + +#include <limits.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + +#include "setlocale-messages.h" +#include "setlocale_null.h" + +#if (__GLIBC__ >= 2 && !defined __UCLIBC__) || (defined __linux__ && HAVE_LANGINFO_H) || defined __CYGWIN__ +# include <langinfo.h> +#endif +#if defined __sun +# if HAVE_SOLARIS114_LOCALES +# include <sys/localedef.h> +# endif +#endif +#if HAVE_NAMELESS_LOCALES +# include "localename-table.h" +#endif +#if defined __HAIKU__ +# include <dlfcn.h> +#endif + +#include "flexmember.h" +#include "glthread/lock.h" +#include "thread-optim.h" + + +/* Define a local struniq() function. */ +#include "struniq.h" + +#if LOCALENAME_ENHANCE_LOCALE_FUNCS + +/* The 'locale_t' object does not contain the names of the locale categories. + We have to associate them with the object through a hash table. + The hash table is defined in localename-table.[hc]. */ + +/* Returns the name of a given locale category in a given locale_t object, + allocated as a string with indefinite extent. */ +static const char * +get_locale_t_name (int category, locale_t locale) +{ + if (category == LC_ALL) + /* Invalid argument. */ + abort (); + if (locale == LC_GLOBAL_LOCALE) + { + /* Query the global locale. */ + const char *name = setlocale_null (category); + if (name != NULL) + return struniq (name); + else + /* Should normally not happen. */ + return ""; + } + else + { +# if HAVE_AIX72_LOCALES + if (category == LC_MESSAGES) + { + const char *name = ((__locale_t) locale)->locale_name; + if (name != NULL) + return struniq (name); + } +# endif + /* Look up the names in the hash table. */ + size_t hashcode = locale_hash_function (locale); + size_t slot = hashcode % LOCALE_HASH_TABLE_SIZE; + /* If the locale was not found in the table, return "". This can + happen if the application uses the original newlocale()/duplocale() + functions instead of the overridden ones. */ + const char *name = ""; + struct locale_hash_node *p; + /* Lock while looking up the hash node. */ + gl_rwlock_rdlock (locale_lock); + for (p = locale_hash_table[slot]; p != NULL; p = p->next) + if (p->locale == locale) + { + name = p->names.category_name[category - LCMIN]; + break; + } + gl_rwlock_unlock (locale_lock); + return name; + } +} + +# if !(defined newlocale && defined duplocale && defined freelocale) +# error "newlocale, duplocale, freelocale not being replaced as expected!" +# endif + +/* newlocale() override. */ +locale_t +newlocale (int category_mask, const char *name, locale_t base) +#undef newlocale +{ + struct locale_categories_names names; + struct locale_hash_node *node; + locale_t result; + + /* Make sure name has indefinite extent. */ + if (((LC_CTYPE_MASK | LC_NUMERIC_MASK | LC_TIME_MASK | LC_COLLATE_MASK + | LC_MONETARY_MASK | LC_MESSAGES_MASK) + & category_mask) != 0) + name = struniq (name); + + /* Determine the category names of the result. */ + if (((LC_CTYPE_MASK | LC_NUMERIC_MASK | LC_TIME_MASK | LC_COLLATE_MASK + | LC_MONETARY_MASK | LC_MESSAGES_MASK) + & ~category_mask) == 0) + { + /* Use name, ignore base. */ + int i; + + name = struniq (name); + for (i = 0; i < 6; i++) + names.category_name[i] = name; + } + else + { + /* Use base, possibly also name. */ + if (base == NULL) + { + int i; + + for (i = 0; i < 6; i++) + { + int category = i + LCMIN; + int mask; + + switch (category) + { + case LC_CTYPE: + mask = LC_CTYPE_MASK; + break; + case LC_NUMERIC: + mask = LC_NUMERIC_MASK; + break; + case LC_TIME: + mask = LC_TIME_MASK; + break; + case LC_COLLATE: + mask = LC_COLLATE_MASK; + break; + case LC_MONETARY: + mask = LC_MONETARY_MASK; + break; + case LC_MESSAGES: + mask = LC_MESSAGES_MASK; + break; + default: + abort (); + } + names.category_name[i] = + ((mask & category_mask) != 0 ? name : "C"); + } + } + else if (base == LC_GLOBAL_LOCALE) + { + int i; + + for (i = 0; i < 6; i++) + { + int category = i + LCMIN; + int mask; + + switch (category) + { + case LC_CTYPE: + mask = LC_CTYPE_MASK; + break; + case LC_NUMERIC: + mask = LC_NUMERIC_MASK; + break; + case LC_TIME: + mask = LC_TIME_MASK; + break; + case LC_COLLATE: + mask = LC_COLLATE_MASK; + break; + case LC_MONETARY: + mask = LC_MONETARY_MASK; + break; + case LC_MESSAGES: + mask = LC_MESSAGES_MASK; + break; + default: + abort (); + } + names.category_name[i] = + ((mask & category_mask) != 0 + ? name + : get_locale_t_name (category, LC_GLOBAL_LOCALE)); + } + } + else + { + /* Look up the names of base in the hash table. Like multiple calls + of get_locale_t_name, but locking only once. */ + struct locale_hash_node *p; + + /* Lock while looking up the hash node. */ + gl_rwlock_rdlock (locale_lock); + for (p = locale_hash_table[locale_hash_function (base) % LOCALE_HASH_TABLE_SIZE]; + p != NULL; + p = p->next) + if (p->locale == base) + break; + + int i; + for (i = 0; i < 6; i++) + { + int category = i + LCMIN; + int mask; + + switch (category) + { + case LC_CTYPE: + mask = LC_CTYPE_MASK; + break; + case LC_NUMERIC: + mask = LC_NUMERIC_MASK; + break; + case LC_TIME: + mask = LC_TIME_MASK; + break; + case LC_COLLATE: + mask = LC_COLLATE_MASK; + break; + case LC_MONETARY: + mask = LC_MONETARY_MASK; + break; + case LC_MESSAGES: + mask = LC_MESSAGES_MASK; + break; + default: + abort (); + } + names.category_name[i] = + ((mask & category_mask) != 0 + ? name + : (p != NULL ? p->names.category_name[i] : "")); + } + + gl_rwlock_unlock (locale_lock); + } + } + + node = (struct locale_hash_node *) malloc (sizeof (struct locale_hash_node)); + if (node == NULL) + /* errno is set to ENOMEM. */ + return NULL; + + result = newlocale (category_mask, name, base); + if (result == NULL) + { + free (node); + return NULL; + } + + /* Fill the hash node. */ + node->locale = result; + node->names = names; + + /* Insert it in the hash table. */ + { + size_t hashcode = locale_hash_function (result); + size_t slot = hashcode % LOCALE_HASH_TABLE_SIZE; + struct locale_hash_node *p; + + /* Lock while inserting the new node. */ + gl_rwlock_wrlock (locale_lock); + for (p = locale_hash_table[slot]; p != NULL; p = p->next) + if (p->locale == result) + { + /* This can happen if the application uses the original freelocale() + function instead of the overridden one. */ + p->names = node->names; + break; + } + if (p == NULL) + { + node->next = locale_hash_table[slot]; + locale_hash_table[slot] = node; + } + + gl_rwlock_unlock (locale_lock); + + if (p != NULL) + free (node); + } + + return result; +} + +/* duplocale() override. */ +locale_t +duplocale (locale_t locale) +#undef duplocale +{ + struct locale_hash_node *node; + locale_t result; + + if (locale == NULL) + /* Invalid argument. */ + abort (); + + node = (struct locale_hash_node *) malloc (sizeof (struct locale_hash_node)); + if (node == NULL) + /* errno is set to ENOMEM. */ + return NULL; + + result = duplocale (locale); + if (result == NULL) + { + free (node); + return NULL; + } + + /* Fill the hash node. */ + node->locale = result; + if (locale == LC_GLOBAL_LOCALE) + { + int i; + + for (i = 0; i < 6; i++) + { + int category = i + LCMIN; + node->names.category_name[i] = + get_locale_t_name (category, LC_GLOBAL_LOCALE); + } + + /* Lock before inserting the new node. */ + gl_rwlock_wrlock (locale_lock); + } + else + { + struct locale_hash_node *p; + + /* Lock once, for the lookup and the insertion. */ + gl_rwlock_wrlock (locale_lock); + + for (p = locale_hash_table[locale_hash_function (locale) % LOCALE_HASH_TABLE_SIZE]; + p != NULL; + p = p->next) + if (p->locale == locale) + break; + if (p != NULL) + node->names = p->names; + else + { + /* This can happen if the application uses the original + newlocale()/duplocale() functions instead of the overridden + ones. */ + int i; + + for (i = 0; i < 6; i++) + node->names.category_name[i] = ""; + } + } + + /* Insert it in the hash table. */ + { + size_t hashcode = locale_hash_function (result); + size_t slot = hashcode % LOCALE_HASH_TABLE_SIZE; + struct locale_hash_node *p; + + for (p = locale_hash_table[slot]; p != NULL; p = p->next) + if (p->locale == result) + { + /* This can happen if the application uses the original freelocale() + function instead of the overridden one. */ + p->names = node->names; + break; + } + if (p == NULL) + { + node->next = locale_hash_table[slot]; + locale_hash_table[slot] = node; + } + + gl_rwlock_unlock (locale_lock); + + if (p != NULL) + free (node); + } + + return result; +} + +/* freelocale() override. */ +void +freelocale (locale_t locale) +#undef freelocale +{ + if (locale == NULL || locale == LC_GLOBAL_LOCALE) + /* Invalid argument. */ + abort (); + + { + size_t hashcode = locale_hash_function (locale); + size_t slot = hashcode % LOCALE_HASH_TABLE_SIZE; + struct locale_hash_node *found; + struct locale_hash_node **p; + + found = NULL; + /* Lock while removing the hash node. */ + gl_rwlock_wrlock (locale_lock); + for (p = &locale_hash_table[slot]; *p != NULL; p = &(*p)->next) + if ((*p)->locale == locale) + { + found = *p; + *p = (*p)->next; + break; + } + gl_rwlock_unlock (locale_lock); + free (found); + } + + freelocale (locale); +} + +#endif + + +const char * +getlocalename_l (int category, locale_t locale) +{ + if (category == LC_ALL) + /* Unsupported in this simple implementation. */ + abort (); + + if (locale != LC_GLOBAL_LOCALE) + { +#if GNULIB_defined_locale_t + struct gl_locale_category_t *plc = + &locale->category[gl_log2_lcmask_to_index (gl_log2_lc_mask (category))]; + return plc->name; +#elif __GLIBC__ >= 2 && !defined __UCLIBC__ + /* Work around an incorrect definition of the _NL_LOCALE_NAME macro in + glibc < 2.12. + See <https://sourceware.org/bugzilla/show_bug.cgi?id=10968>. */ + const char *name = + nl_langinfo_l (_NL_ITEM ((category), _NL_ITEM_INDEX (-1)), locale); + if (name[0] == '\0') + /* Fallback code for glibc < 2.4, which did not implement + nl_langinfo_l (_NL_LOCALE_NAME (category), locale). */ + name = locale->__names[category]; + return name; +#elif defined __linux__ && HAVE_LANGINFO_H && defined NL_LOCALE_NAME + /* musl libc */ + return nl_langinfo_l (NL_LOCALE_NAME (category), locale); +#elif (defined __FreeBSD__ || defined __DragonFly__) || (defined __APPLE__ && defined __MACH__) + /* FreeBSD >= 9.1, Mac OS X */ + int mask; + + switch (category) + { + case LC_CTYPE: + mask = LC_CTYPE_MASK; + break; + case LC_NUMERIC: + mask = LC_NUMERIC_MASK; + break; + case LC_TIME: + mask = LC_TIME_MASK; + break; + case LC_COLLATE: + mask = LC_COLLATE_MASK; + break; + case LC_MONETARY: + mask = LC_MONETARY_MASK; + break; + case LC_MESSAGES: + mask = LC_MESSAGES_MASK; + break; + default: /* We shouldn't get here. */ + return ""; + } + return querylocale (mask, locale); +#elif defined __NetBSD__ + /* NetBSD >= 7.0 */ + #define _LOCALENAME_LEN_MAX 33 + struct _locale { + void *cache; + char query[_LOCALENAME_LEN_MAX * 6]; + const char *part_name[7]; + }; + return ((struct _locale *) locale)->part_name[category]; +#elif defined __sun +# if HAVE_SOLARIS114_LOCALES + /* Solaris >= 11.4. */ + void *lcp = (*locale)->core.data->lcp; + if (lcp != NULL) + switch (category) + { + case LC_CTYPE: + case LC_NUMERIC: + case LC_TIME: + case LC_COLLATE: + case LC_MONETARY: + case LC_MESSAGES: + return ((const char * const *) lcp)[category]; + default: /* We shouldn't get here. */ + return ""; + } +# else + /* Solaris 11 OpenIndiana. + For the internal structure of locale objects, see + https://github.com/OpenIndiana/illumos-gate/blob/master/usr/src/lib/libc/port/locale/localeimpl.h */ + switch (category) + { + case LC_CTYPE: + case LC_NUMERIC: + case LC_TIME: + case LC_COLLATE: + case LC_MONETARY: + case LC_MESSAGES: + return ((const char * const *) locale)[category]; + default: /* We shouldn't get here. */ + return ""; + } +# endif +#elif HAVE_NAMELESS_LOCALES + /* OpenBSD >= 6.2, AIX >= 7.1 */ + return get_locale_t_name (category, locale); +#elif defined __OpenBSD__ && HAVE_FAKE_LOCALES + /* OpenBSD >= 6.2 has only fake locales. */ + if (locale == (locale_t) 2) + return "C.UTF-8"; + return "C"; +#elif defined __CYGWIN__ + /* Cygwin >= 2.6. + Cygwin <= 2.6.1 lacks NL_LOCALE_NAME, requiring peeking inside + an opaque struct. */ +# ifdef NL_LOCALE_NAME + return nl_langinfo_l (NL_LOCALE_NAME (category), locale); +# else + /* FIXME: Remove when we can assume new-enough Cygwin. */ + struct __locale_t { + char categories[7][32]; + }; + return ((struct __locale_t *) locale)->categories[category]; +# endif +#elif defined __HAIKU__ + /* Since 2022, Haiku has per-thread locales. locale_t is 'void *', + but in fact a 'LocaleBackendData *'. */ + struct LocaleBackendData { + int magic; + void /*BPrivate::Libroot::LocaleBackend*/ *backend; + void /*BPrivate::Libroot::LocaleDataBridge*/ *databridge; + }; + void *locale_backend = + ((struct LocaleBackendData *) locale)->backend; + if (locale_backend != NULL) + { + /* The only existing concrete subclass of + BPrivate::Libroot::LocaleBackend is + BPrivate::Libroot::ICULocaleBackend. + Invoke the (non-virtual) method + BPrivate::Libroot::ICULocaleBackend::_QueryLocale on it. + This method is located in a separate shared library, + libroot-addon-icu.so. */ + static void * volatile querylocale_method /* = NULL */; + static int volatile querylocale_found /* = 0 */; + /* Attempt to open this shared library, the first time we get + here. */ + if (querylocale_found == 0) + { + void *handle = + dlopen ("/boot/system/lib/libroot-addon-icu.so", 0); + if (handle != NULL) + { + void *sym = + dlsym (handle, "_ZN8BPrivate7Libroot16ICULocaleBackend12_QueryLocaleEi"); + if (sym != NULL) + { + querylocale_method = sym; + querylocale_found = 1; + } + else + /* Could not find the symbol. */ + querylocale_found = -1; + } + else + /* Could not open the separate shared library. */ + querylocale_found = -1; + } + if (querylocale_found > 0) + { + /* The _QueryLocale method is a non-static C++ method with + parameters (int category) and return type 'const char *'. + See + haiku/headers/private/libroot/locale/ICULocaleBackend.h + haiku/src/system/libroot/add-ons/icu/ICULocaleBackend.cpp + This is the same as a C function with parameters + (BPrivate::Libroot::LocaleBackend* this, int category) + and return type 'const char *'. Invoke it. */ + const char * (*querylocale_func) (void *, int) = + (const char * (*) (void *, int)) querylocale_method; + return querylocale_func (locale_backend, category); + } + } + else + /* It's the "C" or "POSIX" locale. */ + return "C"; +#elif defined __ANDROID__ + /* Android API level >= 21 */ + struct __locale_t { + size_t mb_cur_max; + }; + return ((struct __locale_t *) locale)->mb_cur_max == 4 ? "C.UTF-8" : "C"; +#else + #error "Please port gnulib getlocalename_l.c to your platform! Report this to bug-gnulib." +#endif + } + else + { + /* Query the global locale. */ + const char *name; +#if LC_MESSAGES == 1729 + if (category == LC_MESSAGES) + name = setlocale_messages_null (); + else +#endif + name = setlocale_null (category); + if (name != NULL) + /* Return the result as a string of indefinite extent. + This is required because POSIX says that + "the returned string pointer might be invalidated or the string + content might be overwritten by a subsequent call in the same + thread to getlocalename_l() with LC_GLOBAL_LOCALE" + and a setlocale_null call in a different thread would also invalidate + the result. */ + return struniq (name); + else + /* Should normally not happen. */ + return ""; + } +} diff --git a/lib/locale.in.h b/lib/locale.in.h index 7c35b283de..2b10a6b95d 100644 --- a/lib/locale.in.h +++ b/lib/locale.in.h @@ -300,7 +300,7 @@ _GL_WARN_ON_USE (setlocale, "setlocale works differently on native Windows - " # include "setlocale_null.h" #endif -#if @GNULIB_NEWLOCALE@ || (@GNULIB_LOCALENAME_UNSAFE@ && @LOCALENAME_ENHANCE_LOCALE_FUNCS@ && @HAVE_NEWLOCALE@) +#if @GNULIB_NEWLOCALE@ || (@GNULIB_GETLOCALENAME_L@ && @LOCALENAME_ENHANCE_LOCALE_FUNCS@ && @HAVE_NEWLOCALE@) # if @REPLACE_NEWLOCALE@ # if !(defined __cplusplus && defined GNULIB_NAMESPACE) # undef newlocale @@ -331,7 +331,7 @@ _GL_WARN_ON_USE (newlocale, "newlocale is not portable"); # endif #endif -#if @GNULIB_DUPLOCALE@ || (@GNULIB_LOCALENAME_UNSAFE@ && @LOCALENAME_ENHANCE_LOCALE_FUNCS@ && @HAVE_DUPLOCALE@) +#if @GNULIB_DUPLOCALE@ || (@GNULIB_GETLOCALENAME_L@ && @LOCALENAME_ENHANCE_LOCALE_FUNCS@ && @HAVE_DUPLOCALE@) # if @REPLACE_DUPLOCALE@ # if !(defined __cplusplus && defined GNULIB_NAMESPACE) # undef duplocale @@ -357,7 +357,7 @@ _GL_WARN_ON_USE (duplocale, "duplocale is unportable and buggy on some glibc sys # endif #endif -#if @GNULIB_FREELOCALE@ || (@GNULIB_LOCALENAME_UNSAFE@ && @LOCALENAME_ENHANCE_LOCALE_FUNCS@ && @HAVE_FREELOCALE@) +#if @GNULIB_FREELOCALE@ || (@GNULIB_GETLOCALENAME_L@ && @LOCALENAME_ENHANCE_LOCALE_FUNCS@ && @HAVE_FREELOCALE@) # if @REPLACE_FREELOCALE@ # if !(defined __cplusplus && defined GNULIB_NAMESPACE) # undef freelocale @@ -384,6 +384,24 @@ _GL_WARN_ON_USE (freelocale, "freelocale is not portable"); # endif #endif +#if @GNULIB_GETLOCALENAME_L@ +# if !@HAVE_GETLOCALENAME_L@ +_GL_FUNCDECL_SYS (getlocalename_l, const char *, + (int category, locale_t locale), + _GL_ARG_NONNULL ((2))); +# endif +_GL_CXXALIAS_SYS (getlocalename_l, const char *, + (int category, locale_t locale)); +# if __GLIBC__ >= 2 +_GL_CXXALIASWARN (getlocalename_l); +# endif +#elif defined GNULIB_POSIXCHECK +# undef getlocalename_l +# if HAVE_RAW_DECL_GETLOCALENAME_L +_GL_WARN_ON_USE (getlocalename_l, "getlocalename_l is not portable"); +# endif +#endif + #endif /* _@GUARD_PREFIX@_LOCALE_H */ #endif /* _@GUARD_PREFIX@_LOCALE_H */ #endif /* !(__need_locale_t || _@GUARD_PREFIX@_ALREADY_INCLUDING_LOCALE_H) */ diff --git a/lib/localename-table.h b/lib/localename-table.h index 3fd11fadbd..c9982be128 100644 --- a/lib/localename-table.h +++ b/lib/localename-table.h @@ -23,9 +23,12 @@ # include "glthread/lock.h" +/* Smallest among the six LC_* category values. */ +# define LCMIN (LC_ALL == 0 ? 1 : 0) + struct locale_categories_names { - /* Locale category -> name (allocated with indefinite extent). */ + /* (Locale category - LCMIN) -> name (allocated with indefinite extent). */ const char *category_name[6]; }; diff --git a/lib/localename-unsafe.c b/lib/localename-unsafe.c index 5b34aaf906..569bf9ba99 100644 --- a/lib/localename-unsafe.c +++ b/lib/localename-unsafe.c @@ -29,7 +29,6 @@ /* Specification. */ #include "localename.h" -#include <limits.h> #include <stddef.h> #include <stdlib.h> #include <locale.h> @@ -42,20 +41,6 @@ # if defined __APPLE__ && defined __MACH__ # include <xlocale.h> # endif -# if (__GLIBC__ >= 2 && !defined __UCLIBC__) || (defined __linux__ && HAVE_LANGINFO_H) || defined __CYGWIN__ -# include <langinfo.h> -# endif -# if defined __sun -# if HAVE_SOLARIS114_LOCALES -# include <sys/localedef.h> -# endif -# endif -# if HAVE_NAMELESS_LOCALES -# include "localename-table.h" -# endif -# if defined __HAIKU__ -# include <dlfcn.h> -# endif #endif #if HAVE_CFPREFERENCESCOPYAPPVALUE @@ -68,12 +53,6 @@ # include "glthread/lock.h" #endif -#if LOCALENAME_ENHANCE_LOCALE_FUNCS -# include "flexmember.h" -# include "glthread/lock.h" -# include "thread-optim.h" -#endif - #if defined WINDOWS_NATIVE || defined __CYGWIN__ /* Native Windows or Cygwin */ # define WIN32_LEAN_AND_MEAN # include <windows.h> @@ -2633,396 +2612,6 @@ get_lcid (const char *locale_name) #endif -#if LOCALENAME_ENHANCE_LOCALE_FUNCS - -/* Define a local struniq() function. */ -# include "struniq.h" - -/* The 'locale_t' object does not contain the names of the locale categories. - We have to associate them with the object through a hash table. - The hash table is defined in localename-table.[hc]. */ - -/* Returns the name of a given locale category in a given locale_t object, - allocated as a string with indefinite extent. */ -static const char * -get_locale_t_name (int category, locale_t locale) -{ - if (category == LC_ALL) - /* Invalid argument. */ - abort (); - if (locale == LC_GLOBAL_LOCALE) - { - /* Query the global locale. */ - const char *name = setlocale_null (category); - if (name != NULL) - return struniq (name); - else - /* Should normally not happen. */ - return ""; - } - else - { -# if HAVE_AIX72_LOCALES - if (category == LC_MESSAGES) - { - const char *name = ((__locale_t) locale)->locale_name; - if (name != NULL) - return struniq (name); - } -# endif - /* Look up the names in the hash table. */ - size_t hashcode = locale_hash_function (locale); - size_t slot = hashcode % LOCALE_HASH_TABLE_SIZE; - /* If the locale was not found in the table, return "". This can - happen if the application uses the original newlocale()/duplocale() - functions instead of the overridden ones. */ - const char *name = ""; - struct locale_hash_node *p; - /* Lock while looking up the hash node. */ - gl_rwlock_rdlock (locale_lock); - for (p = locale_hash_table[slot]; p != NULL; p = p->next) - if (p->locale == locale) - { - name = p->names.category_name[category]; - break; - } - gl_rwlock_unlock (locale_lock); - return name; - } -} - -# if !(defined newlocale && defined duplocale && defined freelocale) -# error "newlocale, duplocale, freelocale not being replaced as expected!" -# endif - -/* newlocale() override. */ -locale_t -newlocale (int category_mask, const char *name, locale_t base) -#undef newlocale -{ - struct locale_categories_names names; - struct locale_hash_node *node; - locale_t result; - - /* Make sure name has indefinite extent. */ - if (((LC_CTYPE_MASK | LC_NUMERIC_MASK | LC_TIME_MASK | LC_COLLATE_MASK - | LC_MONETARY_MASK | LC_MESSAGES_MASK) - & category_mask) != 0) - name = struniq (name); - - /* Determine the category names of the result. */ - if (((LC_CTYPE_MASK | LC_NUMERIC_MASK | LC_TIME_MASK | LC_COLLATE_MASK - | LC_MONETARY_MASK | LC_MESSAGES_MASK) - & ~category_mask) == 0) - { - /* Use name, ignore base. */ - int category; - - name = struniq (name); - for (category = 0; category < 6; category++) - names.category_name[category] = name; - } - else - { - /* Use base, possibly also name. */ - if (base == NULL) - { - int category; - - for (category = 0; category < 6; category++) - { - int mask; - - switch (category) - { - case LC_CTYPE: - mask = LC_CTYPE_MASK; - break; - case LC_NUMERIC: - mask = LC_NUMERIC_MASK; - break; - case LC_TIME: - mask = LC_TIME_MASK; - break; - case LC_COLLATE: - mask = LC_COLLATE_MASK; - break; - case LC_MONETARY: - mask = LC_MONETARY_MASK; - break; - case LC_MESSAGES: - mask = LC_MESSAGES_MASK; - break; - default: - abort (); - } - names.category_name[category] = - ((mask & category_mask) != 0 ? name : "C"); - } - } - else if (base == LC_GLOBAL_LOCALE) - { - int category; - - for (category = 0; category < 6; category++) - { - int mask; - - switch (category) - { - case LC_CTYPE: - mask = LC_CTYPE_MASK; - break; - case LC_NUMERIC: - mask = LC_NUMERIC_MASK; - break; - case LC_TIME: - mask = LC_TIME_MASK; - break; - case LC_COLLATE: - mask = LC_COLLATE_MASK; - break; - case LC_MONETARY: - mask = LC_MONETARY_MASK; - break; - case LC_MESSAGES: - mask = LC_MESSAGES_MASK; - break; - default: - abort (); - } - names.category_name[category] = - ((mask & category_mask) != 0 - ? name - : get_locale_t_name (category, LC_GLOBAL_LOCALE)); - } - } - else - { - /* Look up the names of base in the hash table. Like multiple calls - of get_locale_t_name, but locking only once. */ - struct locale_hash_node *p; - int category; - - /* Lock while looking up the hash node. */ - gl_rwlock_rdlock (locale_lock); - for (p = locale_hash_table[locale_hash_function (base) % LOCALE_HASH_TABLE_SIZE]; - p != NULL; - p = p->next) - if (p->locale == base) - break; - - for (category = 0; category < 6; category++) - { - int mask; - - switch (category) - { - case LC_CTYPE: - mask = LC_CTYPE_MASK; - break; - case LC_NUMERIC: - mask = LC_NUMERIC_MASK; - break; - case LC_TIME: - mask = LC_TIME_MASK; - break; - case LC_COLLATE: - mask = LC_COLLATE_MASK; - break; - case LC_MONETARY: - mask = LC_MONETARY_MASK; - break; - case LC_MESSAGES: - mask = LC_MESSAGES_MASK; - break; - default: - abort (); - } - names.category_name[category] = - ((mask & category_mask) != 0 - ? name - : (p != NULL ? p->names.category_name[category] : "")); - } - - gl_rwlock_unlock (locale_lock); - } - } - - node = (struct locale_hash_node *) malloc (sizeof (struct locale_hash_node)); - if (node == NULL) - /* errno is set to ENOMEM. */ - return NULL; - - result = newlocale (category_mask, name, base); - if (result == NULL) - { - free (node); - return NULL; - } - - /* Fill the hash node. */ - node->locale = result; - node->names = names; - - /* Insert it in the hash table. */ - { - size_t hashcode = locale_hash_function (result); - size_t slot = hashcode % LOCALE_HASH_TABLE_SIZE; - struct locale_hash_node *p; - - /* Lock while inserting the new node. */ - gl_rwlock_wrlock (locale_lock); - for (p = locale_hash_table[slot]; p != NULL; p = p->next) - if (p->locale == result) - { - /* This can happen if the application uses the original freelocale() - function instead of the overridden one. */ - p->names = node->names; - break; - } - if (p == NULL) - { - node->next = locale_hash_table[slot]; - locale_hash_table[slot] = node; - } - - gl_rwlock_unlock (locale_lock); - - if (p != NULL) - free (node); - } - - return result; -} - -/* duplocale() override. */ -locale_t -duplocale (locale_t locale) -#undef duplocale -{ - struct locale_hash_node *node; - locale_t result; - - if (locale == NULL) - /* Invalid argument. */ - abort (); - - node = (struct locale_hash_node *) malloc (sizeof (struct locale_hash_node)); - if (node == NULL) - /* errno is set to ENOMEM. */ - return NULL; - - result = duplocale (locale); - if (result == NULL) - { - free (node); - return NULL; - } - - /* Fill the hash node. */ - node->locale = result; - if (locale == LC_GLOBAL_LOCALE) - { - int category; - - for (category = 0; category < 6; category++) - node->names.category_name[category] = - get_locale_t_name (category, LC_GLOBAL_LOCALE); - - /* Lock before inserting the new node. */ - gl_rwlock_wrlock (locale_lock); - } - else - { - struct locale_hash_node *p; - - /* Lock once, for the lookup and the insertion. */ - gl_rwlock_wrlock (locale_lock); - - for (p = locale_hash_table[locale_hash_function (locale) % LOCALE_HASH_TABLE_SIZE]; - p != NULL; - p = p->next) - if (p->locale == locale) - break; - if (p != NULL) - node->names = p->names; - else - { - /* This can happen if the application uses the original - newlocale()/duplocale() functions instead of the overridden - ones. */ - int category; - - for (category = 0; category < 6; category++) - node->names.category_name[category] = ""; - } - } - - /* Insert it in the hash table. */ - { - size_t hashcode = locale_hash_function (result); - size_t slot = hashcode % LOCALE_HASH_TABLE_SIZE; - struct locale_hash_node *p; - - for (p = locale_hash_table[slot]; p != NULL; p = p->next) - if (p->locale == result) - { - /* This can happen if the application uses the original freelocale() - function instead of the overridden one. */ - p->names = node->names; - break; - } - if (p == NULL) - { - node->next = locale_hash_table[slot]; - locale_hash_table[slot] = node; - } - - gl_rwlock_unlock (locale_lock); - - if (p != NULL) - free (node); - } - - return result; -} - -/* freelocale() override. */ -void -freelocale (locale_t locale) -#undef freelocale -{ - if (locale == NULL || locale == LC_GLOBAL_LOCALE) - /* Invalid argument. */ - abort (); - - { - size_t hashcode = locale_hash_function (locale); - size_t slot = hashcode % LOCALE_HASH_TABLE_SIZE; - struct locale_hash_node *found; - struct locale_hash_node **p; - - found = NULL; - /* Lock while removing the hash node. */ - gl_rwlock_wrlock (locale_lock); - for (p = &locale_hash_table[slot]; *p != NULL; p = &(*p)->next) - if ((*p)->locale == locale) - { - found = *p; - *p = (*p)->next; - break; - } - gl_rwlock_unlock (locale_lock); - free (found); - } - - freelocale (locale); -} - -#endif - - const char * gl_locale_name_thread_unsafe (int category, _GL_UNUSED const char *categoryname) { @@ -3033,164 +2622,7 @@ gl_locale_name_thread_unsafe (int category, _GL_UNUSED const char *categoryname) { locale_t thread_locale = uselocale (NULL); if (thread_locale != LC_GLOBAL_LOCALE) - { -# if __GLIBC__ >= 2 && !defined __UCLIBC__ - /* Work around an incorrect definition of the _NL_LOCALE_NAME macro in - glibc < 2.12. - See <https://sourceware.org/bugzilla/show_bug.cgi?id=10968>. */ - const char *name = - nl_langinfo (_NL_ITEM ((category), _NL_ITEM_INDEX (-1))); - if (name[0] == '\0') - /* Fallback code for glibc < 2.4, which did not implement - nl_langinfo (_NL_LOCALE_NAME (category)). */ - name = thread_locale->__names[category]; - return name; -# elif defined __linux__ && HAVE_LANGINFO_H && defined NL_LOCALE_NAME - /* musl libc */ - return nl_langinfo_l (NL_LOCALE_NAME (category), thread_locale); -# elif (defined __FreeBSD__ || defined __DragonFly__) || (defined __APPLE__ && defined __MACH__) - /* FreeBSD, Mac OS X */ - int mask; - - switch (category) - { - case LC_CTYPE: - mask = LC_CTYPE_MASK; - break; - case LC_NUMERIC: - mask = LC_NUMERIC_MASK; - break; - case LC_TIME: - mask = LC_TIME_MASK; - break; - case LC_COLLATE: - mask = LC_COLLATE_MASK; - break; - case LC_MONETARY: - mask = LC_MONETARY_MASK; - break; - case LC_MESSAGES: - mask = LC_MESSAGES_MASK; - break; - default: /* We shouldn't get here. */ - return ""; - } - return querylocale (mask, thread_locale); -# elif defined __sun -# if HAVE_SOLARIS114_LOCALES - /* Solaris >= 11.4. */ - void *lcp = (*thread_locale)->core.data->lcp; - if (lcp != NULL) - switch (category) - { - case LC_CTYPE: - case LC_NUMERIC: - case LC_TIME: - case LC_COLLATE: - case LC_MONETARY: - case LC_MESSAGES: - return ((const char * const *) lcp)[category]; - default: /* We shouldn't get here. */ - return ""; - } -# else - /* Solaris 11 OpenIndiana. - For the internal structure of locale objects, see - https://github.com/OpenIndiana/illumos-gate/blob/master/usr/src/lib/libc/port/locale/localeimpl.h */ - switch (category) - { - case LC_CTYPE: - case LC_NUMERIC: - case LC_TIME: - case LC_COLLATE: - case LC_MONETARY: - case LC_MESSAGES: - return ((const char * const *) thread_locale)[category]; - default: /* We shouldn't get here. */ - return ""; - } -# endif -# elif defined _AIX && HAVE_NAMELESS_LOCALES - return get_locale_t_name (category, thread_locale); -# elif defined __CYGWIN__ - /* Cygwin < 2.6 lacks uselocale and thread-local locales altogether. - Cygwin <= 2.6.1 lacks NL_LOCALE_NAME, requiring peeking inside - an opaque struct. */ -# ifdef NL_LOCALE_NAME - return nl_langinfo_l (NL_LOCALE_NAME (category), thread_locale); -# else - /* FIXME: Remove when we can assume new-enough Cygwin. */ - struct __locale_t { - char categories[7][32]; - }; - return ((struct __locale_t *) thread_locale)->categories[category]; -# endif -# elif defined __HAIKU__ - /* Since 2022, Haiku has per-thread locales. locale_t is 'void *', - but in fact a 'LocaleBackendData *'. */ - struct LocaleBackendData { - int magic; - void /*BPrivate::Libroot::LocaleBackend*/ *backend; - void /*BPrivate::Libroot::LocaleDataBridge*/ *databridge; - }; - void *thread_locale_backend = - ((struct LocaleBackendData *) thread_locale)->backend; - if (thread_locale_backend != NULL) - { - /* The only existing concrete subclass of - BPrivate::Libroot::LocaleBackend is - BPrivate::Libroot::ICULocaleBackend. - Invoke the (non-virtual) method - BPrivate::Libroot::ICULocaleBackend::_QueryLocale on it. - This method is located in a separate shared library, - libroot-addon-icu.so. */ - static void * volatile querylocale_method /* = NULL */; - static int volatile querylocale_found /* = 0 */; - /* Attempt to open this shared library, the first time we get - here. */ - if (querylocale_found == 0) - { - void *handle = - dlopen ("/boot/system/lib/libroot-addon-icu.so", 0); - if (handle != NULL) - { - void *sym = - dlsym (handle, "_ZN8BPrivate7Libroot16ICULocaleBackend12_QueryLocaleEi"); - if (sym != NULL) - { - querylocale_method = sym; - querylocale_found = 1; - } - else - /* Could not find the symbol. */ - querylocale_found = -1; - } - else - /* Could not open the separate shared library. */ - querylocale_found = -1; - } - if (querylocale_found > 0) - { - /* The _QueryLocale method is a non-static C++ method with - parameters (int category) and return type 'const char *'. - See - haiku/headers/private/libroot/locale/ICULocaleBackend.h - haiku/src/system/libroot/add-ons/icu/ICULocaleBackend.cpp - This is the same as a C function with parameters - (BPrivate::Libroot::LocaleBackend* this, int category) - and return type 'const char *'. Invoke it. */ - const char * (*querylocale_func) (void *, int) = - (const char * (*) (void *, int)) querylocale_method; - return querylocale_func (thread_locale_backend, category); - } - } - else - /* It's the "C" or "POSIX" locale. */ - return "C"; -# elif defined __ANDROID__ - return MB_CUR_MAX == 4 ? "C.UTF-8" : "C"; -# endif - } + return getlocalename_l (category, thread_locale); } #endif /* On WINDOWS_NATIVE, don't use GetThreadLocale() here, because when diff --git a/lib/struniq.h b/lib/struniq.h index f3c50de820..7bc77d1cc9 100644 --- a/lib/struniq.h +++ b/lib/struniq.h @@ -30,7 +30,7 @@ flexmember lock - stdbool + bool thread-optim */ diff --git a/m4/getlocalename_l.m4 b/m4/getlocalename_l.m4 new file mode 100644 index 0000000000..e007b24b3c --- /dev/null +++ b/m4/getlocalename_l.m4 @@ -0,0 +1,76 @@ +# getlocalename_l.m4 +# serial 1 +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, +dnl with or without modifications, as long as this notice is preserved. +dnl This file is offered as-is, without any warranty. + +AC_DEFUN([gl_FUNC_GETLOCALENAME_L_SIMPLE], +[ + AC_REQUIRE([gl_LOCALE_H_DEFAULTS]) + + dnl Persuade glibc <locale.h> to declare getlocalename_l(). + AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS]) + + AC_REQUIRE([gl_FUNC_SETLOCALE_NULL]) + AC_CHECK_FUNCS_ONCE([getlocalename_l]) + if test $ac_cv_func_getlocalename_l = yes; then + GETLOCALENAME_L_LIB= + else + HAVE_GETLOCALENAME_L=0 + GETLOCALENAME_L_LIB="$SETLOCALE_NULL_LIB" + fi + dnl GETLOCALENAME_L_LIB is expected to be '-pthread' or '-lpthread' on AIX + dnl with gcc or xlc, and empty otherwise. + AC_SUBST([GETLOCALENAME_L_LIB]) +]) + +# Prerequisites of lib/getlocalename_l.c. +AC_DEFUN([gl_PREREQ_GETLOCALENAME_L_SIMPLE], +[ + AC_REQUIRE([gl_LOCALE_H_DEFAULTS]) + AC_REQUIRE([gl_LOCALE_T]) + AC_REQUIRE([gt_INTL_THREAD_LOCALE_NAME]) + AC_CHECK_HEADERS_ONCE([langinfo.h]) + if test $HAVE_LOCALE_T = 1; then + gl_CHECK_FUNCS_ANDROID([newlocale], [[#include <locale.h>]]) + gl_CHECK_FUNCS_ANDROID([duplocale], [[#include <locale.h>]]) + gl_CHECK_FUNCS_ANDROID([freelocale], [[#include <locale.h>]]) + gl_func_newlocale="$ac_cv_func_newlocale" + gl_func_duplocale="$ac_cv_func_duplocale" + 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_newlocale='future OS version' + gl_cv_onwards_func_duplocale='future OS version' + gl_cv_onwards_func_freelocale='future OS version' + gl_func_newlocale=no + gl_func_duplocale=no + gl_func_freelocale=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 + if test $gl_func_duplocale != yes; then + HAVE_DUPLOCALE=0 + case "$gl_cv_onwards_func_duplocale" in + future*) REPLACE_DUPLOCALE=1 ;; + esac + fi + if test $gl_func_freelocale != yes; then + HAVE_FREELOCALE=0 + case "$gl_cv_onwards_func_freelocale" in + future*) REPLACE_FREELOCALE=1 ;; + esac + fi + if test $gt_localename_enhances_locale_funcs = yes; then + REPLACE_NEWLOCALE=1 + REPLACE_DUPLOCALE=1 + REPLACE_FREELOCALE=1 + fi +]) diff --git a/m4/intl-thread-locale.m4 b/m4/intl-thread-locale.m4 index aff83a7ad6..084eacf14a 100644 --- a/m4/intl-thread-locale.m4 +++ b/m4/intl-thread-locale.m4 @@ -1,5 +1,5 @@ # intl-thread-locale.m4 -# serial 13 +# serial 14 dnl Copyright (C) 2015-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, @@ -38,8 +38,9 @@ AC_DEFUN([gt_INTL_THREAD_LOCALE_NAME] dnl OpenBSD base system, store complete information about the global locale, dnl such that third-party software can access it"), but for uselocale() dnl they did not think about the programs. - dnl In this situation, even the HAVE_NAMELESS_LOCALES support does not work. - dnl So, define HAVE_FAKE_LOCALES and disable all locale_t support. + dnl In this situation, even the HAVE_NAMELESS_LOCALES support cannot make + dnl uselocale() work. + dnl So, define HAVE_FAKE_LOCALES and disable all per-thread locale support. dnl Expected result: HAVE_FAKE_LOCALES is defined on OpenBSD ??? 6.2. case "$gt_cv_func_uselocale_works" in *yes) @@ -123,16 +124,14 @@ AC_DEFUN([gt_INTL_THREAD_LOCALE_NAME] dnl requires the gnulib overrides of 'newlocale', 'duplocale', 'freelocale', dnl which is a problem for GNU libunistring. Therefore try hard to avoid dnl enabling this code! - dnl Expected result: HAVE_NAMELESS_LOCALES is defined on AIX, + dnl Expected result: HAVE_NAMELESS_LOCALES is defined on OpenBSD ??? 6.2, AIX, dnl and HAVE_AIX72_LOCALES is defined on AIX ??? 7.2. - gt_nameless_locales=no + gt_nameless_locales=$gt_fake_locales case "$host_os" in dnl It's needed on AIX 7.2. aix*) gt_nameless_locales=yes - AC_DEFINE([HAVE_NAMELESS_LOCALES], [1], - [Define if the locale_t type does not contain the name of each locale category.]) - dnl In AIX ??? 7.2, a locale contains at least the name of the LC_MESSSAGES + dnl In AIX ??? 7.2, a locale contains at least the name of the LC_MESSAGES dnl category (fix of defect 823926). AC_CACHE_CHECK([for AIX locales with LC_MESSAGES name], [gt_cv_locale_aix72], @@ -153,6 +152,10 @@ AC_DEFUN([gt_INTL_THREAD_LOCALE_NAME] fi ;; esac + if test $gt_nameless_locales = yes; then + AC_DEFINE([HAVE_NAMELESS_LOCALES], [1], + [Define if the locale_t type does not contain the name of each locale category.]) + fi dnl We cannot support uselocale() on platforms where the locale_t type is dnl fake. So, set @@ -172,8 +175,8 @@ AC_DEFUN([gt_INTL_THREAD_LOCALE_NAME] dnl overrides newlocale(), duplocale(), freelocale() to keep track of locale dnl names. dnl Expected result: LOCALENAME_ENHANCE_LOCALE_FUNCS is defined on - dnl AIX 7.1, AIX ??? 7.3. - if test $gt_good_uselocale = yes && test $gt_nameless_locales = yes; then + dnl OpenBSD ??? 6.2, AIX 7.1, AIX ??? 7.3. + if test $gt_working_uselocale = yes && test $gt_nameless_locales = yes; then gt_localename_enhances_locale_funcs=yes LOCALENAME_ENHANCE_LOCALE_FUNCS=1 AC_DEFINE([LOCALENAME_ENHANCE_LOCALE_FUNCS], [1], diff --git a/m4/locale_h.m4 b/m4/locale_h.m4 index 1e3cc11bf2..0b6b6f182d 100644 --- a/m4/locale_h.m4 +++ b/m4/locale_h.m4 @@ -1,5 +1,5 @@ # locale_h.m4 -# serial 34 +# serial 35 dnl Copyright (C) 2007, 2009-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, @@ -107,7 +107,7 @@ AC_DEFUN_ONCE([gl_LOCALE_H] # include <xlocale.h> #endif ]], - [setlocale newlocale duplocale freelocale]) + [setlocale newlocale duplocale freelocale getlocalename_l]) ]) dnl Checks to determine whether the system has the locale_t type, @@ -179,6 +179,7 @@ AC_DEFUN([gl_LOCALE_H_REQUIRE_DEFAULTS] gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_NEWLOCALE]) gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_DUPLOCALE]) gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FREELOCALE]) + gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_GETLOCALENAME_L]) gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_LOCALENAME_UNSAFE]) ]) m4_require(GL_MODULE_INDICATOR_PREFIX[_LOCALE_H_MODULE_INDICATOR_DEFAULTS]) @@ -191,6 +192,7 @@ AC_DEFUN([gl_LOCALE_H_DEFAULTS] HAVE_NEWLOCALE=1; AC_SUBST([HAVE_NEWLOCALE]) HAVE_DUPLOCALE=1; AC_SUBST([HAVE_DUPLOCALE]) HAVE_FREELOCALE=1; AC_SUBST([HAVE_FREELOCALE]) + HAVE_GETLOCALENAME_L=1; AC_SUBST([HAVE_GETLOCALENAME_L]) REPLACE_LOCALECONV=0; AC_SUBST([REPLACE_LOCALECONV]) REPLACE_SETLOCALE=0; AC_SUBST([REPLACE_SETLOCALE]) REPLACE_NEWLOCALE=0; AC_SUBST([REPLACE_NEWLOCALE]) diff --git a/m4/localename.m4 b/m4/localename.m4 index af94411b23..b5677d8944 100644 --- a/m4/localename.m4 +++ b/m4/localename.m4 @@ -1,5 +1,5 @@ # localename.m4 -# serial 13 +# serial 14 dnl Copyright (C) 2007, 2009-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,57 +9,13 @@ AC_DEFUN([gl_LOCALENAME_UNSAFE], [ AC_REQUIRE([gl_LOCALE_H_DEFAULTS]) - AC_REQUIRE([gl_LOCALE_T]) AC_REQUIRE([gt_LC_MESSAGES]) - AC_REQUIRE([gt_INTL_THREAD_LOCALE_NAME]) AC_REQUIRE([gt_INTL_MACOSX]) - AC_CHECK_HEADERS_ONCE([langinfo.h]) - if test $HAVE_LOCALE_T = 1; then - gl_CHECK_FUNCS_ANDROID([newlocale], [[#include <locale.h>]]) - gl_CHECK_FUNCS_ANDROID([duplocale], [[#include <locale.h>]]) - gl_CHECK_FUNCS_ANDROID([freelocale], [[#include <locale.h>]]) - gl_func_newlocale="$ac_cv_func_newlocale" - gl_func_duplocale="$ac_cv_func_duplocale" - 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_newlocale='future OS version' - gl_cv_onwards_func_duplocale='future OS version' - gl_cv_onwards_func_freelocale='future OS version' - gl_func_newlocale=no - gl_func_duplocale=no - gl_func_freelocale=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 - if test $gl_func_duplocale != yes; then - HAVE_DUPLOCALE=0 - case "$gl_cv_onwards_func_duplocale" in - future*) REPLACE_DUPLOCALE=1 ;; - esac - fi - if test $gl_func_freelocale != yes; then - HAVE_FREELOCALE=0 - case "$gl_cv_onwards_func_freelocale" in - future*) REPLACE_FREELOCALE=1 ;; - esac - fi - if test $gt_localename_enhances_locale_funcs = yes; then - REPLACE_NEWLOCALE=1 - REPLACE_DUPLOCALE=1 - REPLACE_FREELOCALE=1 - fi ]) AC_DEFUN([gl_LOCALENAME_UNSAFE_LIMITED], [ AC_REQUIRE([gt_LC_MESSAGES]) - AC_REQUIRE([gt_INTL_THREAD_LOCALE_NAME]) ]) AC_DEFUN([gl_LOCALENAME_ENVIRON], diff --git a/modules/duplocale b/modules/duplocale index 8d34e9476a..e4fce0ed04 100644 --- a/modules/duplocale +++ b/modules/duplocale @@ -29,7 +29,7 @@ Include: <locale.h> Link: -$(DUPLOCALE_LIB) +$(DUPLOCALE_LIB) $(GETLOCALENAME_L_LIB) License: LGPLv2+ diff --git a/modules/duplocale-tests b/modules/duplocale-tests index 395d252dc8..bcc8ea1d44 100644 --- a/modules/duplocale-tests +++ b/modules/duplocale-tests @@ -17,4 +17,4 @@ gt_FUNC_USELOCALE Makefile.am: TESTS += test-duplocale check_PROGRAMS += test-duplocale -test_duplocale_LDADD = $(LDADD) $(SETLOCALE_LIB) @DUPLOCALE_LIB@ +test_duplocale_LDADD = $(LDADD) $(SETLOCALE_LIB) @DUPLOCALE_LIB@ $(GETLOCALENAME_L_LIB) diff --git a/modules/freelocale b/modules/freelocale index fe1faaae59..4c99c4b5c4 100644 --- a/modules/freelocale +++ b/modules/freelocale @@ -24,6 +24,9 @@ endif Include: <locale.h> +Link: +$(GETLOCALENAME_L_LIB) + License: LGPLv2+ diff --git a/modules/freelocale-tests b/modules/freelocale-tests index 9c4c322e38..6cb7463323 100644 --- a/modules/freelocale-tests +++ b/modules/freelocale-tests @@ -11,3 +11,4 @@ configure.ac: Makefile.am: TESTS += test-freelocale check_PROGRAMS += test-freelocale +test_freelocale_LDADD = $(LDADD) $(GETLOCALENAME_L_LIB) diff --git a/modules/getlocalename_l-simple b/modules/getlocalename_l-simple new file mode 100644 index 0000000000..e169721fd5 --- /dev/null +++ b/modules/getlocalename_l-simple @@ -0,0 +1,47 @@ +Description: +getlocalename_l() function: return name of a single locale category. + +Files: +lib/getlocalename_l.c +lib/localename-table.h +lib/localename-table.c +lib/struniq.h +m4/getlocalename_l.m4 +m4/intl-thread-locale.m4 + +Depends-on: +locale-h +extensions +flexmember [test $HAVE_GETLOCALENAME_L = 0] +lock [test $HAVE_GETLOCALENAME_L = 0] +bool [test $HAVE_GETLOCALENAME_L = 0] +thread-optim [test $HAVE_GETLOCALENAME_L = 0] +setlocale-messages [test $HAVE_GETLOCALENAME_L = 0] +setlocale-null [test $HAVE_GETLOCALENAME_L = 0] +free-posix [test $HAVE_GETLOCALENAME_L = 0] + +configure.ac: +gl_FUNC_GETLOCALENAME_L_SIMPLE +gl_CONDITIONAL([GL_COND_OBJ_GETLOCALENAME_L], [test $HAVE_GETLOCALENAME_L = 0]) +AM_COND_IF([GL_COND_OBJ_GETLOCALENAME_L], [ + gl_PREREQ_GETLOCALENAME_L_SIMPLE +]) +gl_MODULE_INDICATOR([getlocalename_l]) +gl_LOCALE_MODULE_INDICATOR([getlocalename_l]) + +Makefile.am: +if GL_COND_OBJ_GETLOCALENAME_L +lib_SOURCES += getlocalename_l.c localename-table.c +endif + +Include: +<locale.h> + +Link: +$(GETLOCALENAME_L_LIB) + +License: +LGPLv2+ + +Maintainer: +all diff --git a/modules/isalnum_l-tests b/modules/isalnum_l-tests index 2fab4fd61f..c84fa3581f 100644 --- a/modules/isalnum_l-tests +++ b/modules/isalnum_l-tests @@ -14,3 +14,4 @@ gl_MUSL_LIBC Makefile.am: TESTS += test-isalnum_l check_PROGRAMS += test-isalnum_l +test_isalnum_l_LDADD = $(LDADD) $(GETLOCALENAME_L_LIB) diff --git a/modules/isalpha_l-tests b/modules/isalpha_l-tests index 14fe1a0804..9ea4f90ec8 100644 --- a/modules/isalpha_l-tests +++ b/modules/isalpha_l-tests @@ -14,3 +14,4 @@ gl_MUSL_LIBC Makefile.am: TESTS += test-isalpha_l check_PROGRAMS += test-isalpha_l +test_isalpha_l_LDADD = $(LDADD) $(GETLOCALENAME_L_LIB) diff --git a/modules/isblank_l-tests b/modules/isblank_l-tests index f6511a3262..be0dd7962a 100644 --- a/modules/isblank_l-tests +++ b/modules/isblank_l-tests @@ -14,3 +14,4 @@ gl_MUSL_LIBC Makefile.am: TESTS += test-isblank_l check_PROGRAMS += test-isblank_l +test_isblank_l_LDADD = $(LDADD) $(GETLOCALENAME_L_LIB) diff --git a/modules/iscntrl_l-tests b/modules/iscntrl_l-tests index b35b8264b2..c1542350d4 100644 --- a/modules/iscntrl_l-tests +++ b/modules/iscntrl_l-tests @@ -14,3 +14,4 @@ gl_MUSL_LIBC Makefile.am: TESTS += test-iscntrl_l check_PROGRAMS += test-iscntrl_l +test_iscntrl_l_LDADD = $(LDADD) $(GETLOCALENAME_L_LIB) diff --git a/modules/isdigit_l-tests b/modules/isdigit_l-tests index 78d479b4c1..971238df99 100644 --- a/modules/isdigit_l-tests +++ b/modules/isdigit_l-tests @@ -14,3 +14,4 @@ gl_MUSL_LIBC Makefile.am: TESTS += test-isdigit_l check_PROGRAMS += test-isdigit_l +test_isdigit_l_LDADD = $(LDADD) $(GETLOCALENAME_L_LIB) diff --git a/modules/isgraph_l-tests b/modules/isgraph_l-tests index 519e40b623..01b422515b 100644 --- a/modules/isgraph_l-tests +++ b/modules/isgraph_l-tests @@ -14,3 +14,4 @@ gl_MUSL_LIBC Makefile.am: TESTS += test-isgraph_l check_PROGRAMS += test-isgraph_l +test_isgraph_l_LDADD = $(LDADD) $(GETLOCALENAME_L_LIB) diff --git a/modules/islower_l-tests b/modules/islower_l-tests index 8df465f55f..91ca010b48 100644 --- a/modules/islower_l-tests +++ b/modules/islower_l-tests @@ -14,3 +14,4 @@ gl_MUSL_LIBC Makefile.am: TESTS += test-islower_l check_PROGRAMS += test-islower_l +test_islower_l_LDADD = $(LDADD) $(GETLOCALENAME_L_LIB) diff --git a/modules/isprint_l-tests b/modules/isprint_l-tests index 317d88fbfe..2babfa40c3 100644 --- a/modules/isprint_l-tests +++ b/modules/isprint_l-tests @@ -14,3 +14,4 @@ gl_MUSL_LIBC Makefile.am: TESTS += test-isprint_l check_PROGRAMS += test-isprint_l +test_isprint_l_LDADD = $(LDADD) $(GETLOCALENAME_L_LIB) diff --git a/modules/ispunct_l-tests b/modules/ispunct_l-tests index d74a6baf56..e405d5079b 100644 --- a/modules/ispunct_l-tests +++ b/modules/ispunct_l-tests @@ -14,3 +14,4 @@ gl_MUSL_LIBC Makefile.am: TESTS += test-ispunct_l check_PROGRAMS += test-ispunct_l +test_ispunct_l_LDADD = $(LDADD) $(GETLOCALENAME_L_LIB) diff --git a/modules/isspace_l-tests b/modules/isspace_l-tests index 4e0e2384a3..2a02fadc96 100644 --- a/modules/isspace_l-tests +++ b/modules/isspace_l-tests @@ -14,3 +14,4 @@ gl_MUSL_LIBC Makefile.am: TESTS += test-isspace_l check_PROGRAMS += test-isspace_l +test_isspace_l_LDADD = $(LDADD) $(GETLOCALENAME_L_LIB) diff --git a/modules/isupper_l-tests b/modules/isupper_l-tests index 488b6b2e5a..605363fc3a 100644 --- a/modules/isupper_l-tests +++ b/modules/isupper_l-tests @@ -14,3 +14,4 @@ gl_MUSL_LIBC Makefile.am: TESTS += test-isupper_l check_PROGRAMS += test-isupper_l +test_isupper_l_LDADD = $(LDADD) $(GETLOCALENAME_L_LIB) diff --git a/modules/isxdigit_l-tests b/modules/isxdigit_l-tests index 8565a6dff1..25d82c4c44 100644 --- a/modules/isxdigit_l-tests +++ b/modules/isxdigit_l-tests @@ -14,3 +14,4 @@ gl_MUSL_LIBC Makefile.am: TESTS += test-isxdigit_l check_PROGRAMS += test-isxdigit_l +test_isxdigit_l_LDADD = $(LDADD) $(GETLOCALENAME_L_LIB) diff --git a/modules/locale-h b/modules/locale-h index 52f456ea86..42eb9b5fbc 100644 --- a/modules/locale-h +++ b/modules/locale-h @@ -41,10 +41,12 @@ locale.h: locale.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H -e 's/@''GNULIB_NEWLOCALE''@/$(GNULIB_NEWLOCALE)/g' \ -e 's/@''GNULIB_DUPLOCALE''@/$(GNULIB_DUPLOCALE)/g' \ -e 's/@''GNULIB_FREELOCALE''@/$(GNULIB_FREELOCALE)/g' \ + -e 's/@''GNULIB_GETLOCALENAME_L''@/$(GNULIB_GETLOCALENAME_L)/g' \ -e 's/@''GNULIB_LOCALENAME_UNSAFE''@/$(GNULIB_LOCALENAME_UNSAFE)/g' \ -e 's|@''HAVE_NEWLOCALE''@|$(HAVE_NEWLOCALE)|g' \ -e 's|@''HAVE_DUPLOCALE''@|$(HAVE_DUPLOCALE)|g' \ -e 's|@''HAVE_FREELOCALE''@|$(HAVE_FREELOCALE)|g' \ + -e 's|@''HAVE_GETLOCALENAME_L''@|$(HAVE_GETLOCALENAME_L)|g' \ -e 's|@''HAVE_XLOCALE_H''@|$(HAVE_XLOCALE_H)|g' \ -e 's|@''REPLACE_LOCALECONV''@|$(REPLACE_LOCALECONV)|g' \ -e 's|@''REPLACE_SETLOCALE''@|$(REPLACE_SETLOCALE)|g' \ diff --git a/modules/localename-unsafe b/modules/localename-unsafe index 18fa5433bd..b7d073deea 100644 --- a/modules/localename-unsafe +++ b/modules/localename-unsafe @@ -5,9 +5,6 @@ in thread-local (unsafe) storage. Files: lib/localename.h lib/localename-unsafe.c -lib/localename-table.h -lib/localename-table.c -lib/struniq.h m4/localename.m4 m4/intl-thread-locale.m4 m4/intlmacosx.m4 @@ -18,16 +15,11 @@ Depends-on: localename-unsafe-limited localename-environ extensions -bool locale-h -flexmember -free-posix strdup lock -langinfo-h -setlocale-null +getlocalename_l-simple setlocale-null-unlocked -thread-optim configure.ac: gl_LOCALENAME_UNSAFE @@ -38,7 +30,6 @@ Makefile.am: if !GL_COND_OBJ_LOCALENAME_UNSAFE_LIMITED lib_SOURCES += localename-unsafe.c endif -lib_SOURCES += localename-table.c Include: "localename.h" diff --git a/modules/localename-unsafe-limited b/modules/localename-unsafe-limited index 74bf0d5697..51e8b1e882 100644 --- a/modules/localename-unsafe-limited +++ b/modules/localename-unsafe-limited @@ -13,6 +13,7 @@ m4/lcmessage.m4 Depends-on: extensions locale-h +getlocalename_l-simple setlocale-null-unlocked configure.ac: diff --git a/modules/newlocale b/modules/newlocale index 04f6df28f9..c48ff2b8cd 100644 --- a/modules/newlocale +++ b/modules/newlocale @@ -26,6 +26,9 @@ endif Include: <locale.h> +Link: +$(GETLOCALENAME_L_LIB) + License: LGPLv2+ diff --git a/modules/newlocale-tests b/modules/newlocale-tests index 6876456784..d9b32f78bf 100644 --- a/modules/newlocale-tests +++ b/modules/newlocale-tests @@ -11,3 +11,4 @@ gl_CHECK_FUNCS_ANDROID([nl_langinfo_l], [[#include <langinfo.h>]]) Makefile.am: TESTS += test-newlocale check_PROGRAMS += test-newlocale +test_newlocale_LDADD = $(LDADD) $(GETLOCALENAME_L_LIB) diff --git a/modules/strcasecmp_l-tests b/modules/strcasecmp_l-tests index b096df6fb8..d99f610172 100644 --- a/modules/strcasecmp_l-tests +++ b/modules/strcasecmp_l-tests @@ -14,3 +14,4 @@ gl_MUSL_LIBC Makefile.am: TESTS += test-strcasecmp_l check_PROGRAMS += test-strcasecmp_l +test_strcasecmp_l_LDADD = $(LDADD) $(GETLOCALENAME_L_LIB) diff --git a/modules/strerror_l-tests b/modules/strerror_l-tests index 07ef0cd21a..356788852e 100644 --- a/modules/strerror_l-tests +++ b/modules/strerror_l-tests @@ -13,3 +13,4 @@ configure.ac: Makefile.am: TESTS += test-strerror_l check_PROGRAMS += test-strerror_l +test_strerror_l_LDADD = $(LDADD) $(GETLOCALENAME_L_LIB) diff --git a/modules/strncasecmp_l-tests b/modules/strncasecmp_l-tests index 909d11701e..12c99d1419 100644 --- a/modules/strncasecmp_l-tests +++ b/modules/strncasecmp_l-tests @@ -14,3 +14,4 @@ gl_MUSL_LIBC Makefile.am: TESTS += test-strncasecmp_l check_PROGRAMS += test-strncasecmp_l +test_strncasecmp_l_LDADD = $(LDADD) $(GETLOCALENAME_L_LIB) diff --git a/modules/tolower_l-tests b/modules/tolower_l-tests index 5a08826b1f..4897f9229a 100644 --- a/modules/tolower_l-tests +++ b/modules/tolower_l-tests @@ -14,3 +14,4 @@ gl_MUSL_LIBC Makefile.am: TESTS += test-tolower_l check_PROGRAMS += test-tolower_l +test_tolower_l_LDADD = $(LDADD) $(GETLOCALENAME_L_LIB) diff --git a/modules/toupper_l-tests b/modules/toupper_l-tests index bfd92effdd..eea338d5a5 100644 --- a/modules/toupper_l-tests +++ b/modules/toupper_l-tests @@ -14,3 +14,4 @@ gl_MUSL_LIBC Makefile.am: TESTS += test-toupper_l check_PROGRAMS += test-toupper_l +test_toupper_l_LDADD = $(LDADD) $(GETLOCALENAME_L_LIB) -- 2.43.0
>From e3466db8e26caa34d025fb9a2624c6afba1d00aa Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Fri, 21 Feb 2025 11:25:54 +0100 Subject: [PATCH 2/2] getlocalename_l-simple: Add tests. * tests/test-getlocalename_l.c: New file. * modules/getlocalename_l-simple-tests: New file. --- ChangeLog | 4 + modules/getlocalename_l-simple-tests | 16 ++ tests/test-getlocalename_l.c | 368 +++++++++++++++++++++++++++ 3 files changed, 388 insertions(+) create mode 100644 modules/getlocalename_l-simple-tests create mode 100644 tests/test-getlocalename_l.c diff --git a/ChangeLog b/ChangeLog index cc692df338..cf4cebea9e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2025-02-21 Bruno Haible <br...@clisp.org> + getlocalename_l-simple: Add tests. + * tests/test-getlocalename_l.c: New file. + * modules/getlocalename_l-simple-tests: New file. + getlocalename_l-simple: New module. * lib/locale.in.h (newlocale, duplocale, freelocale): Now enabled by module 'getlocalename_l-simple'. diff --git a/modules/getlocalename_l-simple-tests b/modules/getlocalename_l-simple-tests new file mode 100644 index 0000000000..dfbc3af7e7 --- /dev/null +++ b/modules/getlocalename_l-simple-tests @@ -0,0 +1,16 @@ +Files: +tests/test-getlocalename_l.c +tests/signature.h +tests/macros.h + +Depends-on: +newlocale +freelocale +setlocale + +configure.ac: + +Makefile.am: +TESTS += test-getlocalename_l +check_PROGRAMS += test-getlocalename_l +test_getlocalename_l_LDADD = $(LDADD) @GETLOCALENAME_L_LIB@ $(SETLOCALE_LIB) diff --git a/tests/test-getlocalename_l.c b/tests/test-getlocalename_l.c new file mode 100644 index 0000000000..77ebf4614b --- /dev/null +++ b/tests/test-getlocalename_l.c @@ -0,0 +1,368 @@ +/* Test of getlocalename_l function. + Copyright (C) 2025 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 <br...@clisp.org>, 2025. */ + +#include <config.h> + +/* Specification. */ +#include <locale.h> + +#include "signature.h" +SIGNATURE_CHECK (getlocalename_l, const char *, (int, locale_t)); + +#include <string.h> + +#include "macros.h" + +#ifdef __HAIKU__ +/* Work around Haiku bug <https://dev.haiku-os.org/ticket/18344>. */ +# define freelocale(loc) ((void) (loc)) +#endif + +/* The name that setlocale(,NULL) returns for the "C" locale. */ +#ifdef __HAIKU__ +# define C_CANONICALIZED "POSIX" +#else +# define C_CANONICALIZED "C" +#endif + +#if defined _WIN32 && !defined __CYGWIN__ + +# define ENGLISH "English_United States" +# define FRENCH "French_France" +# define ENCODING ".1252" + +# define LOCALE1 ENGLISH ENCODING +# define LOCALE2 FRENCH ENCODING + +#else + +# define LOCALE1 "en_US.UTF-8" +# define LOCALE2 "fr_FR.UTF-8" + +#endif + +int +main () +{ + /* Test the "C" locale. */ + { + const char *ret; + locale_t locale = newlocale (LC_ALL_MASK, "C", NULL); + ASSERT (locale != NULL); + + ret = getlocalename_l (LC_COLLATE, locale); + ASSERT (strcmp (ret, "C") == 0); + + ret = getlocalename_l (LC_CTYPE, locale); + ASSERT (strcmp (ret, "C") == 0); + + ret = getlocalename_l (LC_MESSAGES, locale); + ASSERT (strcmp (ret, "C") == 0); + + ret = getlocalename_l (LC_MONETARY, locale); + ASSERT (strcmp (ret, "C") == 0); + + ret = getlocalename_l (LC_NUMERIC, locale); + ASSERT (strcmp (ret, "C") == 0); + + ret = getlocalename_l (LC_TIME, locale); + ASSERT (strcmp (ret, "C") == 0); + + freelocale (locale); + } + + /* Test an English locale. */ + { + locale_t locale1 = newlocale (LC_ALL_MASK, LOCALE1, NULL); + if (locale1 != NULL) + { + const char *ret; + + ret = getlocalename_l (LC_COLLATE, locale1); + ASSERT (strcmp (ret, LOCALE1) == 0); + + ret = getlocalename_l (LC_CTYPE, locale1); + ASSERT (strcmp (ret, LOCALE1) == 0); + + ret = getlocalename_l (LC_MESSAGES, locale1); + ASSERT (strcmp (ret, LOCALE1) == 0); + + ret = getlocalename_l (LC_MONETARY, locale1); + ASSERT (strcmp (ret, LOCALE1) == 0); + + ret = getlocalename_l (LC_NUMERIC, locale1); + ASSERT (strcmp (ret, LOCALE1) == 0); + + ret = getlocalename_l (LC_TIME, locale1); + ASSERT (strcmp (ret, LOCALE1) == 0); + + freelocale (locale1); + } + } + + /* Test a mixed locale. */ + { + locale_t locale1 = newlocale (LC_ALL_MASK, LOCALE1, NULL); + if (locale1 != NULL) + { + locale_t locale2 = newlocale (LC_COLLATE_MASK, LOCALE2, locale1); + if (locale2 != NULL) + { + const char *ret; + + ret = getlocalename_l (LC_COLLATE, locale2); + ASSERT (strcmp (ret, LOCALE2) == 0); + + ret = getlocalename_l (LC_CTYPE, locale2); + ASSERT (strcmp (ret, LOCALE1) == 0); + + ret = getlocalename_l (LC_MESSAGES, locale2); + ASSERT (strcmp (ret, LOCALE1) == 0); + + ret = getlocalename_l (LC_MONETARY, locale2); + ASSERT (strcmp (ret, LOCALE1) == 0); + + ret = getlocalename_l (LC_NUMERIC, locale2); + ASSERT (strcmp (ret, LOCALE1) == 0); + + ret = getlocalename_l (LC_TIME, locale2); + ASSERT (strcmp (ret, LOCALE1) == 0); + + freelocale (locale2); + } + else + freelocale (locale1); + } + } + { + locale_t locale1 = newlocale (LC_ALL_MASK, LOCALE1, NULL); + if (locale1 != NULL) + { + locale_t locale2 = newlocale (LC_CTYPE_MASK, LOCALE2, locale1); + if (locale2 != NULL) + { + const char *ret; + + ret = getlocalename_l (LC_COLLATE, locale2); + ASSERT (strcmp (ret, LOCALE1) == 0); + + ret = getlocalename_l (LC_CTYPE, locale2); + ASSERT (strcmp (ret, LOCALE2) == 0); + + ret = getlocalename_l (LC_MESSAGES, locale2); + ASSERT (strcmp (ret, LOCALE1) == 0); + + ret = getlocalename_l (LC_MONETARY, locale2); + ASSERT (strcmp (ret, LOCALE1) == 0); + + ret = getlocalename_l (LC_NUMERIC, locale2); + ASSERT (strcmp (ret, LOCALE1) == 0); + + ret = getlocalename_l (LC_TIME, locale2); + ASSERT (strcmp (ret, LOCALE1) == 0); + + freelocale (locale2); + } + else + freelocale (locale1); + } + } + { + locale_t locale1 = newlocale (LC_ALL_MASK, LOCALE1, NULL); + if (locale1 != NULL) + { + locale_t locale2 = newlocale (LC_MESSAGES_MASK, LOCALE2, locale1); + if (locale2 != NULL) + { + const char *ret; + + ret = getlocalename_l (LC_COLLATE, locale2); + ASSERT (strcmp (ret, LOCALE1) == 0); + + ret = getlocalename_l (LC_CTYPE, locale2); + ASSERT (strcmp (ret, LOCALE1) == 0); + + ret = getlocalename_l (LC_MESSAGES, locale2); + ASSERT (strcmp (ret, LOCALE2) == 0); + + ret = getlocalename_l (LC_MONETARY, locale2); + ASSERT (strcmp (ret, LOCALE1) == 0); + + ret = getlocalename_l (LC_NUMERIC, locale2); + ASSERT (strcmp (ret, LOCALE1) == 0); + + ret = getlocalename_l (LC_TIME, locale2); + ASSERT (strcmp (ret, LOCALE1) == 0); + + freelocale (locale2); + } + else + freelocale (locale1); + } + } + { + locale_t locale1 = newlocale (LC_ALL_MASK, LOCALE1, NULL); + if (locale1 != NULL) + { + locale_t locale2 = newlocale (LC_MONETARY_MASK, LOCALE2, locale1); + if (locale2 != NULL) + { + const char *ret; + + ret = getlocalename_l (LC_COLLATE, locale2); + ASSERT (strcmp (ret, LOCALE1) == 0); + + ret = getlocalename_l (LC_CTYPE, locale2); + ASSERT (strcmp (ret, LOCALE1) == 0); + + ret = getlocalename_l (LC_MESSAGES, locale2); + ASSERT (strcmp (ret, LOCALE1) == 0); + + ret = getlocalename_l (LC_MONETARY, locale2); + ASSERT (strcmp (ret, LOCALE2) == 0); + + ret = getlocalename_l (LC_NUMERIC, locale2); + ASSERT (strcmp (ret, LOCALE1) == 0); + + ret = getlocalename_l (LC_TIME, locale2); + ASSERT (strcmp (ret, LOCALE1) == 0); + + freelocale (locale2); + } + else + freelocale (locale1); + } + } + { + locale_t locale1 = newlocale (LC_ALL_MASK, LOCALE1, NULL); + if (locale1 != NULL) + { + locale_t locale2 = newlocale (LC_NUMERIC_MASK, LOCALE2, locale1); + if (locale2 != NULL) + { + const char *ret; + + ret = getlocalename_l (LC_COLLATE, locale2); + ASSERT (strcmp (ret, LOCALE1) == 0); + + ret = getlocalename_l (LC_CTYPE, locale2); + ASSERT (strcmp (ret, LOCALE1) == 0); + + ret = getlocalename_l (LC_MESSAGES, locale2); + ASSERT (strcmp (ret, LOCALE1) == 0); + + ret = getlocalename_l (LC_MONETARY, locale2); + ASSERT (strcmp (ret, LOCALE1) == 0); + + ret = getlocalename_l (LC_NUMERIC, locale2); + ASSERT (strcmp (ret, LOCALE2) == 0); + + ret = getlocalename_l (LC_TIME, locale2); + ASSERT (strcmp (ret, LOCALE1) == 0); + + freelocale (locale2); + } + else + freelocale (locale1); + } + } + { + locale_t locale1 = newlocale (LC_ALL_MASK, LOCALE1, NULL); + if (locale1 != NULL) + { + locale_t locale2 = newlocale (LC_TIME_MASK, LOCALE2, locale1); + if (locale2 != NULL) + { + const char *ret; + + ret = getlocalename_l (LC_COLLATE, locale2); + ASSERT (strcmp (ret, LOCALE1) == 0); + + ret = getlocalename_l (LC_CTYPE, locale2); + ASSERT (strcmp (ret, LOCALE1) == 0); + + ret = getlocalename_l (LC_MESSAGES, locale2); + ASSERT (strcmp (ret, LOCALE1) == 0); + + ret = getlocalename_l (LC_MONETARY, locale2); + ASSERT (strcmp (ret, LOCALE1) == 0); + + ret = getlocalename_l (LC_NUMERIC, locale2); + ASSERT (strcmp (ret, LOCALE1) == 0); + + ret = getlocalename_l (LC_TIME, locale2); + ASSERT (strcmp (ret, LOCALE2) == 0); + + freelocale (locale2); + } + else + freelocale (locale1); + } + } + + /* Test the global locale. */ + { + const char *ret; + + ret = getlocalename_l (LC_COLLATE, LC_GLOBAL_LOCALE); + ASSERT (strcmp (ret, C_CANONICALIZED) == 0); + + ret = getlocalename_l (LC_CTYPE, LC_GLOBAL_LOCALE); + ASSERT (strcmp (ret, C_CANONICALIZED) == 0); + + ret = getlocalename_l (LC_MESSAGES, LC_GLOBAL_LOCALE); + ASSERT (strcmp (ret, C_CANONICALIZED) == 0); + + ret = getlocalename_l (LC_MONETARY, LC_GLOBAL_LOCALE); + ASSERT (strcmp (ret, C_CANONICALIZED) == 0); + + ret = getlocalename_l (LC_NUMERIC, LC_GLOBAL_LOCALE); + ASSERT (strcmp (ret, C_CANONICALIZED) == 0); + + ret = getlocalename_l (LC_TIME, LC_GLOBAL_LOCALE); + ASSERT (strcmp (ret, C_CANONICALIZED) == 0); + } + + /* Skip this part on OpenBSD <= 6.1. */ +#if !(defined __OpenBSD__ && !HAVE_NEWLOCALE) + if (setlocale (LC_ALL, LOCALE1) != NULL) + { + const char *ret; + + ret = getlocalename_l (LC_COLLATE, LC_GLOBAL_LOCALE); + ASSERT (strcmp (ret, LOCALE1) == 0); + + ret = getlocalename_l (LC_CTYPE, LC_GLOBAL_LOCALE); + ASSERT (strcmp (ret, LOCALE1) == 0); + + ret = getlocalename_l (LC_MESSAGES, LC_GLOBAL_LOCALE); + ASSERT (strcmp (ret, LOCALE1) == 0); + + ret = getlocalename_l (LC_MONETARY, LC_GLOBAL_LOCALE); + ASSERT (strcmp (ret, LOCALE1) == 0); + + ret = getlocalename_l (LC_NUMERIC, LC_GLOBAL_LOCALE); + ASSERT (strcmp (ret, LOCALE1) == 0); + + ret = getlocalename_l (LC_TIME, LC_GLOBAL_LOCALE); + ASSERT (strcmp (ret, LOCALE1) == 0); + } +#endif + + return 0; +} -- 2.43.0