OSDN Git Service

8dbfd9b308668ce253007a6176a301714ec161ef
[pf3gnuchains/pf3gnuchains4x.git] / winsup / utils / locale.cc
1 /*
2  * Copyright (c) 2010, Corinna Vinschen
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 #include <stdio.h>
27 #include <ctype.h>
28 #include <getopt.h>
29 #include <string.h>
30 #include <wchar.h>
31 #include <locale.h>
32 #include <langinfo.h>
33 #include <limits.h>
34 #include <sys/cygwin.h>
35 #define WINVER 0x0601
36 #include <windows.h>
37
38 #define LOCALE_ALIAS            "/usr/share/locale/locale.alias"
39 #define LOCALE_ALIAS_LINE_LEN   255
40
41 extern char *__progname;
42
43 void usage (FILE *, int) __attribute__ ((noreturn));
44
45 void
46 usage (FILE * stream, int status)
47 {
48   fprintf (stream,
49            "Usage: %s [-amsuUvh]\n"
50            "   or: %s [-ck] NAME\n"
51            "Get locale-specific information.\n"
52            "\n"
53            "Options:\n"
54            "\n"
55            "  -a, --all-locales    List all available supported locales\n"
56            "  -c, --category-name  List information about given category NAME\n"
57            "  -k, --keyword-name   Print information about given keyword NAME\n"
58            "  -m, --charmaps       List all available character maps\n"
59            "  -s, --system         Print system default locale\n"
60            "  -u, --user           Print user's default locale\n"
61            "  -U, --utf            Attach \".UTF-8\" to the result\n"
62            "  -v, --verbose        More verbose output\n"
63            "  -h, --help           This text\n",
64            __progname, __progname);
65   exit (status);
66 }
67
68 struct option longopts[] = {
69   {"all-locales", no_argument, NULL, 'a'},
70   {"category-name", no_argument, NULL, 'c'},
71   {"keyword-name", no_argument, NULL, 'k'},
72   {"charmaps", no_argument, NULL, 'm'},
73   {"system", no_argument, NULL, 's'},
74   {"user", no_argument, NULL, 'u'},
75   {"utf", no_argument, NULL, 'U'},
76   {"verbose", no_argument, NULL, 'v'},
77   {"help", no_argument, NULL, 'h'},
78   {0, no_argument, NULL, 0}
79 };
80 const char *opts = "achkmsuUv";
81
82 int
83 getlocale (LCID lcid, char *name)
84 {
85   char iso639[10];
86   char iso3166[10];
87
88   iso3166[0] = '\0';
89   if (!GetLocaleInfo (lcid, LOCALE_SISO639LANGNAME, iso639, 10))
90     return 0;
91   GetLocaleInfo (lcid, LOCALE_SISO3166CTRYNAME, iso3166, 10);
92   sprintf (name, "%s%s%s", iso639, lcid > 0x3ff ? "_" : "",
93                            lcid > 0x3ff ? iso3166 : "");
94   return 1;
95 }
96
97 typedef struct {
98   const char *name;
99   const wchar_t *language;
100   const wchar_t *territory;
101   const char *codeset;
102   bool alias;
103 } loc_t;
104 loc_t *locale;
105 size_t loc_max;
106 size_t loc_num;
107
108 void
109 print_codeset (const char *codeset)
110 {
111   for (; *codeset; ++codeset)
112     if (*codeset != '-')
113       putc (tolower ((int)(unsigned char) *codeset), stdout);
114 }
115
116 void
117 print_locale_with_codeset (int verbose, loc_t *locale, bool utf8,
118                            const char *modifier)
119 {
120   static const char *sysroot;
121   char locname[32];
122
123   if (verbose
124       && (!strcmp (locale->name, "C") || !strcmp (locale->name, "POSIX")))
125     return;
126   if (!sysroot)
127     {
128       char sysbuf[PATH_MAX];
129       stpcpy (stpcpy (sysbuf, getenv ("SYSTEMROOT")),
130               "\\system32\\kernel32.dll");
131       sysroot = (const char *) cygwin_create_path (CCP_WIN_A_TO_POSIX, sysbuf);
132       if (!sysroot)
133         sysroot = "kernel32.dll";
134     }
135   snprintf (locname, 32, "%s%s%s%s", locale->name, utf8 ? ".utf8" : "",
136                                      modifier ? "@" : "", modifier ?: "");
137   if (verbose)
138     fputs ("locale: ", stdout);
139   printf ("%-15s ", locname);
140   if (verbose)
141     {
142       printf ("archive: %s\n",
143       locale->alias ? LOCALE_ALIAS : sysroot);
144       puts ("-------------------------------------------------------------------------------");
145       printf (" language | %ls\n", locale->language);
146       printf ("territory | %ls\n", locale->territory);
147       printf ("  codeset | %s\n", utf8 ? "UTF-8" : locale->codeset);
148     }
149   putc ('\n', stdout);
150 }
151
152 void
153 print_locale (int verbose, loc_t *locale)
154 {
155   print_locale_with_codeset (verbose, locale, false, NULL);
156   char *modifier = strchr (locale->name, '@');
157   if (!locale->alias)
158     {
159       if (!modifier)
160         print_locale_with_codeset (verbose, locale, true, NULL);
161       else if (!strcmp (modifier, "@cjknarrow"))
162         {
163           *modifier++ = '\0';
164           print_locale_with_codeset (verbose, locale, true, modifier);
165         }
166     }
167 }
168
169 int
170 compare_locales (const void *a, const void *b)
171 {
172   const loc_t *la = (const loc_t *) a;
173   const loc_t *lb = (const loc_t *) b;
174   return strcmp (la->name, lb->name);
175 }
176
177 void
178 add_locale (const char *name, const wchar_t *language, const wchar_t *territory,
179             bool alias = false)
180 {
181   char orig_locale[32];
182
183   if (loc_num >= loc_max)
184     {
185       loc_t *tmp = (loc_t *) realloc (locale, (loc_max + 32) * sizeof (loc_t));
186       if (!tmp)
187         {
188           fprintf (stderr, "Out of memory!\n");
189           exit (1);
190         }
191       locale = tmp;
192       loc_max += 32;
193     }
194   locale[loc_num].name = strdup (name);
195   locale[loc_num].language = wcsdup (language);
196   locale[loc_num].territory = wcsdup (territory);
197   strcpy (orig_locale, setlocale (LC_CTYPE, NULL));
198   setlocale (LC_CTYPE, name);
199   locale[loc_num].codeset = strdup (nl_langinfo (CODESET));
200   setlocale (LC_CTYPE, orig_locale);
201   locale[loc_num].alias = alias;
202   ++loc_num;
203 }
204
205 void
206 add_locale_alias_locales ()
207 {
208   char alias_buf[LOCALE_ALIAS_LINE_LEN + 1], *c;
209   const char *alias, *replace;
210   char orig_locale[32];
211   loc_t search, *loc;
212   size_t orig_loc_num = loc_num;
213
214   FILE *fp = fopen (LOCALE_ALIAS, "rt");
215   if (!fp)
216     return;
217   strcpy (orig_locale, setlocale (LC_CTYPE, NULL));
218   while (fgets (alias_buf, LOCALE_ALIAS_LINE_LEN + 1, fp))
219     {
220       alias_buf[LOCALE_ALIAS_LINE_LEN] = '\0';
221       c = strrchr (alias_buf, '\n');
222       if (c)
223         *c = '\0';
224       c = alias_buf;
225       c += strspn (c, " \t");
226       if (!*c || *c == '#')
227         continue;
228       alias = c;
229       c += strcspn (c, " \t");
230       *c++ = '\0';
231       c += strspn (c, " \t");
232       if (*c == '#')
233         continue;
234       replace = c;
235       c += strcspn (c, " \t");
236       *c++ = '\0';
237       c = strchr (replace, '.');
238       if (c)
239         *c = '\0';
240       search.name = replace;
241       loc = (loc_t *) bsearch (&search, locale, orig_loc_num, sizeof (loc_t),
242                                compare_locales);
243       add_locale (alias, loc ? loc->language : L"", loc ? loc->territory : L"",
244                   true);
245     }
246   fclose (fp);
247 }
248
249 void
250 print_all_locales (int verbose)
251 {
252   LCID lcid = 0;
253   char name[32];
254   DWORD cp;
255
256   unsigned lang, sublang;
257
258   add_locale ("C", L"C", L"POSIX");
259   add_locale ("POSIX", L"C", L"POSIX", true);
260   for (lang = 1; lang <= 0xff; ++lang)
261     {
262       struct {
263         wchar_t language[256];
264         wchar_t country[256];
265         char loc[32];
266       } loc_list[32];
267       int lcnt = 0;
268
269       for (sublang = 1; sublang <= 0x3f; ++sublang)
270         {
271           lcid = (sublang << 10) | lang;
272           if (getlocale (lcid, name))
273             {
274               wchar_t language[256];
275               wchar_t country[256];
276               int i;
277               char *c, loc[32];
278               wchar_t wbuf[9];
279
280               /* Go figure.  Even the English name of a language or
281                  locale might contain native characters. */
282               GetLocaleInfoW (lcid, LOCALE_SENGLANGUAGE, language, 256);
283               GetLocaleInfoW (lcid, LOCALE_SENGCOUNTRY, country, 256);
284               /* Avoid dups */
285               for (i = 0; i < lcnt; ++ i)
286                 if (!wcscmp (loc_list[i].language, language)
287                     && !wcscmp (loc_list[i].country, country))
288                   break;
289               if (i < lcnt)
290                 continue;
291               if (lcnt < 32)
292                 {
293                   wcscpy (loc_list[lcnt].language, language);
294                   wcscpy (loc_list[lcnt].country, country);
295                 }
296               c = stpcpy (loc, name);
297               /* Convert old sr_SP silently to sr_CS on old systems.
298                  Make sure sr_CS country is in recent shape. */
299               if (lang == LANG_SERBIAN
300                   && (sublang == SUBLANG_SERBIAN_LATIN
301                       || sublang == SUBLANG_SERBIAN_CYRILLIC))
302                 {
303                   c = stpcpy (loc, "sr_CS");
304                   wcscpy (country, L"Serbia and Montenegro (Former)");
305                 }
306               /* Now check certain conditions to figure out if that
307                  locale requires a modifier. */
308               if (lang == LANG_SERBIAN && !strncmp (loc, "sr_", 3)
309                   && wcsstr (language, L"(Latin)"))
310                 stpcpy (c, "@latin");
311               else if (lang == LANG_UZBEK
312                        && sublang == SUBLANG_UZBEK_CYRILLIC)
313                 stpcpy (c, "@cyrillic");
314               /* Avoid more dups */
315               for (i = 0; i < lcnt; ++ i)
316                 if (!strcmp (loc_list[i].loc, loc))
317                   {
318                     lcnt++;
319                     break;
320                   }
321               if (i < lcnt)
322                 continue;
323               if (lcnt < 32)
324                 strcpy (loc_list[lcnt++].loc, loc);
325               /* Print */
326               add_locale (loc, language, country);
327               /* Check for locales which sport a modifier for
328                  changing the codeset and other stuff. */
329               if (lang == LANG_BELARUSIAN
330                   && sublang == SUBLANG_BELARUSIAN_BELARUS)
331                 stpcpy (c, "@latin");
332               else if (lang == LANG_TATAR
333                        && sublang == SUBLANG_TATAR_RUSSIA)
334                 stpcpy (c, "@iqtelif");
335               else if (GetLocaleInfoW (lcid,
336                                        LOCALE_IDEFAULTANSICODEPAGE
337                                        | LOCALE_RETURN_NUMBER,
338                                        (PWCHAR) &cp, sizeof cp)
339                        && cp == 1252 /* Latin1*/
340                        && GetLocaleInfoW (lcid, LOCALE_SINTLSYMBOL, wbuf, 9)
341                        && !wcsncmp (wbuf, L"EUR", 3))
342                 stpcpy (c, "@euro");
343               else if (lang == LANG_JAPANESE
344                        || lang == LANG_KOREAN
345                        || lang == LANG_CHINESE)
346                 stpcpy (c, "@cjknarrow");
347               else
348                 continue;
349               add_locale (loc, language, country);
350             }
351         }
352       /* Check Serbian language for the available territories.  Up to
353          Server 2003 we only had sr_SP (silently converted to sr_CS
354          above), in Vista we had only sr_CS.  First starting with W7 we
355          have the actual sr_RS and sr_ME.  However, all of them are
356          supported on all systems in Cygwin.  So we fake them here, if
357          they are missing. */
358       if (lang == LANG_SERBIAN)
359         {
360           int sr_CS_idx = -1;
361           int sr_RS_idx = -1;
362           int i;
363
364           for (i = 0; i < lcnt; ++ i)
365             if (!strcmp (loc_list[i].loc, "sr_CS"))
366               sr_CS_idx = i;
367             else if (!strcmp (loc_list[i].loc, "sr_RS"))
368               sr_RS_idx = i;
369           if (sr_CS_idx > 0 && sr_RS_idx == -1)
370             {
371               add_locale ("sr_RS@latin", L"Serbian (Latin)", L"Serbia");
372               add_locale ("sr_RS", L"Serbian (Cyrillic)", L"Serbia");
373               add_locale ("sr_ME@latin", L"Serbian (Latin)", L"Montenegro");
374               add_locale ("sr_ME", L"Serbian (Cyrillic)", L"Montenegro");
375             }
376         }
377     }
378   /* First sort allows add_locale_alias_locales to bsearch in locales. */
379   qsort (locale, loc_num, sizeof (loc_t), compare_locales);
380   add_locale_alias_locales ();
381   qsort (locale, loc_num, sizeof (loc_t), compare_locales);
382   for (size_t i = 0; i < loc_num; ++i)
383     print_locale (verbose, &locale[i]);
384 }
385
386 void
387 print_charmaps ()
388 {
389   /* FIXME: We need a method to fetch the available charsets from Cygwin, */
390   const char *charmaps[] =
391   {
392     "ASCII",
393     "BIG5",
394     "CP1125",
395     "CP1250",
396     "CP1251",
397     "CP1252",
398     "CP1253",
399     "CP1254",
400     "CP1255",
401     "CP1256",
402     "CP1257",
403     "CP1258",
404     "CP437",
405     "CP720",
406     "CP737",
407     "CP775",
408     "CP850",
409     "CP852",
410     "CP855",
411     "CP857",
412     "CP858",
413     "CP862",
414     "CP866",
415     "CP874",
416     "CP932",
417     "EUC-CN",
418     "EUC-JP",
419     "EUC-KR",
420     "GB2312",
421     "GBK",
422     "GEORGIAN-PS",
423     "ISO-8859-1",
424     "ISO-8859-10",
425     "ISO-8859-11",
426     "ISO-8859-13",
427     "ISO-8859-14",
428     "ISO-8859-15",
429     "ISO-8859-16",
430     "ISO-8859-2",
431     "ISO-8859-3",
432     "ISO-8859-4",
433     "ISO-8859-5",
434     "ISO-8859-6",
435     "ISO-8859-7",
436     "ISO-8859-8",
437     "ISO-8859-9",
438     "KOI8-R",
439     "KOI8-U",
440     "PT154",
441     "SJIS",
442     "TIS-620",
443     "UTF-8",
444     NULL
445   };
446   const char **charmap = charmaps;
447   while (*charmap)
448     printf ("%s\n", *charmap++);
449 }
450
451 void
452 print_lc_ivalue (int key, const char *name, int value)
453 {
454   if (key)
455     printf ("%s=", name);
456   printf ("%d", value == CHAR_MAX ? -1 : value);
457   fputc ('\n', stdout);
458 }
459
460 void
461 print_lc_svalue (int key, const char *name, const char *value)
462 {
463   if (key)
464     printf ("%s=\"", name);
465   fputs (value, stdout);
466   if (key)
467     fputc ('"', stdout);
468   fputc ('\n', stdout);
469 }
470
471 void
472 print_lc_sepstrings (int key, const char *name, const char *value)
473 {
474   char *c;
475
476   if (key)
477     printf ("%s=", name);
478   while (value && *value)
479     {
480       if (key)
481         fputc ('"', stdout);
482       c = strchr (value, ';');
483       if (!c)
484         {
485           fputs (value, stdout);
486           value = NULL;
487         }
488       else
489         {
490           printf ("%.*s", c - value, value);
491           value = c + 1;
492         }
493       if (key)
494         fputc ('"', stdout);
495       if (value && *value)
496         fputc (';', stdout);
497     }
498   fputc ('\n', stdout);
499 }
500
501 void
502 print_lc_strings (int key, const char *name, int from, int to)
503 {
504   if (key)
505     printf ("%s=\"", name);
506   for (int i = from; i <= to; ++i)
507     printf ("%s%s", i > from ? ";" : "", nl_langinfo (i));
508   if (key)
509     fputc ('"', stdout);
510   fputc ('\n', stdout);
511 }
512
513 void
514 print_lc_grouping (int key, const char *name, const char *grouping)
515 {
516   if (key)
517     printf ("%s=", name);
518   for (const char *g = grouping; *g; ++g)
519     printf ("%s%d", g > grouping ? ";" : "", *g == CHAR_MAX ? -1 : *g);
520   fputc ('\n', stdout);
521 }
522
523 enum type_t
524 {
525   is_string_fake,
526   is_grouping,
527   is_string,
528   is_mstrings,
529   is_sepstrings,
530   is_int,
531   is_wchar,
532   is_end
533 };
534
535 struct lc_names_t
536 {
537   const char *name;
538   type_t      type;
539   size_t      fromval;
540   size_t      toval;
541 };
542
543 const char *fake_string[] = {
544  "upper;lower;alpha;digit;xdigit;space;print;graph;blank;cntrl;punct;alnum",
545  "upper\";\"lower\";\"alpha\";\"digit\";\"xdigit\";\"space\";\"print\";\"graph\";\"blank\";\"cntrl\";\"punct\";\"alnum",
546  "toupper;tolower",
547  "toupper\";\"tolower"
548 };
549
550 lc_names_t lc_ctype_names[] =
551 {
552   { "ctype-class-names",         is_string_fake, 0,                      0 },
553   { "ctype-map-names",           is_string_fake, 2,                      0 },
554   { "ctype-outdigit0_mb",        is_string,     _NL_CTYPE_OUTDIGITS0_MB, 0 },
555   { "ctype-outdigit1_mb",        is_string,     _NL_CTYPE_OUTDIGITS1_MB, 0 },
556   { "ctype-outdigit2_mb",        is_string,     _NL_CTYPE_OUTDIGITS2_MB, 0 },
557   { "ctype-outdigit3_mb",        is_string,     _NL_CTYPE_OUTDIGITS3_MB, 0 },
558   { "ctype-outdigit4_mb",        is_string,     _NL_CTYPE_OUTDIGITS4_MB, 0 },
559   { "ctype-outdigit5_mb",        is_string,     _NL_CTYPE_OUTDIGITS5_MB, 0 },
560   { "ctype-outdigit6_mb",        is_string,     _NL_CTYPE_OUTDIGITS6_MB, 0 },
561   { "ctype-outdigit7_mb",        is_string,     _NL_CTYPE_OUTDIGITS7_MB, 0 },
562   { "ctype-outdigit8_mb",        is_string,     _NL_CTYPE_OUTDIGITS8_MB, 0 },
563   { "ctype-outdigit9_mb",        is_string,     _NL_CTYPE_OUTDIGITS9_MB, 0 },
564   { "ctype-outdigit0_wc",        is_wchar,      _NL_CTYPE_OUTDIGITS0_WC, 0 },
565   { "ctype-outdigit1_wc",        is_wchar,      _NL_CTYPE_OUTDIGITS1_WC, 0 },
566   { "ctype-outdigit2_wc",        is_wchar,      _NL_CTYPE_OUTDIGITS2_WC, 0 },
567   { "ctype-outdigit3_wc",        is_wchar,      _NL_CTYPE_OUTDIGITS3_WC, 0 },
568   { "ctype-outdigit4_wc",        is_wchar,      _NL_CTYPE_OUTDIGITS4_WC, 0 },
569   { "ctype-outdigit5_wc",        is_wchar,      _NL_CTYPE_OUTDIGITS5_WC, 0 },
570   { "ctype-outdigit6_wc",        is_wchar,      _NL_CTYPE_OUTDIGITS6_WC, 0 },
571   { "ctype-outdigit7_wc",        is_wchar,      _NL_CTYPE_OUTDIGITS7_WC, 0 },
572   { "ctype-outdigit8_wc",        is_wchar,      _NL_CTYPE_OUTDIGITS8_WC, 0 },
573   { "ctype-outdigit9_wc",        is_wchar,      _NL_CTYPE_OUTDIGITS9_WC, 0 },
574   { "charmap",                   is_string,     CODESET,                 0 },
575   { "ctype-mb-cur-max",          is_int,        _NL_CTYPE_MB_CUR_MAX,    0 },
576   { NULL,                        is_end,        0,                       0 }
577 };
578
579 lc_names_t lc_numeric_names[] =
580 {
581   { "decimal_point",             is_string,     RADIXCHAR,               0 },
582   { "thousands_sep",             is_string,     THOUSEP,                 0 },
583   { "grouping",                  is_grouping,   _NL_NUMERIC_GROUPING,    0 },
584   { "numeric-decimal-point-wc",  is_wchar,      _NL_NUMERIC_DECIMAL_POINT_WC, 0 },
585   { "numeric-thousands-sep-wc",  is_wchar,      _NL_NUMERIC_THOUSANDS_SEP_WC, 0 },
586   { "numeric-codeset",           is_string,     _NL_NUMERIC_CODESET,     0 },
587   { NULL,                        is_end,        0,                       0 }
588 };
589
590 lc_names_t lc_time_names[] =
591 {
592   { "abday",                     is_mstrings,   ABDAY_1,         ABDAY_7  },
593   { "day",                       is_mstrings,   DAY_1,           DAY_7    },
594   { "abmon",                     is_mstrings,   ABMON_1,         ABMON_12 },
595   { "mon",                       is_mstrings,   MON_1,           MON_12   },
596   { "am_pm",                     is_mstrings,   AM_STR,          PM_STR   },
597   { "d_t_fmt",                   is_string,     D_T_FMT,                0 },
598   { "d_fmt",                     is_string,     D_FMT,                  0 },
599   { "t_fmt",                     is_string,     T_FMT,                  0 },
600   { "t_fmt_ampm",                is_string,     T_FMT_AMPM,             0 },
601   { "era",                       is_sepstrings, ERA,                    0 },
602   { "era_d_fmt",                 is_string,     ERA_D_FMT,              0 },
603   { "alt_digits",                is_sepstrings,ALT_DIGITS,              0 },
604   { "era_d_t_fmt",               is_string,     ERA_D_T_FMT,            0 },
605   { "era_t_fmt",                 is_string,     ERA_T_FMT,              0 },
606   { "date_fmt",                  is_string,     _DATE_FMT,              0 },
607   { "time-codeset",              is_string,     _NL_TIME_CODESET,       0 },
608   { NULL,                        is_end,        0,                      0 }
609 };
610
611 lc_names_t lc_collate_names[] =
612 {
613   { "collate-codeset",           is_string,     _NL_COLLATE_CODESET,    0 },
614   { NULL,                        is_end,        0,                      0 }
615 };
616
617 lc_names_t lc_monetary_names[] =
618 {
619   { "int_curr_symbol",           is_string,     _NL_MONETARY_INT_CURR_SYMBOL, 0 },
620   { "currency_symbol",           is_string,     _NL_MONETARY_CURRENCY_SYMBOL, 0 },
621   { "mon_decimal_point",         is_string,     _NL_MONETARY_MON_DECIMAL_POINT, 0 },
622   { "mon_thousands_sep",         is_string,     _NL_MONETARY_MON_THOUSANDS_SEP, 0 },
623   { "mon_grouping",              is_grouping,   _NL_MONETARY_MON_GROUPING, 0 },
624   { "positive_sign",             is_string,     _NL_MONETARY_POSITIVE_SIGN, 0 },
625   { "negative_sign",             is_string,     _NL_MONETARY_NEGATIVE_SIGN, 0 },
626   { "int_frac_digits",           is_int,        _NL_MONETARY_INT_FRAC_DIGITS, 0 },
627   { "frac_digits",               is_int,        _NL_MONETARY_FRAC_DIGITS,   0 },
628   { "p_cs_precedes",             is_int,        _NL_MONETARY_P_CS_PRECEDES, 0 },
629   { "p_sep_by_space",            is_int,        _NL_MONETARY_P_SEP_BY_SPACE, 0 },
630   { "n_cs_precedes",             is_int,        _NL_MONETARY_N_CS_PRECEDES, 0 },
631   { "n_sep_by_space",            is_int,        _NL_MONETARY_N_SEP_BY_SPACE, 0 },
632   { "p_sign_posn",               is_int,        _NL_MONETARY_P_SIGN_POSN,   0 },
633   { "n_sign_posn",               is_int,        _NL_MONETARY_N_SIGN_POSN,   0 },
634   { "int_p_cs_precedes",         is_int,        _NL_MONETARY_INT_P_CS_PRECEDES, 0 },
635   { "int_p_sep_by_space",        is_int,        _NL_MONETARY_INT_P_SEP_BY_SPACE,0 },
636   { "int_n_cs_precedes",         is_int,        _NL_MONETARY_INT_N_CS_PRECEDES, 0 },
637   { "int_n_sep_by_space",        is_int,        _NL_MONETARY_INT_N_SEP_BY_SPACE,0 },
638   { "int_p_sign_posn",           is_int,        _NL_MONETARY_INT_P_SIGN_POSN, 0 },
639   { "int_n_sign_posn",           is_int,        _NL_MONETARY_INT_N_SIGN_POSN, 0 },
640   { "monetary-decimal-point-wc", is_wchar,      _NL_MONETARY_WMON_DECIMAL_POINT, 0 },
641   { "monetary-thousands-sep-wc", is_wchar,      _NL_MONETARY_WMON_THOUSANDS_SEP, 0 },
642   { "monetary-codeset",          is_string,     _NL_MONETARY_CODESET,      0 },
643   { NULL,                        is_end,        0,                         0 }
644 };
645
646 lc_names_t lc_messages_names[] =
647 {
648   { "yesexpr",                   is_string,     YESEXPR,                0 },
649   { "noexpr",                    is_string,     NOEXPR,                 0 },
650   { "yesstr",                    is_string,     YESSTR,                 0 },
651   { "nostr",                     is_string,     NOSTR,                  0 },
652   { "messages-codeset",          is_string,     _NL_MESSAGES_CODESET,   0 },
653   { NULL,                        is_end,        0,                      0 }
654 };
655
656 void
657 print_lc (int cat, int key, const char *category, const char *name,
658           lc_names_t *lc_name)
659 {
660   if (cat)
661     printf ("%s\n", category);
662   for (lc_names_t *lc = lc_name; lc->type != is_end; ++lc)
663     if (!name || !strcmp (name, lc->name))
664       switch (lc->type)
665         {
666         case is_string_fake:
667           print_lc_svalue (key, lc->name, fake_string[lc->fromval + key]);
668           break;
669         case is_grouping:
670           print_lc_grouping (key, lc->name, nl_langinfo (lc->fromval));
671           break;
672         case is_string:
673           print_lc_svalue (key, lc->name, nl_langinfo (lc->fromval));
674           break;
675         case is_sepstrings:
676           print_lc_sepstrings (key, lc->name, nl_langinfo (lc->fromval));
677           break;
678         case is_mstrings:
679           print_lc_strings (key, lc->name, lc->fromval, lc->toval);
680           break;
681         case is_int:
682           print_lc_ivalue (key, lc->name, (int) *nl_langinfo (lc->fromval));
683           break;
684         case is_wchar:
685           print_lc_ivalue (key, lc->name,
686                            *(wchar_t *) nl_langinfo (lc->fromval));
687           break;
688         default:
689           break;
690         }
691 }
692
693 struct cat_t
694 {
695   const char *category;
696   int lc_cat;
697   lc_names_t *lc_names;
698 } categories[] =
699 {
700   { "LC_CTYPE",    LC_CTYPE,    lc_ctype_names    },
701   { "LC_NUMERIC",  LC_NUMERIC,  lc_numeric_names  },
702   { "LC_TIME",     LC_TIME,     lc_time_names     },
703   { "LC_COLLATE",  LC_COLLATE,  lc_collate_names  },
704   { "LC_MONETARY", LC_MONETARY, lc_monetary_names },
705   { "LC_MESSAGES", LC_MESSAGES, lc_messages_names },
706   { NULL,          0,           NULL              }
707 };
708
709 void
710 print_names (int cat, int key, const char *name)
711 {
712   struct cat_t *c;
713   lc_names_t *lc;
714
715   for (c = categories; c->category; ++c)
716     if (!strcmp (name, c->category))
717       {
718         print_lc (cat, key, c->category, NULL, c->lc_names);
719         return;
720       }
721   for (c = categories; c->category; ++c)
722     for (lc = c->lc_names; lc->type != is_end; ++lc)
723       if (!strcmp (name, lc->name))
724       {
725         print_lc (cat, key, c->category, lc->name, lc);
726         return;
727       }
728 }
729
730 void
731 print_lc ()
732 {
733   printf ("LANG=%s\n", getenv ("LANG") ?: "");
734   printf ("LC_CTYPE=\"%s\"\n", setlocale (LC_CTYPE, NULL));
735   printf ("LC_NUMERIC=\"%s\"\n", setlocale (LC_NUMERIC, NULL));
736   printf ("LC_TIME=\"%s\"\n", setlocale (LC_TIME, NULL));
737   printf ("LC_COLLATE=\"%s\"\n", setlocale (LC_COLLATE, NULL));
738   printf ("LC_MONETARY=\"%s\"\n", setlocale (LC_MONETARY, NULL));
739   printf ("LC_MESSAGES=\"%s\"\n", setlocale (LC_MESSAGES, NULL));
740   printf ("LC_ALL=%s\n", getenv ("LC_ALL") ?: "");
741 }
742
743 int
744 main (int argc, char **argv)
745 {
746   int opt;
747   LCID lcid = 0;
748   int all = 0;
749   int cat = 0;
750   int key = 0;
751   int maps = 0;
752   int verbose = 0;
753   const char *utf = "";
754   char name[32];
755
756   setlocale (LC_ALL, "");
757   while ((opt = getopt_long (argc, argv, opts, longopts, NULL)) != EOF)
758     switch (opt)
759       {
760       case 'a':
761         all = 1;
762         break;
763       case 'c':
764         cat = 1;
765         break;
766       case 'k':
767         key = 1;
768         break;
769       case 'm':
770         maps = 1;
771         break;
772       case 's':
773         lcid = LOCALE_SYSTEM_DEFAULT;
774         break;
775       case 'u':
776         lcid = LOCALE_USER_DEFAULT;
777         break;
778       case 'U':
779         utf = ".UTF-8";
780         break;
781       case 'v':
782         verbose = 1;
783         break;
784       case 'h':
785         usage (stdout, 0);
786         break;
787       default:
788         usage (stderr, 1);
789         break;
790       }
791   if (all)
792     print_all_locales (verbose);
793   else if (maps)
794     print_charmaps ();
795   else if (lcid)
796     {
797       if (getlocale (lcid, name))
798         printf ("%s%s\n", name, utf);
799     }
800   else if (optind < argc)
801     while (optind < argc)
802       print_names (cat, key, argv[optind++]);
803   else
804     print_lc ();
805   return 0;
806 }