diff options
Diffstat (limited to 'lib/localename.c')
| -rw-r--r-- | lib/localename.c | 693 | 
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";        } | 
