changeset 39917:5f0e12c5ef27

localename: Add support for per-thread locales on Solaris 11.4. * lib/locale.in.h (newlocale, freelocale): New declarations. (duplocale): Declare also when the 'localename' module requests it. * lib/localename.c (struniq_hash_node): Renamed from hash_node. (STRUNIQ_HASH_TABLE_SIZE): Renamed from HASH_TABLE_SIZE. (struniq): Update. (struct locale_categories_names, struct locale_hash_node): New types. (LOCALE_HASH_TABLE_SIZE): New constant. (locale_hash_table, locale_lock): New variables. (pointer_hash, get_locale_t_name): New functions. (newlocale, duplocale, freelocale): New overridden functions. (gl_locale_name_thread_unsafe): Use get_locale_t_name. * m4/intlsolaris.m4: New file. * m4/localename.m4 (gl_LOCALENAME): Require gl_LOCALE_H_DEFAULTS. Invoke gt_INTL_SOLARIS. Set HAVE_NEWLOCALE, HAVE_DUPLOCALE, HAVE_FREELOCALE, REPLACE_NEWLOCALE, REPLACE_DUPLOCALE, REPLACE_FREELOCALE. * m4/locale_h.m4 (gl_LOCALE_H): Test whether newlocale, freelocale are declared. (gl_LOCALE_H_DEFAULTS): Initialize GNULIB_LOCALENAME, HAVE_NEWLOCALE, HAVE_FREELOCALE, REPLACE_NEWLOCALE, REPLACE_FREELOCALE. * modules/locale (Makefile.am): Substitute GNULIB_LOCALENAME, HAVE_NEWLOCALE, HAVE_FREELOCALE, REPLACE_NEWLOCALE, REPLACE_FREELOCALE. * modules/localename (Files): Add intlsolaris.m4. (Depends-on): Add 'locale'. (configure.ac): Invoke gl_LOCALE_MODULE_INDICATOR. * tests/test-locale-c++.cc (newlocale, freelocale): Prepare for checking the signatures.
author Bruno Haible <bruno@clisp.org>
date Sun, 14 Oct 2018 17:03:01 +0200
parents dbcdcd1db895
children c17f5376064e
files ChangeLog lib/locale.in.h lib/localename.c m4/intlsolaris.m4 m4/locale_h.m4 m4/localename.m4 modules/locale modules/localename tests/test-locale-c++.cc
diffstat 9 files changed, 606 insertions(+), 20 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Fri Oct 12 06:46:09 2018 +0200
+++ b/ChangeLog	Sun Oct 14 17:03:01 2018 +0200
@@ -1,3 +1,33 @@
+2018-10-14  Bruno Haible  <bruno@clisp.org>
+
+	localename: Add support for per-thread locales on Solaris 11.4.
+	* lib/locale.in.h (newlocale, freelocale): New declarations.
+	(duplocale): Declare also when the 'localename' module requests it.
+	* lib/localename.c (struniq_hash_node): Renamed from hash_node.
+	(STRUNIQ_HASH_TABLE_SIZE): Renamed from HASH_TABLE_SIZE.
+	(struniq): Update.
+	(struct locale_categories_names, struct locale_hash_node): New types.
+	(LOCALE_HASH_TABLE_SIZE): New constant.
+	(locale_hash_table, locale_lock): New variables.
+	(pointer_hash, get_locale_t_name): New functions.
+	(newlocale, duplocale, freelocale): New overridden functions.
+	(gl_locale_name_thread_unsafe): Use get_locale_t_name.
+	* m4/intlsolaris.m4: New file.
+	* m4/localename.m4 (gl_LOCALENAME): Require gl_LOCALE_H_DEFAULTS. Invoke
+	gt_INTL_SOLARIS. Set HAVE_NEWLOCALE, HAVE_DUPLOCALE, HAVE_FREELOCALE,
+	REPLACE_NEWLOCALE, REPLACE_DUPLOCALE, REPLACE_FREELOCALE.
+	* m4/locale_h.m4 (gl_LOCALE_H): Test whether newlocale, freelocale are
+	declared.
+	(gl_LOCALE_H_DEFAULTS): Initialize GNULIB_LOCALENAME, HAVE_NEWLOCALE,
+	HAVE_FREELOCALE, REPLACE_NEWLOCALE, REPLACE_FREELOCALE.
+	* modules/locale (Makefile.am): Substitute GNULIB_LOCALENAME,
+	HAVE_NEWLOCALE, HAVE_FREELOCALE, REPLACE_NEWLOCALE, REPLACE_FREELOCALE.
+	* modules/localename (Files): Add intlsolaris.m4.
+	(Depends-on): Add 'locale'.
+	(configure.ac): Invoke gl_LOCALE_MODULE_INDICATOR.
+	* tests/test-locale-c++.cc (newlocale, freelocale): Prepare for checking
+	the signatures.
+
 2018-10-14  Akim Demaille  <akim@lrde.epita.fr>
 
 	timevar: use gethrxtime to get wall clock time
@@ -15,7 +45,6 @@
 	(timevar_print): Instead of checking whether the timings themselves
 	are large enough for the timevar to be printed, check the percentages.
 
-
 2018-10-14  Bruno Haible  <bruno@clisp.org>
 
 	wcsnrtombs: Work around Solaris 11.4 bug.
--- a/lib/locale.in.h	Fri Oct 12 06:46:09 2018 +0200
+++ b/lib/locale.in.h	Sun Oct 14 17:03:01 2018 +0200
@@ -190,7 +190,34 @@
 # endif
 #endif
 
-#if @GNULIB_DUPLOCALE@
+#if /*@GNULIB_NEWLOCALE@ ||*/ (@GNULIB_LOCALENAME@ && @HAVE_NEWLOCALE@)
+# if @REPLACE_NEWLOCALE@
+#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#   undef newlocale
+#   define newlocale rpl_newlocale
+#  endif
+_GL_FUNCDECL_RPL (newlocale, locale_t,
+                  (int category_mask, const char *name, locale_t base)
+                  _GL_ARG_NONNULL ((2)));
+_GL_CXXALIAS_RPL (newlocale, locale_t,
+                  (int category_mask, const char *name, locale_t base));
+# else
+#  if @HAVE_NEWLOCALE@
+_GL_CXXALIAS_SYS (newlocale, locale_t,
+                  (int category_mask, const char *name, locale_t base));
+#  endif
+# endif
+# if @HAVE_NEWLOCALE@
+_GL_CXXALIASWARN (newlocale);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef newlocale
+# if HAVE_RAW_DECL_NEWLOCALE
+_GL_WARN_ON_USE (newlocale, "newlocale is not portable");
+# endif
+#endif
+
+#if @GNULIB_DUPLOCALE@ || (@GNULIB_LOCALENAME@ && @HAVE_DUPLOCALE@)
 # if @REPLACE_DUPLOCALE@
 #  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
 #   undef duplocale
@@ -214,6 +241,29 @@
 # endif
 #endif
 
+#if /*@GNULIB_FREELOCALE@ ||*/ (@GNULIB_LOCALENAME@ && @HAVE_FREELOCALE@)
+# if @REPLACE_FREELOCALE@
+#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#   undef freelocale
+#   define freelocale rpl_freelocale
+#  endif
+_GL_FUNCDECL_RPL (freelocale, void, (locale_t locale) _GL_ARG_NONNULL ((1)));
+_GL_CXXALIAS_RPL (freelocale, void, (locale_t locale));
+# else
+#  if @HAVE_FREELOCALE@
+_GL_CXXALIAS_SYS (freelocale, void, (locale_t locale));
+#  endif
+# endif
+# if @HAVE_FREELOCALE@
+_GL_CXXALIASWARN (freelocale);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef freelocale
+# if HAVE_RAW_DECL_FREELOCALE
+_GL_WARN_ON_USE (freelocale, "freelocale is not portable");
+# endif
+#endif
+
 #endif /* _@GUARD_PREFIX@_LOCALE_H */
 #endif /* _@GUARD_PREFIX@_LOCALE_H */
 #endif /* !(__need_locale_t || _GL_ALREADY_INCLUDING_LOCALE_H) */
--- a/lib/localename.c	Fri Oct 12 06:46:09 2018 +0200
+++ b/lib/localename.c	Sun Oct 14 17:03:01 2018 +0200
@@ -50,6 +50,10 @@
 /* Solaris >= 12.  */
 extern char * getlocalename_l(int, locale_t);
 # endif
+# if HAVE_NAMELESS_LOCALES
+#  include <errno.h>
+#  include <stdint.h>
+# endif
 #endif
 
 #if HAVE_CFLOCALECOPYCURRENT || HAVE_CFPREFERENCESCOPYAPPVALUE
@@ -2615,7 +2619,7 @@
 #endif
 
 
-#if HAVE_USELOCALE /* glibc, Mac OS X, Solaris 11 OpenIndiana, or Solaris 12  */
+#if HAVE_USELOCALE /* glibc, Mac OS X, Solaris 11 OpenIndiana, or Solaris >= 11.4  */
 
 /* Simple hash set of strings.  We don't want to drag in lots of hash table
    code here.  */
@@ -2641,14 +2645,14 @@
    simultaneously, but only one thread can insert into it at the same time.  */
 
 /* A node in a hash bucket collision list.  */
-struct hash_node
+struct struniq_hash_node
   {
-    struct hash_node * volatile next;
+    struct struniq_hash_node * volatile next;
     char contents[FLEXIBLE_ARRAY_MEMBER];
   };
 
-# define HASH_TABLE_SIZE 257
-static struct hash_node * volatile struniq_hash_table[HASH_TABLE_SIZE]
+# define STRUNIQ_HASH_TABLE_SIZE 257
+static struct struniq_hash_node * volatile struniq_hash_table[STRUNIQ_HASH_TABLE_SIZE]
   /* = { NULL, ..., NULL } */;
 
 /* This lock protects the struniq_hash_table against multiple simultaneous
@@ -2661,17 +2665,17 @@
 struniq (const char *string)
 {
   size_t hashcode = string_hash (string);
-  size_t slot = hashcode % HASH_TABLE_SIZE;
+  size_t slot = hashcode % STRUNIQ_HASH_TABLE_SIZE;
   size_t size;
-  struct hash_node *new_node;
-  struct hash_node *p;
+  struct struniq_hash_node *new_node;
+  struct struniq_hash_node *p;
   for (p = struniq_hash_table[slot]; p != NULL; p = p->next)
     if (strcmp (p->contents, string) == 0)
       return p->contents;
   size = strlen (string) + 1;
   new_node =
-    (struct hash_node *)
-    malloc (FLEXSIZEOF (struct hash_node, contents, size));
+    (struct struniq_hash_node *)
+    malloc (FLEXSIZEOF (struct struniq_hash_node, contents, size));
   if (new_node == NULL)
     /* Out of memory.  Return a statically allocated string.  */
     return "C";
@@ -2700,6 +2704,421 @@
 #endif
 
 
+#if HAVE_USELOCALE && HAVE_NAMELESS_LOCALES /* Solaris >= 11.4 */
+
+/* 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.  */
+
+struct locale_categories_names
+  {
+    /* Locale category -> name (allocated with indefinite extent).  */
+    const char *category_name[6];
+  };
+
+/* A hash function for pointers.  */
+static size_t _GL_ATTRIBUTE_CONST
+pointer_hash (const void *x)
+{
+  uintptr_t p = (uintptr_t) x;
+  size_t h = ((p % 4177) << 12) + ((p % 79) << 6) + (p % 61);
+  return h;
+}
+
+/* A hash table of fixed size.  Multiple threads can access it read-only
+   simultaneously, but only one thread can insert into it or remove from it
+   at the same time.  */
+
+/* A node in a hash bucket collision list.  */
+struct locale_hash_node
+  {
+    struct locale_hash_node *next;
+    locale_t locale;
+    struct locale_categories_names names;
+  };
+
+# define LOCALE_HASH_TABLE_SIZE 101
+static struct locale_hash_node * locale_hash_table[LOCALE_HASH_TABLE_SIZE]
+  /* = { NULL, ..., NULL } */;
+
+/* This lock protects the locale_hash_table against multiple simultaneous
+   accesses (except that multiple simultaneous read accesses are allowed).  */
+
+gl_rwlock_define_initialized(static, locale_lock)
+
+/* 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 (locale == LC_GLOBAL_LOCALE)
+    {
+      /* Query the global locale.  */
+      const char *name = setlocale (category, NULL);
+      if (name != NULL)
+        return struniq (name);
+      else
+        /* Should normally not happen.  */
+        return "";
+    }
+  else
+    {
+      /* Look up the names in the hash table.  */
+      size_t hashcode = pointer_hash (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[pointer_hash (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)
+    {
+      int saved_errno = errno;
+      free (node);
+      errno = saved_errno;
+      return NULL;
+    }
+
+  /* Fill the hash node.  */
+  node->locale = result;
+  node->names = names;
+
+  /* Insert it in the hash table.  */
+  {
+    size_t hashcode = pointer_hash (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)
+    {
+      int saved_errno = errno;
+      free (node);
+      errno = saved_errno;
+      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[pointer_hash (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 = pointer_hash (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 = pointer_hash (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
+
+
 #if defined IN_LIBINTL || HAVE_USELOCALE
 
 /* Like gl_locale_name_thread, except that the result is not in storage of
@@ -2761,6 +3180,8 @@
 #   if HAVE_GETLOCALENAME_L
         /* Solaris >= 12.  */
         return getlocalename_l (category, thread_locale);
+#   elif HAVE_NAMELESS_LOCALES
+        return get_locale_t_name (category, thread_locale);
 #   else
         /* Solaris 11 OpenIndiana.
            For the internal structure of locale objects, see
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/m4/intlsolaris.m4	Sun Oct 14 17:03:01 2018 +0200
@@ -0,0 +1,53 @@
+# intlsolaris.m4 serial 1
+dnl Copyright (C) 2015-2018 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
+dnl This file can be used in projects which are not available under
+dnl the GNU General Public License or the GNU Library General Public
+dnl License but which still want to provide support for the GNU gettext
+dnl functionality.
+dnl Please note that the actual code of the GNU gettext library is covered
+dnl by the GNU Library General Public License, and the rest of the GNU
+dnl gettext package is covered by the GNU General Public License.
+dnl They are *not* in the public domain.
+
+dnl Checks for special localename support needed on Solaris.
+dnl Sets gt_nameless_locales.
+AC_DEFUN([gt_INTL_SOLARIS],
+[
+  dnl Persuade Solaris <locale.h> to define 'locale_t'.
+  AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS])
+
+  AC_CHECK_FUNCS_ONCE([uselocale])
+
+  gt_nameless_locales=no
+  dnl Solaris 12 provides getlocalename_l, while Illumos doesn't have
+  dnl it nor the equivalent.
+  if test $ac_cv_func_uselocale = yes; then
+    AC_CHECK_FUNCS([getlocalename_l])
+    if test $ac_cv_func_getlocalename_l != yes; then
+      AC_CACHE_CHECK([for Solaris 11.4 locale system],
+        [gt_cv_locale_solaris114],
+        [dnl Test whether <locale.h> defines locale_t as a typedef of
+         dnl 'struct _LC_locale_t **' (whereas Illumos defines it as a
+         dnl typedef of 'struct _locale *').
+         AC_COMPILE_IFELSE(
+           [AC_LANG_PROGRAM([[
+              #include <locale.h>
+              struct _LC_locale_t *x;
+              locale_t y;
+            ]],
+            [[*y = x;]])],
+           [gt_cv_locale_solaris114=yes],
+           [gt_cv_locale_solaris114=no])
+        ])
+      if test $gt_cv_locale_solaris114 = yes; then
+        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.])
+      fi
+    fi
+  fi
+])
--- a/m4/locale_h.m4	Fri Oct 12 06:46:09 2018 +0200
+++ b/m4/locale_h.m4	Sun Oct 14 17:03:01 2018 +0200
@@ -1,4 +1,4 @@
-# locale_h.m4 serial 20
+# locale_h.m4 serial 21
 dnl Copyright (C) 2007, 2009-2018 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -96,7 +96,7 @@
 # include <xlocale.h>
 #endif
     ]],
-    [setlocale duplocale])
+    [setlocale newlocale duplocale freelocale])
 ])
 
 AC_DEFUN([gl_LOCALE_MODULE_INDICATOR],
@@ -113,10 +113,15 @@
   GNULIB_LOCALECONV=0; AC_SUBST([GNULIB_LOCALECONV])
   GNULIB_SETLOCALE=0;  AC_SUBST([GNULIB_SETLOCALE])
   GNULIB_DUPLOCALE=0;  AC_SUBST([GNULIB_DUPLOCALE])
+  GNULIB_LOCALENAME=0; AC_SUBST([GNULIB_LOCALENAME])
   dnl Assume proper GNU behavior unless another module says otherwise.
+  HAVE_NEWLOCALE=1;       AC_SUBST([HAVE_NEWLOCALE])
   HAVE_DUPLOCALE=1;       AC_SUBST([HAVE_DUPLOCALE])
+  HAVE_FREELOCALE=1;      AC_SUBST([HAVE_FREELOCALE])
   REPLACE_LOCALECONV=0;   AC_SUBST([REPLACE_LOCALECONV])
   REPLACE_SETLOCALE=0;    AC_SUBST([REPLACE_SETLOCALE])
+  REPLACE_NEWLOCALE=0;    AC_SUBST([REPLACE_NEWLOCALE])
   REPLACE_DUPLOCALE=0;    AC_SUBST([REPLACE_DUPLOCALE])
+  REPLACE_FREELOCALE=0;   AC_SUBST([REPLACE_FREELOCALE])
   REPLACE_STRUCT_LCONV=0; AC_SUBST([REPLACE_STRUCT_LCONV])
 ])
--- a/m4/localename.m4	Fri Oct 12 06:46:09 2018 +0200
+++ b/m4/localename.m4	Sun Oct 14 17:03:01 2018 +0200
@@ -1,4 +1,4 @@
-# localename.m4 serial 3
+# localename.m4 serial 4
 dnl Copyright (C) 2007, 2009-2018 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -6,13 +6,25 @@
 
 AC_DEFUN([gl_LOCALENAME],
 [
+  AC_REQUIRE([gl_LOCALE_H_DEFAULTS])
   AC_REQUIRE([gt_LC_MESSAGES])
   AC_REQUIRE([gt_INTL_MACOSX])
   AC_CHECK_HEADERS_ONCE([langinfo.h])
-  AC_CHECK_FUNCS([setlocale uselocale])
-  dnl Solaris 12 provides getlocalename_l, while Illumos doesn't have
-  dnl it nor the equivalent.
-  if test $ac_cv_func_uselocale = yes; then
-    AC_CHECK_FUNCS([getlocalename_l])
+  AC_CHECK_FUNCS([setlocale])
+  AC_CHECK_FUNCS_ONCE([uselocale newlocale duplocale freelocale])
+  if test $ac_cv_func_newlocale != yes; then
+    HAVE_NEWLOCALE=0
+  fi
+  if test $ac_cv_func_duplocale != yes; then
+    HAVE_DUPLOCALE=0
+  fi
+  if test $ac_cv_func_freelocale != yes; then
+    HAVE_FREELOCALE=0
+  fi
+  gt_INTL_SOLARIS
+  if test $gt_nameless_locales = yes; then
+    REPLACE_NEWLOCALE=1
+    REPLACE_DUPLOCALE=1
+    REPLACE_FREELOCALE=1
   fi
 ])
--- a/modules/locale	Fri Oct 12 06:46:09 2018 +0200
+++ b/modules/locale	Sun Oct 14 17:03:01 2018 +0200
@@ -32,11 +32,16 @@
 	      -e 's/@''GNULIB_LOCALECONV''@/$(GNULIB_LOCALECONV)/g' \
 	      -e 's/@''GNULIB_SETLOCALE''@/$(GNULIB_SETLOCALE)/g' \
 	      -e 's/@''GNULIB_DUPLOCALE''@/$(GNULIB_DUPLOCALE)/g' \
+	      -e 's/@''GNULIB_LOCALENAME''@/$(GNULIB_LOCALENAME)/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_XLOCALE_H''@|$(HAVE_XLOCALE_H)|g' \
 	      -e 's|@''REPLACE_LOCALECONV''@|$(REPLACE_LOCALECONV)|g' \
 	      -e 's|@''REPLACE_SETLOCALE''@|$(REPLACE_SETLOCALE)|g' \
+	      -e 's|@''REPLACE_NEWLOCALE''@|$(REPLACE_NEWLOCALE)|g' \
 	      -e 's|@''REPLACE_DUPLOCALE''@|$(REPLACE_DUPLOCALE)|g' \
+	      -e 's|@''REPLACE_FREELOCALE''@|$(REPLACE_FREELOCALE)|g' \
 	      -e 's|@''REPLACE_STRUCT_LCONV''@|$(REPLACE_STRUCT_LCONV)|g' \
 	      -e '/definitions of _GL_FUNCDECL_RPL/r $(CXXDEFS_H)' \
 	      -e '/definition of _GL_ARG_NONNULL/r $(ARG_NONNULL_H)' \
--- a/modules/localename	Fri Oct 12 06:46:09 2018 +0200
+++ b/modules/localename	Sun Oct 14 17:03:01 2018 +0200
@@ -6,10 +6,12 @@
 lib/localename.c
 m4/localename.m4
 m4/intlmacosx.m4
+m4/intlsolaris.m4
 m4/lcmessage.m4
 
 Depends-on:
 extensions
+locale
 flexmember
 strdup
 lock
@@ -17,6 +19,7 @@
 
 configure.ac:
 gl_LOCALENAME
+gl_LOCALE_MODULE_INDICATOR([localename])
 
 Makefile.am:
 lib_SOURCES += localename.c
--- a/tests/test-locale-c++.cc	Fri Oct 12 06:46:09 2018 +0200
+++ b/tests/test-locale-c++.cc	Sun Oct 14 17:03:01 2018 +0200
@@ -32,10 +32,18 @@
 SIGNATURE_CHECK (GNULIB_NAMESPACE::setlocale, char *, (int, const char *));
 #endif
 
+#if 0
+SIGNATURE_CHECK (GNULIB_NAMESPACE::newlocale, locale_t, (int const char *, locale_t));
+#endif
+
 #if GNULIB_TEST_DUPLOCALE && HAVE_DUPLOCALE
 SIGNATURE_CHECK (GNULIB_NAMESPACE::duplocale, locale_t, (locale_t));
 #endif
 
+#if 0
+SIGNATURE_CHECK (GNULIB_NAMESPACE::freelocale, void, (locale_t));
+#endif
+
 
 int
 main ()