+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.
#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. */
#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
{
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 "";
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'."
locale
setenv
unsetenv
+strdup
configure.ac:
AC_CHECK_FUNCS_ONCE([newlocale])
}
}
}
+
+ /* 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);