# HG changeset patch # User Bruno Haible # Date 1261834221 -3600 # Node ID d015b5753e52f1318ccd8c7fd9ab7925235378b0 # Parent 6d11047aa87babe94e8ac51d1eea1ec65c4a06d8 localename: Fix storage allocation of gl_locale_name_thread's result. diff --git a/ChangeLog b/ChangeLog --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +2009-12-26 Bruno Haible + + 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 * tests/test-localename.c (categories): Fill in the strings. diff --git a/lib/localename.c b/lib/localename.c --- 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'." diff --git a/modules/localename-tests b/modules/localename-tests --- a/modules/localename-tests +++ b/modules/localename-tests @@ -6,6 +6,7 @@ locale setenv unsetenv +strdup configure.ac: AC_CHECK_FUNCS_ONCE([newlocale]) diff --git a/tests/test-localename.c b/tests/test-localename.c --- 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);