From: Bruno Haible Date: Mon, 22 Oct 2018 23:02:02 +0000 (+0200) Subject: intl: Add support for per-thread locales on Solaris 11.4. X-Git-Tag: v0.20~309 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=87f97355050c19391466406e9a820e36b3ebdcae;p=thirdparty%2Fgettext.git intl: Add support for per-thread locales on Solaris 11.4. 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. --- diff --git a/Makefile.am b/Makefile.am index 43007f230..8260e9aa5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 c4a8cbfe5..aee71951a 100644 --- 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 on native Windows and NetBSD are now POSIX compliant. There is no conflict any more between these replacements diff --git a/PACKAGING b/PACKAGING index 71ce2452f..4fbf4424e 100644 --- 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 diff --git a/gettext-runtime/intl/Makefile.in b/gettext-runtime/intl/Makefile.in index 51632f693..5efdb4791 100644 --- a/gettext-runtime/intl/Makefile.in +++ b/gettext-runtime/intl/Makefile.in @@ -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 diff --git a/gettext-runtime/intl/libgnuintl.in.h b/gettext-runtime/intl/libgnuintl.in.h index 5637fda02..4b715156b 100644 --- a/gettext-runtime/intl/libgnuintl.in.h +++ b/gettext-runtime/intl/libgnuintl.in.h @@ -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 index 000000000..54fd67d08 --- /dev/null +++ b/gettext-runtime/intl/localename-table.c @@ -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 . */ + +/* Written by Bruno Haible , 2018. */ + +#include + +#if HAVE_USELOCALE && HAVE_NAMELESS_LOCALES /* Solaris >= 11.4 */ + +/* Specification. */ +#include "localename-table.h" + +#include + +/* 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 index 000000000..0d204d6d1 --- /dev/null +++ b/gettext-runtime/intl/localename-table.h @@ -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 . */ + +/* Written by Bruno Haible , 2018. */ + +#if HAVE_USELOCALE && HAVE_NAMELESS_LOCALES /* Solaris >= 11.4 */ + +# include +# include + +# 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 diff --git a/gettext-runtime/intl/localename.c b/gettext-runtime/intl/localename.c index f9f9cc9d8..04e6d42b3 100644 --- a/gettext-runtime/intl/localename.c +++ b/gettext-runtime/intl/localename.c @@ -50,6 +50,10 @@ /* Solaris >= 12. */ extern char * getlocalename_l(int, locale_t); # endif +# if HAVE_NAMELESS_LOCALES +# include +# 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 diff --git a/gettext-runtime/m4/Makefile.am b/gettext-runtime/m4/Makefile.am index 1544a7c46..bbb4e6626 100644 --- a/gettext-runtime/m4/Makefile.am +++ b/gettext-runtime/m4/Makefile.am @@ -15,6 +15,7 @@ intdiv0.m4 \ intl.m4 \ intldir.m4 \ intlmacosx.m4 \ +intlsolaris.m4 \ intmax.m4 \ inttypes-pri.m4 \ inttypes_h.m4 \ diff --git a/gettext-runtime/m4/intl.m4 b/gettext-runtime/m4/intl.m4 index 0cc18d9ea..7efc7e855 100644 --- a/gettext-runtime/m4/intl.m4 +++ b/gettext-runtime/m4/intl.m4 @@ -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 index 000000000..6d3ade465 --- /dev/null +++ b/gettext-runtime/m4/intlsolaris.m4 @@ -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 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 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 + 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 +]) diff --git a/gettext-tools/doc/gettext.texi b/gettext-tools/doc/gettext.texi index 5409cd59c..54a870b18 100644 --- a/gettext-tools/doc/gettext.texi +++ b/gettext-tools/doc/gettext.texi @@ -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}, diff --git a/gettext-tools/m4/Makefile.am b/gettext-tools/m4/Makefile.am index db732c9e6..3a5d9e993 100644 --- a/gettext-tools/m4/Makefile.am +++ b/gettext-tools/m4/Makefile.am @@ -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 \ diff --git a/gettext-tools/misc/gettextize.in b/gettext-tools/misc/gettextize.in index fe7b405b0..b2e8127dd 100644 --- a/gettext-tools/misc/gettextize.in +++ b/gettext-tools/misc/gettextize.in @@ -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