Re: BUG #4186: set lc_messages does not work - Mailing list pgsql-bugs

From Hiroshi Inoue
Subject Re: BUG #4186: set lc_messages does not work
Date
Msg-id 493BFE54.7050300@tpf.co.jp
Whole thread Raw
In response to Re: BUG #4186: set lc_messages does not work  (Bruce Momjian <bruce@momjian.us>)
List pgsql-bugs
Bruce Momjian wrote:
> Tom Lane wrote:
>> Magnus Hagander <magnus@hagander.net> writes:
>>> Thomas H. wrote:
>>>> so at least that explains the "changed" behaviour. nevertheless,
>>>> LC_MESSAGES seems to be defunct - with the "locale" folder present,
>>>> pg always picks the os' language and ignores the lc_message value.
>>> This looks like I can reproduce though, at least on cvs head. Did this
>>> work for you in previous versions?
>> Maybe we were using a different build of gettext in the previous
>> releases, one that didn't look at the same info as the current code?
>>
>> Anyway the patch mentioned at the start of the thread
>> http://archives.postgresql.org/pgsql-patches/2008-02/msg00038.php
>> purports to fix this.  It doesn't seem to have gotten reviewed
>> though.
>
> Where are we on this?

Saito-san and I have been working on this item.
The attached patch is for MSVC version and does the following
when built under vc8 or later version MSVC.

1. Accepts Windows or ISO style locale name.
2. _putenv the ISO style locale name as LC_MESSAGES environemnt
    variable so as to be referenced by the gettext library.
    Note that we have to call _putenv() in msvcrt.dll not in
    each MSVC version's runtime library msvcrxx.dll.
3. Calls SetThreadLocale() if necessary.
    The currently used libintl3.dll(0.14.4.1592) seems to need
    this handling. ISTM it is badly built and ignores the setting
    of step 2.

regards,
Hiroshi Inoue





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 <shlwapi.h>
+ 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;
  }

pgsql-bugs by date:

Previous
From: Peter Eisentraut
Date:
Subject: Re: BUG #3818: Cross compilation problems
Next
From: "Regina Obe"
Date:
Subject: BUG #4567: Clustering on GIST INDEX clobbers records in table intermittently