bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

new module 'setlocale'


From: Bruno Haible
Subject: new module 'setlocale'
Date: Sat, 12 Feb 2011 17:05:58 +0100
User-agent: KMail/1.9.9

> The major problem on mingw is that setlocale(LC_ALL,"") does not look at the
> environment variables. On POSIX systems it does, but on mingw it doesn't.
> 
> The second problem is the detection whether setlocale succeeded: When passed
> a string with supported language and country, but unsupported encoding,
> setlocale succeeds but leaves the LC_CTYPE facet in an unusable state.
> Example: setlocale (LC_ALL, "French_France.65001") succeeds and then
> setlocale (LC_ALL, NULL) is (newlines inserted by me):
> LC_COLLATE=French_France.65001;
> LC_CTYPE=C;
> LC_MONETARY=French_France.65001;
> LC_NUMERIC=French_France.65001;
> LC_TIME=French_France.65001
> 
> The third problem is that the setlocale function accepts different names,
> not based on ISO 639 and ISO 3166.

After locale-??.m4 recognizes the mingw locale names, now 15 tests fail.
This is because our tests assume the setlocale(LC_ALL,"") looks at
the LC_ALL environment variable.

It would be quite cumbersome to have to rewrite all tests to pass the
locale as a command-line argument instead of through LC_ALL. We want to
be able to write not only our code, but also our unit tests, in the
POSIXy way. Therefore it's necessary to override setlocale() on mingw.

But gettext also contains an override of setlocale(), declared in libintl.h.
These overrides would clash. It's better to have gnulib override libintl
than vice versa. Hence the override in gnulib needs to be at least as good
as the override in libintl, on the platforms where both exist (namely mingw).

I'm therefore taking the code from gettext. It provides a workaround against
the problems 1 and 3.


2011-02-11  Bruno Haible  <address@hidden>

        New module 'setlocale'.
        * lib/locale.in.h (setlocale): New declaration.
        * lib/setlocale.c: New file, based on
        gettext/gettext-runtime/intl/setlocale.c.
        * m4/setlocale.m4: New file.
        * m4/locale_h.m4 (gl_LOCALE_H): Test whether setlocale is declared.
        (gl_LOCALE_H_DEFAULTS): Initialize GNULIB_SETLOCALE, REPLACE_SETLOCALE.
        * modules/locale (Makefile.am): Substitute GNULIB_SETLOCALE,
        REPLACE_SETLOCALE.
        * modules/setlocale: New file.
        * tests/test-locale-c++.cc: Test the declaration of setlocale.
        * doc/posix-functions/setlocale.texi: Mention the new module.

diff --git a/doc/posix-functions/setlocale.texi 
b/doc/posix-functions/setlocale.texi
index f722df0..6e64584 100644
--- a/doc/posix-functions/setlocale.texi
+++ b/doc/posix-functions/setlocale.texi
@@ -4,10 +4,18 @@
 
 POSIX specification:@* 
@url{http://www.opengroup.org/onlinepubs/9699919799/functions/setlocale.html}
 
-Gnulib module: ---
+Gnulib module: setlocale
 
 Portability problems fixed by Gnulib:
 @itemize
address@hidden
+On Windows platforms (excluding Cygwin), @code{setlocale(@var{category},NULL)}
+ignores the environment variables @code{LC_ALL}, @address@hidden, and
address@hidden
address@hidden
+On Windows platforms (excluding Cygwin), @code{setlocale} understands different
+locale names, that are not based on ISO 639 language names and ISO 3166 country
+names.
 @end itemize
 
 Portability problems not fixed by Gnulib:
@@ -15,7 +23,10 @@ Portability problems not fixed by Gnulib:
 @item
 On Cygwin 1.5.x, which doesn't have locales,
 @code{setlocale(LC_ALL,NULL)} always returns @code{"C"}.
-
 @item
 On Cygwin 1.7.0, only the charset portion of a locale designation is honored.
address@hidden
+On Windows platforms (excluding Cygwin), @code{setlocale(LC_ALL,@var{name})}
+succeeds and sets the LC_CTYPE category to @samp{C} when it does not support
+the encoding, instead of failing.
 @end itemize
diff --git a/lib/locale.in.h b/lib/locale.in.h
index 6fa1acc..c7d39cd 100644
--- a/lib/locale.in.h
+++ b/lib/locale.in.h
@@ -47,6 +47,26 @@
 # define LC_MESSAGES 1729
 #endif
 
+#if @GNULIB_SETLOCALE@
+# if @REPLACE_SETLOCALE@
+#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#   undef setlocale
+#   define setlocale rpl_setlocale
+#  endif
+_GL_FUNCDECL_RPL (setlocale, char *, (int category, const char *locale));
+_GL_CXXALIAS_RPL (setlocale, char *, (int category, const char *locale));
+# else
+_GL_CXXALIAS_SYS (setlocale, char *, (int category, const char *locale));
+# endif
+_GL_CXXALIASWARN (setlocale);
+#elif defined GNULIB_POSIXCHECK
+# undef setlocale
+# if HAVE_RAW_DECL_SETLOCALE
+_GL_WARN_ON_USE (setlocale, "setlocale works differently on native Windows - "
+                 "use gnulib module setlocale for portability");
+# endif
+#endif
+
 #if @GNULIB_DUPLOCALE@
 # if @REPLACE_DUPLOCALE@
 #  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
diff --git a/lib/setlocale.c b/lib/setlocale.c
new file mode 100644
index 0000000..9fe1aa9
--- /dev/null
+++ b/lib/setlocale.c
@@ -0,0 +1,892 @@
+/* Set the current locale.
+   Copyright (C) 2009, 2011 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
+   the Free Software Foundation; either version 3 of the License, 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 General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <address@hidden>, 2009.  */
+
+#include <config.h>
+
+/* Override setlocale() so that when the default locale is requested
+   (locale = ""), the environment variables LC_ALL, LC_*, and LANG are
+   considered.
+   Also include all the functionality from libintl's setlocale() override.  */
+
+/* Please keep this file in sync with
+   gettext/gettext-runtime/intl/setlocale.c !  */
+
+/* Specification.  */
+#include <locale.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "localename.h"
+
+#if 1
+
+# undef setlocale
+
+/* 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" },
+    { "address@hidden", "Azeri (Cyrillic)" },
+    { "address@hidden", "Azeri (Latin)" },
+    { "ba", "Bashkir" },
+    { "be", "Belarusian" },
+    { "ber", "Tamazight" },
+    { "address@hidden", "Tamazight (Arabic)" },
+    { "address@hidden", "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)" */
+    { "address@hidden", "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)" },
+    { "address@hidden", "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)" },
+    { "address@hidden", "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
+       address@hidden
+     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
+           address@hidden
+       */
+      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 address@hidden
+         into  ll_buf = address@hidden
+         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
+
+char *
+rpl_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);
+}
+
+#endif
diff --git a/m4/locale_h.m4 b/m4/locale_h.m4
index a1a0870..4d0f894 100644
--- a/m4/locale_h.m4
+++ b/m4/locale_h.m4
@@ -1,4 +1,4 @@
-# locale_h.m4 serial 12
+# locale_h.m4 serial 13
 dnl Copyright (C) 2007, 2009-2011 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -70,7 +70,8 @@ AC_DEFUN([gl_LOCALE_H],
 #if HAVE_XLOCALE_H
 # include <xlocale.h>
 #endif
-   ]], [duplocale])
+    ]],
+    [setlocale duplocale])
 ])
 
 dnl Unconditionally enables the replacement of <locale.h>.
@@ -91,8 +92,10 @@ AC_DEFUN([gl_LOCALE_MODULE_INDICATOR],
 
 AC_DEFUN([gl_LOCALE_H_DEFAULTS],
 [
+  GNULIB_SETLOCALE=0;  AC_SUBST([GNULIB_SETLOCALE])
   GNULIB_DUPLOCALE=0;  AC_SUBST([GNULIB_DUPLOCALE])
   dnl Assume proper GNU behavior unless another module says otherwise.
   HAVE_DUPLOCALE=1;    AC_SUBST([HAVE_DUPLOCALE])
+  REPLACE_SETLOCALE=0; AC_SUBST([REPLACE_SETLOCALE])
   REPLACE_DUPLOCALE=0; AC_SUBST([REPLACE_DUPLOCALE])
 ])
diff --git a/m4/setlocale.m4 b/m4/setlocale.m4
new file mode 100644
index 0000000..6672c34
--- /dev/null
+++ b/m4/setlocale.m4
@@ -0,0 +1,27 @@
+# setlocale.m4 serial 1
+dnl Copyright (C) 2011 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_FUNC_SETLOCALE],
+[
+  AC_REQUIRE([gl_LOCALE_H_DEFAULTS])
+  AC_REQUIRE([AC_CANONICAL_HOST])
+  case "$host_os" in
+    dnl On native Windows systems, setlocale(category,NULL) does not look at
+    dnl the environment variables LC_ALL, category, and LANG.
+    mingw*) REPLACE_SETLOCALE=1 ;;
+  esac
+  if test $REPLACE_SETLOCALE = 1; then
+    gl_REPLACE_LOCALE_H
+    AC_LIBOBJ([setlocale])
+    gl_PREREQ_SETLOCALE
+  fi
+])
+
+# Prerequisites of lib/setlocale.c.
+AC_DEFUN([gl_PREREQ_SETLOCALE],
+[
+  :
+])
diff --git a/modules/locale b/modules/locale
index b80b513..9c350cf 100644
--- a/modules/locale
+++ b/modules/locale
@@ -28,9 +28,11 @@ locale.h: locale.in.h $(CXXDEFS_H) $(ARG_NONNULL_H) 
$(WARN_ON_USE_H)
              -e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \
              -e 's|@''PRAGMA_COLUMNS''@|@PRAGMA_COLUMNS@|g' \
              -e 's|@''NEXT_LOCALE_H''@|$(NEXT_LOCALE_H)|g' \
+             -e 's|@''GNULIB_SETLOCALE''@|$(GNULIB_SETLOCALE)|g' \
              -e 's|@''GNULIB_DUPLOCALE''@|$(GNULIB_DUPLOCALE)|g' \
              -e 's|@''HAVE_DUPLOCALE''@|$(HAVE_DUPLOCALE)|g' \
              -e 's|@''HAVE_XLOCALE_H''@|$(HAVE_XLOCALE_H)|g' \
+             -e 's|@''REPLACE_SETLOCALE''@|$(REPLACE_SETLOCALE)|g' \
              -e 's|@''REPLACE_DUPLOCALE''@|$(REPLACE_DUPLOCALE)|g' \
              -e '/definitions of _GL_FUNCDECL_RPL/r $(CXXDEFS_H)' \
              -e '/definition of _GL_ARG_NONNULL/r $(ARG_NONNULL_H)' \
diff --git a/modules/setlocale b/modules/setlocale
new file mode 100644
index 0000000..5d3e58b
--- /dev/null
+++ b/modules/setlocale
@@ -0,0 +1,25 @@
+Description:
+setlocale() function: set the current locale.
+
+Files:
+lib/setlocale.c
+m4/setlocale.m4
+
+Depends-on:
+locale
+localename
+
+configure.ac:
+gl_FUNC_SETLOCALE
+gl_LOCALE_MODULE_INDICATOR([setlocale])
+
+Makefile.am:
+
+Include:
+<locale.h>
+
+License:
+LGPL
+
+Maintainer:
+Bruno Haible
diff --git a/tests/test-locale-c++.cc b/tests/test-locale-c++.cc
index c9c39fe..06b9a32 100644
--- a/tests/test-locale-c++.cc
+++ b/tests/test-locale-c++.cc
@@ -24,6 +24,10 @@
 #include "signature.h"
 
 
+#if GNULIB_TEST_SETLOCALE
+SIGNATURE_CHECK (GNULIB_NAMESPACE::setlocale, char *, (int, const char *));
+#endif
+
 #if GNULIB_TEST_DUPLOCALE && HAVE_DUPLOCALE
 SIGNATURE_CHECK (GNULIB_NAMESPACE::duplocale, locale_t, (locale_t));
 #endif
-- 
1.6.3.2

-- 
In memoriam France Bloch-Sérazin 
<http://en.wikipedia.org/wiki/France_Bloch-Sérazin>



reply via email to

[Prev in Thread] Current Thread [Next in Thread]