1 /*-----------------------------------------------------------------------
3 * PostgreSQL locale utilities
5 * Portions Copyright (c) 2002-2005, PostgreSQL Global Development Group
7 * $PostgreSQL: pgsql/src/backend/utils/adt/pg_locale.c,v 1.32 2005/10/15 02:49:29 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)
129 * LC_MESSAGES category does not exist everywhere, but accept it anyway
134 if (!setlocale(LC_MESSAGES, value))
138 value = locale_xxx_assign(LC_MESSAGES, value, false, source);
139 #endif /* LC_MESSAGES */
144 * Win32 does not have working setlocale() for LC_MESSAGES. We can only
145 * use environment variables to change it (per gettext FAQ). This means
146 * we can't actually check the supplied value, so always assume it's good.
147 * Also, ignore attempts to set to "", which really means "keep using the
148 * old value". (Actually it means "use the environment value", but we are
149 * too lazy to try to implement that exactly.)
151 if (doit && value[0])
154 * We need to modify both the process environment and the cached
157 static char env[128];
159 if (!SetEnvironmentVariable("LC_MESSAGES", value))
162 snprintf(env, sizeof(env) - 1, "LC_MESSAGES=%s", value);
172 * We'd like to cache whether LC_COLLATE is C (or POSIX), so we can
173 * optimize a few code paths in various places.
176 lc_collate_is_c(void)
178 /* Cache result so we only have to compute it once */
179 static int result = -1;
183 return (bool) result;
184 localeptr = setlocale(LC_COLLATE, NULL);
186 elog(ERROR, "invalid LC_COLLATE setting");
188 if (strcmp(localeptr, "C") == 0)
190 else if (strcmp(localeptr, "POSIX") == 0)
194 return (bool) result;
199 * We'd like to cache whether LC_CTYPE is C (or POSIX), so we can
200 * optimize a few code paths in various places.
205 /* Cache result so we only have to compute it once */
206 static int result = -1;
210 return (bool) result;
211 localeptr = setlocale(LC_CTYPE, NULL);
213 elog(ERROR, "invalid LC_CTYPE setting");
215 if (strcmp(localeptr, "C") == 0)
217 else if (strcmp(localeptr, "POSIX") == 0)
221 return (bool) result;
226 * Frees the malloced content of a struct lconv. (But not the struct
230 free_struct_lconv(struct lconv * s)
235 if (s->currency_symbol)
236 free(s->currency_symbol);
237 if (s->decimal_point)
238 free(s->decimal_point);
241 if (s->thousands_sep)
242 free(s->thousands_sep);
243 if (s->int_curr_symbol)
244 free(s->int_curr_symbol);
245 if (s->mon_decimal_point)
246 free(s->mon_decimal_point);
248 free(s->mon_grouping);
249 if (s->mon_thousands_sep)
250 free(s->mon_thousands_sep);
251 if (s->negative_sign)
252 free(s->negative_sign);
253 if (s->positive_sign)
254 free(s->positive_sign);
259 * Return the POSIX lconv struct (contains number/money formatting
260 * information) with locale information for all categories.
263 PGLC_localeconv(void)
265 static struct lconv CurrentLocaleConv;
266 struct lconv *extlconv;
267 char *save_lc_monetary;
268 char *save_lc_numeric;
270 /* Did we do it already? */
271 if (CurrentLocaleConvValid)
272 return &CurrentLocaleConv;
274 free_struct_lconv(&CurrentLocaleConv);
276 /* Set user's values of monetary and numeric locales */
277 save_lc_monetary = setlocale(LC_MONETARY, NULL);
278 if (save_lc_monetary)
279 save_lc_monetary = pstrdup(save_lc_monetary);
280 save_lc_numeric = setlocale(LC_NUMERIC, NULL);
282 save_lc_numeric = pstrdup(save_lc_numeric);
284 setlocale(LC_MONETARY, locale_monetary);
285 setlocale(LC_NUMERIC, locale_numeric);
287 /* Get formatting information */
288 extlconv = localeconv();
291 * Must copy all values since restoring internal settings may overwrite
292 * localeconv()'s results.
294 CurrentLocaleConv = *extlconv;
295 CurrentLocaleConv.currency_symbol = strdup(extlconv->currency_symbol);
296 CurrentLocaleConv.decimal_point = strdup(extlconv->decimal_point);
297 CurrentLocaleConv.grouping = strdup(extlconv->grouping);
298 CurrentLocaleConv.thousands_sep = strdup(extlconv->thousands_sep);
299 CurrentLocaleConv.int_curr_symbol = strdup(extlconv->int_curr_symbol);
300 CurrentLocaleConv.mon_decimal_point = strdup(extlconv->mon_decimal_point);
301 CurrentLocaleConv.mon_grouping = strdup(extlconv->mon_grouping);
302 CurrentLocaleConv.mon_thousands_sep = strdup(extlconv->mon_thousands_sep);
303 CurrentLocaleConv.negative_sign = strdup(extlconv->negative_sign);
304 CurrentLocaleConv.positive_sign = strdup(extlconv->positive_sign);
305 CurrentLocaleConv.n_sign_posn = extlconv->n_sign_posn;
307 /* Try to restore internal settings */
308 if (save_lc_monetary)
310 setlocale(LC_MONETARY, save_lc_monetary);
311 pfree(save_lc_monetary);
316 setlocale(LC_NUMERIC, save_lc_numeric);
317 pfree(save_lc_numeric);
320 CurrentLocaleConvValid = true;
321 return &CurrentLocaleConv;