]> Savannah Git Hosting - gnulib.git/commitdiff
getlocalename_l-unsafe: New module.
authorBruno Haible <bruno@clisp.org>
Sat, 22 Feb 2025 17:11:38 +0000 (18:11 +0100)
committerBruno Haible <bruno@clisp.org>
Sat, 22 Feb 2025 17:59:59 +0000 (18:59 +0100)
* lib/getlocalename_l-unsafe.h: New file, based on
lib/getlocalename_l.c.
* lib/getlocalename_l-unsafe.c: New file.
* lib/getlocalename_l.c: Most code moved to
lib/getlocalename_l-unsafe.c.
(getlocalename_l): Implement based on getlocalename_l_unsafe.
* m4/getlocalename_l.m4 (gl_FUNC_GETLOCALENAME_L_UNSAFE,
gl_PREREQ_GETLOCALENAME_L_UNSAFE): New macros.
(gl_FUNC_GETLOCALENAME_L_SIMPLE): Require
gl_FUNC_GETLOCALENAME_L_UNSAFE.
(gl_PREREQ_GETLOCALENAME_L_SIMPLE): Now empty.
* modules/getlocalename_l-unsafe: New file.
* modules/getlocalename_l-simple (Files): Remove
lib/localename-table.h, lib/localename-table.c,
m4/intl-thread-locale.m4.
(Depends-on): Add getlocalename_l-unsafe. Remove setlocale-messages,
setlocale-null, free-posix.
(Makefile.am): Don't compile localename-table.c.

ChangeLog
lib/getlocalename_l-unsafe.c [new file with mode: 0644]
lib/getlocalename_l-unsafe.h [new file with mode: 0644]
lib/getlocalename_l.c
m4/getlocalename_l.m4
modules/getlocalename_l-simple
modules/getlocalename_l-unsafe [new file with mode: 0644]

index 9e389ec0b42e86aab0aee84a431cbea56317f311..dd437a98b74e6b84173ce5c8d1df7c61ce6cea9e 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,25 @@
+2025-02-22  Bruno Haible  <bruno@clisp.org>
+
+       getlocalename_l-unsafe: New module.
+       * lib/getlocalename_l-unsafe.h: New file, based on
+       lib/getlocalename_l.c.
+       * lib/getlocalename_l-unsafe.c: New file.
+       * lib/getlocalename_l.c: Most code moved to
+       lib/getlocalename_l-unsafe.c.
+       (getlocalename_l): Implement based on getlocalename_l_unsafe.
+       * m4/getlocalename_l.m4 (gl_FUNC_GETLOCALENAME_L_UNSAFE,
+       gl_PREREQ_GETLOCALENAME_L_UNSAFE): New macros.
+       (gl_FUNC_GETLOCALENAME_L_SIMPLE): Require
+       gl_FUNC_GETLOCALENAME_L_UNSAFE.
+       (gl_PREREQ_GETLOCALENAME_L_SIMPLE): Now empty.
+       * modules/getlocalename_l-unsafe: New file.
+       * modules/getlocalename_l-simple (Files): Remove
+       lib/localename-table.h, lib/localename-table.c,
+       m4/intl-thread-locale.m4.
+       (Depends-on): Add getlocalename_l-unsafe. Remove setlocale-messages,
+       setlocale-null, free-posix.
+       (Makefile.am): Don't compile localename-table.c.
+
 2025-02-21  Bruno Haible  <bruno@clisp.org>
 
        langinfo-h, nl_langinfo: Support abbreviated alternative month names.
diff --git a/lib/getlocalename_l-unsafe.c b/lib/getlocalename_l-unsafe.c
new file mode 100644 (file)
index 0000000..627434f
--- /dev/null
@@ -0,0 +1,678 @@
+/* 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 "getlocalename_l-unsafe.h"
+
+#include <locale.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if LC_MESSAGES == 1729
+# include "setlocale-messages.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/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 (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 };
+          }
+# 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:
+          {
+            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
+#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
+        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 };
+    }
+}
diff --git a/lib/getlocalename_l-unsafe.h b/lib/getlocalename_l-unsafe.h
new file mode 100644 (file)
index 0000000..d56b113
--- /dev/null
@@ -0,0 +1,67 @@
+/* Return name of a single locale category.
+   Copyright (C) 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/>.  */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2025.  */
+
+#ifndef _GETLOCALENAME_L_UNSAFE_H
+#define _GETLOCALENAME_L_UNSAFE_H
+
+#include <locale.h>
+
+#include "arg-nonnull.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* This enumeration describes the storage of the return value.  */
+enum storage
+{
+  /* Storage with indefinite extent.  */
+  STORAGE_INDEFINITE,
+  /* Storage in a thread-independent location, valid until the next setlocale()
+     call.  */
+  STORAGE_GLOBAL,
+  /* Storage in a thread-local buffer, valid until the next
+     getlocalename_l_unlocked() call in the same thread.  */
+  STORAGE_THREAD,
+  /* Storage in or attached to the locale_t object, valid until the next call
+     to newlocale() with that object as base or until the next freelocale() call
+     for that object.  */
+  STORAGE_OBJECT
+};
+
+/* Return type of getlocalename_l_unlocked.  */
+struct string_with_storage
+{
+  const char *value;
+  enum storage storage;
+};
+
+/* Returns the name of the locale category CATEGORY in the given LOCALE object
+   or, if LOCALE is LC_GLOBAL_LOCALE, in the global locale.
+   CATEGORY must be != LC_ALL.  */
+extern struct string_with_storage
+       getlocalename_l_unsafe (int category, locale_t locale)
+                              _GL_ARG_NONNULL ((2));
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _GETLOCALENAME_L_UNSAFE_H */
index 4cc63a5a94da6c58f4618fbda5f44c30e63b278a..f3fd2d28751fc6b5533b578e5e720e29d2c1c9a5 100644 (file)
 #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 "getlocalename_l-unsafe.h"
 
 #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)
 {
@@ -450,211 +39,17 @@ getlocalename_l (int category, locale_t locale)
     /* 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 "";
-    }
+  struct string_with_storage ret = getlocalename_l_unsafe (category, locale);
+  /* Return the result as a string of indefinite extent.
+     In the case (ret.storage == STORAGE_GLOBAL) 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.  For the other cases, it's a convenience to avoid undefined
+     behaviour in the calling program.  */
+  return (ret.storage != STORAGE_INDEFINITE
+          ? struniq (ret.value)
+          : ret.value);
 }
index e007b24b3c2bbc82d1129d4eccd0a06583a85763..e434dff5f3f2cf2aa08a7f6a8fdcaa5a92b58ead 100644 (file)
@@ -1,5 +1,5 @@
 # getlocalename_l.m4
-# serial 1
+# serial 2
 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,
@@ -13,6 +13,17 @@ AC_DEFUN([gl_FUNC_GETLOCALENAME_L_SIMPLE],
   dnl Persuade glibc <locale.h> to declare getlocalename_l().
   AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS])
 
+  AC_REQUIRE([gl_FUNC_GETLOCALENAME_L_UNSAFE])
+])
+
+# Prerequisites of lib/getlocalename_l.c.
+AC_DEFUN([gl_PREREQ_GETLOCALENAME_L_SIMPLE],
+[
+  :
+])
+
+AC_DEFUN_ONCE([gl_FUNC_GETLOCALENAME_L_UNSAFE],
+[
   AC_REQUIRE([gl_FUNC_SETLOCALE_NULL])
   AC_CHECK_FUNCS_ONCE([getlocalename_l])
   if test $ac_cv_func_getlocalename_l = yes; then
@@ -26,8 +37,8 @@ AC_DEFUN([gl_FUNC_GETLOCALENAME_L_SIMPLE],
   AC_SUBST([GETLOCALENAME_L_LIB])
 ])
 
-# Prerequisites of lib/getlocalename_l.c.
-AC_DEFUN([gl_PREREQ_GETLOCALENAME_L_SIMPLE],
+# Prerequisites of lib/getlocalename_l-unsafe.c.
+AC_DEFUN([gl_PREREQ_GETLOCALENAME_L_UNSAFE],
 [
   AC_REQUIRE([gl_LOCALE_H_DEFAULTS])
   AC_REQUIRE([gl_LOCALE_T])
index e169721fd533e90afafebc0fec1abbad3a5253b5..f750146d9719fa2e0295246c9570fdfc35520084 100644 (file)
@@ -3,22 +3,17 @@ 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]
+getlocalename_l-unsafe [test $HAVE_GETLOCALENAME_L = 0]
+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]
 
 configure.ac:
 gl_FUNC_GETLOCALENAME_L_SIMPLE
@@ -31,7 +26,7 @@ gl_LOCALE_MODULE_INDICATOR([getlocalename_l])
 
 Makefile.am:
 if GL_COND_OBJ_GETLOCALENAME_L
-lib_SOURCES += getlocalename_l.c localename-table.c
+lib_SOURCES += getlocalename_l.c
 endif
 
 Include:
diff --git a/modules/getlocalename_l-unsafe b/modules/getlocalename_l-unsafe
new file mode 100644 (file)
index 0000000..937d443
--- /dev/null
@@ -0,0 +1,40 @@
+Description:
+getlocalename_l_unsafe() function: return name of a single locale category.
+
+Files:
+lib/getlocalename_l-unsafe.h
+lib/getlocalename_l-unsafe.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
+flexmember
+lock
+bool
+thread-optim
+setlocale-messages
+setlocale-null
+free-posix
+
+configure.ac:
+gl_FUNC_GETLOCALENAME_L_UNSAFE
+gl_PREREQ_GETLOCALENAME_L_UNSAFE
+
+Makefile.am:
+lib_SOURCES += getlocalename_l-unsafe.c localename-table.c
+
+Include:
+"getlocalename_l-unsafe.h"
+
+Link:
+$(GETLOCALENAME_L_LIB)
+
+License:
+LGPLv2+
+
+Maintainer:
+all