Index: pg_locale.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/utils/adt/pg_locale.c,v retrieving revision 1.42 diff -c -c -r1.42 pg_locale.c *** pg_locale.c 23 Sep 2008 09:20:36 -0000 1.42 --- pg_locale.c 7 Dec 2008 15:53:58 -0000 *************** *** 101,112 **** --- 101,303 ---- * LC_XXX variables have been set correctly. (Thank you Perl for making this * kluge necessary.) */ + + #if defined(WIN32) && defined(_MSC_VER) + typedef int (_cdecl *PUTENVPROC)(const char *); + #if defined(LC_MESSAGES) && (_MSC_VER >= 1400) + #define _TYPE_LOCALE_T_AVAILABLE + #include + typedef const char * (_cdecl *NLLOCALENAMEPOSIXPROC)(int, const char *); + + /* + * Never use DLLGETVERSIONPROC typedef'd in Shlwapi.h. + * It's problematic and would cause a crash. + */ + typedef HRESULT (_cdecl *DLLGETVERSIONFUNC)(DLLVERSIONINFO *); + + static char get_lang[64] = ""; + static char get_country[64] = ""; + static LCID glcid = (LCID) -1; + + static BOOL CALLBACK lclist(LPTSTR lcname) + { + static char tmp_country[128] = ""; + DWORD lcid; + char llang[32], lcountry[32]; + + sscanf_s(lcname, "%x", &lcid); + GetLocaleInfoA(lcid, LOCALE_SISO639LANGNAME, llang, sizeof(llang)); + if (0 != _stricmp(llang, get_lang)) + return TRUE; + GetLocaleInfoA(lcid, LOCALE_SISO3166CTRYNAME, lcountry, sizeof(lcountry)); + if ('\0' == get_country[0]) + { + if (SUBLANG_DEFAULT == SUBLANGID(LANGIDFROMLCID(lcid))) + { + glcid = lcid; + return FALSE; + } + return TRUE; + } + if (0 == _stricmp(lcountry, get_country)) + { + glcid = lcid; + return FALSE; + } + return TRUE; + } + #endif /* defined(LC_MESSAGES) && (_MSC_VER >= 1400) */ + + /* + * This function can accept Windows or ISO style locale name. + * Calls SetThreadLocale() if neccesary. + * Sets the ISO style LC_MESSAGES environment variable using + * _putenv() in msvcrt.dll which may be referenced by gettext. + * Returns ISO style LC_MESSAGES locale name. + */ + static char *adjust_LC_MESSAGES(const char *locale, char *envbuf, char *new_locale) + { + #ifdef _TYPE_LOCALE_T_AVAILABLE + char *rtn_locale = locale; + bool isClocale = false; + LCID lcid = (LCID) -1; + int usecategory = LC_CTYPE; + _locale_t loct = NULL; + + if (0 == stricmp("c", locale) || + 0 == stricmp("posix", locale)) + isClocale = true; + if (isClocale) + loct = _create_locale(usecategory, "C"); + else + loct = _create_locale(usecategory, locale); + + if (NULL != loct) + { + lcid = loct->locinfo->lc_handle[usecategory]; + if (0 == lcid) + lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT); + _free_locale(loct); + } + else + { + char del[16]; + int scount = sscanf_s(locale, "%[^_-.]%[_-.]%[^.]", get_lang, sizeof(get_lang), del, sizeof(del), get_country, sizeof(get_country)); + switch (scount) + { + case 1: + case 2: + get_country[0] = '\0'; + break; + case 3: + if ('.' == del[0]) + get_country[0] = '\0'; + break; + } + glcid = (LCID) -1; + EnumSystemLocales(lclist, LCID_SUPPORTED); + lcid = glcid; + } + if ((LCID) -1 == lcid) + return NULL; + else + { + /* + * Though GNU gettext FAQ says setting the environtment LC_MESSAGES etc variables + * determines the locale used by gettext, it doesn't seem true for 0.14.4.1952 + * binary version of libintl3.dll. To make sure we do a test and set not only the + * LC_MESSAGES variable but also the current thread's locale if the test shows that + * the DLL doesn't follow the specification(FAQ). + */ + static bool initCheck = true, setThread = false, setEnv = true; + HMODULE hModule; + DLLGETVERSIONFUNC DllGetVersion; + + if (initCheck && + NULL != (hModule = LoadLibrary("libintl3.dll")) && + NULL != (DllGetVersion = (DLLGETVERSIONFUNC) GetProcAddress(hModule, "DllGetVersion"))) + { + DLLVERSIONINFO2 dvi; + HRESULT hr; + NLLOCALENAMEPOSIXPROC locale_name_posix; + + dvi.info1.cbSize = sizeof(dvi); + hr = DllGetVersion((DLLVERSIONINFO *) &dvi); + if (SUCCEEDED(hr) && + MAKEDLLVERULL(0, 14, 4, 1952) == dvi.ullVersion && + NULL != (locale_name_posix = (NLLOCALENAMEPOSIXPROC) GetProcAddress(hModule, "_nl_locale_name_posix"))) + { + + /* + * Here we test the DLL's behavior. + * + * Does the function _nl_locale_name_posix() which is + * internally called by gettext() call setlocale()? + */ + if (NULL != locale_name_posix(LC_CTYPE, "ignore_this_value") && + NULL == locale_name_posix(LC_MESSAGES, "ignore_this_value")) + { + setThread = true; + elog(WARNING, "linking a mal-configured libintl3.dll?"); + } + } + } + initCheck = false; + if (NULL != hModule) + FreeLibrary(hModule); + if (setThread) + { + SetThreadLocale(lcid); + } + if (setEnv && !isClocale) + { + char isolang[64], isocrty[64]; + + GetLocaleInfoA(lcid, LOCALE_SISO639LANGNAME, isolang, sizeof(isolang)); + GetLocaleInfoA(lcid, LOCALE_SISO3166CTRYNAME, isocrty, sizeof(isocrty)); + snprintf(new_locale, LC_ENV_BUFSIZE - 1, "%s_%s", isolang, isocrty); + rtn_locale = new_locale; + } + } + #endif /* _TYPE_LOCALE_T_AVAILABLE */ + #if (_MSC_VER >= 1300) + { + /* + * Each MSVC version has its own _putenv() in its runtime library msvcrXX.dll. + * We call _putenv() in msvcrt.dll so as to be referenced by GnuWin32 library. + */ + HMODULE hmodule; + static PUTENVPROC putenvFunc = NULL; + + if (NULL == putenvFunc) + { + if (hmodule = GetModuleHandle("msvcrt"), NULL == hmodule) + return NULL; + putenvFunc = (PUTENVPROC)GetProcAddress(hmodule, "_putenv"); + } + if (NULL == putenvFunc) + return NULL; + snprintf(envbuf, LC_ENV_BUFSIZE - 1, "%s=%s", "LC_MESSAGES", rtn_locale); + if (putenvFunc(envbuf)) + return NULL; + /* Refresh msgid pool maintained by gettext? */ + textdomain("postgres"); + } + #endif /* _MSC_VER >= 1300 */ + + return rtn_locale; + } + #endif /* defined(WIN32) && defined(_MSC_VER) */ + char * pg_perm_setlocale(int category, const char *locale) { char *result; const char *envvar; char *envbuf; + #ifdef LC_MESSAGES + char newlocale[128]; + #endif /* LC_MESSAGES */ #ifndef WIN32 result = setlocale(category, locale); *************** *** 147,152 **** --- 338,347 ---- case LC_MESSAGES: envvar = "LC_MESSAGES"; envbuf = lc_messages_envbuf; + #if defined(WIN32) && defined(_MSC_VER) + if (result = adjust_LC_MESSAGES(result, envbuf, newlocale), NULL == result) + return NULL; + #endif /* WIN32 */ break; #endif case LC_MONETARY: *************** *** 165,170 **** --- 360,366 ---- elog(FATAL, "unrecognized LC category: %d", category); envvar = NULL; /* keep compiler quiet */ envbuf = NULL; + return NULL; break; } *************** *** 182,189 **** if (!SetEnvironmentVariable(envvar, result)) return NULL; if (_putenv(envbuf)) ! return NULL; ! #endif return result; } --- 378,385 ---- if (!SetEnvironmentVariable(envvar, result)) return NULL; if (_putenv(envbuf)) ! result = NULL; ! #endif /* WIN32 */ return result; }