1 /*-----------------------------------------------------------------------
3 * PostgreSQL locale utilities
5 * Portions Copyright (c) 2002-2004, PostgreSQL Global Development Group
7 * $PostgreSQL: pgsql/src/backend/utils/adt/pg_locale.c,v 1.27 2004/08/29 04:12:52 momjian Exp $
9 *-----------------------------------------------------------------------
13 * Here is how the locale stuff is handled: LC_COLLATE and LC_CTYPE
14 * are fixed by initdb, stored in pg_control, and cannot be changed.
15 * Thus, the effects of strcoll(), strxfrm(), isupper(), toupper(),
16 * etc. are always in the same fixed locale.
18 * LC_MESSAGES is settable at run time and will take effect
21 * The other categories, LC_MONETARY, LC_NUMERIC, and LC_TIME are also
22 * settable at run-time. However, we don't actually set those locale
23 * categories permanently. This would have bizarre effects like no
24 * longer accepting standard floating-point literals in some locales.
25 * Instead, we only set the locales briefly when needed, cache the
26 * required information obtained from localeconv(), and set them back.
27 * The cached information is only used by the formatting functions
28 * (to_char, etc.) and the money type. For the user, this should all be
29 * transparent. (Actually, LC_TIME doesn't do anything at all right
32 * !!! NOW HEAR THIS !!!
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))
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.
53 #include "utils/pg_locale.h"
56 /* indicated whether locale information cache is valid */
57 static bool CurrentLocaleConvValid = false;
60 /* GUC storage area */
62 char *locale_messages;
63 char *locale_monetary;
68 /* GUC assign hooks */
71 * This is common code for several locale categories. This doesn't
72 * actually set the locale permanently, it only tests if the locale is
73 * valid. (See explanation at the top of this file.)
76 locale_xxx_assign(int category, const char *value, bool doit, GucSource source)
80 save = setlocale(category, NULL);
82 return NULL; /* won't happen, we hope */
84 /* save may be pointing at a modifiable scratch variable, see above */
87 if (!setlocale(category, value))
88 value = NULL; /* set failure return marker */
90 setlocale(category, save); /* assume this won't fail */
93 /* need to reload cache next time? */
94 if (doit && value != NULL)
95 CurrentLocaleConvValid = false;
102 locale_monetary_assign(const char *value, bool doit, GucSource source)
104 return locale_xxx_assign(LC_MONETARY, value, doit, source);
108 locale_numeric_assign(const char *value, bool doit, GucSource source)
110 return locale_xxx_assign(LC_NUMERIC, value, doit, source);
114 locale_time_assign(const char *value, bool doit, GucSource source)
116 return locale_xxx_assign(LC_TIME, value, doit, source);
121 * We allow LC_MESSAGES to actually be set globally.
124 locale_messages_assign(const char *value, bool doit, GucSource source)
127 * LC_MESSAGES category does not exist everywhere, but accept it
133 if (!setlocale(LC_MESSAGES, value))
137 * Win32 returns NULL when you set LC_MESSAGES to "". So don't
138 * complain unless we're trying to set it to something else.
148 value = locale_xxx_assign(LC_MESSAGES, value, false, source);
149 #endif /* LC_MESSAGES */
155 * We'd like to cache whether LC_COLLATE is C (or POSIX), so we can
156 * optimize a few code paths in various places.
159 lc_collate_is_c(void)
161 /* Cache result so we only have to compute it once */
162 static int result = -1;
166 return (bool) result;
167 localeptr = setlocale(LC_COLLATE, NULL);
169 elog(ERROR, "invalid LC_COLLATE setting");
171 if (strcmp(localeptr, "C") == 0)
173 else if (strcmp(localeptr, "POSIX") == 0)
177 return (bool) result;
182 * Frees the malloced content of a struct lconv. (But not the struct
186 free_struct_lconv(struct lconv * s)
191 if (s->currency_symbol)
192 free(s->currency_symbol);
193 if (s->decimal_point)
194 free(s->decimal_point);
197 if (s->thousands_sep)
198 free(s->thousands_sep);
199 if (s->int_curr_symbol)
200 free(s->int_curr_symbol);
201 if (s->mon_decimal_point)
202 free(s->mon_decimal_point);
204 free(s->mon_grouping);
205 if (s->mon_thousands_sep)
206 free(s->mon_thousands_sep);
207 if (s->negative_sign)
208 free(s->negative_sign);
209 if (s->positive_sign)
210 free(s->positive_sign);
215 * Return the POSIX lconv struct (contains number/money formatting
216 * information) with locale information for all categories.
219 PGLC_localeconv(void)
221 static struct lconv CurrentLocaleConv;
222 struct lconv *extlconv;
223 char *save_lc_monetary;
224 char *save_lc_numeric;
226 /* Did we do it already? */
227 if (CurrentLocaleConvValid)
228 return &CurrentLocaleConv;
230 free_struct_lconv(&CurrentLocaleConv);
232 /* Set user's values of monetary and numeric locales */
233 save_lc_monetary = setlocale(LC_MONETARY, NULL);
234 if (save_lc_monetary)
235 save_lc_monetary = pstrdup(save_lc_monetary);
236 save_lc_numeric = setlocale(LC_NUMERIC, NULL);
238 save_lc_numeric = pstrdup(save_lc_numeric);
240 setlocale(LC_MONETARY, locale_monetary);
241 setlocale(LC_NUMERIC, locale_numeric);
243 /* Get formatting information */
244 extlconv = localeconv();
247 * Must copy all values since restoring internal settings may
248 * overwrite localeconv()'s results.
250 CurrentLocaleConv = *extlconv;
251 CurrentLocaleConv.currency_symbol = strdup(extlconv->currency_symbol);
252 CurrentLocaleConv.decimal_point = strdup(extlconv->decimal_point);
253 CurrentLocaleConv.grouping = strdup(extlconv->grouping);
254 CurrentLocaleConv.thousands_sep = strdup(extlconv->thousands_sep);
255 CurrentLocaleConv.int_curr_symbol = strdup(extlconv->int_curr_symbol);
256 CurrentLocaleConv.mon_decimal_point = strdup(extlconv->mon_decimal_point);
257 CurrentLocaleConv.mon_grouping = strdup(extlconv->mon_grouping);
258 CurrentLocaleConv.mon_thousands_sep = strdup(extlconv->mon_thousands_sep);
259 CurrentLocaleConv.negative_sign = strdup(extlconv->negative_sign);
260 CurrentLocaleConv.positive_sign = strdup(extlconv->positive_sign);
261 CurrentLocaleConv.n_sign_posn = extlconv->n_sign_posn;
263 /* Try to restore internal settings */
264 if (save_lc_monetary)
266 setlocale(LC_MONETARY, save_lc_monetary);
267 pfree(save_lc_monetary);
272 setlocale(LC_NUMERIC, save_lc_numeric);
273 pfree(save_lc_numeric);
276 CurrentLocaleConvValid = true;
277 return &CurrentLocaleConv;