diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2005-12-28 23:22:51 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2005-12-28 23:22:51 +0000 |
commit | ab51bbaa06b4363a7397a1f394902ad57ed52fc9 (patch) | |
tree | fce620abf844eac5ad32b471935665f2951f7326 /src | |
parent | b6c881ce6227e239f59e07a177208841b14d1ec6 (diff) | |
download | postgresql-ab51bbaa06b4363a7397a1f394902ad57ed52fc9.tar.gz |
Arrange to set the LC_XXX environment variables to match our locale
setup. This protects against undesired changes in locale behavior
if someone carelessly does setlocale(LC_ALL, "") (and we know who
you are, perl guys).
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/access/transam/xlog.c | 7 | ||||
-rw-r--r-- | src/backend/main/main.c | 31 | ||||
-rw-r--r-- | src/backend/utils/adt/pg_locale.c | 155 | ||||
-rw-r--r-- | src/include/utils/pg_locale.h | 4 |
4 files changed, 146 insertions, 51 deletions
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 54d2f2cc78..4ed5c3eb8f 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.223 2005/11/22 18:17:07 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.224 2005/12/28 23:22:50 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -45,6 +45,7 @@ #include "utils/builtins.h" #include "utils/guc.h" #include "utils/nabstime.h" +#include "utils/pg_locale.h" #include "utils/relcache.h" @@ -3619,14 +3620,14 @@ ReadControlFile(void) " but the server was compiled with LOCALE_NAME_BUFLEN %d.", ControlFile->localeBuflen, LOCALE_NAME_BUFLEN), errhint("It looks like you need to recompile or initdb."))); - if (setlocale(LC_COLLATE, ControlFile->lc_collate) == NULL) + if (pg_perm_setlocale(LC_COLLATE, ControlFile->lc_collate) == NULL) ereport(FATAL, (errmsg("database files are incompatible with operating system"), errdetail("The database cluster was initialized with LC_COLLATE \"%s\"," " which is not recognized by setlocale().", ControlFile->lc_collate), errhint("It looks like you need to initdb or install locale support."))); - if (setlocale(LC_CTYPE, ControlFile->lc_ctype) == NULL) + if (pg_perm_setlocale(LC_CTYPE, ControlFile->lc_ctype) == NULL) ereport(FATAL, (errmsg("database files are incompatible with operating system"), errdetail("The database cluster was initialized with LC_CTYPE \"%s\"," diff --git a/src/backend/main/main.c b/src/backend/main/main.c index dab99bab9d..866c922afd 100644 --- a/src/backend/main/main.c +++ b/src/backend/main/main.c @@ -13,13 +13,12 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/main/main.c,v 1.97 2005/11/22 18:17:11 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/main/main.c,v 1.98 2005/12/28 23:22:51 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" -#include <errno.h> #include <pwd.h> #include <unistd.h> @@ -40,6 +39,7 @@ #include "postmaster/postmaster.h" #include "tcop/tcopprot.h" #include "utils/help_config.h" +#include "utils/pg_locale.h" #include "utils/ps_status.h" #ifdef WIN32 #include "libpq/pqsignal.h" @@ -162,30 +162,37 @@ main(int argc, char *argv[]) */ if ((env_locale = getenv("LC_COLLATE")) != NULL) - setlocale(LC_COLLATE, env_locale); + pg_perm_setlocale(LC_COLLATE, env_locale); else - setlocale(LC_COLLATE, ""); + pg_perm_setlocale(LC_COLLATE, ""); if ((env_locale = getenv("LC_CTYPE")) != NULL) - setlocale(LC_CTYPE, env_locale); + pg_perm_setlocale(LC_CTYPE, env_locale); else - setlocale(LC_CTYPE, ""); + pg_perm_setlocale(LC_CTYPE, ""); #else - setlocale(LC_COLLATE, ""); - setlocale(LC_CTYPE, ""); + pg_perm_setlocale(LC_COLLATE, ""); + pg_perm_setlocale(LC_CTYPE, ""); #endif #ifdef LC_MESSAGES - setlocale(LC_MESSAGES, ""); + pg_perm_setlocale(LC_MESSAGES, ""); #endif /* * We keep these set to "C" always, except transiently in pg_locale.c; see * that file for explanations. */ - setlocale(LC_MONETARY, "C"); - setlocale(LC_NUMERIC, "C"); - setlocale(LC_TIME, "C"); + pg_perm_setlocale(LC_MONETARY, "C"); + pg_perm_setlocale(LC_NUMERIC, "C"); + pg_perm_setlocale(LC_TIME, "C"); + + /* + * Now that we have absorbed as much as we wish to from the locale + * environment, remove any LC_ALL setting, so that the environment + * variables installed by pg_perm_setlocale have force. + */ + unsetenv("LC_ALL"); /* * Skip permission checks if we're just trying to do --help or --version; diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c index 303fec745a..055082e65f 100644 --- a/src/backend/utils/adt/pg_locale.c +++ b/src/backend/utils/adt/pg_locale.c @@ -4,7 +4,7 @@ * * Portions Copyright (c) 2002-2005, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/backend/utils/adt/pg_locale.c,v 1.32 2005/10/15 02:49:29 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/pg_locale.c,v 1.33 2005/12/28 23:22:51 tgl Exp $ * *----------------------------------------------------------------------- */ @@ -50,13 +50,10 @@ #include <locale.h> +#include "catalog/pg_control.h" #include "utils/pg_locale.h" -/* indicated whether locale information cache is valid */ -static bool CurrentLocaleConvValid = false; - - /* GUC storage area */ char *locale_messages; @@ -64,6 +61,118 @@ char *locale_monetary; char *locale_numeric; char *locale_time; +/* indicates whether locale information cache is valid */ +static bool CurrentLocaleConvValid = false; + +/* Environment variable storage area */ + +#define LC_ENV_BUFSIZE (LOCALE_NAME_BUFLEN + 20) + +static char lc_collate_envbuf[LC_ENV_BUFSIZE]; +static char lc_ctype_envbuf[LC_ENV_BUFSIZE]; +#ifdef LC_MESSAGES +static char lc_messages_envbuf[LC_ENV_BUFSIZE]; +#endif +static char lc_monetary_envbuf[LC_ENV_BUFSIZE]; +static char lc_numeric_envbuf[LC_ENV_BUFSIZE]; +static char lc_time_envbuf[LC_ENV_BUFSIZE]; + + +/* + * pg_perm_setlocale + * + * This is identical to the libc function setlocale(), with the addition + * that if the operation is successful, the corresponding LC_XXX environment + * variable is set to match. By setting the environment variable, we ensure + * that any subsequent use of setlocale(..., "") will preserve the settings + * made through this routine. Of course, LC_ALL must also be unset to fully + * ensure that, but that has to be done elsewhere after all the individual + * LC_XXX variables have been set correctly. (Thank you Perl for making this + * kluge necessary.) + */ +char * +pg_perm_setlocale(int category, const char *locale) +{ + char *result; + const char *envvar; + char *envbuf; + +#ifndef WIN32 + result = setlocale(category, locale); +#else + /* + * On Windows, setlocale(LC_MESSAGES) does not work, so just assume + * that the given value is good and set it in the environment variables. + * We must ignore attempts to set to "", which means "keep using the + * old environment value". + */ + if (category != LC_MESSAGES) + result = setlocale(category, locale); + else + { + result = (char *) locale; + if (locale == NULL || locale[0] == '\0') + return result; + } +#endif + + if (result == NULL) + return result; /* fall out immediately on failure */ + + switch (category) + { + case LC_COLLATE: + envvar = "LC_COLLATE"; + envbuf = lc_collate_envbuf; + break; + case LC_CTYPE: + envvar = "LC_CTYPE"; + envbuf = lc_ctype_envbuf; + break; +#ifdef LC_MESSAGES + case LC_MESSAGES: + envvar = "LC_MESSAGES"; + envbuf = lc_messages_envbuf; + break; +#endif + case LC_MONETARY: + envvar = "LC_MONETARY"; + envbuf = lc_monetary_envbuf; + break; + case LC_NUMERIC: + envvar = "LC_NUMERIC"; + envbuf = lc_numeric_envbuf; + break; + case LC_TIME: + envvar = "LC_TIME"; + envbuf = lc_time_envbuf; + break; + default: + elog(FATAL, "unrecognized LC category: %d", category); + envvar = NULL; /* keep compiler quiet */ + envbuf = NULL; + break; + } + + snprintf(envbuf, LC_ENV_BUFSIZE-1, "%s=%s", envvar, result); + +#ifndef WIN32 + if (putenv(envbuf)) + return NULL; +#else + /* + * On Windows, we need to modify both the process environment and the + * cached version in msvcrt + */ + if (!SetEnvironmentVariable(envvar, result)) + return NULL; + if (_putenv(envbuf)) + return NULL; +#endif + + return result; +} + /* GUC assign hooks */ @@ -123,48 +232,24 @@ locale_time_assign(const char *value, bool doit, GucSource source) const char * locale_messages_assign(const char *value, bool doit, GucSource source) { -#ifndef WIN32 - /* * LC_MESSAGES category does not exist everywhere, but accept it anyway + * + * On Windows, we can't even check the value, so the non-doit case + * is a no-op */ #ifdef LC_MESSAGES if (doit) { - if (!setlocale(LC_MESSAGES, value)) + if (!pg_perm_setlocale(LC_MESSAGES, value)) return NULL; } +#ifndef WIN32 else value = locale_xxx_assign(LC_MESSAGES, value, false, source); +#endif /* WIN32 */ #endif /* LC_MESSAGES */ return value; -#else /* WIN32 */ - - /* - * Win32 does not have working setlocale() for LC_MESSAGES. We can only - * use environment variables to change it (per gettext FAQ). This means - * we can't actually check the supplied value, so always assume it's good. - * Also, ignore attempts to set to "", which really means "keep using the - * old value". (Actually it means "use the environment value", but we are - * too lazy to try to implement that exactly.) - */ - if (doit && value[0]) - { - /* - * We need to modify both the process environment and the cached - * version in msvcrt - */ - static char env[128]; - - if (!SetEnvironmentVariable("LC_MESSAGES", value)) - return NULL; - - snprintf(env, sizeof(env) - 1, "LC_MESSAGES=%s", value); - if (_putenv(env)) - return NULL; - } - return value; -#endif /* WIN32 */ } diff --git a/src/include/utils/pg_locale.h b/src/include/utils/pg_locale.h index ef138d1bcb..7be6c24886 100644 --- a/src/include/utils/pg_locale.h +++ b/src/include/utils/pg_locale.h @@ -2,7 +2,7 @@ * * PostgreSQL locale utilities * - * $PostgreSQL: pgsql/src/include/utils/pg_locale.h,v 1.20 2005/03/16 00:02:49 momjian Exp $ + * $PostgreSQL: pgsql/src/include/utils/pg_locale.h,v 1.21 2005/12/28 23:22:51 tgl Exp $ * * Copyright (c) 2002-2005, PostgreSQL Global Development Group * @@ -31,6 +31,8 @@ extern const char *locale_numeric_assign(const char *value, extern const char *locale_time_assign(const char *value, bool doit, GucSource source); +extern char *pg_perm_setlocale(int category, const char *locale); + extern bool lc_collate_is_c(void); extern bool lc_ctype_is_c(void); |