]>
Commit | Line | Data |
---|---|---|
ce4f5f76 CV |
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> | |
a2036998 | 27 | #include <ctype.h> |
ce4f5f76 CV |
28 | #include <getopt.h> |
29 | #include <string.h> | |
30 | #include <wchar.h> | |
31 | #include <locale.h> | |
32 | #include <langinfo.h> | |
33 | #include <limits.h> | |
a2036998 | 34 | #include <sys/cygwin.h> |
ce4f5f76 CV |
35 | #define WINVER 0x0601 |
36 | #include <windows.h> | |
37 | ||
a2036998 CV |
38 | #define LOCALE_ALIAS "/usr/share/locale/locale.alias" |
39 | #define LOCALE_ALIAS_LINE_LEN 255 | |
40 | ||
ce4f5f76 CV |
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 | ||
a2036998 CV |
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 | ||
ce4f5f76 | 116 | void |
1d4c87a1 CV |
117 | print_locale_with_codeset (int verbose, loc_t *locale, bool utf8, |
118 | const char *modifier) | |
ce4f5f76 | 119 | { |
a2036998 CV |
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 | } | |
1d4c87a1 CV |
135 | snprintf (locname, 32, "%s%s%s%s", locale->name, utf8 ? ".utf8" : "", |
136 | modifier ? "@" : "", modifier ?: ""); | |
a2036998 CV |
137 | if (verbose) |
138 | fputs ("locale: ", stdout); | |
139 | printf ("%-15s ", locname); | |
ce4f5f76 | 140 | if (verbose) |
a2036998 CV |
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 | { | |
1d4c87a1 | 155 | print_locale_with_codeset (verbose, locale, false, NULL); |
a2036998 CV |
156 | char *modifier = strchr (locale->name, '@'); |
157 | if (!locale->alias) | |
158 | { | |
159 | if (!modifier) | |
1d4c87a1 | 160 | print_locale_with_codeset (verbose, locale, true, NULL); |
a2036998 CV |
161 | else if (!strcmp (modifier, "@cjknarrow")) |
162 | { | |
163 | *modifier++ = '\0'; | |
1d4c87a1 | 164 | print_locale_with_codeset (verbose, locale, true, modifier); |
a2036998 CV |
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; | |
536ad253 | 212 | size_t orig_loc_num = loc_num; |
a2036998 CV |
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; | |
536ad253 | 241 | loc = (loc_t *) bsearch (&search, locale, orig_loc_num, sizeof (loc_t), |
a2036998 CV |
242 | compare_locales); |
243 | add_locale (alias, loc ? loc->language : L"", loc ? loc->territory : L"", | |
244 | true); | |
245 | } | |
246 | fclose (fp); | |
ce4f5f76 CV |
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 | ||
a2036998 CV |
258 | add_locale ("C", L"C", L"POSIX"); |
259 | add_locale ("POSIX", L"C", L"POSIX", true); | |
ce4f5f76 CV |
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 */ | |
a2036998 | 326 | add_locale (loc, language, country); |
ce4f5f76 CV |
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; | |
a2036998 | 349 | add_locale (loc, language, country); |
ce4f5f76 CV |
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 | { | |
a2036998 CV |
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"); | |
ce4f5f76 CV |
375 | } |
376 | } | |
377 | } | |
a2036998 CV |
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]); | |
ce4f5f76 CV |
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", | |
0b66e4d7 | 417 | "EUC-CN", |
ce4f5f76 CV |
418 | "EUC-JP", |
419 | "EUC-KR", | |
0b66e4d7 | 420 | "GB2312", |
ce4f5f76 CV |
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 | ||
f13fe164 CV |
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 | ||
ce4f5f76 CV |
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 | ||
ce4f5f76 CV |
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, | |
22b6e810 CV |
526 | is_grouping, |
527 | is_string, | |
528 | is_mstrings, | |
529 | is_sepstrings, | |
530 | is_int, | |
531 | is_wchar, | |
ce4f5f76 CV |
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 | ||
ce4f5f76 CV |
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 | { | |
22b6e810 CV |
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 } | |
ce4f5f76 CV |
577 | }; |
578 | ||
579 | lc_names_t lc_numeric_names[] = | |
580 | { | |
22b6e810 CV |
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 } | |
ce4f5f76 CV |
588 | }; |
589 | ||
590 | lc_names_t lc_time_names[] = | |
591 | { | |
22b6e810 CV |
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 } | |
ce4f5f76 CV |
609 | }; |
610 | ||
611 | lc_names_t lc_collate_names[] = | |
612 | { | |
22b6e810 CV |
613 | { "collate-codeset", is_string, _NL_COLLATE_CODESET, 0 }, |
614 | { NULL, is_end, 0, 0 } | |
ce4f5f76 CV |
615 | }; |
616 | ||
617 | lc_names_t lc_monetary_names[] = | |
618 | { | |
22b6e810 CV |
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 } | |
ce4f5f76 CV |
644 | }; |
645 | ||
646 | lc_names_t lc_messages_names[] = | |
647 | { | |
22b6e810 CV |
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 } | |
ce4f5f76 CV |
654 | }; |
655 | ||
656 | void | |
657 | print_lc (int cat, int key, const char *category, const char *name, | |
658 | lc_names_t *lc_name) | |
659 | { | |
ce4f5f76 CV |
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; | |
22b6e810 CV |
669 | case is_grouping: |
670 | print_lc_grouping (key, lc->name, nl_langinfo (lc->fromval)); | |
ce4f5f76 | 671 | break; |
22b6e810 | 672 | case is_string: |
ce4f5f76 CV |
673 | print_lc_svalue (key, lc->name, nl_langinfo (lc->fromval)); |
674 | break; | |
22b6e810 | 675 | case is_sepstrings: |
f13fe164 CV |
676 | print_lc_sepstrings (key, lc->name, nl_langinfo (lc->fromval)); |
677 | break; | |
22b6e810 | 678 | case is_mstrings: |
ce4f5f76 CV |
679 | print_lc_strings (key, lc->name, lc->fromval, lc->toval); |
680 | break; | |
22b6e810 CV |
681 | case is_int: |
682 | print_lc_ivalue (key, lc->name, (int) *nl_langinfo (lc->fromval)); | |
ce4f5f76 | 683 | break; |
22b6e810 CV |
684 | case is_wchar: |
685 | print_lc_ivalue (key, lc->name, | |
686 | *(wchar_t *) nl_langinfo (lc->fromval)); | |
ce4f5f76 CV |
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 | } |