Welcome to mirror list, hosted at ThFree Co, Russian Federation.

cygwin.com/git/newlib-cygwin.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCorinna Vinschen <corinna@vinschen.de>2010-02-17 18:01:56 +0300
committerCorinna Vinschen <corinna@vinschen.de>2010-02-17 18:01:56 +0300
commitce4f5f76abcdc6c889bfc3815bd106e9088591cd (patch)
tree22256c81d19d7f418dad7b36fef356acde2db981
parentc4889b234155f454d98cae5293d81877d92cef1e (diff)
* Makefile.in (CYGWIN_BINS): Rename getlocale to locale.
* getlocale.c: Rename to ... * locale.cc: Revamp to add full functionality of POSIX locale(1) tool, as far as Cygwin supports it. * utils.sgml (getlocale): Move and rename to ... (locale): Accommodate new functionality.
-rw-r--r--winsup/utils/ChangeLog9
-rw-r--r--winsup/utils/Makefile.in2
-rw-r--r--winsup/utils/getlocale.c236
-rw-r--r--winsup/utils/locale.cc622
-rw-r--r--winsup/utils/utils.sgml142
5 files changed, 719 insertions, 292 deletions
diff --git a/winsup/utils/ChangeLog b/winsup/utils/ChangeLog
index 8b006f455..4c622207c 100644
--- a/winsup/utils/ChangeLog
+++ b/winsup/utils/ChangeLog
@@ -1,3 +1,12 @@
+2010-02-17 Corinna Vinschen <corinna@vinschen.de>
+
+ * Makefile.in (CYGWIN_BINS): Rename getlocale to locale.
+ * getlocale.c: Rename to ...
+ * locale.cc: Revamp to add full functionality of POSIX locale(1) tool,
+ as far as Cygwin supports it.
+ * utils.sgml (getlocale): Move and rename to ...
+ (locale): Accommodate new functionality.
+
2010-02-13 Ilguiz Latypov <ilatypov@infradead.org>
* cygpath.cc (do_pathconv): Fix potential crash.
diff --git a/winsup/utils/Makefile.in b/winsup/utils/Makefile.in
index 04ce39fad..165ac60aa 100644
--- a/winsup/utils/Makefile.in
+++ b/winsup/utils/Makefile.in
@@ -52,7 +52,7 @@ MINGW_CXX := ${srcdir}/mingw ${CXX} -I${updir}
# List all binaries to be linked in Cygwin mode. Each binary on this list
# must have a corresponding .o of the same name.
-CYGWIN_BINS := ${addsuffix .exe,cygpath getfacl getlocale ldd kill mkgroup \
+CYGWIN_BINS := ${addsuffix .exe,cygpath getfacl ldd locale kill mkgroup \
mkpasswd mount passwd ps regtool setfacl setmetamode ssp umount}
# List all binaries to be linked in MinGW mode. Each binary on this list
diff --git a/winsup/utils/getlocale.c b/winsup/utils/getlocale.c
deleted file mode 100644
index 9d6a3827a..000000000
--- a/winsup/utils/getlocale.c
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * Copyright (c) 2010, Corinna Vinschen
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-#include <stdio.h>
-#include <getopt.h>
-#include <string.h>
-#include <wchar.h>
-#include <locale.h>
-#define WINVER 0x0601
-#include <windows.h>
-
-extern char *__progname;
-
-void usage (FILE *, int) __attribute__ ((noreturn));
-
-void
-usage (FILE * stream, int status)
-{
- fprintf (stream,
- "Usage: %s [-asuh]\n"
- "Print default locale or list of all supported locales\n"
- "\n"
- "Options:\n"
- "\n"
- " -a, --all List all available supported locales\n"
- " -s, --system Print system default locale\n"
- " (default is current user default locale)\n"
- " -u, --utf Attach \".UTF-8\" to the result\n"
- " -h, --help this text\n",
- __progname);
- exit (status);
-}
-
-struct option longopts[] = {
- {"all", no_argument, NULL, 'a'},
- {"system", no_argument, NULL, 's'},
- {"utf", no_argument, NULL, 'u'},
- {"help", no_argument, NULL, 'h'},
- {0, no_argument, NULL, 0}
-};
-const char *opts = "ahsu";
-
-int
-getlocale (LCID lcid, char *name)
-{
- char iso639[10];
- char iso3166[10];
-
- iso3166[0] = '\0';
- if (!GetLocaleInfo (lcid, LOCALE_SISO639LANGNAME, iso639, 10))
- return 0;
- GetLocaleInfo (lcid, LOCALE_SISO3166CTRYNAME, iso3166, 10);
- sprintf (name, "%s%s%s", iso639, lcid > 0x3ff ? "_" : "",
- lcid > 0x3ff ? iso3166 : "");
- return 1;
-}
-
-int main (int argc, char **argv)
-{
- int opt;
- LCID lcid = LOCALE_USER_DEFAULT;
- int all = 0;
- const char *utf = "";
- char name[32];
- DWORD cp;
-
- setlocale (LC_ALL, "");
- while ((opt = getopt_long (argc, argv, opts, longopts, NULL)) != EOF)
- switch (opt)
- {
- case 'a':
- all = 1;
- break;
- case 's':
- lcid = LOCALE_SYSTEM_DEFAULT;
- break;
- case 'u':
- utf = ".UTF-8";
- break;
- case 'h':
- usage (stdout, 0);
- break;
- default:
- usage (stderr, 1);
- break;
- }
- if (all)
- {
- unsigned lang, sublang;
-
- for (lang = 1; lang <= 0xff; ++lang)
- {
- struct {
- wchar_t language[256];
- wchar_t country[256];
- char loc[32];
- } loc_list[32];
- int lcnt = 0;
-
- for (sublang = 1; sublang <= 0x3f; ++sublang)
- {
- lcid = (sublang << 10) | lang;
- if (getlocale (lcid, name))
- {
- wchar_t language[256];
- wchar_t country[256];
- int i;
- char *c, loc[32];
- wchar_t wbuf[9];
-
- /* Go figure. Even the English name of a language or
- locale might contain native characters. */
- GetLocaleInfoW (lcid, LOCALE_SENGLANGUAGE, language, 256);
- GetLocaleInfoW (lcid, LOCALE_SENGCOUNTRY, country, 256);
- /* Avoid dups */
- for (i = 0; i < lcnt; ++ i)
- if (!wcscmp (loc_list[i].language, language)
- && !wcscmp (loc_list[i].country, country))
- break;
- if (i < lcnt)
- continue;
- if (lcnt < 32)
- {
- wcscpy (loc_list[lcnt].language, language);
- wcscpy (loc_list[lcnt].country, country);
- }
- c = stpcpy (loc, name);
- /* Convert old sr_SP silently to sr_CS on old systems.
- Make sure sr_CS country is in recent shape. */
- if (lang == LANG_SERBIAN
- && (sublang == SUBLANG_SERBIAN_LATIN
- || sublang == SUBLANG_SERBIAN_CYRILLIC))
- {
- c = stpcpy (loc, "sr_CS");
- wcscpy (country, L"Serbia and Montenegro (Former)");
- }
- /* Now check certain conditions to figure out if that
- locale requires a modifier. */
- if (lang == LANG_SERBIAN && !strncmp (loc, "sr_", 3)
- && wcsstr (language, L"(Latin)"))
- stpcpy (c, "@latin");
- else if (lang == LANG_UZBEK
- && sublang == SUBLANG_UZBEK_CYRILLIC)
- stpcpy (c, "@cyrillic");
- /* Avoid more dups */
- for (i = 0; i < lcnt; ++ i)
- if (!strcmp (loc_list[i].loc, loc))
- {
- lcnt++;
- break;
- }
- if (i < lcnt)
- continue;
- if (lcnt < 32)
- strcpy (loc_list[lcnt++].loc, loc);
- /* Print */
- printf ("%-16s %ls (%ls)\n", loc, language, country);
- /* Check for locales which sport a modifier for
- changing the codeset and other stuff. */
- if (lang == LANG_BELARUSIAN
- && sublang == SUBLANG_BELARUSIAN_BELARUS)
- stpcpy (c, "@latin");
- else if (lang == LANG_TATAR
- && sublang == SUBLANG_TATAR_RUSSIA)
- stpcpy (c, "@iqtelif");
- else if (GetLocaleInfoW (lcid,
- LOCALE_IDEFAULTANSICODEPAGE
- | LOCALE_RETURN_NUMBER,
- (PWCHAR) &cp, sizeof cp)
- && cp == 1252 /* Latin1*/
- && GetLocaleInfoW (lcid, LOCALE_SINTLSYMBOL, wbuf, 9)
- && !wcsncmp (wbuf, L"EUR", 3))
- stpcpy (c, "@euro");
- else if (lang == LANG_JAPANESE
- || lang == LANG_KOREAN
- || lang == LANG_CHINESE)
- stpcpy (c, "@cjknarrow");
- else
- continue;
- printf ("%-16s %ls (%ls)\n", loc, language, country);
- }
- }
- /* Check Serbian language for the available territories. Up to
- Server 2003 we only had sr_SP (silently converted to sr_CS
- above), in Vista we had only sr_CS. First starting with W7 we
- have the actual sr_RS and sr_ME. However, all of them are
- supported on all systems in Cygwin. So we fake them here, if
- they are missing. */
- if (lang == LANG_SERBIAN)
- {
- int sr_CS_idx = -1;
- int sr_RS_idx = -1;
- int i;
-
- for (i = 0; i < lcnt; ++ i)
- if (!strcmp (loc_list[i].loc, "sr_CS"))
- sr_CS_idx = i;
- else if (!strcmp (loc_list[i].loc, "sr_RS"))
- sr_RS_idx = i;
- if (sr_CS_idx > 0 && sr_RS_idx == -1)
- {
- puts ("sr_RS@latin Serbian (Latin) (Serbia)");
- puts ("sr_RS Serbian (Latin) (Serbia)");
- puts ("sr_ME@latin Serbian (Latin) (Montenegro)");
- puts ("sr_ME Serbian (Latin) (Montenegro)");
- }
- }
- }
- return 0;
- }
- if (getlocale (lcid, name))
- printf ("%s%s\n", name, utf);
- return 0;
-}
diff --git a/winsup/utils/locale.cc b/winsup/utils/locale.cc
new file mode 100644
index 000000000..3691278d7
--- /dev/null
+++ b/winsup/utils/locale.cc
@@ -0,0 +1,622 @@
+/*
+ * Copyright (c) 2010, Corinna Vinschen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <stdio.h>
+#include <getopt.h>
+#include <string.h>
+#include <wchar.h>
+#include <locale.h>
+#include <langinfo.h>
+#include <limits.h>
+#define WINVER 0x0601
+#include <windows.h>
+
+extern char *__progname;
+
+void usage (FILE *, int) __attribute__ ((noreturn));
+
+void
+usage (FILE * stream, int status)
+{
+ fprintf (stream,
+ "Usage: %s [-amsuUvh]\n"
+ " or: %s [-ck] NAME\n"
+ "Get locale-specific information.\n"
+ "\n"
+ "Options:\n"
+ "\n"
+ " -a, --all-locales List all available supported locales\n"
+ " -c, --category-name List information about given category NAME\n"
+ " -k, --keyword-name Print information about given keyword NAME\n"
+ " -m, --charmaps List all available character maps\n"
+ " -s, --system Print system default locale\n"
+ " -u, --user Print user's default locale\n"
+ " -U, --utf Attach \".UTF-8\" to the result\n"
+ " -v, --verbose More verbose output\n"
+ " -h, --help This text\n",
+ __progname, __progname);
+ exit (status);
+}
+
+struct option longopts[] = {
+ {"all-locales", no_argument, NULL, 'a'},
+ {"category-name", no_argument, NULL, 'c'},
+ {"keyword-name", no_argument, NULL, 'k'},
+ {"charmaps", no_argument, NULL, 'm'},
+ {"system", no_argument, NULL, 's'},
+ {"user", no_argument, NULL, 'u'},
+ {"utf", no_argument, NULL, 'U'},
+ {"verbose", no_argument, NULL, 'v'},
+ {"help", no_argument, NULL, 'h'},
+ {0, no_argument, NULL, 0}
+};
+const char *opts = "achkmsuUv";
+
+int
+getlocale (LCID lcid, char *name)
+{
+ char iso639[10];
+ char iso3166[10];
+
+ iso3166[0] = '\0';
+ if (!GetLocaleInfo (lcid, LOCALE_SISO639LANGNAME, iso639, 10))
+ return 0;
+ GetLocaleInfo (lcid, LOCALE_SISO3166CTRYNAME, iso3166, 10);
+ sprintf (name, "%s%s%s", iso639, lcid > 0x3ff ? "_" : "",
+ lcid > 0x3ff ? iso3166 : "");
+ return 1;
+}
+
+void
+printlocale (int verbose, const char *loc,
+ const wchar_t *lang, const wchar_t *ctry)
+{
+ printf ("%-16s", loc);
+ if (verbose)
+ printf ("%ls (%ls)", lang, ctry);
+ fputc ('\n', stdout);
+}
+
+void
+print_all_locales (int verbose)
+{
+ LCID lcid = 0;
+ char name[32];
+ DWORD cp;
+
+ unsigned lang, sublang;
+
+ printlocale (verbose, "C", L"C", L"POSIX");
+ printlocale (verbose, "POSIX", L"C", L"POSIX");
+ for (lang = 1; lang <= 0xff; ++lang)
+ {
+ struct {
+ wchar_t language[256];
+ wchar_t country[256];
+ char loc[32];
+ } loc_list[32];
+ int lcnt = 0;
+
+ for (sublang = 1; sublang <= 0x3f; ++sublang)
+ {
+ lcid = (sublang << 10) | lang;
+ if (getlocale (lcid, name))
+ {
+ wchar_t language[256];
+ wchar_t country[256];
+ int i;
+ char *c, loc[32];
+ wchar_t wbuf[9];
+
+ /* Go figure. Even the English name of a language or
+ locale might contain native characters. */
+ GetLocaleInfoW (lcid, LOCALE_SENGLANGUAGE, language, 256);
+ GetLocaleInfoW (lcid, LOCALE_SENGCOUNTRY, country, 256);
+ /* Avoid dups */
+ for (i = 0; i < lcnt; ++ i)
+ if (!wcscmp (loc_list[i].language, language)
+ && !wcscmp (loc_list[i].country, country))
+ break;
+ if (i < lcnt)
+ continue;
+ if (lcnt < 32)
+ {
+ wcscpy (loc_list[lcnt].language, language);
+ wcscpy (loc_list[lcnt].country, country);
+ }
+ c = stpcpy (loc, name);
+ /* Convert old sr_SP silently to sr_CS on old systems.
+ Make sure sr_CS country is in recent shape. */
+ if (lang == LANG_SERBIAN
+ && (sublang == SUBLANG_SERBIAN_LATIN
+ || sublang == SUBLANG_SERBIAN_CYRILLIC))
+ {
+ c = stpcpy (loc, "sr_CS");
+ wcscpy (country, L"Serbia and Montenegro (Former)");
+ }
+ /* Now check certain conditions to figure out if that
+ locale requires a modifier. */
+ if (lang == LANG_SERBIAN && !strncmp (loc, "sr_", 3)
+ && wcsstr (language, L"(Latin)"))
+ stpcpy (c, "@latin");
+ else if (lang == LANG_UZBEK
+ && sublang == SUBLANG_UZBEK_CYRILLIC)
+ stpcpy (c, "@cyrillic");
+ /* Avoid more dups */
+ for (i = 0; i < lcnt; ++ i)
+ if (!strcmp (loc_list[i].loc, loc))
+ {
+ lcnt++;
+ break;
+ }
+ if (i < lcnt)
+ continue;
+ if (lcnt < 32)
+ strcpy (loc_list[lcnt++].loc, loc);
+ /* Print */
+ printlocale (verbose, loc, language, country);
+ /* Check for locales which sport a modifier for
+ changing the codeset and other stuff. */
+ if (lang == LANG_BELARUSIAN
+ && sublang == SUBLANG_BELARUSIAN_BELARUS)
+ stpcpy (c, "@latin");
+ else if (lang == LANG_TATAR
+ && sublang == SUBLANG_TATAR_RUSSIA)
+ stpcpy (c, "@iqtelif");
+ else if (GetLocaleInfoW (lcid,
+ LOCALE_IDEFAULTANSICODEPAGE
+ | LOCALE_RETURN_NUMBER,
+ (PWCHAR) &cp, sizeof cp)
+ && cp == 1252 /* Latin1*/
+ && GetLocaleInfoW (lcid, LOCALE_SINTLSYMBOL, wbuf, 9)
+ && !wcsncmp (wbuf, L"EUR", 3))
+ stpcpy (c, "@euro");
+ else if (lang == LANG_JAPANESE
+ || lang == LANG_KOREAN
+ || lang == LANG_CHINESE)
+ stpcpy (c, "@cjknarrow");
+ else
+ continue;
+ printlocale (verbose, loc, language, country);
+ }
+ }
+ /* Check Serbian language for the available territories. Up to
+ Server 2003 we only had sr_SP (silently converted to sr_CS
+ above), in Vista we had only sr_CS. First starting with W7 we
+ have the actual sr_RS and sr_ME. However, all of them are
+ supported on all systems in Cygwin. So we fake them here, if
+ they are missing. */
+ if (lang == LANG_SERBIAN)
+ {
+ int sr_CS_idx = -1;
+ int sr_RS_idx = -1;
+ int i;
+
+ for (i = 0; i < lcnt; ++ i)
+ if (!strcmp (loc_list[i].loc, "sr_CS"))
+ sr_CS_idx = i;
+ else if (!strcmp (loc_list[i].loc, "sr_RS"))
+ sr_RS_idx = i;
+ if (sr_CS_idx > 0 && sr_RS_idx == -1)
+ {
+ printlocale (verbose, "sr_RS@latin",
+ L"Serbian (Latin)", L"Serbia");
+ printlocale (verbose, "sr_RS",
+ L"Serbian (Cyrillic)", L"Serbia");
+ printlocale (verbose, "sr_ME@latin",
+ L"Serbian (Latin)", L"Montenegro");
+ printlocale (verbose, "sr_ME",
+ L"Serbian (Cyrillic)", L"Montenegro");
+ }
+ }
+ }
+}
+
+void
+print_charmaps ()
+{
+ /* FIXME: We need a method to fetch the available charsets from Cygwin, */
+ const char *charmaps[] =
+ {
+ "ASCII",
+ "BIG5",
+ "CP1125",
+ "CP1250",
+ "CP1251",
+ "CP1252",
+ "CP1253",
+ "CP1254",
+ "CP1255",
+ "CP1256",
+ "CP1257",
+ "CP1258",
+ "CP437",
+ "CP720",
+ "CP737",
+ "CP775",
+ "CP850",
+ "CP852",
+ "CP855",
+ "CP857",
+ "CP858",
+ "CP862",
+ "CP866",
+ "CP874",
+ "CP932",
+ "EUC-JP",
+ "EUC-KR",
+ "GBK",
+ "GEORGIAN-PS",
+ "ISO-8859-1",
+ "ISO-8859-10",
+ "ISO-8859-11",
+ "ISO-8859-13",
+ "ISO-8859-14",
+ "ISO-8859-15",
+ "ISO-8859-16",
+ "ISO-8859-2",
+ "ISO-8859-3",
+ "ISO-8859-4",
+ "ISO-8859-5",
+ "ISO-8859-6",
+ "ISO-8859-7",
+ "ISO-8859-8",
+ "ISO-8859-9",
+ "KOI8-R",
+ "KOI8-U",
+ "PT154",
+ "SJIS",
+ "TIS-620",
+ "UTF-8",
+ NULL
+ };
+ const char **charmap = charmaps;
+ while (*charmap)
+ printf ("%s\n", *charmap++);
+}
+
+void
+print_lc_ivalue (int key, const char *name, int value)
+{
+ if (key)
+ printf ("%s=", name);
+ printf ("%d", value == CHAR_MAX ? -1 : value);
+ fputc ('\n', stdout);
+}
+
+void
+print_lc_svalue (int key, const char *name, const char *value)
+{
+ if (key)
+ printf ("%s=\"", name);
+ fputs (value, stdout);
+ if (key)
+ fputc ('"', stdout);
+ fputc ('\n', stdout);
+}
+
+void
+print_lc_strings (int key, const char *name, int from, int to)
+{
+ if (key)
+ printf ("%s=\"", name);
+ for (int i = from; i <= to; ++i)
+ printf ("%s%s", i > from ? ";" : "", nl_langinfo (i));
+ if (key)
+ fputc ('"', stdout);
+ fputc ('\n', stdout);
+}
+
+void
+print_lc_xxx_charset (int key, int lc_cat, const char *name)
+{
+ char lc_ctype_locale[32];
+ char lc_xxx_locale[32];
+
+ strcpy (lc_ctype_locale, setlocale (LC_CTYPE, NULL));
+ strcpy (lc_xxx_locale, setlocale (lc_cat, NULL));
+ setlocale (LC_CTYPE, lc_xxx_locale);
+ print_lc_svalue (key, name, nl_langinfo (CODESET));
+ setlocale (LC_CTYPE, lc_ctype_locale);
+}
+
+void
+print_lc_grouping (int key, const char *name, const char *grouping)
+{
+ if (key)
+ printf ("%s=", name);
+ for (const char *g = grouping; *g; ++g)
+ printf ("%s%d", g > grouping ? ";" : "", *g == CHAR_MAX ? -1 : *g);
+ fputc ('\n', stdout);
+}
+
+enum type_t
+{
+ is_string_fake,
+ is_string_lconv,
+ is_int_lconv,
+ is_grouping_lconv,
+ is_string_linf,
+ is_mstrings_linf,
+ is_mb_cur_max,
+ is_codeset,
+ is_end
+};
+
+struct lc_names_t
+{
+ const char *name;
+ type_t type;
+ size_t fromval;
+ size_t toval;
+};
+
+#define _O(M) __builtin_offsetof (struct lconv, M)
+#define _MS(l,lc) (*(const char **)(((const char *)(l))+(lc)->fromval))
+#define _MI(l,lc) ((int)*(((const char *)(l))+(lc)->fromval))
+
+const char *fake_string[] = {
+ "upper;lower;alpha;digit;xdigit;space;print;graph;blank;cntrl;punct;alnum",
+ "upper\";\"lower\";\"alpha\";\"digit\";\"xdigit\";\"space\";\"print\";\"graph\";\"blank\";\"cntrl\";\"punct\";\"alnum",
+ "toupper;tolower",
+ "toupper\";\"tolower"
+};
+
+lc_names_t lc_ctype_names[] =
+{
+ { "ctype-class-names",is_string_fake, 0, 0 },
+ { "ctype-map-names", is_string_fake, 2, 0 },
+ { "charmap", is_string_linf, CODESET, 0 },
+ { "ctype-mb-cur-max", is_mb_cur_max, 0, 0 },
+ { NULL, is_end, 0, 0 }
+};
+
+lc_names_t lc_numeric_names[] =
+{
+ { "decimal_point", is_string_lconv, _O(decimal_point), 0 },
+ { "thousands_sep", is_string_lconv, _O(thousands_sep), 0 },
+ { "grouping", is_grouping_lconv, _O(grouping), 0 },
+ { "numeric-codeset", is_codeset, LC_NUMERIC, 0 },
+ { NULL, is_end, 0, 0 }
+};
+
+lc_names_t lc_time_names[] =
+{
+ { "abday", is_mstrings_linf, ABDAY_1, ABDAY_7 },
+ { "day", is_mstrings_linf, DAY_1, DAY_7 },
+ { "abmon", is_mstrings_linf, ABMON_1, ABMON_12 },
+ { "mon", is_mstrings_linf, MON_1, MON_12 },
+ { "am_pm", is_mstrings_linf, AM_STR, PM_STR },
+ { "d_t_fmt", is_string_linf, D_T_FMT, 0 },
+ { "d_fmt", is_string_linf, D_FMT, 0 },
+ { "t_fmt", is_string_linf, T_FMT, 0 },
+ { "t_fmt_ampm", is_string_linf, T_FMT_AMPM, 0 },
+ { "era", is_string_linf, ERA, 0 },
+ { "era_d_fmt", is_string_linf, ERA_D_FMT, 0 },
+ { "alt_digits", is_string_linf, ALT_DIGITS, 0 },
+ { "era_d_t_fmt", is_string_linf, ERA_D_T_FMT, 0 },
+ { "era_t_fmt", is_string_linf, ERA_T_FMT, 0 },
+ { "time-codeset", is_codeset, LC_TIME, 0 },
+ { NULL, is_end, 0, 0 }
+};
+
+lc_names_t lc_collate_names[] =
+{
+ { "collate-codeset", is_codeset, LC_COLLATE, 0 },
+ { NULL, is_end, 0, 0 }
+};
+
+lc_names_t lc_monetary_names[] =
+{
+ { "int_curr_symbol", is_string_lconv, _O(int_curr_symbol), 0 },
+ { "currency_symbol", is_string_lconv, _O(currency_symbol), 0 },
+ { "mon_decimal_point",is_string_lconv, _O(mon_decimal_point), 0 },
+ { "mon_thousands_sep",is_string_lconv, _O(mon_thousands_sep), 0 },
+ { "mon_grouping", is_grouping_lconv, _O(mon_grouping), 0 },
+ { "positive_sign", is_string_lconv, _O(positive_sign), 0 },
+ { "negative_sign", is_string_lconv, _O(negative_sign), 0 },
+ { "int_frac_digits", is_int_lconv, _O(int_frac_digits), 0 },
+ { "frac_digits", is_int_lconv, _O(frac_digits), 0 },
+ { "p_cs_precedes", is_int_lconv, _O(p_cs_precedes), 0 },
+ { "p_sep_by_space", is_int_lconv, _O(p_sep_by_space), 0 },
+ { "n_cs_precedes", is_int_lconv, _O(n_cs_precedes), 0 },
+ { "n_sep_by_space", is_int_lconv, _O(n_sep_by_space), 0 },
+ { "p_sign_posn", is_int_lconv, _O(p_sign_posn), 0 },
+ { "n_sign_posn", is_int_lconv, _O(n_sign_posn), 0 },
+ { "int_p_cs_precedes",is_int_lconv, _O(int_p_cs_precedes), 0 },
+ { "int_p_sep_by_space",is_int_lconv, _O(int_p_sep_by_space), 0 },
+ { "int_n_cs_precedes",is_int_lconv, _O(int_n_cs_precedes), 0 },
+ { "int_n_sep_by_space",is_int_lconv, _O(int_n_sep_by_space), 0 },
+ { "int_p_sign_posn", is_int_lconv, _O(int_p_sign_posn), 0 },
+ { "int_n_sign_posn", is_int_lconv, _O(int_n_sign_posn), 0 },
+ { "monetary-codeset", is_codeset, LC_MONETARY, 0 },
+ { NULL, is_end, 0, 0 }
+};
+
+lc_names_t lc_messages_names[] =
+{
+ { "yesexpr", is_string_linf, YESEXPR, 0 },
+ { "noexpr", is_string_linf, NOEXPR, 0 },
+ { "yesstr", is_string_linf, YESSTR, 0 },
+ { "nostr", is_string_linf, NOSTR, 0 },
+ { "messages-codeset", is_codeset, LC_MESSAGES, 0 },
+ { NULL, is_end, 0, 0 }
+};
+
+void
+print_lc (int cat, int key, const char *category, const char *name,
+ lc_names_t *lc_name)
+{
+ struct lconv *l = localeconv ();
+
+ if (cat)
+ printf ("%s\n", category);
+ for (lc_names_t *lc = lc_name; lc->type != is_end; ++lc)
+ if (!name || !strcmp (name, lc->name))
+ switch (lc->type)
+ {
+ case is_string_fake:
+ print_lc_svalue (key, lc->name, fake_string[lc->fromval + key]);
+ break;
+ case is_string_lconv:
+ print_lc_svalue (key, lc->name, _MS (l, lc));
+ break;
+ case is_int_lconv:
+ print_lc_ivalue (key, lc->name, _MI (l, lc));
+ break;
+ case is_grouping_lconv:
+ print_lc_grouping (key, lc->name, _MS (l, lc));
+ break;
+ case is_string_linf:
+ print_lc_svalue (key, lc->name, nl_langinfo (lc->fromval));
+ break;
+ case is_mstrings_linf:
+ print_lc_strings (key, lc->name, lc->fromval, lc->toval);
+ break;
+ case is_mb_cur_max:
+ print_lc_ivalue (key, lc->name, MB_CUR_MAX);
+ break;
+ case is_codeset:
+ print_lc_xxx_charset (key, lc->fromval, lc->name);
+ break;
+ default:
+ break;
+ }
+}
+
+struct cat_t
+{
+ const char *category;
+ int lc_cat;
+ lc_names_t *lc_names;
+} categories[] =
+{
+ { "LC_CTYPE", LC_CTYPE, lc_ctype_names },
+ { "LC_NUMERIC", LC_NUMERIC, lc_numeric_names },
+ { "LC_TIME", LC_TIME, lc_time_names },
+ { "LC_COLLATE", LC_COLLATE, lc_collate_names },
+ { "LC_MONETARY", LC_MONETARY, lc_monetary_names },
+ { "LC_MESSAGES", LC_MESSAGES, lc_messages_names },
+ { NULL, 0, NULL }
+};
+
+void
+print_names (int cat, int key, const char *name)
+{
+ struct cat_t *c;
+ lc_names_t *lc;
+
+ for (c = categories; c->category; ++c)
+ if (!strcmp (name, c->category))
+ {
+ print_lc (cat, key, c->category, NULL, c->lc_names);
+ return;
+ }
+ for (c = categories; c->category; ++c)
+ for (lc = c->lc_names; lc->type != is_end; ++lc)
+ if (!strcmp (name, lc->name))
+ {
+ print_lc (cat, key, c->category, lc->name, lc);
+ return;
+ }
+}
+
+void
+print_lc ()
+{
+ printf ("LANG=%s\n", getenv ("LANG") ?: "");
+ printf ("LC_CTYPE=\"%s\"\n", setlocale (LC_CTYPE, NULL));
+ printf ("LC_NUMERIC=\"%s\"\n", setlocale (LC_NUMERIC, NULL));
+ printf ("LC_TIME=\"%s\"\n", setlocale (LC_TIME, NULL));
+ printf ("LC_COLLATE=\"%s\"\n", setlocale (LC_COLLATE, NULL));
+ printf ("LC_MONETARY=\"%s\"\n", setlocale (LC_MONETARY, NULL));
+ printf ("LC_MESSAGES=\"%s\"\n", setlocale (LC_MESSAGES, NULL));
+ printf ("LC_ALL=%s\n", getenv ("LC_ALL") ?: "");
+}
+
+int
+main (int argc, char **argv)
+{
+ int opt;
+ LCID lcid = 0;
+ int all = 0;
+ int cat = 0;
+ int key = 0;
+ int maps = 0;
+ int verbose = 0;
+ const char *utf = "";
+ char name[32];
+
+ setlocale (LC_ALL, "");
+ while ((opt = getopt_long (argc, argv, opts, longopts, NULL)) != EOF)
+ switch (opt)
+ {
+ case 'a':
+ all = 1;
+ break;
+ case 'c':
+ cat = 1;
+ break;
+ case 'k':
+ key = 1;
+ break;
+ case 'm':
+ maps = 1;
+ break;
+ case 's':
+ lcid = LOCALE_SYSTEM_DEFAULT;
+ break;
+ case 'u':
+ lcid = LOCALE_USER_DEFAULT;
+ break;
+ case 'U':
+ utf = ".UTF-8";
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'h':
+ usage (stdout, 0);
+ break;
+ default:
+ usage (stderr, 1);
+ break;
+ }
+ if (all)
+ print_all_locales (verbose);
+ else if (maps)
+ print_charmaps ();
+ else if (lcid)
+ {
+ if (getlocale (lcid, name))
+ printf ("%s%s\n", name, utf);
+ }
+ else if (optind < argc)
+ while (optind < argc)
+ print_names (cat, key, argv[optind++]);
+ else
+ print_lc ();
+ return 0;
+}
diff --git a/winsup/utils/utils.sgml b/winsup/utils/utils.sgml
index 79673c274..fcd259a6d 100644
--- a/winsup/utils/utils.sgml
+++ b/winsup/utils/utils.sgml
@@ -501,61 +501,6 @@ The format for ACL output is as follows:
</para>
</sect2>
-<sect2 id="getlocale"><title>getlocale</title>
-
-<screen>
-Usage: getlocale [-asuh]
-Print Windows locale(s)
-
-Options:
-
- -a, --all List all available locales
- -s, --system Print system default locale
- (default is current user default locale)
- -u, --utf Attach ".UTF-8" to the result
- -h, --help this text
-</screen>
-
-<para><command>getlocale</command> without parameters just prints the
-current user's Windows default locale to stdout. The <literal>-s</literal>
-option prints the systems default locale instead. With the
-<literal>-u</literal> option <command>getlocale</command> appends a ".UTF-8".
-This can be used in scripts to set the Cygwin locale to the Windows user
-or system default, for instance, for a US-based user:
-</para>
-
-<screen>
-bash$ export LANG=$(getlocale -u)
-bash$ echo $LANG
-en_US.UTF-8
-</screen>
-
-<para>The <literal>-a</literal> option is helpful to learn which locales
-are supported by your Windows machine. It prints all available locales,
-their meaning, and the allowed modifiers. Example:</para>
-
-<screen>
-bash$ getlocale -a
-ar_SA Arabic (Saudi Arabia)
-ar_IQ Arabic (Iraq)
-...
-zh_TW Chinese (Traditional) (Taiwan)
-zh_TW@cjknarrow Chinese (Traditional) (Taiwan)
-...
-de_AT German (Austria)
-de_AT@euro German (Austria)
-...
-en_US English (United States)
-en_GB English (United Kingdom)
-en_AU English (Australia)
-...
-sr_RS Serbian (Cyrillic) (Serbia)
-sr_RS@latin Serbian (Latin) (Serbia)
-...
-</screen>
-
-</sect2>
-
<sect2 id="kill"><title>kill</title>
<screen>
@@ -655,6 +600,93 @@ SIGUSR2 31 user defined signal 2
</sect2>
+<sect2 id="locale"><title>locale</title>
+
+<screen>
+Usage: locale [-amsuUvh]
+ or: locale [-ck] NAME
+Get locale-specific information.
+
+Options:
+
+ -a, --all-locales List all available supported locales
+ -c, --category-name List information about given category NAME
+ -k, --keyword-name Print information about given keyword NAME
+ -m, --charmaps List all available character maps
+ -s, --system Print system default locale
+ -u, --user Print user's default locale
+ -U, --utf Attach ".UTF-8" to the result
+ -v, --verbose More verbose output
+ -h, --help This text
+</screen>
+
+<para><command>locale</command> without parameters prints information about
+the current locale environment settings.</para>
+
+<para>The <literal>-u</literal> option prints the current user's Windows
+default locale to stdout. The <literal>-s</literal> option prints the
+systems default locale instead. With the <literal>-U</literal> option
+<command>locale</command> appends a ".UTF-8". This can be used in scripts
+to set the Cygwin locale to the Windows user or system default, for instance:
+</para>
+
+<screen>
+bash$ export LANG=$(locale -uU)
+bash$ echo $LANG
+en_US.UTF-8
+</screen>
+
+<para>The <literal>-a</literal> option is helpful to learn which locales
+are supported by your Windows machine. It prints all available locales
+and the allowed modifiers. The <literal>-v</literal> option prints
+additionally the English names of the language and territory connected
+to this locale. Example:</para>
+
+<screen>
+bash$ locale -av
+ar_SA Arabic (Saudi Arabia)
+ar_IQ Arabic (Iraq)
+...
+zh_TW Chinese (Traditional) (Taiwan)
+zh_TW@cjknarrow Chinese (Traditional) (Taiwan)
+...
+de_AT German (Austria)
+de_AT@euro German (Austria)
+...
+en_US English (United States)
+en_GB English (United Kingdom)
+en_AU English (Australia)
+...
+sr_RS Serbian (Cyrillic) (Serbia)
+sr_RS@latin Serbian (Latin) (Serbia)
+...
+</screen>
+
+<para>The <literal>-m</literal> option prints the names of the available
+charmaps supported by Cygwin to stdout.</para>
+
+<para>Otherwise, if arguments are given, <command>locale</command> prints
+the values assigned to these arguments. Arguments can be names of locale
+categories (for instance: LC_CTYPE, LC_MONETARY), or names of keywords
+supported in the locale categories (for instance: thousands_sep, charmap).
+The <literal>-c</literal> option prints additionally the name of the category.
+The <literal>-k</literal> option prints additionally the name of the keyword.
+Example:</para>
+
+<screen>
+bash$ locale -ck LC_MESSAGES
+LC_MESSAGES
+yesexpr="^[yY]"
+noexpr="^[nN]"
+yesstr="yes"
+nostr="no"
+messages-codeset="UTF-8"
+bash$ locale noexpr
+^[nN]
+</screen>
+
+</sect2>
+
<sect2 id="mkgroup"><title>mkgroup</title>
<screen>