]> git.ipfire.org Git - thirdparty/glibc.git/blob - locale/programs/locale.c
Add #include <stdint.h> for uint[32|64]_t usage (except installed headers).
[thirdparty/glibc.git] / locale / programs / locale.c
1 /* Implementation of the locale program according to POSIX 9945-2.
2 Copyright (C) 1995-2013 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1995.
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published
8 by the Free Software Foundation; version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, see <http://www.gnu.org/licenses/>. */
18
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22
23 #include <argp.h>
24 #include <argz.h>
25 #include <dirent.h>
26 #include <errno.h>
27 #include <error.h>
28 #include <fcntl.h>
29 #include <langinfo.h>
30 #include <libintl.h>
31 #include <limits.h>
32 #include <locale.h>
33 #include <search.h>
34 #include <stdio.h>
35 #include <stdio_ext.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39 #include <stdint.h>
40 #include <sys/mman.h>
41 #include <sys/stat.h>
42
43 #include "localeinfo.h"
44 #include "charmap-dir.h"
45 #include "../locarchive.h"
46 #include <programs/xmalloc.h>
47
48 #define ARCHIVE_NAME LOCALEDIR "/locale-archive"
49
50 /* If set print the name of the category. */
51 static int show_category_name;
52
53 /* If set print the name of the item. */
54 static int show_keyword_name;
55
56 /* Print names of all available locales. */
57 static int do_all;
58
59 /* Print names of all available character maps. */
60 static int do_charmaps = 0;
61
62 /* Nonzero if verbose output is wanted. */
63 static int verbose;
64
65 /* Name and version of program. */
66 static void print_version (FILE *stream, struct argp_state *state);
67 void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
68
69 /* Definitions of arguments for argp functions. */
70 static const struct argp_option options[] =
71 {
72 { NULL, 0, NULL, 0, N_("System information:") },
73 { "all-locales", 'a', NULL, OPTION_NO_USAGE,
74 N_("Write names of available locales") },
75 { "charmaps", 'm', NULL, OPTION_NO_USAGE,
76 N_("Write names of available charmaps") },
77 { NULL, 0, NULL, 0, N_("Modify output format:") },
78 { "category-name", 'c', NULL, 0, N_("Write names of selected categories") },
79 { "keyword-name", 'k', NULL, 0, N_("Write names of selected keywords") },
80 { "verbose", 'v', NULL, 0, N_("Print more information") },
81 { NULL, 0, NULL, 0, NULL }
82 };
83
84 /* Short description of program. */
85 static const char doc[] = N_("Get locale-specific information.");
86
87 /* Strings for arguments in help texts. */
88 static const char args_doc[] = N_("NAME\n[-a|-m]");
89
90 /* Prototype for option handler. */
91 static error_t parse_opt (int key, char *arg, struct argp_state *state);
92
93 /* Function to print some extra text in the help message. */
94 static char *more_help (int key, const char *text, void *input);
95
96 /* Data structure to communicate with argp functions. */
97 static struct argp argp =
98 {
99 options, parse_opt, args_doc, doc, NULL, more_help
100 };
101
102
103 /* We don't have these constants defined because we don't use them. Give
104 default values. */
105 #define CTYPE_MB_CUR_MIN 0
106 #define CTYPE_MB_CUR_MAX 0
107 #define CTYPE_HASH_SIZE 0
108 #define CTYPE_HASH_LAYERS 0
109 #define CTYPE_CLASS 0
110 #define CTYPE_TOUPPER_EB 0
111 #define CTYPE_TOLOWER_EB 0
112 #define CTYPE_TOUPPER_EL 0
113 #define CTYPE_TOLOWER_EL 0
114
115 /* Definition of the data structure which represents a category and its
116 items. */
117 struct category
118 {
119 int cat_id;
120 const char *name;
121 size_t number;
122 struct cat_item
123 {
124 int item_id;
125 const char *name;
126 enum { std, opt } status;
127 enum value_type value_type;
128 int min;
129 int max;
130 } *item_desc;
131 };
132
133 /* Simple helper macro. */
134 #define NELEMS(arr) ((sizeof (arr)) / (sizeof (arr[0])))
135
136 /* For some tricky stuff. */
137 #define NO_PAREN(Item, More...) Item, ## More
138
139 /* We have all categories defined in `categories.def'. Now construct
140 the description and data structure used for all categories. */
141 #define DEFINE_ELEMENT(Item, More...) { Item, ## More },
142 #define DEFINE_CATEGORY(category, name, items, postload) \
143 static struct cat_item category##_desc[] = \
144 { \
145 NO_PAREN items \
146 };
147
148 #include "categories.def"
149 #undef DEFINE_CATEGORY
150
151 static struct category category[] =
152 {
153 #define DEFINE_CATEGORY(category, name, items, postload) \
154 [category] = { _NL_NUM_##category, name, NELEMS (category##_desc), \
155 category##_desc },
156 #include "categories.def"
157 #undef DEFINE_CATEGORY
158 };
159 #define NCATEGORIES NELEMS (category)
160
161
162 /* Automatically set variable. */
163 extern const char *__progname;
164
165 /* helper function for extended name handling. */
166 extern void locale_special (const char *name, int show_category_name,
167 int show_keyword_name);
168
169 /* Prototypes for local functions. */
170 static void print_LC_IDENTIFICATION (void *mapped, size_t size);
171 static void print_LC_CTYPE (void *mapped, size_t size);
172 static void write_locales (void);
173 static int nameentcmp (const void *a, const void *b);
174 static int write_archive_locales (void **all_datap, char *linebuf);
175 static void write_charmaps (void);
176 static void show_locale_vars (void);
177 static void show_info (const char *name);
178
179
180 int
181 main (int argc, char *argv[])
182 {
183 int remaining;
184
185 /* Set initial values for global variables. */
186 show_category_name = 0;
187 show_keyword_name = 0;
188
189 /* Set locale. Do not set LC_ALL because the other categories must
190 not be affected (according to POSIX.2). */
191 if (setlocale (LC_CTYPE, "") == NULL)
192 error (0, errno, gettext ("Cannot set LC_CTYPE to default locale"));
193 if (setlocale (LC_MESSAGES, "") == NULL)
194 error (0, errno, gettext ("Cannot set LC_MESSAGES to default locale"));
195
196 /* Initialize the message catalog. */
197 textdomain (PACKAGE);
198
199 /* Parse and process arguments. */
200 argp_parse (&argp, argc, argv, 0, &remaining, NULL);
201
202 /* `-a' requests the names of all available locales. */
203 if (do_all != 0)
204 {
205 if (setlocale (LC_COLLATE, "") == NULL)
206 error (0, errno,
207 gettext ("Cannot set LC_COLLATE to default locale"));
208 write_locales ();
209 exit (EXIT_SUCCESS);
210 }
211
212 /* `m' requests the names of all available charmaps. The names can be
213 used for the -f argument to localedef(1). */
214 if (do_charmaps != 0)
215 {
216 write_charmaps ();
217 exit (EXIT_SUCCESS);
218 }
219
220 /* Specific information about the current locale are requested.
221 Change to this locale now. */
222 if (setlocale (LC_ALL, "") == NULL)
223 error (0, errno, gettext ("Cannot set LC_ALL to default locale"));
224
225 /* If no real argument is given we have to print the contents of the
226 current locale definition variables. These are LANG and the LC_*. */
227 if (remaining == argc && show_keyword_name == 0 && show_category_name == 0)
228 {
229 show_locale_vars ();
230 exit (EXIT_SUCCESS);
231 }
232
233 /* Process all given names. */
234 while (remaining < argc)
235 show_info (argv[remaining++]);
236
237 exit (EXIT_SUCCESS);
238 }
239
240
241 /* Handle program arguments. */
242 static error_t
243 parse_opt (int key, char *arg, struct argp_state *state)
244 {
245 switch (key)
246 {
247 case 'a':
248 do_all = 1;
249 break;
250 case 'c':
251 show_category_name = 1;
252 break;
253 case 'm':
254 do_charmaps = 1;
255 break;
256 case 'k':
257 show_keyword_name = 1;
258 break;
259 case 'v':
260 verbose = 1;
261 break;
262 default:
263 return ARGP_ERR_UNKNOWN;
264 }
265 return 0;
266 }
267
268
269 static char *
270 more_help (int key, const char *text, void *input)
271 {
272 char *tp = NULL;
273 switch (key)
274 {
275 case ARGP_KEY_HELP_EXTRA:
276 /* We print some extra information. */
277 if (asprintf (&tp, gettext ("\
278 For bug reporting instructions, please see:\n\
279 %s.\n"), REPORT_BUGS_TO) < 0)
280 return NULL;
281 return tp;
282 default:
283 break;
284 }
285 return (char *) text;
286 }
287
288
289 /* Print the version information. */
290 static void
291 print_version (FILE *stream, struct argp_state *state)
292 {
293 fprintf (stream, "locale %s%s\n", PKGVERSION, VERSION);
294 fprintf (stream, gettext ("\
295 Copyright (C) %s Free Software Foundation, Inc.\n\
296 This is free software; see the source for copying conditions. There is NO\n\
297 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
298 "), "2013");
299 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
300 }
301
302
303 /* Simple action function which prints arguments as strings. */
304 static void
305 print_names (const void *nodep, VISIT value, int level)
306 {
307 if (value == postorder || value == leaf)
308 puts (*(char **) nodep);
309 }
310
311
312 static int
313 select_dirs (const struct dirent *dirent)
314 {
315 int result = 0;
316
317 if (strcmp (dirent->d_name, ".") != 0 && strcmp (dirent->d_name, "..") != 0)
318 {
319 mode_t mode = 0;
320
321 #ifdef _DIRENT_HAVE_D_TYPE
322 if (dirent->d_type != DT_UNKNOWN && dirent->d_type != DT_LNK)
323 mode = DTTOIF (dirent->d_type);
324 else
325 #endif
326 {
327 struct stat64 st;
328 char buf[sizeof (LOCALEDIR) + strlen (dirent->d_name) + 1];
329
330 stpcpy (stpcpy (stpcpy (buf, LOCALEDIR), "/"), dirent->d_name);
331
332 if (stat64 (buf, &st) == 0)
333 mode = st.st_mode;
334 }
335
336 result = S_ISDIR (mode);
337 }
338
339 return result;
340 }
341
342
343 static void
344 print_LC_IDENTIFICATION (void *mapped, size_t size)
345 {
346 /* Read the information from the file. */
347 struct
348 {
349 unsigned int magic;
350 unsigned int nstrings;
351 unsigned int strindex[0];
352 } *filedata = mapped;
353
354 if (filedata->magic == LIMAGIC (LC_IDENTIFICATION)
355 && (sizeof *filedata
356 + (filedata->nstrings
357 * sizeof (unsigned int))
358 <= size))
359 {
360 const char *str;
361
362 #define HANDLE(idx, name) \
363 str = ((char *) mapped \
364 + filedata->strindex[_NL_ITEM_INDEX (_NL_IDENTIFICATION_##idx)]); \
365 if (*str != '\0') \
366 printf ("%9s | %s\n", name, str)
367 HANDLE (TITLE, "title");
368 HANDLE (SOURCE, "source");
369 HANDLE (ADDRESS, "address");
370 HANDLE (CONTACT, "contact");
371 HANDLE (EMAIL, "email");
372 HANDLE (TEL, "telephone");
373 HANDLE (FAX, "fax");
374 HANDLE (LANGUAGE, "language");
375 HANDLE (TERRITORY, "territory");
376 HANDLE (AUDIENCE, "audience");
377 HANDLE (APPLICATION, "application");
378 HANDLE (ABBREVIATION, "abbreviation");
379 HANDLE (REVISION, "revision");
380 HANDLE (DATE, "date");
381 }
382 }
383
384
385 static void
386 print_LC_CTYPE (void *mapped, size_t size)
387 {
388 struct
389 {
390 unsigned int magic;
391 unsigned int nstrings;
392 unsigned int strindex[0];
393 } *filedata = mapped;
394
395 if (filedata->magic == LIMAGIC (LC_CTYPE)
396 && (sizeof *filedata
397 + (filedata->nstrings
398 * sizeof (unsigned int))
399 <= size))
400 {
401 const char *str;
402
403 str = ((char *) mapped
404 + filedata->strindex[_NL_ITEM_INDEX (_NL_CTYPE_CODESET_NAME)]);
405 if (*str != '\0')
406 printf (" codeset | %s\n", str);
407 }
408 }
409
410
411 /* Write the names of all available locales to stdout. We have some
412 sources of the information: the contents of the locale directory
413 and the locale.alias file. To avoid duplicates and print the
414 result is a reasonable order we put all entries is a search tree
415 and print them afterwards. */
416 static void
417 write_locales (void)
418 {
419 char linebuf[80];
420 void *all_data = NULL;
421 struct dirent **dirents;
422 int ndirents;
423 int cnt;
424 char *alias_path;
425 size_t alias_path_len;
426 char *entry;
427 int first_locale = 1;
428
429 #define PUT(name) tsearch (name, &all_data, \
430 (int (*) (const void *, const void *)) strcoll)
431 #define GET(name) tfind (name, &all_data, \
432 (int (*) (const void *, const void *)) strcoll)
433
434 /* `POSIX' locale is always available (POSIX.2 4.34.3). */
435 PUT ("POSIX");
436 /* And so is the "C" locale. */
437 PUT ("C");
438
439 memset (linebuf, '-', sizeof (linebuf) - 1);
440 linebuf[sizeof (linebuf) - 1] = '\0';
441
442 /* First scan the locale archive. */
443 if (write_archive_locales (&all_data, linebuf))
444 first_locale = 0;
445
446 /* Now we can look for all files in the directory. */
447 ndirents = scandir (LOCALEDIR, &dirents, select_dirs, alphasort);
448 for (cnt = 0; cnt < ndirents; ++cnt)
449 {
450 /* Test whether at least the LC_CTYPE data is there. Some
451 directories only contain translations. */
452 char buf[sizeof (LOCALEDIR) + strlen (dirents[cnt]->d_name)
453 + sizeof "/LC_IDENTIFICATION"];
454 char *enddir;
455 struct stat64 st;
456
457 stpcpy (enddir = stpcpy (stpcpy (stpcpy (buf, LOCALEDIR), "/"),
458 dirents[cnt]->d_name),
459 "/LC_IDENTIFICATION");
460
461 if (stat64 (buf, &st) == 0 && S_ISREG (st.st_mode))
462 {
463 if (verbose && GET (dirents[cnt]->d_name) == NULL)
464 {
465 /* Provide some nice output of all kinds of
466 information. */
467 int fd;
468
469 if (! first_locale)
470 putchar_unlocked ('\n');
471 first_locale = 0;
472
473 printf ("locale: %-15.15s directory: %.*s\n%s\n",
474 dirents[cnt]->d_name, (int) (enddir - buf), buf,
475 linebuf);
476
477 fd = open64 (buf, O_RDONLY);
478 if (fd != -1)
479 {
480 void *mapped = mmap64 (NULL, st.st_size, PROT_READ,
481 MAP_SHARED, fd, 0);
482 if (mapped != MAP_FAILED)
483 {
484 print_LC_IDENTIFICATION (mapped, st.st_size);
485
486 munmap (mapped, st.st_size);
487 }
488
489 close (fd);
490
491 /* Now try to get the charset information. */
492 strcpy (enddir, "/LC_CTYPE");
493 fd = open64 (buf, O_RDONLY);
494 if (fd != -1 && fstat64 (fd, &st) >= 0
495 && ((mapped = mmap64 (NULL, st.st_size, PROT_READ,
496 MAP_SHARED, fd, 0))
497 != MAP_FAILED))
498 {
499 print_LC_CTYPE (mapped, st.st_size);
500
501 munmap (mapped, st.st_size);
502 }
503
504 if (fd != -1)
505 close (fd);
506 }
507 }
508
509 /* If the verbose format is not selected we simply
510 collect the names. */
511 PUT (xstrdup (dirents[cnt]->d_name));
512 }
513 }
514 if (ndirents > 0)
515 free (dirents);
516
517 /* Now read the locale.alias files. */
518 if (argz_create_sep (LOCALE_ALIAS_PATH, ':', &alias_path, &alias_path_len))
519 error (1, errno, gettext ("while preparing output"));
520
521 entry = NULL;
522 while ((entry = argz_next (alias_path, alias_path_len, entry)))
523 {
524 static const char aliasfile[] = "/locale.alias";
525 FILE *fp;
526 char full_name[strlen (entry) + sizeof aliasfile];
527
528 stpcpy (stpcpy (full_name, entry), aliasfile);
529 fp = fopen (full_name, "rm");
530 if (fp == NULL)
531 /* Ignore non-existing files. */
532 continue;
533
534 /* No threads present. */
535 __fsetlocking (fp, FSETLOCKING_BYCALLER);
536
537 while (! feof_unlocked (fp))
538 {
539 /* It is a reasonable approach to use a fix buffer here
540 because
541 a) we are only interested in the first two fields
542 b) these fields must be usable as file names and so must
543 not be that long */
544 char buf[BUFSIZ];
545 char *alias;
546 char *value;
547 char *cp;
548
549 if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
550 /* EOF reached. */
551 break;
552
553 cp = buf;
554 /* Ignore leading white space. */
555 while (isspace (cp[0]) && cp[0] != '\n')
556 ++cp;
557
558 /* A leading '#' signals a comment line. */
559 if (cp[0] != '\0' && cp[0] != '#' && cp[0] != '\n')
560 {
561 alias = cp++;
562 while (cp[0] != '\0' && !isspace (cp[0]))
563 ++cp;
564 /* Terminate alias name. */
565 if (cp[0] != '\0')
566 *cp++ = '\0';
567
568 /* Now look for the beginning of the value. */
569 while (isspace (cp[0]))
570 ++cp;
571
572 if (cp[0] != '\0')
573 {
574 value = cp++;
575 while (cp[0] != '\0' && !isspace (cp[0]))
576 ++cp;
577 /* Terminate value. */
578 if (cp[0] == '\n')
579 {
580 /* This has to be done to make the following
581 test for the end of line possible. We are
582 looking for the terminating '\n' which do not
583 overwrite here. */
584 *cp++ = '\0';
585 *cp = '\n';
586 }
587 else if (cp[0] != '\0')
588 *cp++ = '\0';
589
590 /* Add the alias. */
591 if (! verbose && GET (value) != NULL)
592 PUT (xstrdup (alias));
593 }
594 }
595
596 /* Possibly not the whole line fits into the buffer.
597 Ignore the rest of the line. */
598 while (strchr (cp, '\n') == NULL)
599 {
600 cp = buf;
601 if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
602 /* Make sure the inner loop will be left. The outer
603 loop will exit at the `feof' test. */
604 *cp = '\n';
605 }
606 }
607
608 fclose (fp);
609 }
610
611 if (! verbose)
612 {
613 twalk (all_data, print_names);
614 }
615 }
616
617
618 struct nameent
619 {
620 char *name;
621 uint32_t locrec_offset;
622 };
623
624
625 static int
626 nameentcmp (const void *a, const void *b)
627 {
628 return strcoll (((const struct nameent *) a)->name,
629 ((const struct nameent *) b)->name);
630 }
631
632
633 static int
634 write_archive_locales (void **all_datap, char *linebuf)
635 {
636 struct stat64 st;
637 void *all_data = *all_datap;
638 size_t len = 0;
639 struct locarhead *head;
640 struct namehashent *namehashtab;
641 char *addr = MAP_FAILED;
642 int fd, ret = 0;
643 uint32_t cnt;
644
645 fd = open64 (ARCHIVE_NAME, O_RDONLY);
646 if (fd < 0)
647 return 0;
648
649 if (fstat64 (fd, &st) < 0 || st.st_size < sizeof (*head))
650 goto error_out;
651
652 len = st.st_size;
653 addr = mmap64 (NULL, len, PROT_READ, MAP_SHARED, fd, 0);
654 if (addr == MAP_FAILED)
655 goto error_out;
656
657 head = (struct locarhead *) addr;
658 if (head->namehash_offset + head->namehash_size > len
659 || head->string_offset + head->string_size > len
660 || head->locrectab_offset + head->locrectab_size > len
661 || head->sumhash_offset + head->sumhash_size > len)
662 goto error_out;
663
664 namehashtab = (struct namehashent *) (addr + head->namehash_offset);
665 if (! verbose)
666 {
667 for (cnt = 0; cnt < head->namehash_size; ++cnt)
668 if (namehashtab[cnt].locrec_offset != 0)
669 {
670 PUT (xstrdup (addr + namehashtab[cnt].name_offset));
671 ++ret;
672 }
673 }
674 else
675 {
676 struct nameent *names;
677 uint32_t used;
678
679 names = (struct nameent *) xmalloc (head->namehash_used
680 * sizeof (struct nameent));
681 for (cnt = used = 0; cnt < head->namehash_size; ++cnt)
682 if (namehashtab[cnt].locrec_offset != 0)
683 {
684 names[used].name = addr + namehashtab[cnt].name_offset;
685 names[used++].locrec_offset = namehashtab[cnt].locrec_offset;
686 }
687
688 /* Sort the names. */
689 qsort (names, used, sizeof (struct nameent), nameentcmp);
690
691 for (cnt = 0; cnt < used; ++cnt)
692 {
693 struct locrecent *locrec;
694
695 PUT (xstrdup (names[cnt].name));
696
697 if (cnt)
698 putchar_unlocked ('\n');
699
700 printf ("locale: %-15.15s archive: " ARCHIVE_NAME "\n%s\n",
701 names[cnt].name, linebuf);
702
703 locrec = (struct locrecent *) (addr + names[cnt].locrec_offset);
704
705 print_LC_IDENTIFICATION (addr
706 + locrec->record[LC_IDENTIFICATION].offset,
707 locrec->record[LC_IDENTIFICATION].len);
708
709 print_LC_CTYPE (addr + locrec->record[LC_CTYPE].offset,
710 locrec->record[LC_CTYPE].len);
711 }
712
713 ret = used;
714 }
715
716 error_out:
717 if (addr != MAP_FAILED)
718 munmap (addr, len);
719 close (fd);
720 *all_datap = all_data;
721 return ret;
722 }
723
724
725 /* Write the names of all available character maps to stdout. */
726 static void
727 write_charmaps (void)
728 {
729 void *all_data = NULL;
730 CHARMAP_DIR *dir;
731 const char *dirent;
732
733 /* Look for all files in the charmap directory. */
734 dir = charmap_opendir (CHARMAP_PATH);
735 if (dir == NULL)
736 return;
737
738 while ((dirent = charmap_readdir (dir)) != NULL)
739 {
740 char **aliases;
741 char **p;
742
743 PUT (xstrdup (dirent));
744
745 aliases = charmap_aliases (CHARMAP_PATH, dirent);
746
747 #if 0
748 /* Add the code_set_name and the aliases. */
749 for (p = aliases; *p; p++)
750 PUT (xstrdup (*p));
751 #else
752 /* Add the code_set_name only. Most aliases are obsolete. */
753 p = aliases;
754 if (*p)
755 PUT (xstrdup (*p));
756 #endif
757
758 charmap_free_aliases (aliases);
759 }
760
761 charmap_closedir (dir);
762
763 twalk (all_data, print_names);
764 }
765
766 /* Print a properly quoted assignment of NAME with VAL, using double
767 quotes iff DQUOTE is true. */
768 static void
769 print_assignment (const char *name, const char *val, bool dquote)
770 {
771 printf ("%s=", name);
772 if (dquote)
773 putchar ('"');
774 while (*val != '\0')
775 {
776 size_t segment
777 = strcspn (val, dquote ? "$`\"\\" : "~|&;<>()$`\\\"' \t\n");
778 printf ("%.*s", (int) segment, val);
779 val += segment;
780 if (*val == '\0')
781 break;
782 putchar ('\\');
783 putchar (*val++);
784 }
785 if (dquote)
786 putchar ('"');
787 putchar ('\n');
788 }
789
790 /* We have to show the contents of the environments determining the
791 locale. */
792 static void
793 show_locale_vars (void)
794 {
795 size_t cat_no;
796 const char *lcall = getenv ("LC_ALL") ? : "";
797 const char *lang = getenv ("LANG") ? : "";
798
799 auto void get_source (const char *name);
800
801 void get_source (const char *name)
802 {
803 char *val = getenv (name);
804
805 if (lcall[0] != '\0' || val == NULL)
806 print_assignment (name, lcall[0] ? lcall : lang[0] ? lang : "POSIX",
807 true);
808 else
809 print_assignment (name, val, false);
810 }
811
812 /* LANG has to be the first value. */
813 print_assignment ("LANG", lang, false);
814
815 /* Now all categories in an unspecified order. */
816 for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
817 if (cat_no != LC_ALL)
818 get_source (category[cat_no].name);
819
820 /* The last is the LC_ALL value. */
821 print_assignment ("LC_ALL", lcall, false);
822 }
823
824
825 /* Show the information request for NAME. */
826 static void
827 show_info (const char *name)
828 {
829 size_t cat_no;
830
831 auto void print_item (struct cat_item *item);
832
833 void print_item (struct cat_item *item)
834 {
835 switch (item->value_type)
836 {
837 case string:
838 if (show_keyword_name)
839 printf ("%s=\"", item->name);
840 fputs (nl_langinfo (item->item_id) ? : "", stdout);
841 if (show_keyword_name)
842 putchar ('"');
843 putchar ('\n');
844 break;
845 case stringarray:
846 {
847 int cnt;
848 const char *val;
849
850 if (show_keyword_name)
851 printf ("%s=\"", item->name);
852
853 for (cnt = 0; cnt < item->max - 1; ++cnt)
854 {
855 val = nl_langinfo (item->item_id + cnt);
856 if (val != NULL)
857 fputs (val, stdout);
858 putchar (';');
859 }
860
861 val = nl_langinfo (item->item_id + cnt);
862 if (val != NULL)
863 fputs (val, stdout);
864
865 if (show_keyword_name)
866 putchar ('"');
867 putchar ('\n');
868 }
869 break;
870 case stringlist:
871 {
872 int first = 1;
873 const char *val = nl_langinfo (item->item_id) ? : "";
874 int cnt;
875
876 if (show_keyword_name)
877 printf ("%s=", item->name);
878
879 for (cnt = 0; cnt < item->max && *val != '\0'; ++cnt)
880 {
881 printf ("%s%s%s%s", first ? "" : ";",
882 show_keyword_name ? "\"" : "", val,
883 show_keyword_name ? "\"" : "");
884 val = strchr (val, '\0') + 1;
885 first = 0;
886 }
887 putchar ('\n');
888 }
889 break;
890 case byte:
891 {
892 const char *val = nl_langinfo (item->item_id);
893
894 if (show_keyword_name)
895 printf ("%s=", item->name);
896
897 if (val != NULL)
898 printf ("%d", *val == '\177' ? -1 : *val);
899 putchar ('\n');
900 }
901 break;
902 case bytearray:
903 {
904 const char *val = nl_langinfo (item->item_id);
905 int cnt = val ? strlen (val) : 0;
906
907 if (show_keyword_name)
908 printf ("%s=", item->name);
909
910 while (cnt > 1)
911 {
912 printf ("%d;", *val == '\177' ? -1 : *val);
913 --cnt;
914 ++val;
915 }
916
917 printf ("%d\n", cnt == 0 || *val == '\177' ? -1 : *val);
918 }
919 break;
920 case word:
921 {
922 union { unsigned int word; char *string; } val;
923 val.string = nl_langinfo (item->item_id);
924 if (show_keyword_name)
925 printf ("%s=", item->name);
926
927 printf ("%d\n", val.word);
928 }
929 break;
930 case wstring:
931 case wstringarray:
932 case wstringlist:
933 /* We don't print wide character information since the same
934 information is available in a multibyte string. */
935 default:
936 break;
937
938 }
939 }
940
941 for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
942 if (cat_no != LC_ALL)
943 {
944 size_t item_no;
945
946 if (strcmp (name, category[cat_no].name) == 0)
947 /* Print the whole category. */
948 {
949 if (show_category_name != 0)
950 puts (category[cat_no].name);
951
952 for (item_no = 0; item_no < category[cat_no].number; ++item_no)
953 print_item (&category[cat_no].item_desc[item_no]);
954
955 return;
956 }
957
958 for (item_no = 0; item_no < category[cat_no].number; ++item_no)
959 if (strcmp (name, category[cat_no].item_desc[item_no].name) == 0)
960 {
961 if (show_category_name != 0)
962 puts (category[cat_no].name);
963
964 print_item (&category[cat_no].item_desc[item_no]);
965 return;
966 }
967 }
968
969 /* The name is not a standard one.
970 For testing and perhaps advanced use allow some more symbols. */
971 locale_special (name, show_category_name, show_keyword_name);
972 }