]> git.ipfire.org Git - thirdparty/gettext.git/commitdiff
intl: Add support for per-thread locales on Solaris 11.4.
authorBruno Haible <bruno@clisp.org>
Mon, 22 Oct 2018 23:02:02 +0000 (01:02 +0200)
committerBruno Haible <bruno@clisp.org>
Mon, 22 Oct 2018 23:02:02 +0000 (01:02 +0200)
Relies on the recent changes to the 'localename' module in gnulib.

* gettext-runtime/intl/localename.c: Apply changes from gnulib.
* gettext-runtime/intl/localename-table.h: New file, from gnulib.
* gettext-runtime/intl/localename-table.c: New file, from gnulib.
* gettext-runtime/intl/libgnuintl.in.h (newlocale, duplocale, freelocale): New
overriding declarations.
* gettext-runtime/intl/Makefile.in (HEADERS): Add localename-table.h.
(SOURCES): Add localename-table.c.
(OBJECTS): Add localename-table.$lo.
(localename-table.lo): New target.
(libgnuintl.h, libintl.h): Substitute also HAVE_NAMELESS_LOCALES.
* gettext-runtime/m4/intl.m4: Apply changes from gnulib:
(gt_INTL_SUBDIR_CORE): Don't test for 'uselocale' and 'getlocalename_l'.
Instead, invoke gt_INTL_SOLARIS. Set HAVE_NAMELESS_LOCALES.
* gettext-runtime/m4/intlsolaris.m4: New file, from gnulib.
* gettext-runtime/m4/Makefile.am (EXTRA_DIST): Add it.
* gettext-tools/m4/Makefile.am (aclocal_DATA): Add intlsolaris.m4.
* gettext-tools/misc/gettextize.in (m4filelist): Add intlsolaris.m4.
* Makefile.am (distcheck-hook): Verify that intlsolaris.m4 is consistent with
gnulib.
* gettext-tools/doc/gettext.texi (aclocal): Add intlsolaris.m4 to the file list.
* PACKAGING: Add intlsolaris.m4 to the list of installed files.
* NEWS: Mention the change.

14 files changed:
Makefile.am
NEWS
PACKAGING
gettext-runtime/intl/Makefile.in
gettext-runtime/intl/libgnuintl.in.h
gettext-runtime/intl/localename-table.c [new file with mode: 0644]
gettext-runtime/intl/localename-table.h [new file with mode: 0644]
gettext-runtime/intl/localename.c
gettext-runtime/m4/Makefile.am
gettext-runtime/m4/intl.m4
gettext-runtime/m4/intlsolaris.m4 [new file with mode: 0644]
gettext-tools/doc/gettext.texi
gettext-tools/m4/Makefile.am
gettext-tools/misc/gettextize.in

index 43007f23075c56114d37a3c34004e15f5e8408ec..8260e9aa55644736ddb08c2585f35ed36f005d49 100644 (file)
@@ -82,6 +82,7 @@ distcheck-hook:
        cmp -s gettext-runtime/m4/intl.m4 gettext-tools/gnulib-m4/intl.m4
        cmp -s gettext-runtime/m4/intldir.m4 gettext-tools/gnulib-m4/intldir.m4
        cmp -s gettext-runtime/m4/intlmacosx.m4 gettext-tools/gnulib-m4/intlmacosx.m4
+       cmp -s gettext-runtime/m4/intlsolaris.m4 gettext-tools/gnulib-m4/intlsolaris.m4
        cmp -s gettext-runtime/m4/intmax.m4 gettext-tools/gnulib-m4/intmax.m4
        cmp -s gettext-runtime/m4/inttypes-pri.m4 gettext-tools/gnulib-m4/inttypes-pri.m4
        cmp -s gettext-runtime/m4/inttypes_h.m4 gettext-tools/gnulib-m4/inttypes_h.m4
diff --git a/NEWS b/NEWS
index c4a8cbfe58f58fd3877db5db1fd750693baa4a9b..aee71951ad5d46e75031bddfaefd36ae502da116 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -37,6 +37,7 @@
 
 * Runtime behaviour:
   - The interpretation of the language preferences on macOS has been fixed.
+  - Per-thread locales are now also supported on Solaris 11.4.
   - The replacements for the printf()/fprintf()/... functions that are
     provided through <libintl.h> on native Windows and NetBSD are now POSIX
     compliant.  There is no conflict any more between these replacements
index 71ce2452f8ac574f7277f60bcb14cbff75eba398..4fbf4424e60a5bbb840920bd54a1baadf2ddef06 100644 (file)
--- a/PACKAGING
+++ b/PACKAGING
@@ -126,6 +126,7 @@ following file list.
       $prefix/share/aclocal/intl.m4
       $prefix/share/aclocal/intldir.m4
       $prefix/share/aclocal/intlmacosx.m4
+      $prefix/share/aclocal/intlsolaris.m4
       $prefix/share/aclocal/intmax.m4
       $prefix/share/aclocal/inttypes_h.m4
       $prefix/share/aclocal/inttypes-pri.m4
index 51632f6935b8e11c87032f2b40887053b8f43186..5efdb4791721c2c51d45e33a013588b94c3873cb 100644 (file)
@@ -127,6 +127,7 @@ HEADERS = \
   localcharset.h \
   lock.h \
   relocatable.h \
+  localename-table.h \
   tsearch.h tsearch.c \
   verify.h \
   xsize.h \
@@ -159,6 +160,7 @@ SOURCES = \
   relocatable.c \
   langprefs.c \
   localename.c \
+  localename-table.c \
   flexmember.h \
   log.c \
   printf.c \
@@ -193,6 +195,7 @@ OBJECTS = \
   relocatable.$lo \
   langprefs.$lo \
   localename.$lo \
+  localename-table.$lo \
   log.$lo \
   printf.$lo \
   setlocale.$lo \
@@ -306,6 +309,8 @@ langprefs.lo: $(srcdir)/langprefs.c
        $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC --mode=compile $(COMPILE) $(srcdir)/langprefs.c
 localename.lo: $(srcdir)/localename.c
        $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC --mode=compile $(COMPILE) $(srcdir)/localename.c
+localename-table.lo: $(srcdir)/localename-table.c
+       $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC --mode=compile $(COMPILE) $(srcdir)/localename-table.c
 log.lo: $(srcdir)/log.c
        $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC --mode=compile $(COMPILE) $(srcdir)/log.c
 printf.lo: $(srcdir)/printf.c
@@ -362,9 +367,10 @@ libgnuintl.h: $(srcdir)/libgnuintl.in.h
        sed -e '/IN_LIBGLOCALE/d' \
            -e 's,@''HAVE_POSIX_PRINTF''@,@HAVE_POSIX_PRINTF@,g' \
            -e 's,@''HAVE_ASPRINTF''@,@HAVE_ASPRINTF@,g' \
-           -e 's,@''HAVE_NEWLOCALE''@,@HAVE_NEWLOCALE@,g' \
            -e 's,@''HAVE_SNPRINTF''@,@HAVE_SNPRINTF@,g' \
            -e 's,@''HAVE_WPRINTF''@,@HAVE_WPRINTF@,g' \
+           -e 's,@''HAVE_NAMELESS_LOCALES''@,@HAVE_NAMELESS_LOCALES@,g' \
+           -e 's,@''HAVE_NEWLOCALE''@,@HAVE_NEWLOCALE@,g' \
          < $(srcdir)/libgnuintl.in.h \
        | if test '@WOE32DLL@' = yes; then \
            sed -e 's/extern \([^()]*\);/extern __declspec (dllimport) \1;/'; \
@@ -380,9 +386,10 @@ libintl.h: $(srcdir)/libgnuintl.in.h
        sed -e '/IN_LIBGLOCALE/d' \
            -e 's,@''HAVE_POSIX_PRINTF''@,@HAVE_POSIX_PRINTF@,g' \
            -e 's,@''HAVE_ASPRINTF''@,@HAVE_ASPRINTF@,g' \
-           -e 's,@''HAVE_NEWLOCALE''@,@HAVE_NEWLOCALE@,g' \
            -e 's,@''HAVE_SNPRINTF''@,@HAVE_SNPRINTF@,g' \
            -e 's,@''HAVE_WPRINTF''@,@HAVE_WPRINTF@,g' \
+           -e 's,@''HAVE_NAMELESS_LOCALES''@,@HAVE_NAMELESS_LOCALES@,g' \
+           -e 's,@''HAVE_NEWLOCALE''@,@HAVE_NEWLOCALE@,g' \
          < $(srcdir)/libgnuintl.in.h > libintl.h
 
 check: all
@@ -534,6 +541,7 @@ info dvi ps pdf html:
 $(OBJECTS): ../config.h libgnuintl.h
 bindtextdom.$lo dcgettext.$lo dcigettext.$lo dcngettext.$lo dgettext.$lo dngettext.$lo finddomain.$lo gettext.$lo intl-compat.$lo loadmsgcat.$lo localealias.$lo ngettext.$lo setlocale.$lo textdomain.$lo: $(srcdir)/gettextP.h $(srcdir)/gmo.h $(srcdir)/loadinfo.h
 localename.$lo: $(srcdir)/gettextP.h
+localename.$lo localename-table.$lo: $(srcdir)/localename-table.h
 hash-string.$lo dcigettext.$lo loadmsgcat.$lo: $(srcdir)/hash-string.h
 explodename.$lo l10nflist.$lo: $(srcdir)/loadinfo.h
 dcigettext.$lo loadmsgcat.$lo $(PLURAL_OBJECT) plural-exp.$lo: $(srcdir)/plural-exp.h
index 5637fda02de6b5c9a130c78216abc2e9bc5de6a5..4b715156ba60f7265d53db6f10514c9543e146cc 100644 (file)
@@ -437,6 +437,30 @@ extern int vswprintf (wchar_t *, size_t, const wchar_t *, va_list);
 #endif
 
 
+/* Support for retrieving the name of a locale_t object.  */
+#if @HAVE_NAMELESS_LOCALES@
+
+#ifndef GNULIB_defined_newlocale /* don't override gnulib */
+#undef newlocale
+#define newlocale libintl_newlocale
+extern locale_t newlocale (int, const char *, locale_t);
+#endif
+
+#ifndef GNULIB_defined_duplocale /* don't override gnulib */
+#undef duplocale
+#define duplocale libintl_duplocale
+extern locale_t duplocale (locale_t);
+#endif
+
+#ifndef GNULIB_defined_freelocale /* don't override gnulib */
+#undef freelocale
+#define freelocale libintl_freelocale
+extern void freelocale (locale_t);
+#endif
+
+#endif
+
+
 /* Support for the locale chosen by the user.  */
 #if (defined __APPLE__ && defined __MACH__) || defined _WIN32 || defined __CYGWIN__
 
diff --git a/gettext-runtime/intl/localename-table.c b/gettext-runtime/intl/localename-table.c
new file mode 100644 (file)
index 0000000..54fd67d
--- /dev/null
@@ -0,0 +1,48 @@
+/* Table that maps a locale object to the names of the locale categories.
+   Copyright (C) 2018 Free Software Foundation, Inc.
+
+   This program 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 program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU 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>, 2018.  */
+
+#include <config.h>
+
+#if HAVE_USELOCALE && HAVE_NAMELESS_LOCALES /* Solaris >= 11.4 */
+
+/* Specification.  */
+#include "localename-table.h"
+
+#include <stdint.h>
+
+/* A hash function for pointers.  */
+size_t _GL_ATTRIBUTE_CONST
+locale_hash_function (locale_t x)
+{
+  uintptr_t p = (uintptr_t) x;
+  size_t h = ((p % 4177) << 12) + ((p % 79) << 6) + (p % 61);
+  return h;
+}
+
+struct locale_hash_node * locale_hash_table[LOCALE_HASH_TABLE_SIZE]
+  /* = { NULL, ..., NULL } */;
+
+gl_rwlock_define_initialized(, locale_lock)
+
+#else
+
+/* This declaration is solely to ensure that after preprocessing
+   this file is never empty.  */
+typedef int dummy;
+
+#endif
diff --git a/gettext-runtime/intl/localename-table.h b/gettext-runtime/intl/localename-table.h
new file mode 100644 (file)
index 0000000..0d204d6
--- /dev/null
@@ -0,0 +1,73 @@
+/* Table that maps a locale object to the names of the locale categories.
+   Copyright (C) 2018 Free Software Foundation, Inc.
+
+   This program 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 program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU 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>, 2018.  */
+
+#if HAVE_USELOCALE && HAVE_NAMELESS_LOCALES /* Solaris >= 11.4 */
+
+# include <stddef.h>
+# include <locale.h>
+
+# ifdef IN_LIBINTL
+#  include "lock.h"
+# else
+#  include "glthread/lock.h"
+# endif
+
+struct locale_categories_names
+  {
+    /* Locale category -> name (allocated with indefinite extent).  */
+    const char *category_name[6];
+  };
+
+/* 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.
+   This hash table has global scope, so that when an application uses both
+   GNU libintl and gnulib, the application sees only one hash table.  (When
+   linking statically with libintl, the fact that localename-table.c is a
+   separate compilation unit resolves the duplicate symbol conflict.  When
+   linking with libintl as a shared library, we rely on ELF and the symbol
+   conflict resolution implemented in the ELF dynamic loader here.)
+   Both the libintl overrides and the gnulib overrides of the functions
+   newlocale, duplocale, freelocale see the same hash table (and the same lock).
+   For this reason, the internal layout of the hash table and the hash function
+   MUST NEVER CHANGE.  If you need to change the internal layout or the hash
+   function, introduce versioning by appending a version suffix to the symbols
+   at the linker level.  */
+# define locale_hash_function libintl_locale_hash_function
+# define locale_hash_table libintl_locale_hash_table
+# define locale_lock libintl_locale_lock
+
+extern size_t _GL_ATTRIBUTE_CONST locale_hash_function (locale_t x);
+
+/* 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
+extern struct locale_hash_node * locale_hash_table[LOCALE_HASH_TABLE_SIZE];
+
+/* This lock protects the locale_hash_table against multiple simultaneous
+   accesses (except that multiple simultaneous read accesses are allowed).  */
+
+gl_rwlock_define(extern, locale_lock)
+
+#endif
index f9f9cc9d84161ef9bdf131b0bed1e386c9128d04..04e6d42b34ac5ce583a62480b965243f68b5d202 100644 (file)
 /* Solaris >= 12.  */
 extern char * getlocalename_l(int, locale_t);
 # endif
+# if HAVE_NAMELESS_LOCALES
+#  include <errno.h>
+#  include "localename-table.h"
+# endif
 #endif
 
 #if HAVE_CFLOCALECOPYCURRENT || HAVE_CFPREFERENCESCOPYAPPVALUE
@@ -2615,7 +2619,7 @@ get_lcid (const char *locale_name)
 #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 @@ string_hash (const void *x)
    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 @@ static const char *
 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,386 @@ struniq (const char *string)
 #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.
+   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 (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 = locale_hash_function (locale);
+      size_t slot = hashcode % LOCALE_HASH_TABLE_SIZE;
+      /* If the locale was not found in the table, return "".  This can
+         happen if the application uses the original newlocale()/duplocale()
+         functions instead of the overridden ones.  */
+      const char *name = "";
+      struct locale_hash_node *p;
+      /* Lock while looking up the hash node.  */
+      gl_rwlock_rdlock (locale_lock);
+      for (p = locale_hash_table[slot]; p != NULL; p = p->next)
+        if (p->locale == locale)
+          {
+            name = p->names.category_name[category];
+            break;
+          }
+      gl_rwlock_unlock (locale_lock);
+      return name;
+    }
+}
+
+# if !(defined newlocale && defined duplocale && defined freelocale)
+#  error "newlocale, duplocale, freelocale not being replaced as expected!"
+# endif
+
+/* newlocale() override.  */
+locale_t
+newlocale (int category_mask, const char *name, locale_t base)
+#undef newlocale
+{
+  struct locale_categories_names names;
+  struct locale_hash_node *node;
+  locale_t result;
+
+  /* Make sure name has indefinite extent.  */
+  if (((LC_CTYPE_MASK | LC_NUMERIC_MASK | LC_TIME_MASK | LC_COLLATE_MASK
+        | LC_MONETARY_MASK | LC_MESSAGES_MASK)
+       & category_mask) != 0)
+    name = struniq (name);
+
+  /* Determine the category names of the result.  */
+  if (((LC_CTYPE_MASK | LC_NUMERIC_MASK | LC_TIME_MASK | LC_COLLATE_MASK
+        | LC_MONETARY_MASK | LC_MESSAGES_MASK)
+       & ~category_mask) == 0)
+    {
+      /* Use name, ignore base.  */
+      int category;
+
+      name = struniq (name);
+      for (category = 0; category < 6; category++)
+        names.category_name[category] = name;
+    }
+  else
+    {
+      /* Use base, possibly also name.  */
+      if (base == NULL)
+        {
+          int category;
+
+          for (category = 0; category < 6; category++)
+            {
+              int mask;
+
+              switch (category)
+                {
+                case LC_CTYPE:
+                  mask = LC_CTYPE_MASK;
+                  break;
+                case LC_NUMERIC:
+                  mask = LC_NUMERIC_MASK;
+                  break;
+                case LC_TIME:
+                  mask = LC_TIME_MASK;
+                  break;
+                case LC_COLLATE:
+                  mask = LC_COLLATE_MASK;
+                  break;
+                case LC_MONETARY:
+                  mask = LC_MONETARY_MASK;
+                  break;
+                case LC_MESSAGES:
+                  mask = LC_MESSAGES_MASK;
+                  break;
+                default:
+                  abort ();
+                }
+              names.category_name[category] =
+                ((mask & category_mask) != 0 ? name : "C");
+            }
+        }
+      else if (base == LC_GLOBAL_LOCALE)
+        {
+          int category;
+
+          for (category = 0; category < 6; category++)
+            {
+              int mask;
+
+              switch (category)
+                {
+                case LC_CTYPE:
+                  mask = LC_CTYPE_MASK;
+                  break;
+                case LC_NUMERIC:
+                  mask = LC_NUMERIC_MASK;
+                  break;
+                case LC_TIME:
+                  mask = LC_TIME_MASK;
+                  break;
+                case LC_COLLATE:
+                  mask = LC_COLLATE_MASK;
+                  break;
+                case LC_MONETARY:
+                  mask = LC_MONETARY_MASK;
+                  break;
+                case LC_MESSAGES:
+                  mask = LC_MESSAGES_MASK;
+                  break;
+                default:
+                  abort ();
+                }
+              names.category_name[category] =
+                ((mask & category_mask) != 0
+                 ? name
+                 : get_locale_t_name (category, LC_GLOBAL_LOCALE));
+            }
+        }
+      else
+        {
+          /* Look up the names of base in the hash table.  Like multiple calls
+             of get_locale_t_name, but locking only once.  */
+          struct locale_hash_node *p;
+          int category;
+
+          /* Lock while looking up the hash node.  */
+          gl_rwlock_rdlock (locale_lock);
+          for (p = locale_hash_table[locale_hash_function (base) % LOCALE_HASH_TABLE_SIZE];
+               p != NULL;
+               p = p->next)
+            if (p->locale == base)
+              break;
+
+          for (category = 0; category < 6; category++)
+            {
+              int mask;
+
+              switch (category)
+                {
+                case LC_CTYPE:
+                  mask = LC_CTYPE_MASK;
+                  break;
+                case LC_NUMERIC:
+                  mask = LC_NUMERIC_MASK;
+                  break;
+                case LC_TIME:
+                  mask = LC_TIME_MASK;
+                  break;
+                case LC_COLLATE:
+                  mask = LC_COLLATE_MASK;
+                  break;
+                case LC_MONETARY:
+                  mask = LC_MONETARY_MASK;
+                  break;
+                case LC_MESSAGES:
+                  mask = LC_MESSAGES_MASK;
+                  break;
+                default:
+                  abort ();
+                }
+              names.category_name[category] =
+                ((mask & category_mask) != 0
+                 ? name
+                 : (p != NULL ? p->names.category_name[category] : ""));
+            }
+
+          gl_rwlock_unlock (locale_lock);
+        }
+    }
+
+  node = (struct locale_hash_node *) malloc (sizeof (struct locale_hash_node));
+  if (node == NULL)
+    /* errno is set to ENOMEM.  */
+    return NULL;
+
+  result = newlocale (category_mask, name, base);
+  if (result == NULL)
+    {
+      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 = 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)
+    {
+      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[locale_hash_function (locale) % LOCALE_HASH_TABLE_SIZE];
+           p != NULL;
+           p = p->next)
+        if (p->locale == locale)
+          break;
+      if (p != NULL)
+        node->names = p->names;
+      else
+        {
+          /* This can happen if the application uses the original
+             newlocale()/duplocale() functions instead of the overridden
+             ones.  */
+          int category;
+
+          for (category = 0; category < 6; category++)
+            node->names.category_name[category] = "";
+        }
+    }
+
+  /* Insert it in the hash table.  */
+  {
+    size_t hashcode = locale_hash_function (result);
+    size_t slot = hashcode % LOCALE_HASH_TABLE_SIZE;
+    struct locale_hash_node *p;
+
+    for (p = locale_hash_table[slot]; p != NULL; p = p->next)
+      if (p->locale == result)
+        {
+          /* This can happen if the application uses the original freelocale()
+             function instead of the overridden one.  */
+          p->names = node->names;
+          break;
+        }
+    if (p == NULL)
+      {
+        node->next = locale_hash_table[slot];
+        locale_hash_table[slot] = node;
+      }
+
+    gl_rwlock_unlock (locale_lock);
+
+    if (p != NULL)
+      free (node);
+  }
+
+  return result;
+}
+
+/* freelocale() override.  */
+void
+freelocale (locale_t locale)
+#undef freelocale
+{
+  if (locale == NULL || locale == LC_GLOBAL_LOCALE)
+    /* Invalid argument.  */
+    abort ();
+
+  {
+    size_t hashcode = locale_hash_function (locale);
+    size_t slot = hashcode % LOCALE_HASH_TABLE_SIZE;
+    struct locale_hash_node *found;
+    struct locale_hash_node **p;
+
+    found = NULL;
+    /* Lock while removing the hash node.  */
+    gl_rwlock_wrlock (locale_lock);
+    for (p = &locale_hash_table[slot]; *p != NULL; p = &(*p)->next)
+      if ((*p)->locale == locale)
+        {
+          found = *p;
+          *p = (*p)->next;
+          break;
+        }
+    gl_rwlock_unlock (locale_lock);
+    free (found);
+  }
+
+  freelocale (locale);
+}
+
+#endif
+
+
 #if defined IN_LIBINTL || HAVE_USELOCALE
 
 /* Like gl_locale_name_thread, except that the result is not in storage of
@@ -2761,6 +3145,8 @@ gl_locale_name_thread_unsafe (int category, const char *categoryname)
 #   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
index 1544a7c4613f46ff764649f51624ca376eacef89..bbb4e66267a94e6fd0d433b39578d7d5a63c8d7d 100644 (file)
@@ -15,6 +15,7 @@ intdiv0.m4 \
 intl.m4 \
 intldir.m4 \
 intlmacosx.m4 \
+intlsolaris.m4 \
 intmax.m4 \
 inttypes-pri.m4 \
 inttypes_h.m4 \
index 0cc18d9ea1c1284c41ce616520e3cc49d4129680..7efc7e8552767d93660cacb09f23e02ac39f7c3d 100644 (file)
@@ -1,4 +1,4 @@
-# intl.m4 serial 32 (gettext-0.19.9)
+# intl.m4 serial 33 (gettext-0.19.9)
 dnl Copyright (C) 1995-2014, 2016-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,
@@ -232,14 +232,17 @@ AC_DEFUN([gt_INTL_SUBDIR_CORE],
 
   AC_CHECK_HEADERS([argz.h inttypes.h limits.h unistd.h sys/param.h])
   AC_CHECK_FUNCS([getcwd getegid geteuid getgid getuid mempcpy munmap \
-    stpcpy strcasecmp strdup strtoul tsearch uselocale argz_count \
-    argz_stringify argz_next __fsetlocking])
+    stpcpy strcasecmp strdup strtoul tsearch argz_count argz_stringify \
+    argz_next __fsetlocking])
 
-  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])
+  dnl For Solaris 11.4 and 12.
+  gt_INTL_SOLARIS
+  if test $gt_nameless_locales = yes; then
+    HAVE_NAMELESS_LOCALES=1
+  else
+    HAVE_NAMELESS_LOCALES=0
   fi
+  AC_SUBST([HAVE_NAMELESS_LOCALES])
 
   dnl Use the *_unlocked functions only if they are declared.
   dnl (because some of them were defined without being declared in Solaris
diff --git a/gettext-runtime/m4/intlsolaris.m4 b/gettext-runtime/m4/intlsolaris.m4
new file mode 100644 (file)
index 0000000..6d3ade4
--- /dev/null
@@ -0,0 +1,65 @@
+# intlsolaris.m4 serial 2
+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],
+[
+  AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+
+  dnl Persuade Solaris <locale.h> to define 'locale_t'.
+  AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS])
+
+  AC_CHECK_FUNCS_ONCE([uselocale])
+
+  gt_nameless_locales=no
+  if test $ac_cv_func_uselocale = yes; then
+    AC_CACHE_CHECK([for Solaris 11.4 locale system],
+      [gt_cv_locale_solaris114],
+      [case "$host_os" in
+         solaris*)
+           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])
+           ;;
+         *) gt_cv_locale_solaris114=no ;;
+       esac
+      ])
+  fi
+  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
+
+  dnl Solaris 12 will maybe provide getlocalename_l.  If it does, it will
+  dnl simplify the implementation of gl_locale_name_thread().  But the overrides
+  dnl of newlocale, duplocale, freelocale will still be necessary, in order to
+  dnl keep the libintl_locale_hash_table up-to-date, which may be used by
+  dnl libintl or gnulib code that was compiled on Solaris 11.4, before
+  dnl getlocalename_l was introduced.
+  if test $ac_cv_func_uselocale = yes; then
+    AC_CHECK_FUNCS([getlocalename_l])
+  fi
+])
index 5409cd59c06cf42eff7583c05b2e1712b0c67e1f..54a870b18cf456c4283514d7d0bad1192b89ef13 100644 (file)
@@ -8043,12 +8043,13 @@ If you do not have an @file{aclocal.m4} file in your distribution,
 the simplest is to concatenate the files @file{codeset.m4}, @file{fcntl-o.m4},
 @file{gettext.m4}, @file{glibc2.m4}, @file{glibc21.m4}, @file{iconv.m4},
 @file{intdiv0.m4}, @file{intl.m4}, @file{intldir.m4}, @file{intlmacosx.m4},
-@file{intmax.m4}, @file{inttypes_h.m4}, @file{inttypes-pri.m4},
-@file{lcmessage.m4}, @file{lib-ld.m4}, @file{lib-link.m4},
-@file{lib-prefix.m4}, @file{lock.m4}, @file{longlong.m4}, @file{nls.m4},
-@file{po.m4}, @file{printf-posix.m4}, @file{progtest.m4}, @file{size_max.m4},
-@file{stdint_h.m4}, @file{threadlib.m4}, @file{uintmax_t.m4},
-@file{visibility.m4}, @file{wchar_t.m4}, @file{wint_t.m4}, @file{xsize.m4}
+@file{intlsolaris.m4}, @file{intmax.m4}, @file{inttypes_h.m4},
+@file{inttypes-pri.m4}, @file{lcmessage.m4}, @file{lib-ld.m4},
+@file{lib-link.m4}, @file{lib-prefix.m4}, @file{lock.m4}, @file{longlong.m4},
+@file{nls.m4}, @file{po.m4}, @file{printf-posix.m4}, @file{progtest.m4},
+@file{size_max.m4}, @file{stdint_h.m4}, @file{threadlib.m4},
+@file{uintmax_t.m4}, @file{visibility.m4}, @file{wchar_t.m4}, @file{wint_t.m4},
+@file{xsize.m4}
 from GNU @code{gettext}'s
 @file{m4/} directory into a single file.  If you have suppressed the
 @file{intl/} directory, only @file{gettext.m4}, @file{intlmacosx.m4},
index db732c9e68f45a9061724c1b34859724a37e613b..3a5d9e993cc38a28b9e66d7de8c345b265e7c0c4 100644 (file)
@@ -18,6 +18,7 @@ aclocal_DATA = \
   ../../gettext-runtime/m4/intl.m4 \
   ../../gettext-runtime/m4/intldir.m4 \
   ../../gettext-runtime/m4/intlmacosx.m4 \
+  ../../gettext-runtime/m4/intlsolaris.m4 \
   ../../gettext-runtime/m4/intmax.m4 \
   ../../gettext-runtime/m4/inttypes_h.m4 \
   ../../gettext-runtime/m4/inttypes-pri.m4 \
index fe7b405b04627be995bf040cf7f5e990ae9b1197..b2e8127dd1c4b17888ba1d566705e4395c217cde 100644 (file)
@@ -847,6 +847,7 @@ if test -n "$intldir" || test -z "$have_automake19"; then
     intdiv0.m4
     intl.m4
     intldir.m4
+    intlsolaris.m4
     intmax.m4
     inttypes_h.m4
     inttypes-pri.m4