summaryrefslogtreecommitdiff
path: root/lib/localename.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/localename.c')
-rw-r--r--lib/localename.c693
1 files changed, 563 insertions, 130 deletions
diff --git a/lib/localename.c b/lib/localename.c
index 7432978d..446fc033 100644
--- a/lib/localename.c
+++ b/lib/localename.c
@@ -1,20 +1,11 @@
/* Determine name of the currently selected locale.
- Copyright (C) 1995-2018 Free Software Foundation, Inc.
+ Copyright (C) 1995-2022 Free Software Foundation, Inc.
- This program is free software: you can redistribute it and/or
- modify it under the terms of either:
+ 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.
- * the GNU Lesser General Public License as published by the Free
- Software Foundation; either version 3 of the License, or (at your
- option) any later version.
-
- or
-
- * the GNU General Public License as published by the Free
- Software Foundation; either version 2 of the License, or (at your
- option) any later version.
-
- or both in parallel, as here.
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
@@ -30,21 +21,20 @@
#include <config.h>
/* Specification. */
-#ifdef IN_LIBINTL
-# include "gettextP.h"
-#else
-# include "localename.h"
-#endif
+#include "localename.h"
#include <limits.h>
+#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include <locale.h>
#include <string.h>
#include "flexmember.h"
+#include "setlocale_null.h"
+#include "thread-optim.h"
-#if HAVE_USELOCALE
+#if HAVE_GOOD_USELOCALE
/* Mac OS X 10.5 defines the locale_t type in <xlocale.h>. */
# if defined __APPLE__ && defined __MACH__
# include <xlocale.h>
@@ -52,29 +42,28 @@
# if (__GLIBC__ >= 2 && !defined __UCLIBC__) || (defined __linux__ && HAVE_LANGINFO_H) || defined __CYGWIN__
# include <langinfo.h>
# endif
-# if !defined IN_LIBINTL
-# include "glthread/lock.h"
-# endif
-# if defined __sun && HAVE_GETLOCALENAME_L
+# include "glthread/lock.h"
+# if defined __sun
+# if HAVE_GETLOCALENAME_L
/* Solaris >= 12. */
extern char * getlocalename_l(int, locale_t);
+# elif HAVE_SOLARIS114_LOCALES
+# include <sys/localedef.h>
+# endif
+# endif
+# if HAVE_NAMELESS_LOCALES
+# include "localename-table.h"
# endif
#endif
-#if HAVE_CFLOCALECOPYCURRENT || HAVE_CFPREFERENCESCOPYAPPVALUE
+#if HAVE_CFPREFERENCESCOPYAPPVALUE
# include <CoreFoundation/CFString.h>
-# if HAVE_CFLOCALECOPYCURRENT
-# include <CoreFoundation/CFLocale.h>
-# elif HAVE_CFPREFERENCESCOPYAPPVALUE
-# include <CoreFoundation/CFPreferences.h>
-# endif
+# include <CoreFoundation/CFPreferences.h>
#endif
#if defined _WIN32 && !defined __CYGWIN__
# define WINDOWS_NATIVE
-# if !defined IN_LIBINTL
-# include "glthread/lock.h"
-# endif
+# include "glthread/lock.h"
#endif
#if defined WINDOWS_NATIVE || defined __CYGWIN__ /* Native Windows or Cygwin */
@@ -1148,11 +1137,20 @@ extern char * getlocalename_l(int, locale_t);
# ifndef LOCALE_NAME_MAX_LENGTH
# define LOCALE_NAME_MAX_LENGTH 85
# endif
+/* Don't assume that UNICODE is not defined. */
+# undef GetLocaleInfo
+# define GetLocaleInfo GetLocaleInfoA
+# undef EnumSystemLocales
+# define EnumSystemLocales EnumSystemLocalesA
#endif
+/* We want to use the system's setlocale() function here, not the gnulib
+ override. */
+#undef setlocale
-#if HAVE_CFLOCALECOPYCURRENT || HAVE_CFPREFERENCESCOPYAPPVALUE
-/* Mac OS X 10.2 or newer */
+
+#if HAVE_CFPREFERENCESCOPYAPPVALUE
+/* Mac OS X 10.4 or newer */
/* Canonicalize a Mac OS X locale name to a Unix locale name.
NAME is a sufficiently large buffer.
@@ -1317,22 +1315,44 @@ gl_locale_name_canonicalize (char *name)
/* Mac OS X has "az-Arab", "az-Cyrl", "az-Latn".
The default script for az on Unix is Latin. */
{ "az-Latn", "az" },
+ /* Mac OS X has "bs-Cyrl", "bs-Latn".
+ The default script for bs on Unix is Latin. */
+ { "bs-Latn", "bs" },
/* Mac OS X has "ga-dots". Does not yet exist on Unix. */
{ "ga-dots", "ga" },
- /* Mac OS X has "kk-Cyrl". Does not yet exist on Unix. */
+ /* Mac OS X has "kk-Cyrl".
+ The default script for kk on Unix is Cyrillic. */
+ { "kk-Cyrl", "kk" },
/* Mac OS X has "mn-Cyrl", "mn-Mong".
The default script for mn on Unix is Cyrillic. */
{ "mn-Cyrl", "mn" },
/* Mac OS X has "ms-Arab", "ms-Latn".
The default script for ms on Unix is Latin. */
{ "ms-Latn", "ms" },
+ /* Mac OS X has "pa-Arab", "pa-Guru".
+ Country codes are used to distinguish these on Unix. */
+ { "pa-Arab", "pa_PK" },
+ { "pa-Guru", "pa_IN" },
+ /* Mac OS X has "shi-Latn", "shi-Tfng". Does not yet exist on Unix. */
+ /* Mac OS X has "sr-Cyrl", "sr-Latn".
+ The default script for sr on Unix is Cyrillic. */
+ { "sr-Cyrl", "sr" },
/* Mac OS X has "tg-Cyrl".
The default script for tg on Unix is Cyrillic. */
{ "tg-Cyrl", "tg" },
- /* Mac OS X has "tk-Cyrl". Does not yet exist on Unix. */
+ /* Mac OS X has "tk-Cyrl".
+ The default script for tk on Unix is Cyrillic. */
+ { "tk-Cyrl", "tk" },
/* Mac OS X has "tt-Cyrl".
The default script for tt on Unix is Cyrillic. */
{ "tt-Cyrl", "tt" },
+ /* Mac OS X has "uz-Arab", "uz-Cyrl", "uz-Latn".
+ The default script for uz on Unix is Latin. */
+ { "uz-Latn", "uz" },
+ /* Mac OS X has "vai-Latn", "vai-Vaii". Does not yet exist on Unix. */
+ /* Mac OS X has "yue-Hans", "yue-Hant".
+ The default script for yue on Unix is Simplified Han. */
+ { "yue-Hans", "yue" },
/* Mac OS X has "zh-Hans", "zh-Hant".
Country codes are used to distinguish these on Unix. */
{ "zh-Hans", "zh_CN" },
@@ -1346,6 +1366,7 @@ gl_locale_name_canonicalize (char *name)
static const script_entry script_table[] = {
{ "Arab", "arabic" },
{ "Cyrl", "cyrillic" },
+ { "Latn", "latin" },
{ "Mong", "mongolian" }
};
@@ -2268,8 +2289,8 @@ gl_locale_name_from_win32_LANGID (LANGID langid)
}
return "wen";
case LANG_SOTHO:
- /* <https://msdn.microsoft.com/en-us/library/dd318693.aspx> calls
- it "Sesotho sa Leboa"; according to
+ /* <https://docs.microsoft.com/en-us/windows/desktop/Intl/language-identifier-constants-and-strings>
+ calls it "Sesotho sa Leboa"; according to
<https://www.ethnologue.com/show_language.asp?code=nso>
<https://www.ethnologue.com/show_language.asp?code=sot>
it's the same as Northern Sotho. */
@@ -2535,7 +2556,7 @@ static char lname[LC_MAX * (LOCALE_NAME_MAX_LENGTH + 1) + 1];
/* Callback function for EnumLocales. */
static BOOL CALLBACK
-enum_locales_fn (LPTSTR locale_num_str)
+enum_locales_fn (LPSTR locale_num_str)
{
char *endp;
char locval[2 * LOCALE_NAME_MAX_LENGTH + 1 + 1];
@@ -2601,7 +2622,8 @@ get_lcid (const char *locale_name)
#endif
-#if HAVE_USELOCALE /* glibc, Mac OS X, Solaris 11 OpenIndiana, or Solaris 12 */
+#if HAVE_GOOD_USELOCALE /* glibc, Mac OS X, FreeBSD >= 9.1, Cygwin >= 2.6,
+ 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. */
@@ -2627,14 +2649,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
@@ -2647,46 +2669,425 @@ 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";
memcpy (new_node->contents, string, size);
- /* Lock while inserting new_node. */
- gl_lock_lock (struniq_lock);
- /* Check whether another thread already added the string while we were
- waiting on the lock. */
- for (p = struniq_hash_table[slot]; p != NULL; p = p->next)
- if (strcmp (p->contents, string) == 0)
+ {
+ bool mt = gl_multithreaded ();
+ /* Lock while inserting new_node. */
+ if (mt) gl_lock_lock (struniq_lock);
+ /* Check whether another thread already added the string while we were
+ waiting on the lock. */
+ for (p = struniq_hash_table[slot]; p != NULL; p = p->next)
+ if (strcmp (p->contents, string) == 0)
+ {
+ free (new_node);
+ new_node = p;
+ goto done;
+ }
+ /* Really insert new_node into the hash table. Fill new_node entirely
+ first, because other threads may be iterating over the linked list. */
+ new_node->next = struniq_hash_table[slot];
+ struniq_hash_table[slot] = new_node;
+ done:
+ /* Unlock after new_node is inserted. */
+ if (mt) gl_lock_unlock (struniq_lock);
+ }
+ return new_node->contents;
+}
+
+#endif
+
+
+#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 (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
+ {
+ /* 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)
+ {
+ 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)
{
- free (new_node);
- new_node = p;
- goto done;
+ node->next = locale_hash_table[slot];
+ locale_hash_table[slot] = node;
}
- /* Really insert new_node into the hash table. Fill new_node entirely first,
- because other threads may be iterating over the linked list. */
- new_node->next = struniq_hash_table[slot];
- struniq_hash_table[slot] = new_node;
- done:
- /* Unlock after new_node is inserted. */
- gl_lock_unlock (struniq_lock);
- return new_node->contents;
+
+ 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 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
+#if defined IN_LIBINTL || HAVE_GOOD_USELOCALE
/* Like gl_locale_name_thread, except that the result is not in storage of
indefinite extent. */
@@ -2694,9 +3095,9 @@ struniq (const char *string)
static
# endif
const char *
-gl_locale_name_thread_unsafe (int category, const char *categoryname)
+gl_locale_name_thread_unsafe (int category, _GL_UNUSED const char *categoryname)
{
-# if HAVE_USELOCALE
+# if HAVE_GOOD_USELOCALE
{
locale_t thread_locale = uselocale (NULL);
if (thread_locale != LC_GLOBAL_LOCALE)
@@ -2747,6 +3148,24 @@ gl_locale_name_thread_unsafe (int category, const char *categoryname)
# if HAVE_GETLOCALENAME_L
/* Solaris >= 12. */
return getlocalename_l (category, thread_locale);
+# elif HAVE_SOLARIS114_LOCALES
+ /* Solaris >= 11.4. */
+ void *lcp = (*thread_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 "";
+ }
+# elif HAVE_NAMELESS_LOCALES
+ return get_locale_t_name (category, thread_locale);
# else
/* Solaris 11 OpenIndiana.
For the internal structure of locale objects, see
@@ -2764,6 +3183,8 @@ gl_locale_name_thread_unsafe (int category, const char *categoryname)
return "";
}
# endif
+# elif defined _AIX && HAVE_NAMELESS_LOCALES
+ return get_locale_t_name (category, thread_locale);
# elif defined __CYGWIN__
/* Cygwin < 2.6 lacks uselocale and thread-local locales altogether.
Cygwin <= 2.6.1 lacks NL_LOCALE_NAME, requiring peeking inside
@@ -2789,9 +3210,9 @@ gl_locale_name_thread_unsafe (int category, const char *categoryname)
#endif
const char *
-gl_locale_name_thread (int category, const char *categoryname)
+gl_locale_name_thread (int category, _GL_UNUSED const char *categoryname)
{
-#if HAVE_USELOCALE
+#if HAVE_GOOD_USELOCALE
const char *name = gl_locale_name_thread_unsafe (category, categoryname);
if (name != NULL)
return struniq (name);
@@ -2813,58 +3234,72 @@ gl_locale_name_thread (int category, const char *categoryname)
#endif
const char *
-gl_locale_name_posix (int category, const char *categoryname)
+gl_locale_name_posix (int category, _GL_UNUSED const char *categoryname)
{
#if defined WINDOWS_NATIVE
if (LC_MIN <= category && category <= LC_MAX)
{
- char *locname = setlocale (category, NULL);
- LCID lcid = 0;
-
- /* If CATEGORY is LC_ALL, the result might be a semi-colon
- separated list of locales. We need only one, so we take the
- one corresponding to LC_CTYPE, as the most important for
- character translations. */
- if (category == LC_ALL && strchr (locname, ';'))
- locname = setlocale (LC_CTYPE, NULL);
+ const char *locname =
+ /* setlocale_null (category) is identical to setlocale (category, NULL)
+ on this platform. */
+ setlocale (category, NULL);
/* Convert locale name to LCID. We don't want to use
LocaleNameToLCID because (a) it is only available since Vista,
and (b) it doesn't accept locale names returned by 'setlocale'. */
- lcid = get_lcid (locname);
+ LCID lcid = get_lcid (locname);
if (lcid > 0)
return gl_locale_name_from_win32_LCID (lcid);
}
#endif
- /* Use the POSIX methods of looking to 'LC_ALL', 'LC_xxx', and 'LANG'.
- On some systems this can be done by the 'setlocale' function itself. */
-#if defined HAVE_SETLOCALE && defined HAVE_LC_MESSAGES && defined HAVE_LOCALE_NULL
- return setlocale (category, NULL);
+ {
+ const char *locname;
+
+ /* Use the POSIX methods of looking to 'LC_ALL', 'LC_xxx', and 'LANG'.
+ On some systems this can be done by the 'setlocale' function itself. */
+#if defined HAVE_LC_MESSAGES && defined HAVE_LOCALE_NULL
+ locname = setlocale_null (category);
#else
- /* On other systems we ignore what setlocale reports and instead look at the
- environment variables directly. This is necessary
- 1. on systems which have a facility for customizing the default locale
- (Mac OS X, native Windows, Cygwin) and where the system's setlocale()
- function ignores this default locale (Mac OS X, Cygwin), in two cases:
- a. when the user missed to use the setlocale() override from libintl
- (for example by not including <libintl.h>),
- b. when setlocale supports only the "C" locale, such as on Cygwin
- 1.5.x. In this case even the override from libintl cannot help.
- 2. on all systems where setlocale supports only the "C" locale. */
- /* Strictly speaking, it is a POSIX violation to look at the environment
- variables regardless whether setlocale has been called or not. POSIX
- says:
- "For C-language programs, the POSIX locale shall be the
- default locale when the setlocale() function is not called."
- But we assume that all programs that use internationalized APIs call
- setlocale (LC_ALL, ""). */
- return gl_locale_name_environ (category, categoryname);
+ /* On other systems we ignore what setlocale reports and instead look at the
+ environment variables directly. This is necessary
+ 1. on systems which have a facility for customizing the default locale
+ (Mac OS X, native Windows, Cygwin) and where the system's setlocale()
+ function ignores this default locale (Mac OS X, Cygwin), in two cases:
+ a. when the user missed to use the setlocale() override from libintl
+ (for example by not including <libintl.h>),
+ b. when setlocale supports only the "C" locale, such as on Cygwin
+ 1.5.x. In this case even the override from libintl cannot help.
+ 2. on all systems where setlocale supports only the "C" locale. */
+ /* Strictly speaking, it is a POSIX violation to look at the environment
+ variables regardless whether setlocale has been called or not. POSIX
+ says:
+ "For C-language programs, the POSIX locale shall be the
+ default locale when the setlocale() function is not called."
+ But we assume that all programs that use internationalized APIs call
+ setlocale (LC_ALL, ""). */
+ locname = gl_locale_name_environ (category, categoryname);
+#endif
+ /* Convert the locale name from the format returned by setlocale() or found
+ in the environment variables to the XPG syntax. */
+#if defined WINDOWS_NATIVE
+ if (locname != NULL)
+ {
+ /* Convert locale name to LCID. We don't want to use
+ LocaleNameToLCID because (a) it is only available since Vista,
+ and (b) it doesn't accept locale names returned by 'setlocale'. */
+ LCID lcid = get_lcid (locname);
+
+ if (lcid > 0)
+ return gl_locale_name_from_win32_LCID (lcid);
+ }
#endif
+ return locname;
+ }
}
const char *
-gl_locale_name_environ (int category, const char *categoryname)
+gl_locale_name_environ (_GL_UNUSED int category, const char *categoryname)
{
const char *retval;
@@ -2880,7 +3315,7 @@ gl_locale_name_environ (int category, const char *categoryname)
retval = getenv ("LANG");
if (retval != NULL && retval[0] != '\0')
{
-#if HAVE_CFLOCALECOPYCURRENT || HAVE_CFPREFERENCESCOPYAPPVALUE
+#if HAVE_CFPREFERENCESCOPYAPPVALUE
/* Mac OS X 10.2 or newer.
Ignore invalid LANG value set by the Terminal application. */
if (strcmp (retval, "UTF-8") != 0)
@@ -2927,7 +3362,7 @@ gl_locale_name_default (void)
"C.UTF-8" locale, which operates in the same way as the "C" locale.
*/
-#if !(HAVE_CFLOCALECOPYCURRENT || HAVE_CFPREFERENCESCOPYAPPVALUE || defined WINDOWS_NATIVE || defined __CYGWIN__)
+#if !(HAVE_CFPREFERENCESCOPYAPPVALUE || defined WINDOWS_NATIVE || defined __CYGWIN__)
/* The system does not have a way of setting the locale, other than the
POSIX specified environment variables. We use C as default locale. */
@@ -2940,8 +3375,17 @@ gl_locale_name_default (void)
context, because message catalogs are not specific to a single
codeset. */
-# if HAVE_CFLOCALECOPYCURRENT || HAVE_CFPREFERENCESCOPYAPPVALUE
- /* Mac OS X 10.2 or newer */
+# if HAVE_CFPREFERENCESCOPYAPPVALUE
+ /* Mac OS X 10.4 or newer */
+ /* Don't use the API introduced in Mac OS X 10.5, CFLocaleCopyCurrent,
+ because in macOS 10.13.4 it has the following behaviour:
+ When two or more languages are specified in the
+ "System Preferences > Language & Region > Preferred Languages" panel,
+ it returns en_CC where CC is the territory (even when English is not among
+ the preferred languages!). What we want instead is what
+ CFLocaleCopyCurrent returned in earlier macOS releases and what
+ CFPreferencesCopyAppValue still returns, namely ll_CC where ll is the
+ first among the preferred languages and CC is the territory. */
{
/* Cache the locale name, since CoreFoundation calls are expensive. */
static const char *cached_localename;
@@ -2949,31 +3393,20 @@ gl_locale_name_default (void)
if (cached_localename == NULL)
{
char namebuf[256];
-# if HAVE_CFLOCALECOPYCURRENT /* Mac OS X 10.3 or newer */
- CFLocaleRef locale = CFLocaleCopyCurrent ();
- CFStringRef name = CFLocaleGetIdentifier (locale);
-
- if (CFStringGetCString (name, namebuf, sizeof (namebuf),
- kCFStringEncodingASCII))
- {
- gl_locale_name_canonicalize (namebuf);
- cached_localename = strdup (namebuf);
- }
- CFRelease (locale);
-# elif HAVE_CFPREFERENCESCOPYAPPVALUE /* Mac OS X 10.2 or newer */
CFTypeRef value =
CFPreferencesCopyAppValue (CFSTR ("AppleLocale"),
kCFPreferencesCurrentApplication);
- if (value != NULL
- && CFGetTypeID (value) == CFStringGetTypeID ()
- && CFStringGetCString ((CFStringRef)value,
- namebuf, sizeof (namebuf),
- kCFStringEncodingASCII))
+ if (value != NULL && CFGetTypeID (value) == CFStringGetTypeID ())
{
- gl_locale_name_canonicalize (namebuf);
- cached_localename = strdup (namebuf);
+ CFStringRef name = (CFStringRef)value;
+
+ if (CFStringGetCString (name, namebuf, sizeof (namebuf),
+ kCFStringEncodingASCII))
+ {
+ gl_locale_name_canonicalize (namebuf);
+ cached_localename = strdup (namebuf);
+ }
}
-# endif
if (cached_localename == NULL)
cached_localename = "C";
}