changeset 12508:d015b5753e52

localename: Fix storage allocation of gl_locale_name_thread's result.
author Bruno Haible <bruno@clisp.org>
date Sat, 26 Dec 2009 14:30:21 +0100
parents 6d11047aa87b
children aa547f8201c1
files ChangeLog lib/localename.c modules/localename-tests tests/test-localename.c
diffstat 4 files changed, 214 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2009-12-26  Bruno Haible  <bruno@clisp.org>
+
+	localename: Fix storage allocation of gl_locale_name_thread's result.
+	* lib/localename.c (SIZE_BITS, string_hash, struct hash_node,
+	HASH_TABLE_SIZE, struniq_hash_table, struniq_lock, struniq): Define on
+	all platforms that have 'uselocale'.
+	(gl_locale_name_thread_unsafe): New function, extracted from
+	gl_locale_name_thread.
+	(gl_locale_name_thread): Call struniq on all platforms that have
+	'uselocale'.
+	* tests/test-localename.c (test_locale_name_thread): Check that the
+	resulting strings are permanently allocated.
+	* modules/localename-tests (Depends-on): Add strdup.
+
 2009-12-26  Bruno Haible  <bruno@clisp.org>
 
 	* tests/test-localename.c (categories): Fill in the strings.
--- a/lib/localename.c
+++ b/lib/localename.c
@@ -2507,7 +2507,7 @@
 #endif
 
 
-#if defined __APPLE__ && defined __MACH__ && HAVE_USELOCALE /* MacOS X */
+#if HAVE_USELOCALE /* glibc or MacOS X */
 
 /* Simple hash set of strings.  We don't want to drag in lots of hash table
    code here.  */
@@ -2592,8 +2592,13 @@
 #endif
 
 
+/* Like gl_locale_name_thread, except that the result is not in storage of
+   indefinite extent.  */
+#if !defined IN_LIBINTL
+static
+#endif
 const char *
-gl_locale_name_thread (int category, const char *categoryname)
+gl_locale_name_thread_unsafe (int category, const char *categoryname)
 {
 #if HAVE_USELOCALE
   {
@@ -2686,26 +2691,26 @@
         switch (category)
           {
           case LC_CTYPE:
-            return struniq (tlp->__lc_ctype->__ctype_encoding);
+            return tlp->__lc_ctype->__ctype_encoding;
           case LC_NUMERIC:
             return tlp->_numeric_using_locale
-                   ? struniq (tlp->__lc_numeric->_numeric_locale_buf)
+                   ? tlp->__lc_numeric->_numeric_locale_buf
                    : "C";
           case LC_TIME:
             return tlp->_time_using_locale
-                   ? struniq (tlp->__lc_time->_time_locale_buf)
+                   ? tlp->__lc_time->_time_locale_buf
                    : "C";
           case LC_COLLATE:
             return !tlp->__collate_load_error
-                   ? struniq (tlp->__lc_collate->__encoding)
+                   ? tlp->__lc_collate->__encoding
                    : "C";
           case LC_MONETARY:
             return tlp->_monetary_using_locale
-                   ? struniq (tlp->__lc_monetary->_monetary_locale_buf)
+                   ? tlp->__lc_monetary->_monetary_locale_buf
                    : "C";
           case LC_MESSAGES:
             return tlp->_messages_using_locale
-                   ? struniq (tlp->__lc_messages->_messages_locale_buf)
+                   ? tlp->__lc_messages->_messages_locale_buf
                    : "C";
           default: /* We shouldn't get here.  */
             return "";
@@ -2717,6 +2722,17 @@
   return NULL;
 }
 
+const char *
+gl_locale_name_thread (int category, const char *categoryname)
+{
+#if HAVE_USELOCALE
+  const char *name = gl_locale_name_thread_unsafe (category, categoryname);
+  if (name != NULL)
+    return struniq (name);
+#endif
+  return NULL;
+}
+
 /* XPG3 defines the result of 'setlocale (category, NULL)' as:
    "Directs 'setlocale()' to query 'category' and return the current
     setting of 'local'."
--- a/modules/localename-tests
+++ b/modules/localename-tests
@@ -6,6 +6,7 @@
 locale
 setenv
 unsetenv
+strdup
 
 configure.ac:
 AC_CHECK_FUNCS_ONCE([newlocale])
--- a/tests/test-localename.c
+++ b/tests/test-localename.c
@@ -274,6 +274,181 @@
           }
       }
   }
+
+  /* Check that gl_locale_name_thread returns a string that is allocated with
+     indefinite extent.  */
+  {
+    /* Try many locale names in turn, in order to defeat possible caches.  */
+    static const char * const choices[] =
+      {
+        "C",
+        "POSIX",
+        "af_ZA",
+        "af_ZA.UTF-8",
+        "am_ET",
+        "am_ET.UTF-8",
+        "be_BY",
+        "be_BY.UTF-8",
+        "bg_BG",
+        "bg_BG.UTF-8",
+        "ca_ES",
+        "ca_ES.UTF-8",
+        "cs_CZ",
+        "cs_CZ.UTF-8",
+        "da_DK",
+        "da_DK.UTF-8",
+        "de_AT",
+        "de_AT.UTF-8",
+        "de_CH",
+        "de_CH.UTF-8",
+        "de_DE",
+        "de_DE.UTF-8",
+        "el_GR",
+        "el_GR.UTF-8",
+        "en_AU",
+        "en_AU.UTF-8",
+        "en_CA",
+        "en_CA.UTF-8",
+        "en_GB",
+        "en_GB.UTF-8",
+        "en_IE",
+        "en_IE.UTF-8",
+        "en_NZ",
+        "en_NZ.UTF-8",
+        "en_US",
+        "en_US.UTF-8",
+        "es_ES",
+        "es_ES.UTF-8",
+        "et_EE",
+        "et_EE.UTF-8",
+        "eu_ES",
+        "eu_ES.UTF-8",
+        "fi_FI",
+        "fi_FI.UTF-8",
+        "fr_BE",
+        "fr_BE.UTF-8",
+        "fr_CA",
+        "fr_CA.UTF-8",
+        "fr_CH",
+        "fr_CH.UTF-8",
+        "fr_FR",
+        "fr_FR.UTF-8",
+        "he_IL",
+        "he_IL.UTF-8",
+        "hr_HR",
+        "hr_HR.UTF-8",
+        "hu_HU",
+        "hu_HU.UTF-8",
+        "hy_AM",
+        "is_IS",
+        "is_IS.UTF-8",
+        "it_CH",
+        "it_CH.UTF-8",
+        "it_IT",
+        "it_IT.UTF-8",
+        "ja_JP.UTF-8",
+        "kk_KZ",
+        "kk_KZ.UTF-8",
+        "ko_KR.UTF-8",
+        "lt_LT",
+        "lt_LT.UTF-8",
+        "nl_BE",
+        "nl_BE.UTF-8",
+        "nl_NL",
+        "nl_NL.UTF-8",
+        "no_NO",
+        "no_NO.UTF-8",
+        "pl_PL",
+        "pl_PL.UTF-8",
+        "pt_BR",
+        "pt_BR.UTF-8",
+        "pt_PT",
+        "pt_PT.UTF-8",
+        "ro_RO",
+        "ro_RO.UTF-8",
+        "ru_RU",
+        "ru_RU.UTF-8",
+        "sk_SK",
+        "sk_SK.UTF-8",
+        "sl_SI",
+        "sl_SI.UTF-8",
+        "sv_SE",
+        "sv_SE.UTF-8",
+        "tr_TR",
+        "tr_TR.UTF-8",
+        "uk_UA",
+        "uk_UA.UTF-8",
+        "zh_CN",
+        "zh_CN.UTF-8",
+        "zh_HK",
+        "zh_HK.UTF-8",
+        "zh_TW",
+        "zh_TW.UTF-8"
+      };
+    /* Remember which locales are available.  */
+    unsigned char /* bool */ available[SIZEOF (choices)];
+    /* Array of remembered results of gl_locale_name_thread.  */
+    const char *unsaved_names[SIZEOF (choices)][SIZEOF (categories)];
+    /* Array of remembered results of gl_locale_name_thread, stored in safe
+       memory.  */
+    char *saved_names[SIZEOF (choices)][SIZEOF (categories)];
+    unsigned int j;
+
+    for (j = 0; j < SIZEOF (choices); j++)
+      {
+        locale_t locale = newlocale (LC_ALL_MASK, choices[j], NULL);
+        available[j] = (locale != NULL);
+        if (locale != NULL)
+          {
+            unsigned int i;
+
+            uselocale (locale);
+            for (i = 0; i < SIZEOF (categories); i++)
+              {
+                unsaved_names[j][i] = gl_locale_name_thread (categories[i].cat, categories[i].string);
+                saved_names[j][i] = strdup (unsaved_names[j][i]);
+              }
+            uselocale (LC_GLOBAL_LOCALE);
+            freelocale (locale);
+          }
+      }
+    /* Verify the unsaved_names are still valid.  */
+    for (j = 0; j < SIZEOF (choices); j++)
+      {
+        unsigned int i;
+
+        for (i = 0; i < SIZEOF (categories); i++)
+          ASSERT (strcmp (unsaved_names[j][i], saved_names[j][i]) == 0);
+      }
+    /* Allocate many locales, without freeing them.  This is an attempt at
+       overwriting as much of the previously allocated memory as possible.  */
+    for (j = SIZEOF (choices); j > 0; )
+      {
+        j--;
+        if (available[j])
+          {
+            locale_t locale = newlocale (LC_ALL_MASK, choices[j], NULL);
+            unsigned int i;
+
+            ASSERT (locale != NULL);
+            uselocale (locale);
+            for (i = 0; i < SIZEOF (categories); i++)
+              {
+                const char *name = gl_locale_name_thread (categories[i].cat, categories[i].string);
+                ASSERT (strcmp (unsaved_names[j][i], name) == 0);
+              }
+            uselocale (LC_GLOBAL_LOCALE);
+          }
+      }
+    /* Verify the unsaved_names are still valid.  */
+    for (j = 0; j < SIZEOF (choices); j++)
+      {
+        unsigned int i;
+
+        for (i = 0; i < SIZEOF (categories); i++)
+          ASSERT (strcmp (unsaved_names[j][i], saved_names[j][i]) == 0);
+      }
+  }
 #else
   /* Check that gl_locale_name_thread always returns NULL.  */
   ASSERT (gl_locale_name_thread (LC_CTYPE, "LC_CTYPE") == NULL);