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.
This commit is contained in:
Tom Lane 2001-09-29 21:16:30 +00:00
parent dc05a996c5
commit 8ca61476e0
2 changed files with 80 additions and 32 deletions

View 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
*
* -----------------------------------------------------------------------
*/
@ -18,17 +18,46 @@
#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",
#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 */

View 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)