diff options
author | Jörg Frings-Fürst <debian@jff.email> | 2025-10-18 19:06:52 +0200 |
---|---|---|
committer | Jörg Frings-Fürst <debian@jff.email> | 2025-10-18 19:06:52 +0200 |
commit | 27dae84ed92f1ef0300263091972338d12e78348 (patch) | |
tree | 7c52931f474fafb8a4bd4fd15ca3461c77cdecc2 /lib/getlocalename_l-unsafe.c | |
parent | 4682deeb62247d34de87f8e777f99e2d337fd377 (diff) |
New upstream version 1.4.1upstream/1.4.1upstream
Diffstat (limited to 'lib/getlocalename_l-unsafe.c')
-rw-r--r-- | lib/getlocalename_l-unsafe.c | 695 |
1 files changed, 695 insertions, 0 deletions
diff --git a/lib/getlocalename_l-unsafe.c b/lib/getlocalename_l-unsafe.c new file mode 100644 index 00000000..e2738829 --- /dev/null +++ b/lib/getlocalename_l-unsafe.c @@ -0,0 +1,695 @@ +/* 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/>. */ + +/* Don't use __attribute__ __nonnull__ in this compilation unit. Otherwise gcc + optimizes away the locale == NULL tests below. */ +#define _GL_ARG_NONNULL(params) + +#include <config.h> + +/* Specification. */ +#include "getlocalename_l-unsafe.h" + +#include <locale.h> +#include <limits.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + +#if LC_MESSAGES == 1729 || defined __ANDROID__ +# include "setlocale-fixes.h" +#endif +#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 + + +#if LOCALENAME_ENHANCE_LOCALE_FUNCS + +# include "flexmember.h" +# include "glthread/lock.h" +# include "thread-optim.h" + +/* 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. */ +static struct string_with_storage +get_locale_t_name_unsafe (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 (struct string_with_storage) { name, STORAGE_GLOBAL }; + else + /* Should normally not happen. */ + return (struct string_with_storage) { "", STORAGE_INDEFINITE }; + } + else + { +# if HAVE_AIX72_LOCALES + if (category == LC_MESSAGES) + { + const char *name = ((__locale_t) locale)->locale_name; + if (name != NULL) + return (struct string_with_storage) { name, STORAGE_OBJECT }; + } +# 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 (struct string_with_storage) { name, STORAGE_INDEFINITE }; + } +} + +/* 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) +{ + struct string_with_storage ret = get_locale_t_name_unsafe (category, locale); + return (ret.storage != STORAGE_INDEFINITE + ? struniq (ret.value) + : ret.value); +} + +# 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 + + +struct string_with_storage +getlocalename_l_unsafe (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 (struct string_with_storage) { plc->name, STORAGE_OBJECT }; +#elif __GLIBC__ >= 2 && !defined __UCLIBC__ + /* Work around an incorrect definition of the _NL_LOCALE_NAME macro in + glibc < 2.12. + See <https://sourceware.org/PR10968>. */ + 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 (struct string_with_storage) { name, STORAGE_OBJECT }; +#elif defined __linux__ && HAVE_LANGINFO_H && defined NL_LOCALE_NAME + /* musl libc */ + const char *name = nl_langinfo_l (NL_LOCALE_NAME (category), locale); + return (struct string_with_storage) { name, STORAGE_OBJECT }; +#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 (struct string_with_storage) { "", STORAGE_INDEFINITE }; + } + const char *name = querylocale (mask, locale); + return (struct string_with_storage) { name, STORAGE_OBJECT }; +#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]; + }; + const char *name = ((struct _locale *) locale)->part_name[category]; + return (struct string_with_storage) { name, STORAGE_OBJECT }; +#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: + { + const char *name = ((const char * const *) lcp)[category]; + return (struct string_with_storage) { name, STORAGE_OBJECT }; + } + default: /* We shouldn't get here. */ + return (struct string_with_storage) { "", STORAGE_INDEFINITE }; + } + /* We shouldn't get here. */ + return (struct string_with_storage) { "", STORAGE_INDEFINITE }; +# else + /* Solaris 11 OpenIndiana or Solaris 11 OmniOS. */ +# if HAVE_GETLOCALENAME_L + /* illumos after April 2025. */ +# undef getlocalename_l + const char *name = getlocalename_l (category, locale); + return (struct string_with_storage) { name, STORAGE_OBJECT }; +# else + /* 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: + { + const char *name = ((const char * const *) locale)[category]; + return (struct string_with_storage) { name, STORAGE_OBJECT }; + } + default: /* We shouldn't get here. */ + return (struct string_with_storage) { "", STORAGE_INDEFINITE }; + } +# endif +# endif +#elif HAVE_NAMELESS_LOCALES + /* OpenBSD >= 6.2, AIX >= 7.1 */ + return get_locale_t_name_unsafe (category, locale); +#elif defined __OpenBSD__ && HAVE_FAKE_LOCALES + /* OpenBSD >= 6.2 has only fake locales. */ + if (locale == (locale_t) 2) + return (struct string_with_storage) { "C.UTF-8", STORAGE_INDEFINITE }; + return (struct string_with_storage) { "C", STORAGE_INDEFINITE }; +#elif defined __CYGWIN__ + /* Cygwin >= 2.6. + Cygwin <= 2.6.1 lacks NL_LOCALE_NAME, requiring peeking inside + an opaque struct. */ +# ifdef NL_LOCALE_NAME + const char *name = 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]; + }; + const char *name = ((struct __locale_t *) locale)->categories[category]; +# endif + return (struct string_with_storage) { name, STORAGE_OBJECT }; +#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; + const char *name = querylocale_func (locale_backend, category); + return (struct string_with_storage) { name, STORAGE_OBJECT }; + } + } + else + /* It's the "C" or "POSIX" locale. */ + return (struct string_with_storage) { "C", STORAGE_INDEFINITE }; +#elif defined __ANDROID__ + /* Android API level >= 21 */ + struct __locale_t { + size_t mb_cur_max; + }; + const char *name = ((struct __locale_t *) locale)->mb_cur_max == 4 ? "C.UTF-8" : "C"; + return (struct string_with_storage) { name, STORAGE_INDEFINITE }; +#else + #error "Please port gnulib getlocalename_l-unsafe.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 +#if defined __ANDROID__ + name = setlocale_fixed_null (category); +#else + name = setlocale_null (category); +#endif + if (name != NULL) + return (struct string_with_storage) { name, STORAGE_GLOBAL }; + else + /* Should normally not happen. */ + return (struct string_with_storage) { "", STORAGE_INDEFINITE }; + } +} |