From 8ca61476e0c6fb73e17c405d46cae701c7b7d959 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Sat, 29 Sep 2001 21:16:30 +0000 Subject: [PATCH] Cope with the likelihood that setlocale and localeconv will return 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 | 102 +++++++++++++++++++++--------- src/include/utils/pg_locale.h | 10 ++- 2 files changed, 80 insertions(+), 32 deletions(-) diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c index faa81cd09a..513328d35c 100644 --- a/src/backend/utils/adt/pg_locale.c +++ b/src/backend/utils/adt/pg_locale.c @@ -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 * * ----------------------------------------------------------------------- */ @@ -18,17 +18,46 @@ #ifdef USE_LOCALE #include + #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", -#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, + 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", + lc->lang, + lc->lc_ctype, + lc->lc_numeric, + lc->lc_time, + lc->lc_collate, + 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 */ diff --git a/src/include/utils/pg_locale.h b/src/include/utils/pg_locale.h index 062806bc39..d6257b7654 100644 --- a/src/include/utils/pg_locale.h +++ b/src/include/utils/pg_locale.h @@ -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)