Re: [BUGS] BUG #4186: set lc_messages does not work - Mailing list pgsql-hackers

From Magnus Hagander
Subject Re: [BUGS] BUG #4186: set lc_messages does not work
Date
Msg-id 497490A2.8070001@hagander.net
Whole thread Raw
In response to Re: [BUGS] BUG #4186: set lc_messages does not work  (Hiroshi Inoue <inoue@tpf.co.jp>)
Responses Re: [BUGS] BUG #4186: set lc_messages does not work  (Hiroshi Inoue <inoue@tpf.co.jp>)
List pgsql-hackers
Hiroshi Inoue wrote:
> Hiroshi Inoue wrote:
>> Magnus Hagander wrote:
>>>
>>> Do you want to send an updated patch for it, or do you want me to look
>>> at it?
>>
>> I would send a new patch to which I added a simple ISO style check for
>>  locale names.
>
> Attached is a new patch.
> I added a simple ISO style locale name check.
> Avoided codings like *NULL == somthing*.
> It also includes the changes to mbutils.c and elog.c which fix
> recently introduced bug by the domain name change from "postgres"
> to "postgres-8.4".

Attached is a further updated version of this patch. Changes include:

* Actually avoid NULL==something style coding everywhere (I think)
* Avoid coding like "if (ret = putenvFunc(envval), 0 != ret)"
* Per discussion, remove pg_locale.c specific coding, put it in
src/port, and do a redefine so we *always* use these new functions
* Some further minor cleanups
* Removed the change to mbutils.c and elog.c - those are a separate
issue, will deal with those as a separate patch.

There still needs to be some error checking added in IsoLocaleName(),
but this is a start.

Can someone please test this? :-) I can't get NLS to work at all in my
Windows install (this is nothing new, it's always been tricky) right
now. I'll work on trying to fix that, but help with testing would be
very useful meanwhile.

(It passes build on MSVC, it's just runtime i can't check)

//Magnus

*** a/configure
--- b/configure
***************
*** 17920,17925 **** case " $LIBOBJS " in
--- 17920,17931 ----
  esac

  case " $LIBOBJS " in
+   *" win32env.$ac_objext "* ) ;;
+   *) LIBOBJS="$LIBOBJS win32env.$ac_objext"
+  ;;
+ esac
+
+ case " $LIBOBJS " in
    *" win32error.$ac_objext "* ) ;;
    *) LIBOBJS="$LIBOBJS win32error.$ac_objext"
   ;;
*** a/configure.in
--- b/configure.in
***************
*** 1274,1279 **** AC_REPLACE_FUNCS(gettimeofday)
--- 1274,1280 ----
  AC_LIBOBJ(kill)
  AC_LIBOBJ(open)
  AC_LIBOBJ(rand)
+ AC_LIBOBJ(win32env)
  AC_LIBOBJ(win32error)
  AC_DEFINE([HAVE_SYMLINK], 1,
            [Define to 1 if you have the `symlink' function.])
*** a/src/backend/utils/adt/pg_locale.c
--- b/src/backend/utils/adt/pg_locale.c
***************
*** 55,60 ****
--- 55,63 ----
  #include "utils/memutils.h"
  #include "utils/pg_locale.h"

+ #ifdef WIN32
+ #include <shlwapi.h>
+ #endif

  #define        MAX_L10N_DATA        80

***************
*** 89,94 **** static char lc_monetary_envbuf[LC_ENV_BUFSIZE];
--- 92,101 ----
  static char lc_numeric_envbuf[LC_ENV_BUFSIZE];
  static char lc_time_envbuf[LC_ENV_BUFSIZE];

+ #ifdef WIN32
+ static char *IsoLocaleName(const char *); /* MSVC specific */
+ #endif
+

  /*
   * pg_perm_setlocale
***************
*** 148,155 **** pg_perm_setlocale(int category, const char *locale)
          case LC_MESSAGES:
              envvar = "LC_MESSAGES";
              envbuf = lc_messages_envbuf;
              break;
! #endif
          case LC_MONETARY:
              envvar = "LC_MONETARY";
              envbuf = lc_monetary_envbuf;
--- 155,167 ----
          case LC_MESSAGES:
              envvar = "LC_MESSAGES";
              envbuf = lc_messages_envbuf;
+ #ifdef WIN32
+             result = IsoLocaleName(locale);
+             if (result == NULL)
+                 result = locale;
+ #endif /* WIN32 */
              break;
! #endif /* LC_MESSAGES */
          case LC_MONETARY:
              envvar = "LC_MONETARY";
              envbuf = lc_monetary_envbuf;
***************
*** 166,190 **** pg_perm_setlocale(int category, const char *locale)
              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;
  }
--- 178,190 ----
              elog(FATAL, "unrecognized LC category: %d", category);
              envvar = NULL;        /* keep compiler quiet */
              envbuf = NULL;
!             return NULL;
      }

      snprintf(envbuf, LC_ENV_BUFSIZE - 1, "%s=%s", envvar, result);

      if (putenv(envbuf))
          return NULL;

      return result;
  }
***************
*** 599,601 **** cache_locale_time(void)
--- 599,650 ----

      CurrentLCTimeValid = true;
  }
+
+
+ #ifdef WIN32
+ /*
+  *    Convert Windows locale name to the ISO formatted one
+  *    if possible.
+  *
+  *    This function returns NULL if conversion is impossible,
+  *    otherwise returns the pointer to a static area which
+  *    contains the iso formatted locale name.
+  */
+ static
+ char *IsoLocaleName(const char *winlocname)
+ {
+ #if (_MSC_VER >= 1400) /* VC8.0 or later */
+
+     static char iso_lc_messages[32];
+     _locale_t    loct = NULL;
+
+     if (pg_strcasecmp("c", winlocname) == 0 ||
+         pg_strcasecmp("posix", winlocname) == 0)
+     {
+         strncpy(iso_lc_messages, "C", sizeof(iso_lc_messages));
+         return iso_lc_messages;
+     }
+
+     loct = _create_locale(LC_CTYPE, winlocname);
+     if (loct != NULL)
+     {
+         char    isolang[32], isocrty[32];
+         LCID    lcid;
+
+         lcid = loct->locinfo->lc_handle[LC_CTYPE];
+         if (lcid == 0)
+             lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
+         _free_locale(loct);
+
+         GetLocaleInfoA(lcid, LOCALE_SISO639LANGNAME, isolang, sizeof(isolang));
+         GetLocaleInfoA(lcid, LOCALE_SISO3166CTRYNAME, isocrty, sizeof(isocrty));
+         snprintf(iso_lc_messages, sizeof(iso_lc_messages) - 1, "%s_%s", isolang, isocrty);
+         return iso_lc_messages;
+     }
+     return NULL;
+ #else
+     return NULL; /* does nothing. */
+ #endif /* _MSC_VER >= 1400 */
+ }
+ #endif /* WIN32 */
+
*** a/src/include/port/win32.h
--- b/src/include/port/win32.h
***************
*** 291,296 **** extern int    pgwin32_is_service(void);
--- 291,301 ----
  /* in port/win32error.c */
  extern void _dosmaperr(unsigned long);

+ /* in port/win32env.c */
+ extern int pgwin32_putenv(const char *);
+ extern void pgwin32_unsetenv(const char *);
+ #define putenv(x) pgwin32_putenv(x)
+ #define unsetenv(x) pgwin32_unsetenv(x)

  /* Things that exist in MingW headers, but need to be added to MSVC */
  #ifdef WIN32_ONLY_COMPILER
*** /dev/null
--- b/src/port/win32env.c
***************
*** 0 ****
--- 1,92 ----
+ /*-------------------------------------------------------------------------
+  *
+  * win32env.c
+  *    putenv() and unsetenv() for win32, that updates both process
+  *    environment and the cached versions in (potentially multiple)
+  *    MSVCRT.
+  *
+  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  *
+  * IDENTIFICATION
+  *      $PostgreSQL$
+  *
+  *-------------------------------------------------------------------------
+  */
+
+ #include "c.h"
+
+ int pgwin32_putenv(const char *envval)
+ {
+     char   *envcpy;
+     char   *cp;
+
+     /*
+      * Each version of MSVCRT has its own _putenv() call in the runtime
+      * library.
+      *
+      * If we're in VC 7.0 or later (means != mingw), update in
+      * the 6.0 MSVCRT.DLL environment as well, to work with third party
+      * libraries linked against it (such as gnuwin32 libraries).
+      */
+ #if defined(_MSC_VER) && (_MSC_VER >= 1300)
+     typedef int         (_cdecl *PUTENVPROC)(const char *);
+     HMODULE                hmodule;
+     static PUTENVPROC    putenvFunc = NULL;
+     int                    ret;
+
+     if (putenvFunc == NULL)
+     {
+         hmodule = GetModuleHandle("msvcrt");
+         if (hmodule == NULL)
+             return 1;
+         putenvFunc = (PUTENVPROC)GetProcAddress(hmodule, "_putenv");
+         if (putenvFunc == NULL)
+             return 1;
+     }
+     ret = putenvFunc(envval);
+     if (ret != 0)
+         return ret;
+ #endif /* _MSC_VER >= 1300 */
+
+
+     /*
+      * Update the process environment - to make modifications visible
+      * to child processes.
+      *
+      * Need a copy of the string so we can modify it.
+      */
+     envcpy = strdup(envval);
+     cp = strchr(envcpy, '=');
+     if (cp == NULL)
+         return -1;
+     *cp = '\0';
+     cp++;
+     if (strlen(cp) == 0)
+         cp = NULL;
+     if (!SetEnvironmentVariable(envcpy, cp))
+     {
+         free(envcpy);
+         return -1;
+     }
+     free(envcpy);
+
+     /* Finally, update our "own" cache */
+     return _putenv(envval);
+ }
+
+ void
+ pgwin32_unsetenv(const char *name)
+ {
+     char   *envbuf;
+
+     envbuf = (char *) malloc(strlen(name)+2);
+     if (!envbuf)
+         return;
+
+     sprintf(envbuf, "%s=", name);
+     pgwin32_putenv(envbuf);
+     free(envbuf);
+ }
+
*** a/src/tools/msvc/Mkvcbuild.pm
--- b/src/tools/msvc/Mkvcbuild.pm
***************
*** 44,53 **** sub mkvcbuild

      our @pgportfiles = qw(
        chklocale.c crypt.c fseeko.c getrusage.c inet_aton.c random.c srandom.c
!       unsetenv.c getaddrinfo.c gettimeofday.c kill.c open.c rand.c
        snprintf.c strlcat.c strlcpy.c copydir.c dirmod.c exec.c noblock.c path.c pipe.c
        pgsleep.c pgstrcasecmp.c qsort.c qsort_arg.c sprompt.c thread.c
!       getopt.c getopt_long.c dirent.c rint.c win32error.c);

      $libpgport = $solution->AddProject('libpgport','lib','misc');
      $libpgport->AddDefine('FRONTEND');
--- 44,53 ----

      our @pgportfiles = qw(
        chklocale.c crypt.c fseeko.c getrusage.c inet_aton.c random.c srandom.c
!       getaddrinfo.c gettimeofday.c kill.c open.c rand.c
        snprintf.c strlcat.c strlcpy.c copydir.c dirmod.c exec.c noblock.c path.c pipe.c
        pgsleep.c pgstrcasecmp.c qsort.c qsort_arg.c sprompt.c thread.c
!       getopt.c getopt_long.c dirent.c rint.c win32env.c win32error.c);

      $libpgport = $solution->AddProject('libpgport','lib','misc');
      $libpgport->AddDefine('FRONTEND');

pgsql-hackers by date:

Previous
From: Simon Riggs
Date:
Subject: Re: Hot Standby dev build (v8)
Next
From: Alvaro Herrera
Date:
Subject: Re: Review: B-Tree emulation for GIN