+2025-02-21 Bruno Haible <bruno@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 <bruno@clisp.org>
setlocale-messages: New module.
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
--- /dev/null
+/* 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 "";
+ }
+}
# 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
# 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
# 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
# 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) */
# 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];
};
/* Specification. */
#include "localename.h"
-#include <limits.h>
#include <stddef.h>
#include <stdlib.h>
#include <locale.h>
# 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
# 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>
#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)
{
{
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
flexmember
lock
- stdbool
+ bool
thread-optim
*/
--- /dev/null
+# 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
+])
# 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,
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)
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],
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
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],
# 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,
# 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,
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])
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])
# 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,
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],
<locale.h>
Link:
-$(DUPLOCALE_LIB)
+$(DUPLOCALE_LIB) $(GETLOCALENAME_L_LIB)
License:
LGPLv2+
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)
Include:
<locale.h>
+Link:
+$(GETLOCALENAME_L_LIB)
+
License:
LGPLv2+
Makefile.am:
TESTS += test-freelocale
check_PROGRAMS += test-freelocale
+test_freelocale_LDADD = $(LDADD) $(GETLOCALENAME_L_LIB)
--- /dev/null
+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
Makefile.am:
TESTS += test-isalnum_l
check_PROGRAMS += test-isalnum_l
+test_isalnum_l_LDADD = $(LDADD) $(GETLOCALENAME_L_LIB)
Makefile.am:
TESTS += test-isalpha_l
check_PROGRAMS += test-isalpha_l
+test_isalpha_l_LDADD = $(LDADD) $(GETLOCALENAME_L_LIB)
Makefile.am:
TESTS += test-isblank_l
check_PROGRAMS += test-isblank_l
+test_isblank_l_LDADD = $(LDADD) $(GETLOCALENAME_L_LIB)
Makefile.am:
TESTS += test-iscntrl_l
check_PROGRAMS += test-iscntrl_l
+test_iscntrl_l_LDADD = $(LDADD) $(GETLOCALENAME_L_LIB)
Makefile.am:
TESTS += test-isdigit_l
check_PROGRAMS += test-isdigit_l
+test_isdigit_l_LDADD = $(LDADD) $(GETLOCALENAME_L_LIB)
Makefile.am:
TESTS += test-isgraph_l
check_PROGRAMS += test-isgraph_l
+test_isgraph_l_LDADD = $(LDADD) $(GETLOCALENAME_L_LIB)
Makefile.am:
TESTS += test-islower_l
check_PROGRAMS += test-islower_l
+test_islower_l_LDADD = $(LDADD) $(GETLOCALENAME_L_LIB)
Makefile.am:
TESTS += test-isprint_l
check_PROGRAMS += test-isprint_l
+test_isprint_l_LDADD = $(LDADD) $(GETLOCALENAME_L_LIB)
Makefile.am:
TESTS += test-ispunct_l
check_PROGRAMS += test-ispunct_l
+test_ispunct_l_LDADD = $(LDADD) $(GETLOCALENAME_L_LIB)
Makefile.am:
TESTS += test-isspace_l
check_PROGRAMS += test-isspace_l
+test_isspace_l_LDADD = $(LDADD) $(GETLOCALENAME_L_LIB)
Makefile.am:
TESTS += test-isupper_l
check_PROGRAMS += test-isupper_l
+test_isupper_l_LDADD = $(LDADD) $(GETLOCALENAME_L_LIB)
Makefile.am:
TESTS += test-isxdigit_l
check_PROGRAMS += test-isxdigit_l
+test_isxdigit_l_LDADD = $(LDADD) $(GETLOCALENAME_L_LIB)
-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' \
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
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
if !GL_COND_OBJ_LOCALENAME_UNSAFE_LIMITED
lib_SOURCES += localename-unsafe.c
endif
-lib_SOURCES += localename-table.c
Include:
"localename.h"
Depends-on:
extensions
locale-h
+getlocalename_l-simple
setlocale-null-unlocked
configure.ac:
Include:
<locale.h>
+Link:
+$(GETLOCALENAME_L_LIB)
+
License:
LGPLv2+
Makefile.am:
TESTS += test-newlocale
check_PROGRAMS += test-newlocale
+test_newlocale_LDADD = $(LDADD) $(GETLOCALENAME_L_LIB)
Makefile.am:
TESTS += test-strcasecmp_l
check_PROGRAMS += test-strcasecmp_l
+test_strcasecmp_l_LDADD = $(LDADD) $(GETLOCALENAME_L_LIB)
Makefile.am:
TESTS += test-strerror_l
check_PROGRAMS += test-strerror_l
+test_strerror_l_LDADD = $(LDADD) $(GETLOCALENAME_L_LIB)
Makefile.am:
TESTS += test-strncasecmp_l
check_PROGRAMS += test-strncasecmp_l
+test_strncasecmp_l_LDADD = $(LDADD) $(GETLOCALENAME_L_LIB)
Makefile.am:
TESTS += test-tolower_l
check_PROGRAMS += test-tolower_l
+test_tolower_l_LDADD = $(LDADD) $(GETLOCALENAME_L_LIB)
Makefile.am:
TESTS += test-toupper_l
check_PROGRAMS += test-toupper_l
+test_toupper_l_LDADD = $(LDADD) $(GETLOCALENAME_L_LIB)