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