OSDN Git Service

Cope with the likelihood that setlocale and localeconv will return
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 29 Sep 2001 21:16:30 +0000 (21:16 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 29 Sep 2001 21:16:30 +0000 (21:16 +0000)
pointers to data that will be changed by any later call to setlocale.
Must copy what they return to be sure we get the right answer.
Karel Zak, further tweaks by Tom Lane.

src/backend/utils/adt/pg_locale.c
src/include/utils/pg_locale.h

index faa81cd..513328d 100644 (file)
@@ -4,11 +4,11 @@
  *      The PostgreSQL locale utils.
  *
  *
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/pg_locale.c,v 1.9 2001/03/22 03:59:52 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/pg_locale.c,v 1.10 2001/09/29 21:16:30 tgl Exp $
  *
  *      Portions Copyright (c) 1999-2000, PostgreSQL Global Development Group
  *
- *     Karel Zak - Zakkr
+ * Karel Zak
  *
  * -----------------------------------------------------------------------
  */
 #ifdef USE_LOCALE
 
 #include <locale.h>
+
 #include "utils/pg_locale.h"
 
 /* #define DEBUG_LOCALE_UTILS  */
 
 
-static struct lconv *CurrentLocaleConv = NULL;
+static bool CurrentLocaleConvValid = false;
+static struct lconv CurrentLocaleConv;
+
 
 static void PGLC_setlocale(PG_LocaleCategories * lc);
 
 /*------
- * Return in PG_LocaleCategories the current locale settings
+ * Frees memory used in PG_LocaleCategories -- this memory is 
+ * allocated in PGLC_current().
+ *------
+ */
+void
+PGLC_free_categories(PG_LocaleCategories * lc)
+{
+       if (lc->lc_ctype)
+               pfree(lc->lc_ctype);
+       if (lc->lc_numeric)
+               pfree(lc->lc_numeric);
+       if (lc->lc_time)
+               pfree(lc->lc_time);
+       if (lc->lc_collate)
+               pfree(lc->lc_collate);
+       if (lc->lc_monetary);
+               pfree(lc->lc_monetary);
+#ifdef LC_MESSAGES
+       if (lc->lc_messages)
+               pfree(lc->lc_messages);
+#endif
+}
+
+/*------
+ * Return in PG_LocaleCategories the current locale settings.
+ *
+ * NB: strings are allocated in the current memory context!
  *------
  */
 void
@@ -36,13 +65,13 @@ PGLC_current(PG_LocaleCategories * lc)
 {
        lc->lang = getenv("LANG");
 
-       lc->lc_ctype = setlocale(LC_CTYPE, NULL);
-       lc->lc_numeric = setlocale(LC_NUMERIC, NULL);
-       lc->lc_time = setlocale(LC_TIME, NULL);
-       lc->lc_collate = setlocale(LC_COLLATE, NULL);
-       lc->lc_monetary = setlocale(LC_MONETARY, NULL);
+       lc->lc_ctype = pstrdup( setlocale(LC_CTYPE, NULL) );
+       lc->lc_numeric = pstrdup( setlocale(LC_NUMERIC, NULL) );
+       lc->lc_time = pstrdup( setlocale(LC_TIME, NULL) );
+       lc->lc_collate = pstrdup( setlocale(LC_COLLATE, NULL) );
+       lc->lc_monetary = pstrdup( setlocale(LC_MONETARY, NULL) );
 #ifdef LC_MESSAGES
-       lc->lc_messages = setlocale(LC_MESSAGES, NULL);
+       lc->lc_messages = pstrdup( setlocale(LC_MESSAGES, NULL) );
 #endif
 }
 
@@ -58,19 +87,22 @@ PGLC_debug_lc(PG_LocaleCategories * lc)
 {
 #ifdef LC_MESSAGES
        elog(DEBUG, "CURRENT LOCALE ENVIRONMENT:\n\nLANG:   \t%s\nLC_CTYPE:\t%s\nLC_NUMERIC:\t%s\nLC_TIME:\t%s\nLC_COLLATE:\t%s\nLC_MONETARY:\t%s\nLC_MESSAGES:\t%s\n",
+                lc->lang,
+                lc->lc_ctype,
+                lc->lc_numeric,
+                lc->lc_time,
+                lc->lc_collate,
+                lc->lc_monetary,
+                lc->lc_messages);
 #else
        elog(DEBUG, "CURRENT LOCALE ENVIRONMENT:\n\nLANG:   \t%s\nLC_CTYPE:\t%s\nLC_NUMERIC:\t%s\nLC_TIME:\t%s\nLC_COLLATE:\t%s\nLC_MONETARY:\t%s\n",
-#endif
                 lc->lang,
                 lc->lc_ctype,
                 lc->lc_numeric,
                 lc->lc_time,
                 lc->lc_collate,
-                lc->lc_monetary
-#ifdef LC_MESSAGES
-                ,lc->lc_messages
+                lc->lc_monetary);
 #endif
-       );
 }
 
 #endif
@@ -109,7 +141,7 @@ PGLC_setlocale(PG_LocaleCategories * lc)
 
 #ifdef LC_MESSAGES
        if (!setlocale(LC_MESSAGES, lc->lc_messages))
-               elog(NOTICE, "pg_setlocale(): 'LC_MESSAGE=%s' cannot be honored.",
+               elog(NOTICE, "pg_setlocale(): 'LC_MESSAGES=%s' cannot be honored.",
                         lc->lc_messages);
 #endif
 }
@@ -119,24 +151,17 @@ PGLC_setlocale(PG_LocaleCategories * lc)
  * with locale information for all categories. Note that returned lconv
  * does not depend on currently active category settings, but on external
  * environment variables for locale.
- *
- * XXX we assume that restoring old category settings via setlocale() will
- * not immediately corrupt the static data returned by localeconv().
- * How portable is this?
- *
- * XXX in any case, there certainly must not be any other calls to
- * localeconv() anywhere in the backend, else the values reported here
- * will be overwritten with the Postgres-internal locale settings.
  *------
  */
 struct lconv *
 PGLC_localeconv(void)
 {
        PG_LocaleCategories lc;
+       struct lconv *extlconv;
 
        /* Did we do it already? */
-       if (CurrentLocaleConv)
-               return CurrentLocaleConv;
+       if (CurrentLocaleConvValid)
+               return &CurrentLocaleConv;
 
        /* Save current locale setting to lc */
        PGLC_current(&lc);
@@ -145,12 +170,29 @@ PGLC_localeconv(void)
        setlocale(LC_ALL, "");
 
        /* Get formatting information for the external environment */
-       CurrentLocaleConv = localeconv();
+       extlconv = localeconv();
+
+       /* Must copy all values since restoring internal settings may overwrite */
+       CurrentLocaleConv = *extlconv;
+       CurrentLocaleConv.currency_symbol = strdup(extlconv->currency_symbol);
+       CurrentLocaleConv.decimal_point = strdup(extlconv->decimal_point);
+       CurrentLocaleConv.grouping = strdup(extlconv->grouping);
+       CurrentLocaleConv.thousands_sep = strdup(extlconv->thousands_sep);
+       CurrentLocaleConv.int_curr_symbol = strdup(extlconv->int_curr_symbol);
+       CurrentLocaleConv.mon_decimal_point = strdup(extlconv->mon_decimal_point);
+       CurrentLocaleConv.mon_grouping = strdup(extlconv->mon_grouping);
+       CurrentLocaleConv.mon_thousands_sep = strdup(extlconv->mon_thousands_sep);
+       CurrentLocaleConv.negative_sign = strdup(extlconv->negative_sign);
+       CurrentLocaleConv.positive_sign = strdup(extlconv->positive_sign);
 
        /* Restore Postgres' internal locale settings */
        PGLC_setlocale(&lc);
 
-       return CurrentLocaleConv;
+       /* Deallocate category settings allocated in PGLC_current() */
+       PGLC_free_categories(&lc);
+
+       CurrentLocaleConvValid = true;
+       return &CurrentLocaleConv;
 }
 
 #endif  /* USE_LOCALE */
index 062806b..d6257b7 100644 (file)
@@ -4,7 +4,7 @@
  *      The PostgreSQL locale utils.
  *
  *
- * $Id: pg_locale.h,v 1.7 2001/03/22 04:01:14 momjian Exp $
+ * $Id: pg_locale.h,v 1.8 2001/09/29 21:16:30 tgl Exp $
  *
  *      Portions Copyright (c) 1999-2000, PostgreSQL Global Development Group
  *
@@ -33,8 +33,14 @@ typedef struct PG_LocaleCategories
                           *lc_messages;
 }                      PG_LocaleCategories;
 
-
+/*
+ * Save locale category settings into PG memory  
+ */
 extern void PGLC_current(PG_LocaleCategories * lc);
+/*
+ * Free memory allocated in PGLC_current()
+ */
+extern void PGLC_free_categories(PG_LocaleCategories * lc);
 
 /*------
  * Return the POSIX lconv struct (contains number/money formatting information)