]> git.ipfire.org Git - thirdparty/gettext.git/commitdiff
Override setlocale() and newlocale() on MacOS X and Windows platforms.
authorBruno Haible <bruno@clisp.org>
Tue, 22 Dec 2009 00:20:57 +0000 (01:20 +0100)
committerBruno Haible <bruno@clisp.org>
Sat, 26 Dec 2009 03:13:31 +0000 (04:13 +0100)
14 files changed:
NEWS
gettext-runtime/NEWS
gettext-runtime/intl/ChangeLog
gettext-runtime/intl/Makefile.in
gettext-runtime/intl/gettextP.h
gettext-runtime/intl/libgnuintl.h.in
gettext-runtime/intl/localename.c
gettext-runtime/intl/setlocale.c [new file with mode: 0644]
gettext-runtime/m4/ChangeLog
gettext-runtime/m4/intl.m4
gettext-tools/tests/ChangeLog
gettext-tools/tests/format-c-3-prg.c
gettext-tools/tests/format-c-4-prg.c
gettext-tools/tests/plural-1-prg.c

diff --git a/NEWS b/NEWS
index 6635376c778c183918282dd10bf0a7cff5ba5f4a..e7727a0b060dbe7c386293ccffddb7ea4d814597 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,10 @@
 Version 0.18 - January 2008
 
+* Runtime behaviour:
+  On MacOS X and Windows systems, <libintl.h> now extends setlocale() and
+  newlocale() so that their determination of the default locale considers
+  the choice the user has made in the system control panels.
+
 * PO file format:
   There is a new field 'Language' in the header entry.  It denotes the language
   code (plus optional country code) for the PO file.  This field can be used
index 17a3ed2bbf6db40ac6d9d21583a46ec6b221146b..06f504f22a043f1f0d2168dd062bce9cf13ead32 100644 (file)
@@ -1,3 +1,9 @@
+Version 0.18 - December 2009
+
+* On MacOS X and Windows systems, <libintl.h> now extends setlocale() and
+  newlocale() so that their determination of the default locale considers
+  the choice the user has made in the system control panels.
+\f
 Version 0.16.1 - November 2006
 
 * Bug fix in the gettext.m4 autoconf macros.
index 32b51881251433efc03e1a62328e89efbc114bcf..e425d34ad2d48b8af58646ec286ff7ee5be2d6d8 100644 (file)
@@ -1,3 +1,22 @@
+2009-12-21  Bruno Haible  <bruno@clisp.org>
+
+       Define override of setlocale and newlocale functions.
+       * gettextP.h (gl_locale_name_environ): New macro.
+       (_nl_locale_name_environ): New declaration.
+       * localename.c (gl_locale_name_environ): New function, extracted from
+       gl_locale_name_posix. Ignore dummy LANG values on MacOS X and Cygwin.
+       (gl_locale_name_posix): Invoke it.
+       (gl_locale_name_default): Add comments. Use Windows native API also on
+       Cygwin.
+       * libgnuintl.h.in: On MacOS X, include <xlocale.h>.
+       (setlocale, newlocale): New overrides.
+       * setlocale.c: New file.
+       * Makefile.in (SOURCES): Add setlocale.c.
+       (OBJECTS): Add setlocale.$lo.
+       (setlocale.lo): New rule.
+       (libgnuintl.h, libintl.h): Substitute also HAVE_NEWLOCALE.
+       (setlocale.$lo): Define dependencies.
+
 2009-12-21  Bruno Haible  <bruno@clisp.org>
 
        Update list of Win32 locale ids.
index d0a1f80ac2a5111aaa3addc2e64095bc1976e2f3..12390b7837054a5a484238d764697995f971c8b0 100644 (file)
@@ -157,6 +157,7 @@ SOURCES = \
   localename.c \
   log.c \
   printf.c \
+  setlocale.c \
   version.c \
   osdep.c \
   os2compat.c \
@@ -188,6 +189,7 @@ OBJECTS = \
   localename.$lo \
   log.$lo \
   printf.$lo \
+  setlocale.$lo \
   version.$lo \
   osdep.$lo \
   intl-compat.$lo
@@ -293,6 +295,8 @@ log.lo: $(srcdir)/log.c
        $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC --mode=compile $(COMPILE) $(srcdir)/log.c
 printf.lo: $(srcdir)/printf.c
        $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC --mode=compile $(COMPILE) $(srcdir)/printf.c
+setlocale.lo: $(srcdir)/setlocale.c
+       $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC --mode=compile $(COMPILE) $(srcdir)/setlocale.c
 version.lo: $(srcdir)/version.c
        $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC --mode=compile $(COMPILE) $(srcdir)/version.c
 osdep.lo: $(srcdir)/osdep.c
@@ -341,6 +345,7 @@ libgnuintl.h: $(srcdir)/libgnuintl.h.in
        sed -e '/IN_LIBGLOCALE/d' \
            -e 's,@''HAVE_POSIX_PRINTF''@,@HAVE_POSIX_PRINTF@,g' \
            -e 's,@''HAVE_ASPRINTF''@,@HAVE_ASPRINTF@,g' \
+           -e 's,@''HAVE_NEWLOCALE''@,@HAVE_NEWLOCALE@,g' \
            -e 's,@''HAVE_SNPRINTF''@,@HAVE_SNPRINTF@,g' \
            -e 's,@''HAVE_WPRINTF''@,@HAVE_WPRINTF@,g' \
          < $(srcdir)/libgnuintl.h.in \
@@ -358,6 +363,7 @@ libintl.h: $(srcdir)/libgnuintl.h.in
        sed -e '/IN_LIBGLOCALE/d' \
            -e 's,@''HAVE_POSIX_PRINTF''@,@HAVE_POSIX_PRINTF@,g' \
            -e 's,@''HAVE_ASPRINTF''@,@HAVE_ASPRINTF@,g' \
+           -e 's,@''HAVE_NEWLOCALE''@,@HAVE_NEWLOCALE@,g' \
            -e 's,@''HAVE_SNPRINTF''@,@HAVE_SNPRINTF@,g' \
            -e 's,@''HAVE_WPRINTF''@,@HAVE_WPRINTF@,g' \
          < $(srcdir)/libgnuintl.h.in > libintl.h
@@ -571,7 +577,7 @@ uninstall:
 info dvi ps pdf html:
 
 $(OBJECTS): ../config.h libgnuintl.h
-bindtextdom.$lo dcgettext.$lo dcigettext.$lo dcngettext.$lo dgettext.$lo dngettext.$lo finddomain.$lo gettext.$lo intl-compat.$lo loadmsgcat.$lo localealias.$lo ngettext.$lo textdomain.$lo: $(srcdir)/gettextP.h $(srcdir)/gmo.h $(srcdir)/loadinfo.h
+bindtextdom.$lo dcgettext.$lo dcigettext.$lo dcngettext.$lo dgettext.$lo dngettext.$lo finddomain.$lo gettext.$lo intl-compat.$lo loadmsgcat.$lo localealias.$lo ngettext.$lo setlocale.$lo textdomain.$lo: $(srcdir)/gettextP.h $(srcdir)/gmo.h $(srcdir)/loadinfo.h
 hash-string.$lo dcigettext.$lo loadmsgcat.$lo: $(srcdir)/hash-string.h
 explodename.$lo l10nflist.$lo: $(srcdir)/loadinfo.h
 dcigettext.$lo loadmsgcat.$lo plural.$lo plural-exp.$lo: $(srcdir)/plural-exp.h
index 39af4a7704055acf695d4e6d9a0aec90c17f6773..3555ede5422b77019a761ce97640a7ae17b5807f 100644 (file)
@@ -1,5 +1,5 @@
 /* Header describing internals of libintl library.
-   Copyright (C) 1995-1999, 2000-2007 Free Software Foundation, Inc.
+   Copyright (C) 1995-1999, 2000-2007, 2009 Free Software Foundation, Inc.
    Written by Ulrich Drepper <drepper@cygnus.com>, 1995.
 
    This program is free software; you can redistribute it and/or modify it
@@ -238,6 +238,9 @@ extern void _nl_locale_name_canonicalize (char *name);
 # define gl_locale_name_posix _nl_locale_name_posix
 extern const char *_nl_locale_name_posix (int category,
                                          const char *categoryname);
+# define gl_locale_name_environ _nl_locale_name_environ
+extern const char *_nl_locale_name_environ (int category,
+                                           const char *categoryname);
 # define gl_locale_name_default _nl_locale_name_default
 extern const char *_nl_locale_name_default (void);
 # define gl_locale_name _nl_locale_name
index 06ea95c752d5acda6ce94cc18e44c96d9649b5ce..6973c3a1f771d5e761ab8830c4b3a9a518991c7f 100644 (file)
@@ -1,5 +1,5 @@
 /* Message catalogs for internationalization.
-   Copyright (C) 1995-1997, 2000-2008 Free Software Foundation, Inc.
+   Copyright (C) 1995-1997, 2000-2009 Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or modify it
    under the terms of the GNU Library General Public License as published
@@ -20,6 +20,9 @@
 #define _LIBINTL_H 1
 
 #include <locale.h>
+#if (defined __APPLE__ && defined __MACH__) && @HAVE_NEWLOCALE@
+# include <xlocale.h>
+#endif
 
 /* The LC_MESSAGES locale category is the category used by the functions
    gettext() and dgettext().  It is specified in POSIX, but not in ANSI C.
@@ -419,6 +422,24 @@ extern int vswprintf (wchar_t *, size_t, const wchar_t *, va_list);
 #endif
 
 
+/* Support for the locale chosen by the user.  */
+#if (defined __APPLE__ && defined __MACH__) || defined _WIN32 || defined __WIN32__ || defined __CYGWIN__
+
+#undef setlocale
+#define setlocale libintl_setlocale
+extern char *setlocale (int, const char *);
+
+#if @HAVE_NEWLOCALE@
+
+#undef newlocale
+#define newlocale libintl_newlocale
+extern locale_t newlocale (int, const char *, locale_t);
+
+#endif
+
+#endif
+
+
 /* Support for relocatable packages.  */
 
 /* Sets the original and the current installation prefix of the package.
index 1310cb5d91b0757cbc978dd9884b8b6ddb434fa7..ceed70f52930d391450a19054db483e30b3a0c12 100644 (file)
@@ -31,9 +31,9 @@
 
 #include <stdlib.h>
 #include <locale.h>
+#include <string.h>
 
 #if HAVE_CFLOCALECOPYCURRENT || HAVE_CFPREFERENCESCOPYAPPVALUE
-# include <string.h>
 # include <CoreFoundation/CFString.h>
 # if HAVE_CFLOCALECOPYCURRENT
 #  include <CoreFoundation/CFLocale.h>
@@ -46,7 +46,7 @@
 # define WIN32_NATIVE
 #endif
 
-#ifdef WIN32_NATIVE
+#if defined WIN32_NATIVE || defined __CYGWIN__ /* WIN32 or Cygwin */
 # define WIN32_LEAN_AND_MEAN
 # include <windows.h>
 /* List of language codes, sorted by value:
@@ -1394,7 +1394,7 @@ gl_locale_name_canonicalize (char *name)
 #endif
 
 
-#ifdef WIN32_NATIVE
+#if defined WIN32_NATIVE || defined __CYGWIN__ /* WIN32 or Cygwin */
 
 /* Canonicalize a Win32 native locale name to a Unix locale name.
    NAME is a sufficiently large buffer.
@@ -2518,6 +2518,30 @@ gl_locale_name_posix (int category, const char *categoryname)
 #if defined HAVE_SETLOCALE && defined HAVE_LC_MESSAGES && defined HAVE_LOCALE_NULL
   return setlocale (category, NULL);
 #else
+  /* On other systems we ignore what setlocale reports and instead look at the
+     environment variables directly.  This is necessary
+       1. on systems which have a facility for customizing the default locale
+          (MacOS X, native Windows, Cygwin) and where the system's setlocale()
+          function ignores this default locale (MacOS X, Cygwin), in two cases:
+          a. when the user missed to use the setlocale() override from libintl
+             (for example by not including <libintl.h>),
+          b. when setlocale supports only the "C" locale, such as on Cygwin
+             1.5.x.  In this case even the override from libintl cannot help.
+       2. on all systems where setlocale supports only the "C" locale.  */
+  /* Strictly speaking, it is a POSIX violation to look at the environment
+     variables regardless whether setlocale has been called or not.  POSIX
+     says:
+         "For C-language programs, the POSIX locale shall be the
+          default locale when the setlocale() function is not called."
+     But we assume that all programs that use internationalized APIs call
+     setlocale (LC_ALL, "").  */
+  return gl_locale_name_environ (category, categoryname);
+#endif
+}
+
+const char *
+gl_locale_name_environ (int category, const char *categoryname)
+{
   const char *retval;
 
   /* Setting of LC_ALL overrides all other.  */
@@ -2531,10 +2555,21 @@ gl_locale_name_posix (int category, const char *categoryname)
   /* Last possibility is the LANG environment variable.  */
   retval = getenv ("LANG");
   if (retval != NULL && retval[0] != '\0')
-    return retval;
+    {
+#if HAVE_CFLOCALECOPYCURRENT || HAVE_CFPREFERENCESCOPYAPPVALUE
+      /* MacOS X 10.2 or newer.
+         Ignore invalid LANG value set by the Terminal application.  */
+      if (strcmp (retval, "UTF-8") != 0)
+#endif
+#if defined __CYGWIN__
+      /* Cygwin.
+         Ignore dummy LANG value set by ~/.profile.  */
+      if (strcmp (retval, "C.UTF-8") != 0)
+#endif
+        return retval;
+    }
 
   return NULL;
-#endif
 }
 
 const char *
@@ -2547,9 +2582,28 @@ gl_locale_name_default (void)
       implementation-defined locale.  Some implementations may provide
       facilities for local installation administrators to set the default
       locale, customizing it for each location.  POSIX:2001 does not require
-      such a facility.  */
+      such a facility.
+
+     The systems with such a facility are MacOS X and Windows: They provide a
+     GUI that allows the user to choose a locale.
+       - On MacOS X, by default, none of LC_* or LANG are set.  Starting with
+         MacOS X 10.4 or 10.5, LANG is set for processes launched by the
+         'Terminal' application (but sometimes to an incorrect value "UTF-8").
+         When no environment variable is set, setlocale (LC_ALL, "") uses the
+         "C" locale.
+       - On native Windows, by default, none of LC_* or LANG are set.
+         When no environment variable is set, setlocale (LC_ALL, "") uses the
+         locale chosen by the user.
+       - On Cygwin 1.5.x, by default, none of LC_* or LANG are set.
+         When no environment variable is set, setlocale (LC_ALL, "") uses the
+         "C" locale.
+       - On Cygwin 1.7, by default, LANG is set to "C.UTF-8" when the default
+         ~/.profile is executed.
+         When no environment variable is set, setlocale (LC_ALL, "") uses the
+         "C.UTF-8" locale, which operates in the same way as the "C" locale.
+  */
 
-#if !(HAVE_CFLOCALECOPYCURRENT || HAVE_CFPREFERENCESCOPYAPPVALUE || defined(WIN32_NATIVE))
+#if !(HAVE_CFLOCALECOPYCURRENT || HAVE_CFPREFERENCESCOPYAPPVALUE || defined WIN32_NATIVE || defined __CYGWIN__)
 
   /* The system does not have a way of setting the locale, other than the
      POSIX specified environment variables.  We use C as default locale.  */
@@ -2603,7 +2657,7 @@ gl_locale_name_default (void)
 
 # endif
 
-# if defined(WIN32_NATIVE) /* WIN32, not Cygwin */
+# if defined WIN32_NATIVE || defined __CYGWIN__ /* WIN32 or Cygwin */
   {
     LCID lcid;
 
diff --git a/gettext-runtime/intl/setlocale.c b/gettext-runtime/intl/setlocale.c
new file mode 100644 (file)
index 0000000..2a5e260
--- /dev/null
@@ -0,0 +1,1015 @@
+/* setlocale() function that respects the locale chosen by the user.
+   Copyright (C) 2009 Free Software Foundation, Inc.
+   Written by Bruno Haible <bruno@clisp.org>, 2009.
+
+   This program is free software; you can redistribute it and/or modify it
+   under the terms of the GNU Library General Public License as published
+   by the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+   USA.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Override setlocale() and newlocale() so that when the default locale is
+   requested (locale = "") and no relevant environment variable is set, the
+   locale chosen by the user is used.
+   This matters on MacOS X 10 and Windows.
+   See the comments in localename.c, function gl_locale_name_default.  */
+
+#include <locale.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* When building a DLL, we must export some functions.  Note that because
+   the functions are only defined for binary backward compatibility, we
+   don't need to use __declspec(dllimport) in any case.  */
+#if HAVE_VISIBILITY && BUILDING_DLL
+# define DLL_EXPORTED __attribute__((__visibility__("default")))
+#elif defined _MSC_VER && BUILDING_DLL
+# define DLL_EXPORTED __declspec(dllexport)
+#else
+# define DLL_EXPORTED
+#endif
+
+#include "gettextP.h"
+
+#if (defined __APPLE__ && defined __MACH__) || defined _WIN32 || defined __WIN32__ || defined __CYGWIN__
+
+# undef setlocale
+# undef newlocale
+
+/* Return string representation of locale category CATEGORY.  */
+static const char *
+category_to_name (int category)
+{
+  const char *retval;
+
+  switch (category)
+  {
+  case LC_COLLATE:
+    retval = "LC_COLLATE";
+    break;
+  case LC_CTYPE:
+    retval = "LC_CTYPE";
+    break;
+  case LC_MONETARY:
+    retval = "LC_MONETARY";
+    break;
+  case LC_NUMERIC:
+    retval = "LC_NUMERIC";
+    break;
+  case LC_TIME:
+    retval = "LC_TIME";
+    break;
+  case LC_MESSAGES:
+    retval = "LC_MESSAGES";
+    break;
+  default:
+    /* If you have a better idea for a default value let me know.  */
+    retval = "LC_XXX";
+  }
+
+  return retval;
+}
+
+# if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+
+/* The native Win32 setlocale() function expects locale names of the form
+   "German" or "German_Germany" or "DEU", but not "de" or "de_DE".  We need
+   to convert the names from the form with ISO 639 language code and ISO 3166
+   country code to the form with English names or with three-letter identifier.
+   The three-letter identifiers known by a Windows XP SP2 or SP3 are:
+     AFK  Afrikaans_South Africa.1252
+     ARA  Arabic_Saudi Arabia.1256
+     ARB  Arabic_Lebanon.1256
+     ARE  Arabic_Egypt.1256
+     ARG  Arabic_Algeria.1256
+     ARH  Arabic_Bahrain.1256
+     ARI  Arabic_Iraq.1256
+     ARJ  Arabic_Jordan.1256
+     ARK  Arabic_Kuwait.1256
+     ARL  Arabic_Libya.1256
+     ARM  Arabic_Morocco.1256
+     ARO  Arabic_Oman.1256
+     ARQ  Arabic_Qatar.1256
+     ARS  Arabic_Syria.1256
+     ART  Arabic_Tunisia.1256
+     ARU  Arabic_U.A.E..1256
+     ARY  Arabic_Yemen.1256
+     AZE  Azeri (Latin)_Azerbaijan.1254
+     BEL  Belarusian_Belarus.1251
+     BGR  Bulgarian_Bulgaria.1251
+     BSB  Bosnian_Bosnia and Herzegovina.1250
+     BSC  Bosnian (Cyrillic)_Bosnia and Herzegovina.1250  (wrong encoding!)
+     CAT  Catalan_Spain.1252
+     CHH  Chinese_Hong Kong S.A.R..950
+     CHI  Chinese_Singapore.936
+     CHS  Chinese_People's Republic of China.936
+     CHT  Chinese_Taiwan.950
+     CSY  Czech_Czech Republic.1250
+     CYM  Welsh_United Kingdom.1252
+     DAN  Danish_Denmark.1252
+     DEA  German_Austria.1252
+     DEC  German_Liechtenstein.1252
+     DEL  German_Luxembourg.1252
+     DES  German_Switzerland.1252
+     DEU  German_Germany.1252
+     ELL  Greek_Greece.1253
+     ENA  English_Australia.1252
+     ENB  English_Caribbean.1252
+     ENC  English_Canada.1252
+     ENG  English_United Kingdom.1252
+     ENI  English_Ireland.1252
+     ENJ  English_Jamaica.1252
+     ENL  English_Belize.1252
+     ENP  English_Republic of the Philippines.1252
+     ENS  English_South Africa.1252
+     ENT  English_Trinidad and Tobago.1252
+     ENU  English_United States.1252
+     ENW  English_Zimbabwe.1252
+     ENZ  English_New Zealand.1252
+     ESA  Spanish_Panama.1252
+     ESB  Spanish_Bolivia.1252
+     ESC  Spanish_Costa Rica.1252
+     ESD  Spanish_Dominican Republic.1252
+     ESE  Spanish_El Salvador.1252
+     ESF  Spanish_Ecuador.1252
+     ESG  Spanish_Guatemala.1252
+     ESH  Spanish_Honduras.1252
+     ESI  Spanish_Nicaragua.1252
+     ESL  Spanish_Chile.1252
+     ESM  Spanish_Mexico.1252
+     ESN  Spanish_Spain.1252
+     ESO  Spanish_Colombia.1252
+     ESP  Spanish_Spain.1252
+     ESR  Spanish_Peru.1252
+     ESS  Spanish_Argentina.1252
+     ESU  Spanish_Puerto Rico.1252
+     ESV  Spanish_Venezuela.1252
+     ESY  Spanish_Uruguay.1252
+     ESZ  Spanish_Paraguay.1252
+     ETI  Estonian_Estonia.1257
+     EUQ  Basque_Spain.1252
+     FAR  Farsi_Iran.1256
+     FIN  Finnish_Finland.1252
+     FOS  Faroese_Faroe Islands.1252
+     FPO  Filipino_Philippines.1252
+     FRA  French_France.1252
+     FRB  French_Belgium.1252
+     FRC  French_Canada.1252
+     FRL  French_Luxembourg.1252
+     FRM  French_Principality of Monaco.1252
+     FRS  French_Switzerland.1252
+     FYN  Frisian_Netherlands.1252
+     GLC  Galician_Spain.1252
+     HEB  Hebrew_Israel.1255
+     HRB  Croatian_Bosnia and Herzegovina.1250
+     HRV  Croatian_Croatia.1250
+     HUN  Hungarian_Hungary.1250
+     IND  Indonesian_Indonesia.1252
+     IRE  Irish_Ireland.1252
+     ISL  Icelandic_Iceland.1252
+     ITA  Italian_Italy.1252
+     ITS  Italian_Switzerland.1252
+     IUK  Inuktitut (Latin)_Canada.1252
+     JPN  Japanese_Japan.932
+     KKZ  Kazakh_Kazakhstan.1251
+     KOR  Korean_Korea.949
+     KYR  Kyrgyz_Kyrgyzstan.1251
+     LBX  Luxembourgish_Luxembourg.1252
+     LTH  Lithuanian_Lithuania.1257
+     LVI  Latvian_Latvia.1257
+     MKI  FYRO Macedonian_Former Yugoslav Republic of Macedonia.1251
+     MON  Mongolian_Mongolia.1251
+     MPD  Mapudungun_Chile.1252
+     MSB  Malay_Brunei Darussalam.1252
+     MSL  Malay_Malaysia.1252
+     MWK  Mohawk_Canada.1252
+     NLB  Dutch_Belgium.1252
+     NLD  Dutch_Netherlands.1252
+     NON  Norwegian-Nynorsk_Norway.1252
+     NOR  Norwegian (BokmÃ¥l)_Norway.1252
+     NSO  Northern Sotho_South Africa.1252
+     PLK  Polish_Poland.1250
+     PTB  Portuguese_Brazil.1252
+     PTG  Portuguese_Portugal.1252
+     QUB  Quechua_Bolivia.1252
+     QUE  Quechua_Ecuador.1252
+     QUP  Quechua_Peru.1252
+     RMC  Romansh_Switzerland.1252
+     ROM  Romanian_Romania.1250
+     RUS  Russian_Russia.1251
+     SKY  Slovak_Slovakia.1250
+     SLV  Slovenian_Slovenia.1250
+     SMA  Sami (Southern)_Norway.1252
+     SMB  Sami (Southern)_Sweden.1252
+     SME  Sami (Northern)_Norway.1252
+     SMF  Sami (Northern)_Sweden.1252
+     SMG  Sami (Northern)_Finland.1252
+     SMJ  Sami (Lule)_Norway.1252
+     SMK  Sami (Lule)_Sweden.1252
+     SMN  Sami (Inari)_Finland.1252
+     SMS  Sami (Skolt)_Finland.1252
+     SQI  Albanian_Albania.1250
+     SRB  Serbian (Cyrillic)_Serbia and Montenegro.1251
+     SRL  Serbian (Latin)_Serbia and Montenegro.1250
+     SRN  Serbian (Cyrillic)_Bosnia and Herzegovina.1251
+     SRS  Serbian (Latin)_Bosnia and Herzegovina.1250
+     SVE  Swedish_Sweden.1252
+     SVF  Swedish_Finland.1252
+     SWK  Swahili_Kenya.1252
+     THA  Thai_Thailand.874
+     TRK  Turkish_Turkey.1254
+     TSN  Tswana_South Africa.1252
+     TTT  Tatar_Russia.1251
+     UKR  Ukrainian_Ukraine.1251
+     URD  Urdu_Islamic Republic of Pakistan.1256
+     USA  English_United States.1252
+     UZB  Uzbek (Latin)_Uzbekistan.1254
+     VIT  Vietnamese_Viet Nam.1258
+     XHO  Xhosa_South Africa.1252
+     ZHH  Chinese_Hong Kong S.A.R..950
+     ZHI  Chinese_Singapore.936
+     ZHM  Chinese_Macau S.A.R..950
+     ZUL  Zulu_South Africa.1252
+ */
+
+/* Table from ISO 639 language code, optionally with country or script suffix,
+   to English name.
+   Keep in sync with the gl_locale_name_from_win32_LANGID function in
+   localename.c!  */
+struct table_entry
+{
+  const char *code;
+  const char *english;
+};
+static const struct table_entry language_table[] =
+  {
+    { "af", "Afrikaans" },
+    { "am", "Amharic" },
+    { "ar", "Arabic" },
+    { "arn", "Mapudungun" },
+    { "as", "Assamese" },
+    { "az@cyrillic", "Azeri (Cyrillic)" },
+    { "az@latin", "Azeri (Latin)" },
+    { "ba", "Bashkir" },
+    { "be", "Belarusian" },
+    { "ber", "Tamazight" },
+    { "ber@arabic", "Tamazight (Arabic)" },
+    { "ber@latin", "Tamazight (Latin)" },
+    { "bg", "Bulgarian" },
+    { "bin", "Edo" },
+    { "bn", "Bengali" },
+    { "bn_BD", "Bengali (Bangladesh)" },
+    { "bn_IN", "Bengali (India)" },
+    { "bnt", "Sutu" },
+    { "bo", "Tibetan" },
+    { "br", "Breton" },
+    { "bs", "BSB" }, /* "Bosnian (Latin)" */
+    { "bs@cyrillic", "BSC" }, /* Bosnian (Cyrillic) */
+    { "ca", "Catalan" },
+    { "chr", "Cherokee" },
+    { "co", "Corsican" },
+    { "cpe", "Hawaiian" },
+    { "cs", "Czech" },
+    { "cy", "Welsh" },
+    { "da", "Danish" },
+    { "de", "German" },
+    { "dsb", "Lower Sorbian" },
+    { "dv", "Divehi" },
+    { "el", "Greek" },
+    { "en", "English" },
+    { "es", "Spanish" },
+    { "et", "Estonian" },
+    { "eu", "Basque" },
+    { "fa", "Farsi" },
+    { "ff", "Fulfulde" },
+    { "fi", "Finnish" },
+    { "fo", "Faroese" }, /* "Faeroese" does not work */
+    { "fr", "French" },
+    { "fy", "Frisian" },
+    { "ga", "IRE" }, /* Gaelic (Ireland) */
+    { "gd", "Gaelic (Scotland)" },
+    { "gd", "Scottish Gaelic" },
+    { "gl", "Galician" },
+    { "gn", "Guarani" },
+    { "gsw", "Alsatian" },
+    { "gu", "Gujarati" },
+    { "ha", "Hausa" },
+    { "he", "Hebrew" },
+    { "hi", "Hindi" },
+    { "hr", "Croatian" },
+    { "hsb", "Upper Sorbian" },
+    { "hu", "Hungarian" },
+    { "hy", "Armenian" },
+    { "id", "Indonesian" },
+    { "ig", "Igbo" },
+    { "ii", "Yi" },
+    { "is", "Icelandic" },
+    { "it", "Italian" },
+    { "iu", "IUK" }, /* Inuktitut */
+    { "ja", "Japanese" },
+    { "ka", "Georgian" },
+    { "kk", "Kazakh" },
+    { "kl", "Greenlandic" },
+    { "km", "Cambodian" },
+    { "km", "Khmer" },
+    { "kn", "Kannada" },
+    { "ko", "Korean" },
+    { "kok", "Konkani" },
+    { "kr", "Kanuri" },
+    { "ks", "Kashmiri" },
+    { "ks_IN", "Kashmiri_India" },
+    { "ks_PK", "Kashmiri (Arabic)_Pakistan" },
+    { "ky", "Kyrgyz" },
+    { "la", "Latin" },
+    { "lb", "Luxembourgish" },
+    { "lo", "Lao" },
+    { "lt", "Lithuanian" },
+    { "lv", "Latvian" },
+    { "mi", "Maori" },
+    { "mk", "FYRO Macedonian" },
+    { "mk", "Macedonian" },
+    { "ml", "Malayalam" },
+    { "mn", "Mongolian" },
+    { "mni", "Manipuri" },
+    { "moh", "Mohawk" },
+    { "mr", "Marathi" },
+    { "ms", "Malay" },
+    { "mt", "Maltese" },
+    { "my", "Burmese" },
+    { "nb", "NOR" }, /* Norwegian BokmÃ¥l */
+    { "ne", "Nepali" },
+    { "nic", "Ibibio" },
+    { "nl", "Dutch" },
+    { "nn", "NON" }, /* Norwegian Nynorsk */
+    { "no", "Norwegian" },
+    { "nso", "Northern Sotho" },
+    { "nso", "Sepedi" },
+    { "oc", "Occitan" },
+    { "om", "Oromo" },
+    { "or", "Oriya" },
+    { "pa", "Punjabi" },
+    { "pap", "Papiamentu" },
+    { "pl", "Polish" },
+    { "prs", "Dari" },
+    { "ps", "Pashto" },
+    { "pt", "Portuguese" },
+    { "qu", "Quechua" },
+    { "qut", "K'iche'" },
+    { "rm", "Romansh" },
+    { "ro", "Romanian" },
+    { "ru", "Russian" },
+    { "rw", "Kinyarwanda" },
+    { "sa", "Sanskrit" },
+    { "sah", "Yakut" },
+    { "sd", "Sindhi" },
+    { "se", "Sami (Northern)" },
+    { "se", "Northern Sami" },
+    { "si", "Sinhalese" },
+    { "sk", "Slovak" },
+    { "sl", "Slovenian" },
+    { "sma", "Sami (Southern)" },
+    { "sma", "Southern Sami" },
+    { "smj", "Sami (Lule)" },
+    { "smj", "Lule Sami" },
+    { "smn", "Sami (Inari)" },
+    { "smn", "Inari Sami" },
+    { "sms", "Sami (Skolt)" },
+    { "sms", "Skolt Sami" },
+    { "so", "Somali" },
+    { "sq", "Albanian" },
+    { "sr", "Serbian (Latin)" },
+    { "sr@cyrillic", "SRB" }, /* Serbian (Cyrillic) */
+    { "sw", "Swahili" },
+    { "syr", "Syriac" },
+    { "ta", "Tamil" },
+    { "te", "Telugu" },
+    { "tg", "Tajik" },
+    { "th", "Thai" },
+    { "ti", "Tigrinya" },
+    { "tk", "Turkmen" },
+    { "tl", "Filipino" },
+    { "tn", "Tswana" },
+    { "tr", "Turkish" },
+    { "ts", "Tsonga" },
+    { "tt", "Tatar" },
+    { "ug", "Uighur" },
+    { "uk", "Ukrainian" },
+    { "ur", "Urdu" },
+    { "uz", "Uzbek" },
+    { "uz", "Uzbek (Latin)" },
+    { "uz@cyrillic", "Uzbek (Cyrillic)" },
+    { "ve", "Venda" },
+    { "vi", "Vietnamese" },
+    { "wen", "Sorbian" },
+    { "wo", "Wolof" },
+    { "xh", "Xhosa" },
+    { "yi", "Yiddish" },
+    { "yo", "Yoruba" },
+    { "zh", "Chinese" },
+    { "zu", "Zulu" }
+  };
+
+/* Table from ISO 3166 country code to English name.
+   Keep in sync with the gl_locale_name_from_win32_LANGID function in
+   localename.c!  */
+static const struct table_entry country_table[] =
+  {
+    { "AE", "U.A.E." },
+    { "AF", "Afghanistan" },
+    { "AL", "Albania" },
+    { "AM", "Armenia" },
+    { "AN", "Netherlands Antilles" },
+    { "AR", "Argentina" },
+    { "AT", "Austria" },
+    { "AU", "Australia" },
+    { "AZ", "Azerbaijan" },
+    { "BA", "Bosnia and Herzegovina" },
+    { "BD", "Bangladesh" },
+    { "BE", "Belgium" },
+    { "BG", "Bulgaria" },
+    { "BH", "Bahrain" },
+    { "BN", "Brunei Darussalam" },
+    { "BO", "Bolivia" },
+    { "BR", "Brazil" },
+    { "BT", "Bhutan" },
+    { "BY", "Belarus" },
+    { "BZ", "Belize" },
+    { "CA", "Canada" },
+    { "CG", "Congo" },
+    { "CH", "Switzerland" },
+    { "CI", "Cote d'Ivoire" },
+    { "CL", "Chile" },
+    { "CM", "Cameroon" },
+    { "CN", "People's Republic of China" },
+    { "CO", "Colombia" },
+    { "CR", "Costa Rica" },
+    { "CS", "Serbia and Montenegro" },
+    { "CZ", "Czech Republic" },
+    { "DE", "Germany" },
+    { "DK", "Denmark" },
+    { "DO", "Dominican Republic" },
+    { "DZ", "Algeria" },
+    { "EC", "Ecuador" },
+    { "EE", "Estonia" },
+    { "EG", "Egypt" },
+    { "ER", "Eritrea" },
+    { "ES", "Spain" },
+    { "ET", "Ethiopia" },
+    { "FI", "Finland" },
+    { "FO", "Faroe Islands" },
+    { "FR", "France" },
+    { "GB", "United Kingdom" },
+    { "GD", "Caribbean" },
+    { "GE", "Georgia" },
+    { "GL", "Greenland" },
+    { "GR", "Greece" },
+    { "GT", "Guatemala" },
+    { "HK", "Hong Kong" },
+    { "HK", "Hong Kong S.A.R." },
+    { "HN", "Honduras" },
+    { "HR", "Croatia" },
+    { "HT", "Haiti" },
+    { "HU", "Hungary" },
+    { "ID", "Indonesia" },
+    { "IE", "Ireland" },
+    { "IL", "Israel" },
+    { "IN", "India" },
+    { "IQ", "Iraq" },
+    { "IR", "Iran" },
+    { "IS", "Iceland" },
+    { "IT", "Italy" },
+    { "JM", "Jamaica" },
+    { "JO", "Jordan" },
+    { "JP", "Japan" },
+    { "KE", "Kenya" },
+    { "KG", "Kyrgyzstan" },
+    { "KH", "Cambodia" },
+    { "KR", "South Korea" },
+    { "KW", "Kuwait" },
+    { "KZ", "Kazakhstan" },
+    { "LA", "Laos" },
+    { "LB", "Lebanon" },
+    { "LI", "Liechtenstein" },
+    { "LK", "Sri Lanka" },
+    { "LT", "Lithuania" },
+    { "LU", "Luxembourg" },
+    { "LV", "Latvia" },
+    { "LY", "Libya" },
+    { "MA", "Morocco" },
+    { "MC", "Principality of Monaco" },
+    { "MD", "Moldava" },
+    { "MD", "Moldova" },
+    { "ME", "Montenegro" },
+    { "MK", "Former Yugoslav Republic of Macedonia" },
+    { "ML", "Mali" },
+    { "MM", "Myanmar" },
+    { "MN", "Mongolia" },
+    { "MO", "Macau S.A.R." },
+    { "MT", "Malta" },
+    { "MV", "Maldives" },
+    { "MX", "Mexico" },
+    { "MY", "Malaysia" },
+    { "NG", "Nigeria" },
+    { "NI", "Nicaragua" },
+    { "NL", "Netherlands" },
+    { "NO", "Norway" },
+    { "NP", "Nepal" },
+    { "NZ", "New Zealand" },
+    { "OM", "Oman" },
+    { "PA", "Panama" },
+    { "PE", "Peru" },
+    { "PH", "Philippines" },
+    { "PK", "Islamic Republic of Pakistan" },
+    { "PL", "Poland" },
+    { "PR", "Puerto Rico" },
+    { "PT", "Portugal" },
+    { "PY", "Paraguay" },
+    { "QA", "Qatar" },
+    { "RE", "Reunion" },
+    { "RO", "Romania" },
+    { "RS", "Serbia" },
+    { "RU", "Russia" },
+    { "RW", "Rwanda" },
+    { "SA", "Saudi Arabia" },
+    { "SE", "Sweden" },
+    { "SG", "Singapore" },
+    { "SI", "Slovenia" },
+    { "SK", "Slovak" },
+    { "SN", "Senegal" },
+    { "SO", "Somalia" },
+    { "SR", "Suriname" },
+    { "SV", "El Salvador" },
+    { "SY", "Syria" },
+    { "TH", "Thailand" },
+    { "TJ", "Tajikistan" },
+    { "TM", "Turkmenistan" },
+    { "TN", "Tunisia" },
+    { "TR", "Turkey" },
+    { "TT", "Trinidad and Tobago" },
+    { "TW", "Taiwan" },
+    { "TZ", "Tanzania" },
+    { "UA", "Ukraine" },
+    { "US", "United States" },
+    { "UY", "Uruguay" },
+    { "VA", "Vatican" },
+    { "VE", "Venezuela" },
+    { "VN", "Viet Nam" },
+    { "YE", "Yemen" },
+    { "ZA", "South Africa" },
+    { "ZW", "Zimbabwe" }
+  };
+
+/* Given a string STRING, find the set of indices i such that TABLE[i].code is
+   the given STRING.  It is a range [lo,hi-1].  */
+typedef struct { size_t lo; size_t hi; } range_t;
+static void
+search (const struct table_entry *table, size_t table_size, const char *string,
+        range_t *result)
+{
+  /* The table is sorted.  Perform a binary search.  */
+  size_t hi = table_size;
+  size_t lo = 0;
+  while (lo < hi)
+    {
+      /* Invariant:
+         for i < lo, strcmp (table[i].code, string) < 0,
+         for i >= hi, strcmp (table[i].code, string) > 0.  */
+      size_t mid = (hi + lo) >> 1; /* >= lo, < hi */
+      int cmp = strcmp (table[mid].code, string);
+      if (cmp < 0)
+        lo = mid + 1;
+      else if (cmp > 0)
+        hi = mid;
+      else
+        {
+          /* Found an i with
+               strcmp (language_table[i].code, string) == 0.
+             Find the entire interval of such i.  */
+          {
+            size_t i;
+
+            for (i = mid; i > lo; )
+              {
+                i--;
+                if (strcmp (table[i].code, string) < 0)
+                  {
+                    lo = i + 1;
+                    break;
+                  }
+              }
+          }
+          {
+            size_t i;
+
+            for (i = mid; i < hi; i++)
+              {
+                if (strcmp (table[i].code, string) > 0)
+                  {
+                    hi = i;
+                    break;
+                  }
+              }
+          }
+          /* The set of i with
+               strcmp (language_table[i].code, string) == 0
+             is the interval [lo, hi-1].  */
+          break;
+        }
+    }
+  result->lo = lo;
+  result->hi = hi;
+}
+
+/* Like setlocale, but accept also locale names in the form ll or ll_CC,
+   where ll is an ISO 639 language code and CC is an ISO 3166 country code.  */
+static char *
+setlocale_unixlike (int category, const char *locale)
+{
+  char *result;
+  char llCC_buf[64];
+  char ll_buf[64];
+  char CC_buf[64];
+
+  /* First, try setlocale with the original argument unchanged.  */
+  result = setlocale (category, locale);
+  if (result != NULL)
+    return result;
+
+  /* Otherwise, assume the argument is in the form
+       language[_territory][.codeset][@modifier]
+     and try to map it using the tables.  */
+  if (strlen (locale) < sizeof (llCC_buf))
+    {
+      /* Second try: Remove the codeset part.  */
+      {
+        const char *p = locale;
+        char *q = llCC_buf;
+
+        /* Copy the part before the dot.  */
+        for (; *p != '\0' && *p != '.'; p++, q++)
+          *q = *p;
+        if (*p == '.')
+          /* Skip the part up to the '@', if any.  */
+          for (; *p != '\0' && *p != '@'; p++)
+            ;
+        /* Copy the part starting with '@', if any.  */
+        for (; *p != '\0'; p++, q++)
+          *q = *p;
+        *q = '\0';
+      }
+      /* llCC_buf now contains
+           language[_territory][@modifier]
+       */
+      if (strcmp (llCC_buf, locale) != 0)
+        {
+          result = setlocale (category, llCC_buf);
+          if (result != NULL)
+            return result;
+        }
+      /* Look it up in language_table.  */
+      {
+        range_t range;
+        size_t i;
+
+        search (language_table,
+                sizeof (language_table) / sizeof (language_table[0]),
+                llCC_buf,
+                &range);
+
+        for (i = range.lo; i < range.hi; i++)
+          {
+            /* Try the replacement in language_table[i].  */
+            result = setlocale (category, language_table[i].english);
+            if (result != NULL)
+              return result;
+          }
+      }
+      /* Split language[_territory][@modifier]
+         into  ll_buf = language[@modifier]
+         and   CC_buf = territory
+       */
+      {
+        const char *underscore = strchr (llCC_buf, '_');
+        if (underscore != NULL)
+          {
+            const char *territory_start = underscore + 1;
+            const char *territory_end = strchr (territory_start, '@');
+            if (territory_end == NULL)
+              territory_end = territory_start + strlen (territory_start);
+
+            memcpy (ll_buf, llCC_buf, underscore - llCC_buf);
+            strcpy (ll_buf + (underscore - llCC_buf), territory_end);
+
+            memcpy (CC_buf, territory_start, territory_end - territory_start);
+            CC_buf[territory_end - territory_start] = '\0';
+
+            {
+              /* Look up ll_buf in language_table
+                 and CC_buf in country_table.  */
+              range_t language_range;
+
+              search (language_table,
+                      sizeof (language_table) / sizeof (language_table[0]),
+                      ll_buf,
+                      &language_range);
+              if (language_range.lo < language_range.hi)
+                {
+                  range_t country_range;
+
+                  search (country_table,
+                          sizeof (country_table) / sizeof (country_table[0]),
+                          CC_buf,
+                          &country_range);
+                  if (country_range.lo < country_range.hi)
+                    {
+                      size_t i;
+                      size_t j;
+
+                      for (i = language_range.lo; i < language_range.hi; i++)
+                        for (j = country_range.lo; j < country_range.hi; j++)
+                          {
+                            /* Concatenate the replacements.  */
+                            const char *part1 = language_table[i].english;
+                            size_t part1_len = strlen (part1);
+                            const char *part2 = country_table[j].english;
+                            size_t part2_len = strlen (part2) + 1;
+                            char buf[64+64];
+
+                            if (!(part1_len + 1 + part2_len <= sizeof (buf)))
+                              abort ();
+                            memcpy (buf, part1, part1_len);
+                            buf[part1_len] = '_';
+                            memcpy (buf + part1_len + 1, part2, part2_len);
+
+                            /* Try the concatenated replacements.  */
+                            result = setlocale (category, buf);
+                            if (result != NULL)
+                              return result;
+                          }
+                    }
+
+                  /* Try omitting the country entirely.  This may set a locale
+                     corresponding to the wrong country, but is better than
+                     failing entirely.  */
+                  {
+                    size_t i;
+
+                    for (i = language_range.lo; i < language_range.hi; i++)
+                      {
+                        /* Try only the language replacement.  */
+                        result =
+                          setlocale (category, language_table[i].english);
+                        if (result != NULL)
+                          return result;
+                      }
+                  }
+                }
+            }
+          }
+      }
+    }
+
+  /* Failed.  */
+  return NULL;
+}
+
+# else
+#  define setlocale_unixlike setlocale
+# endif
+
+# if LC_MESSAGES == 1729
+
+/* The system does not store an LC_MESSAGES locale category.  Do it here.  */
+static char lc_messages_name[64] = "C";
+
+/* Like setlocale, but support also LC_MESSAGES.  */
+static char *
+setlocale_single (int category, const char *locale)
+{
+  if (category == LC_MESSAGES)
+    {
+      if (locale != NULL)
+        {
+          lc_messages_name[sizeof (lc_messages_name) - 1] = '\0';
+          strncpy (lc_messages_name, locale, sizeof (lc_messages_name) - 1);
+        }
+      return lc_messages_name;
+    }
+  else
+    return setlocale_unixlike (category, locale);
+}
+
+# else
+#  define setlocale_single setlocale_unixlike
+# endif
+
+DLL_EXPORTED
+char *
+libintl_setlocale (int category, const char *locale)
+{
+  if (locale != NULL && locale[0] == '\0')
+    {
+      /* A request to the set the current locale to the default locale.  */
+      if (category == LC_ALL)
+        {
+          /* Set LC_CTYPE first.  Then the other categories.  */
+          static int const categories[] =
+            {
+              LC_NUMERIC,
+              LC_TIME,
+              LC_COLLATE,
+              LC_MONETARY,
+              LC_MESSAGES
+            };
+          char *saved_locale;
+          const char *base_name;
+          unsigned int i;
+
+          /* Back up the old locale, in case one of the steps fails.  */
+          saved_locale = setlocale (LC_ALL, NULL);
+          if (saved_locale == NULL)
+            return NULL;
+          saved_locale = strdup (saved_locale);
+          if (saved_locale == NULL)
+            return NULL;
+
+          /* Set LC_CTYPE category.  Set all other categories (except possibly
+             LC_MESSAGES) to the same value in the same call; this is likely to
+             save calls.  */
+          base_name =
+            gl_locale_name_environ (LC_CTYPE, category_to_name (LC_CTYPE));
+          if (base_name == NULL)
+            base_name = gl_locale_name_default ();
+
+          if (setlocale_unixlike (LC_ALL, base_name) == NULL)
+            goto fail;
+
+          for (i = 0; i < sizeof (categories) / sizeof (categories[0]); i++)
+            {
+              int cat = categories[i];
+              const char *name;
+
+              name = gl_locale_name_environ (cat, category_to_name (cat));
+              if (name == NULL)
+                name = gl_locale_name_default ();
+
+              /* If name is the same as base_name, it has already been set
+                 through the setlocale call before the loop.  */
+              if (strcmp (name, base_name) != 0
+# if LC_MESSAGES == 1729
+                  || cat == LC_MESSAGES
+# endif
+                 )
+                if (setlocale_single (cat, name) == NULL)
+                  goto fail;
+            }
+
+          /* All steps were successful.  */
+          free (saved_locale);
+          return setlocale (LC_ALL, NULL);
+
+        fail:
+          if (saved_locale[0] != '\0') /* don't risk an endless recursion */
+            setlocale (LC_ALL, saved_locale);
+          free (saved_locale);
+          return NULL;
+        }
+      else
+        {
+          const char *name =
+            gl_locale_name_environ (category, category_to_name (category));
+          if (name == NULL)
+            name = gl_locale_name_default ();
+
+          return setlocale_single (category, name);
+        }
+    }
+  else
+    return setlocale_single (category, locale);
+}
+
+# if HAVE_NEWLOCALE
+
+DLL_EXPORTED
+locale_t
+libintl_newlocale (int category_mask, const char *locale, locale_t base)
+{
+  if (category_mask != 0 && locale != NULL && locale[0] == '\0')
+    {
+      /* A request to construct a locale_t object that refers to the default
+         locale.  */
+
+      /* Set LC_CTYPE first.  Then the other categories.  */
+      static struct { int cat; int mask; } const categories[] =
+        {
+          { LC_CTYPE,    LC_CTYPE_MASK },
+          { LC_NUMERIC,  LC_NUMERIC_MASK },
+          { LC_TIME,     LC_TIME_MASK },
+          { LC_COLLATE,  LC_COLLATE_MASK },
+          { LC_MONETARY, LC_MONETARY_MASK },
+          { LC_MESSAGES, LC_MESSAGES_MASK }
+        };
+
+      locale_t orig_base = base;
+
+      if ((LC_ALL_MASK & ~category_mask) == 0)
+        {
+          const char *base_name;
+          unsigned int i;
+
+          /* Set LC_CTYPE category.  Set all other categories (except possibly
+             LC_MESSAGES) to the same value in the same call; this is likely to
+             save calls.  */
+          base_name =
+            gl_locale_name_environ (LC_CTYPE, category_to_name (LC_CTYPE));
+          if (base_name == NULL)
+            base_name = gl_locale_name_default ();
+
+          base = newlocale (LC_ALL_MASK, base_name, base);
+          if (base == NULL)
+            return NULL;
+
+          for (i = 1; i < sizeof (categories) / sizeof (categories[0]); i++)
+            {
+              int category = categories[i].cat;
+              int category_mask = categories[i].mask;
+              const char *name;
+
+              name =
+                gl_locale_name_environ (category, category_to_name (category));
+              if (name == NULL)
+                name = gl_locale_name_default ();
+
+              /* If name is the same as base_name, it has already been set
+                 through the setlocale call before the loop.  */
+              if (strcmp (name, base_name) != 0)
+                {
+                  locale_t copy = newlocale (category_mask, name, base);
+                  if (copy == NULL)
+                    goto fail;
+                  /* No need to call freelocale (base) if copy != base; the
+                     newlocale function already takes care of doing it.  */
+                  base = copy;
+                }
+            }
+        }
+      else
+        {
+          unsigned int i;
+
+          for (i = 0; i < sizeof (categories) / sizeof (categories[0]); i++)
+            {
+              int cat_mask = categories[i].mask;
+
+              if ((category_mask & cat_mask) != 0)
+                {
+                  int cat = categories[i].cat;
+                  const char *name;
+                  locale_t copy;
+
+                  name = gl_locale_name_environ (cat, category_to_name (cat));
+                  if (name == NULL)
+                    name = gl_locale_name_default ();
+
+                  copy = newlocale (cat_mask, name, base);
+                  if (copy == NULL)
+                    goto fail;
+                  /* No need to call freelocale (base) if copy != base; the
+                     newlocale function already takes care of doing it.  */
+                  base = copy;
+                }
+            }
+        }
+
+      /* All steps were successful.  */
+      return base;
+
+    fail:
+      if (base != NULL && orig_base == NULL)
+        {
+          int saved_errno = errno;
+          freelocale (base);
+          errno = saved_errno;
+        }
+      return NULL;
+    }
+  else
+    return newlocale (category_mask, locale, base);
+}
+
+# endif
+
+#endif
index 21ca5af464781806cc29e938fe61517d9a208dcb..7631ec37ecd7f8385605538ba8885584f9f82185 100644 (file)
@@ -1,3 +1,9 @@
+2009-12-21  Bruno Haible  <bruno@clisp.org>
+
+       Define override of setlocale and newlocale functions.
+       * intl.m4 (AM_INTL_SUBDIR): Check also for newlocale. Set
+       HAVE_NEWLOCALE.
+
 2009-12-12  Bruno Haible  <bruno@clisp.org>
 
        * fcntl_h.m4: Untabify.
index a0325c3de6111ae6d86ef300117ea77532a4e3fb..3abacd6a8e97b63ec0eff062f46fe2e725c9ef9b 100644 (file)
@@ -1,4 +1,4 @@
-# intl.m4 serial 14 (gettext-0.18)
+# intl.m4 serial 15 (gettext-0.18)
 dnl Copyright (C) 1995-2009 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -56,8 +56,8 @@ AC_DEFUN([AM_INTL_SUBDIR],
        [Define as the type of the result of subtracting two pointers, if the system doesn't define it.])
     ])
   AC_CHECK_HEADERS([stddef.h stdlib.h string.h])
-  AC_CHECK_FUNCS([asprintf fwprintf putenv setenv setlocale snprintf \
-    strnlen wcslen wcsnlen mbrtowc wcrtomb])
+  AC_CHECK_FUNCS([asprintf fwprintf newlocale putenv setenv setlocale \
+    snprintf strnlen wcslen wcsnlen mbrtowc wcrtomb])
 
   dnl Use the _snprintf function only if it is declared (because on NetBSD it
   dnl is defined as a weak alias of snprintf; we prefer to use the latter).
@@ -88,6 +88,12 @@ AC_DEFUN([AM_INTL_SUBDIR],
     HAVE_SNPRINTF=0
   fi
   AC_SUBST([HAVE_SNPRINTF])
+  if test "$ac_cv_func_newlocale" = yes; then
+    HAVE_NEWLOCALE=1
+  else
+    HAVE_NEWLOCALE=0
+  fi
+  AC_SUBST([HAVE_NEWLOCALE])
   if test "$ac_cv_func_wprintf" = yes; then
     HAVE_WPRINTF=1
   else
index 50edabf6d4c2f0c988ecd2e3d339819cf475f5c3..f6469961c145153c761198a4a7b72e31ff97a9c1 100644 (file)
@@ -1,3 +1,9 @@
+2009-12-21  Bruno Haible  <bruno@clisp.org>
+
+       * format-c-3-prg.c: Don't use setlocale override.
+       * format-c-4-prg.c: Likewise.
+       * plural-1-prg.c: Likewise.
+
 2009-12-13  Bruno Haible  <bruno@clisp.org>
 
        Avoid a test failure on MacOS X 10.5.
index ea8997eddd1d6bc46757b8ad65427efa421450d8..d44831113acd0a0b51da5b4701feb383da7e77ec 100644 (file)
@@ -1,5 +1,5 @@
 /* Test program, used by the format-c-3 test.
-   Copyright (C) 2002 Free Software Foundation, Inc.
+   Copyright (C) 2002, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 #undef _LIBINTL_H
 #include "libgnuintl.h"
 
+/* Disable the override of setlocale that libgnuintl.h activates on MacOS X
+   and Windows.  This test relies on the fake setlocale function in
+   setlocale.c.  */
+#undef setlocale
+
 #define _(string) gettext (string)
 
 /* Fallback definition.  */
index 007f853fc7d4f8b309fca3f9549636f787ae67c5..45a222b3b648b9a2b26d5ca0a2d1ca36b9ed715f 100644 (file)
@@ -1,5 +1,5 @@
 /* Test program, used by the format-c-4 test.
-   Copyright (C) 2002 Free Software Foundation, Inc.
+   Copyright (C) 2002, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 #undef _LIBINTL_H
 #include "libgnuintl.h"
 
+/* Disable the override of setlocale that libgnuintl.h activates on MacOS X
+   and Windows.  This test relies on the fake setlocale function in
+   setlocale.c.  */
+#undef setlocale
+
 #define _(string) gettext (string)
 
 /* Fallback definition.  */
index 2bbd315163acdd6945c4cb10d917b6d1e55a9312..88e24674eca92ca746e266d690587ed8a6be2ec5 100644 (file)
@@ -1,5 +1,5 @@
 /* Test program, used by the plural-1 test.
-   Copyright (C) 2001-2002 Free Software Foundation, Inc.
+   Copyright (C) 2001-2002, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 #undef _LIBINTL_H
 #include "libgnuintl.h"
 
+/* Disable the override of setlocale that libgnuintl.h activates on MacOS X
+   and Windows.  This test relies on the fake setlocale function in
+   setlocale.c.  */
+#undef setlocale
+
 int
 main (int argc, char *argv[])
 {