OSDN Git Service

Standard pgindent run for 8.1.
[pg-rex/syncrep.git] / src / backend / utils / adt / pg_locale.c
1 /*-----------------------------------------------------------------------
2  *
3  * PostgreSQL locale utilities
4  *
5  * Portions Copyright (c) 2002-2005, PostgreSQL Global Development Group
6  *
7  * $PostgreSQL: pgsql/src/backend/utils/adt/pg_locale.c,v 1.32 2005/10/15 02:49:29 momjian Exp $
8  *
9  *-----------------------------------------------------------------------
10  */
11
12 /*----------
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.
17  *
18  * LC_MESSAGES is settable at run time and will take effect
19  * immediately.
20  *
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
30  * 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  *----------
46  */
47
48
49 #include "postgres.h"
50
51 #include <locale.h>
52
53 #include "utils/pg_locale.h"
54
55
56 /* indicated whether locale information cache is valid */
57 static bool CurrentLocaleConvValid = false;
58
59
60 /* GUC storage area */
61
62 char       *locale_messages;
63 char       *locale_monetary;
64 char       *locale_numeric;
65 char       *locale_time;
66
67
68 /* GUC assign hooks */
69
70 /*
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.)
74  */
75 static const char *
76 locale_xxx_assign(int category, const char *value, bool doit, GucSource source)
77 {
78         char       *save;
79
80         save = setlocale(category, NULL);
81         if (!save)
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);
86
87         if (!setlocale(category, value))
88                 value = NULL;                   /* set failure return marker */
89
90         setlocale(category, save);      /* assume this won't fail */
91         pfree(save);
92
93         /* need to reload cache next time? */
94         if (doit && value != NULL)
95                 CurrentLocaleConvValid = false;
96
97         return value;
98 }
99
100
101 const char *
102 locale_monetary_assign(const char *value, bool doit, GucSource source)
103 {
104         return locale_xxx_assign(LC_MONETARY, value, doit, source);
105 }
106
107 const char *
108 locale_numeric_assign(const char *value, bool doit, GucSource source)
109 {
110         return locale_xxx_assign(LC_NUMERIC, value, doit, source);
111 }
112
113 const char *
114 locale_time_assign(const char *value, bool doit, GucSource source)
115 {
116         return locale_xxx_assign(LC_TIME, value, doit, source);
117 }
118
119
120 /*
121  * We allow LC_MESSAGES to actually be set globally.
122  */
123 const char *
124 locale_messages_assign(const char *value, bool doit, GucSource source)
125 {
126 #ifndef WIN32
127
128         /*
129          * LC_MESSAGES category does not exist everywhere, but accept it anyway
130          */
131 #ifdef LC_MESSAGES
132         if (doit)
133         {
134                 if (!setlocale(LC_MESSAGES, value))
135                         return NULL;
136         }
137         else
138                 value = locale_xxx_assign(LC_MESSAGES, value, false, source);
139 #endif   /* LC_MESSAGES */
140         return value;
141 #else                                                   /* WIN32 */
142
143         /*
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.)
150          */
151         if (doit && value[0])
152         {
153                 /*
154                  * We need to modify both the process environment and the cached
155                  * version in msvcrt
156                  */
157                 static char env[128];
158
159                 if (!SetEnvironmentVariable("LC_MESSAGES", value))
160                         return NULL;
161
162                 snprintf(env, sizeof(env) - 1, "LC_MESSAGES=%s", value);
163                 if (_putenv(env))
164                         return NULL;
165         }
166         return value;
167 #endif   /* WIN32 */
168 }
169
170
171 /*
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.
174  */
175 bool
176 lc_collate_is_c(void)
177 {
178         /* Cache result so we only have to compute it once */
179         static int      result = -1;
180         char       *localeptr;
181
182         if (result >= 0)
183                 return (bool) result;
184         localeptr = setlocale(LC_COLLATE, NULL);
185         if (!localeptr)
186                 elog(ERROR, "invalid LC_COLLATE setting");
187
188         if (strcmp(localeptr, "C") == 0)
189                 result = true;
190         else if (strcmp(localeptr, "POSIX") == 0)
191                 result = true;
192         else
193                 result = false;
194         return (bool) result;
195 }
196
197
198 /*
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.
201  */
202 bool
203 lc_ctype_is_c(void)
204 {
205         /* Cache result so we only have to compute it once */
206         static int      result = -1;
207         char       *localeptr;
208
209         if (result >= 0)
210                 return (bool) result;
211         localeptr = setlocale(LC_CTYPE, NULL);
212         if (!localeptr)
213                 elog(ERROR, "invalid LC_CTYPE setting");
214
215         if (strcmp(localeptr, "C") == 0)
216                 result = true;
217         else if (strcmp(localeptr, "POSIX") == 0)
218                 result = true;
219         else
220                 result = false;
221         return (bool) result;
222 }
223
224
225 /*
226  * Frees the malloced content of a struct lconv.  (But not the struct
227  * itself.)
228  */
229 static void
230 free_struct_lconv(struct lconv * s)
231 {
232         if (s == NULL)
233                 return;
234
235         if (s->currency_symbol)
236                 free(s->currency_symbol);
237         if (s->decimal_point)
238                 free(s->decimal_point);
239         if (s->grouping)
240                 free(s->grouping);
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);
247         if (s->mon_grouping)
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);
255 }
256
257
258 /*
259  * Return the POSIX lconv struct (contains number/money formatting
260  * information) with locale information for all categories.
261  */
262 struct lconv *
263 PGLC_localeconv(void)
264 {
265         static struct lconv CurrentLocaleConv;
266         struct lconv *extlconv;
267         char       *save_lc_monetary;
268         char       *save_lc_numeric;
269
270         /* Did we do it already? */
271         if (CurrentLocaleConvValid)
272                 return &CurrentLocaleConv;
273
274         free_struct_lconv(&CurrentLocaleConv);
275
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);
281         if (save_lc_numeric)
282                 save_lc_numeric = pstrdup(save_lc_numeric);
283
284         setlocale(LC_MONETARY, locale_monetary);
285         setlocale(LC_NUMERIC, locale_numeric);
286
287         /* Get formatting information */
288         extlconv = localeconv();
289
290         /*
291          * Must copy all values since restoring internal settings may overwrite
292          * localeconv()'s results.
293          */
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;
306
307         /* Try to restore internal settings */
308         if (save_lc_monetary)
309         {
310                 setlocale(LC_MONETARY, save_lc_monetary);
311                 pfree(save_lc_monetary);
312         }
313
314         if (save_lc_numeric)
315         {
316                 setlocale(LC_NUMERIC, save_lc_numeric);
317                 pfree(save_lc_numeric);
318         }
319
320         CurrentLocaleConvValid = true;
321         return &CurrentLocaleConv;
322 }