22 *
33 * PostgreSQL locale utilities
44 *
5- * $Header: /cvsroot/pgsql/src/backend/utils/adt/pg_locale.c,v 1.19 2002/09/04 20:31:28 momjian Exp $
6- *
75 * Portions Copyright (c) 2002, PostgreSQL Global Development Group
86 *
7+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/pg_locale.c,v 1.20 2002/10/18 20:44:02 tgl Exp $
8+ *
99 *-----------------------------------------------------------------------
1010 */
1111
12- /*
12+ /*----------
1313 * Here is how the locale stuff is handled: LC_COLLATE and LC_CTYPE
1414 * are fixed by initdb, stored in pg_control, and cannot be changed.
1515 * Thus, the effects of strcoll(), strxfrm(), isupper(), toupper(),
2020 *
2121 * The other categories, LC_MONETARY, LC_NUMERIC, and LC_TIME are also
2222 * settable at run-time. However, we don't actually set those locale
23- * categories permanently.This would havebizzare effects like no
23+ * categories permanently.This would havebizarre effects like no
2424 * longer accepting standard floating-point literals in some locales.
2525 * Instead, we only set the locales briefly when needed, cache the
2626 * required information obtained from localeconv(), and set them back.
27- * The information is only used by the formatting functions (to_char,
28- * etc.) and the money type. For the user, this should all be
27+ * Thecached information is only used by the formatting functions
28+ *(to_char, etc.) and the money type. For the user, this should all be
2929 * transparent. (Actually, LC_TIME doesn't do anything at all right
3030 * now.)
31+ *
32+ * !!! NOW HEAR THIS !!!
33+ *
34+ * We've been bitten repeatedly by this bug, so let's try to keep it in
35+ * mind in future: on some platforms, the locale functions return pointers
36+ * to static data that will be overwritten by any later locale function.
37+ * Thus, for example, the obvious-looking sequence
38+ *save = setlocale(category, NULL);
39+ *if (!setlocale(category, value))
40+ *fail = true;
41+ *setlocale(category, save);
42+ * DOES NOT WORK RELIABLY: on some platforms the second setlocale() call
43+ * will change the memory save is pointing at. To do this sort of thing
44+ * safely, you *must* pstrdup what setlocale returns the first time.
45+ *----------
3146 */
3247
3348
@@ -64,15 +79,19 @@ locale_xxx_assign(int category, const char *value, bool doit, bool interactive)
6479
6580save = setlocale (category ,NULL );
6681if (!save )
67- return NULL ;
82+ return NULL ;/* won't happen, we hope */
83+
84+ /* save may be pointing at a modifiable scratch variable, see above */
85+ save = pstrdup (save );
6886
6987if (!setlocale (category ,value ))
70- return NULL ;
88+ value = NULL ;/* set failure return marker */
7189
72- setlocale (category ,save );
90+ setlocale (category ,save );/* assume this won't fail */
91+ pfree (save );
7392
74- /* need to reload cache next time */
75- if (doit )
93+ /* need to reload cache next time? */
94+ if (doit && value != NULL )
7695CurrentLocaleConvValid = false;
7796
7897return value ;
@@ -99,7 +118,7 @@ locale_time_assign(const char *value, bool doit, bool interactive)
99118
100119
101120/*
102- *lc_messages takes effect immediately
121+ *We allow LC_MESSAGES to actually be set globally.
103122 */
104123const char *
105124locale_messages_assign (const char * value ,bool doit ,bool interactive )
@@ -116,16 +135,7 @@ locale_messages_assign(const char *value, bool doit, bool interactive)
116135}
117136else
118137{
119- char * save ;
120-
121- save = setlocale (LC_MESSAGES ,NULL );
122- if (!save )
123- return NULL ;
124-
125- if (!setlocale (LC_MESSAGES ,value ))
126- return NULL ;
127-
128- setlocale (LC_MESSAGES ,save );
138+ value = locale_xxx_assign (LC_MESSAGES ,value , false,interactive );
129139}
130140#endif
131141return value ;
@@ -210,8 +220,13 @@ PGLC_localeconv(void)
210220
211221free_struct_lconv (& CurrentLocaleConv );
212222
223+ /* Set user's values of monetary and numeric locales */
213224save_lc_monetary = setlocale (LC_MONETARY ,NULL );
225+ if (save_lc_monetary )
226+ save_lc_monetary = pstrdup (save_lc_monetary );
214227save_lc_numeric = setlocale (LC_NUMERIC ,NULL );
228+ if (save_lc_numeric )
229+ save_lc_numeric = pstrdup (save_lc_numeric );
215230
216231setlocale (LC_MONETARY ,locale_monetary );
217232setlocale (LC_NUMERIC ,locale_numeric );
@@ -221,7 +236,7 @@ PGLC_localeconv(void)
221236
222237/*
223238 * Must copy all values since restoring internal settings may
224- * overwrite
239+ * overwrite localeconv()'s results.
225240 */
226241CurrentLocaleConv = * extlconv ;
227242CurrentLocaleConv .currency_symbol = strdup (extlconv -> currency_symbol );
@@ -236,8 +251,18 @@ PGLC_localeconv(void)
236251CurrentLocaleConv .positive_sign = strdup (extlconv -> positive_sign );
237252CurrentLocaleConv .n_sign_posn = extlconv -> n_sign_posn ;
238253
239- setlocale (LC_MONETARY ,save_lc_monetary );
240- setlocale (LC_NUMERIC ,save_lc_numeric );
254+ /* Try to restore internal settings */
255+ if (save_lc_monetary )
256+ {
257+ setlocale (LC_MONETARY ,save_lc_monetary );
258+ pfree (save_lc_monetary );
259+ }
260+
261+ if (save_lc_numeric )
262+ {
263+ setlocale (LC_NUMERIC ,save_lc_numeric );
264+ pfree (save_lc_numeric );
265+ }
241266
242267CurrentLocaleConvValid = true;
243268return & CurrentLocaleConv ;