Imported Upstream version 0.19.7
[platform/upstream/gettext.git] / gettext-runtime / intl / localename.c
index 108dd6f..c27be86 100644 (file)
@@ -1,5 +1,5 @@
 /* Determine name of the currently selected locale.
-   Copyright (C) 1995-2012 Free Software Foundation, Inc.
+   Copyright (C) 1995-2015 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
 # if defined __APPLE__ && defined __MACH__
 #  include <xlocale.h>
 # endif
-# include <langinfo.h>
+# if __GLIBC__ >= 2 && !defined __UCLIBC__
+#  include <langinfo.h>
+# endif
 # if !defined IN_LIBINTL
 #  include "glthread/lock.h"
 # endif
+# if defined __sun && HAVE_GETLOCALENAME_L
+/* Solaris >= 12.  */
+extern char * getlocalename_l(int, locale_t);
+# endif
 #endif
 
 #if HAVE_CFLOCALECOPYCURRENT || HAVE_CFPREFERENCESCOPYAPPVALUE
 
 #if defined _WIN32 || defined __WIN32__
 # define WINDOWS_NATIVE
+# if !defined IN_LIBINTL
+#  include "glthread/lock.h"
+# endif
 #endif
 
 #if defined WINDOWS_NATIVE || defined __CYGWIN__ /* Native Windows or Cygwin */
 # define WIN32_LEAN_AND_MEAN
 # include <windows.h>
+# include <winnls.h>
 /* List of language codes, sorted by value:
    0x01 LANG_ARABIC
    0x02 LANG_BULGARIAN
 # ifndef LOCALE_SNAME
 # define LOCALE_SNAME 0x5c
 # endif
+# ifndef LOCALE_NAME_MAX_LENGTH
+# define LOCALE_NAME_MAX_LENGTH 85
+# endif
 #endif
 
 
@@ -2502,10 +2515,82 @@ gl_locale_name_from_win32_LCID (LCID lcid)
   return gl_locale_name_from_win32_LANGID (langid);
 }
 
+# ifdef WINDOWS_NATIVE
+
+/* Two variables to interface between get_lcid and the EnumLocales
+   callback function below.  */
+static LCID found_lcid;
+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)
+{
+  char *endp;
+  char locval[2 * LOCALE_NAME_MAX_LENGTH + 1 + 1];
+  LCID try_lcid = strtoul (locale_num_str, &endp, 16);
+
+  if (GetLocaleInfo (try_lcid, LOCALE_SENGLANGUAGE,
+                    locval, LOCALE_NAME_MAX_LENGTH))
+    {
+      strcat (locval, "_");
+      if (GetLocaleInfo (try_lcid, LOCALE_SENGCOUNTRY,
+                        locval + strlen (locval), LOCALE_NAME_MAX_LENGTH))
+       {
+         size_t locval_len = strlen (locval);
+
+         if (strncmp (locval, lname, locval_len) == 0
+             && (lname[locval_len] == '.'
+                 || lname[locval_len] == '\0'))
+           {
+             found_lcid = try_lcid;
+             return FALSE;
+           }
+       }
+    }
+  return TRUE;
+}
+
+/* This lock protects the get_lcid against multiple simultaneous calls.  */
+gl_lock_define_initialized(static, get_lcid_lock)
+
+/* Return the Locale ID (LCID) number given the locale's name, a
+   string, in LOCALE_NAME.  This works by enumerating all the locales
+   supported by the system, until we find one whose name matches
+   LOCALE_NAME.  */
+static LCID
+get_lcid (const char *locale_name)
+{
+  /* A simple cache.  */
+  static LCID last_lcid;
+  static char last_locale[1000];
+
+  /* Lock while looking for an LCID, to protect access to static
+     variables: last_lcid, last_locale, found_lcid, and lname.  */
+  gl_lock_lock (get_lcid_lock);
+  if (last_lcid > 0 && strcmp (locale_name, last_locale) == 0)
+    {
+      gl_lock_unlock (get_lcid_lock);
+      return last_lcid;
+    }
+  strncpy (lname, locale_name, sizeof (lname) - 1);
+  lname[sizeof (lname) - 1] = '\0';
+  found_lcid = 0;
+  EnumSystemLocales (enum_locales_fn, LCID_SUPPORTED);
+  if (found_lcid > 0)
+    {
+      last_lcid = found_lcid;
+      strcpy (last_locale, locale_name);
+    }
+  gl_lock_unlock (get_lcid_lock);
+  return found_lcid;
+}
+
+# endif
 #endif
 
 
-#if HAVE_USELOCALE /* glibc or Mac OS X */
+#if HAVE_USELOCALE /* glibc, Solaris >= 12 or Mac OS X */
 
 /* Simple hash set of strings.  We don't want to drag in lots of hash table
    code here.  */
@@ -2515,7 +2600,7 @@ gl_locale_name_from_win32_LCID (LCID lcid)
 /* A hash function for NUL-terminated char* strings using
    the method described by Bruno Haible.
    See http://www.haible.de/bruno/hashfunc.html.  */
-static size_t
+static size_t _GL_ATTRIBUTE_PURE
 string_hash (const void *x)
 {
   const char *s = (const char *) x;
@@ -2644,6 +2729,11 @@ gl_locale_name_thread_unsafe (int category, const char *categoryname)
             return "";
           }
         return querylocale (mask, thread_locale);
+#  elif defined __sun && HAVE_GETLOCALENAME_L
+        /* Solaris >= 12.  */
+        return getlocalename_l (category, thread_locale);
+#  elif defined __ANDROID__
+        return MB_CUR_MAX == 4 ? "C.UTF-8" : "C";
 #  endif
       }
   }
@@ -2660,6 +2750,27 @@ gl_locale_name_thread (int category, const char *categoryname)
   const char *name = gl_locale_name_thread_unsafe (category, categoryname);
   if (name != NULL)
     return struniq (name);
+#elif 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 (strchr (locname, ';'))
+       locname = setlocale (LC_CTYPE, 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);
+
+      if (lcid > 0)
+        return gl_locale_name_from_win32_LCID (lcid);
+    }
 #endif
   return NULL;
 }