]> git.ipfire.org Git - thirdparty/glibc.git/blob - locale/programs/locale.c
[BZ #40]
[thirdparty/glibc.git] / locale / programs / locale.c
1 /* Implementation of the locale program according to POSIX 9945-2.
2 Copyright (C) 1995-1997, 1999-2003, 2004 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 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 The GNU C Library 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 GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; if not, write to the Free
18 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA. */
20
21 #ifdef HAVE_CONFIG_H
22 # include <config.h>
23 #endif
24
25 #include <argp.h>
26 #include <argz.h>
27 #include <dirent.h>
28 #include <errno.h>
29 #include <error.h>
30 #include <fcntl.h>
31 #include <langinfo.h>
32 #include <libintl.h>
33 #include <limits.h>
34 #include <locale.h>
35 #include <search.h>
36 #include <stdio.h>
37 #include <stdio_ext.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <unistd.h>
41 #include <sys/mman.h>
42 #include <sys/stat.h>
43
44 #include "localeinfo.h"
45 #include "charmap-dir.h"
46 #include "../locarchive.h"
47
48 extern void *xmalloc (size_t __n);
49 extern char *xstrdup (const char *__str);
50
51 #define ARCHIVE_NAME LOCALEDIR "/locale-archive"
52
53 /* If set print the name of the category. */
54 static int show_category_name;
55
56 /* If set print the name of the item. */
57 static int show_keyword_name;
58
59 /* Print names of all available locales. */
60 static int do_all;
61
62 /* Print names of all available character maps. */
63 static int do_charmaps = 0;
64
65 /* Nonzero if verbose output is wanted. */
66 static int verbose;
67
68 /* Name and version of program. */
69 static void print_version (FILE *stream, struct argp_state *state);
70 void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
71
72 /* Definitions of arguments for argp functions. */
73 static const struct argp_option options[] =
74 {
75 { NULL, 0, NULL, 0, N_("System information:") },
76 { "all-locales", 'a', NULL, OPTION_NO_USAGE,
77 N_("Write names of available locales") },
78 { "charmaps", 'm', NULL, OPTION_NO_USAGE,
79 N_("Write names of available charmaps") },
80 { NULL, 0, NULL, 0, N_("Modify output format:") },
81 { "category-name", 'c', NULL, 0, N_("Write names of selected categories") },
82 { "keyword-name", 'k', NULL, 0, N_("Write names of selected keywords") },
83 { "verbose", 'v', NULL, 0, N_("Print more information") },
84 { NULL, 0, NULL, 0, NULL }
85 };
86
87 /* Short description of program. */
88 static const char doc[] = N_("Get locale-specific information.");
89
90 /* Strings for arguments in help texts. */
91 static const char args_doc[] = N_("NAME\n[-a|-m]");
92
93 /* Prototype for option handler. */
94 static error_t parse_opt (int key, char *arg, struct argp_state *state);
95
96 /* Function to print some extra text in the help message. */
97 static char *more_help (int key, const char *text, void *input);
98
99 /* Data structure to communicate with argp functions. */
100 static struct argp argp =
101 {
102 options, parse_opt, args_doc, doc, NULL, more_help
103 };
104
105
106 /* We don't have these constants defined because we don't use them. Give
107 default values. */
108 #define CTYPE_MB_CUR_MIN 0
109 #define CTYPE_MB_CUR_MAX 0
110 #define CTYPE_HASH_SIZE 0
111 #define CTYPE_HASH_LAYERS 0
112 #define CTYPE_CLASS 0
113 #define CTYPE_TOUPPER_EB 0
114 #define CTYPE_TOLOWER_EB 0
115 #define CTYPE_TOUPPER_EL 0
116 #define CTYPE_TOLOWER_EL 0
117
118 /* Definition of the data structure which represents a category and its
119 items. */
120 struct category
121 {
122 int cat_id;
123 const char *name;
124 size_t number;
125 struct cat_item
126 {
127 int item_id;
128 const char *name;
129 enum { std, opt } status;
130 enum value_type value_type;
131 int min;
132 int max;
133 } *item_desc;
134 };
135
136 /* Simple helper macro. */
137 #define NELEMS(arr) ((sizeof (arr)) / (sizeof (arr[0])))
138
139 /* For some tricky stuff. */
140 #define NO_PAREN(Item, More...) Item, ## More
141
142 /* We have all categories defined in `categories.def'. Now construct
143 the description and data structure used for all categories. */
144 #define DEFINE_ELEMENT(Item, More...) { Item, ## More },
145 #define DEFINE_CATEGORY(category, name, items, postload) \
146 static struct cat_item category##_desc[] = \
147 { \
148 NO_PAREN items \
149 };
150
151 #include "categories.def"
152 #undef DEFINE_CATEGORY
153
154 static struct category category[] =
155 {
156 #define DEFINE_CATEGORY(category, name, items, postload) \
157 [category] = { _NL_NUM_##category, name, NELEMS (category##_desc), \
158 category##_desc },
159 #include "categories.def"
160 #undef DEFINE_CATEGORY
161 };
162 #define NCATEGORIES NELEMS (category)
163
164
165 /* Automatically set variable. */
166 extern const char *__progname;
167
168 /* helper function for extended name handling. */
169 extern void locale_special (const char *name, int show_category_name,
170 int show_keyword_name);
171
172 /* Prototypes for local functions. */
173 static void print_LC_IDENTIFICATION (void *mapped, size_t size);
174 static void print_LC_CTYPE (void *mapped, size_t size);
175 static void write_locales (void);
176 static int nameentcmp (const void *a, const void *b);
177 static int write_archive_locales (void **all_datap, char *linebuf);
178 static void write_charmaps (void);
179 static void show_locale_vars (void);
180 static void show_info (const char *name);
181
182
183 int
184 main (int argc, char *argv[])
185 {
186 int remaining;
187
188 /* Set initial values for global variables. */
189 show_category_name = 0;
190 show_keyword_name = 0;
191
192 /* Set locale. Do not set LC_ALL because the other categories must
193 not be affected (according to POSIX.2). */
194 if (setlocale (LC_CTYPE, "") == NULL)
195 error (0, errno, gettext ("Cannot set LC_CTYPE to default locale"));
196 if (setlocale (LC_MESSAGES, "") == NULL)
197 error (0, errno, gettext ("Cannot set LC_MESSAGES to default locale"));
198
199 /* Initialize the message catalog. */
200 textdomain (PACKAGE);
201
202 /* Parse and process arguments. */
203 argp_parse (&argp, argc, argv, 0, &remaining, NULL);
204
205 /* `-a' requests the names of all available locales. */
206 if (do_all != 0)
207 {
208 if (setlocale (LC_COLLATE, "") == NULL)
209 error (0, errno,
210 gettext ("Cannot set LC_COLLATE to default locale"));
211 write_locales ();
212 exit (EXIT_SUCCESS);
213 }
214
215 /* `m' requests the names of all available charmaps. The names can be
216 used for the -f argument to localedef(1). */
217 if (do_charmaps != 0)
218 {
219 write_charmaps ();
220 exit (EXIT_SUCCESS);
221 }
222
223 /* Specific information about the current locale are requested.
224 Change to this locale now. */
225 if (setlocale (LC_ALL, "") == NULL)
226 error (0, errno, gettext ("Cannot set LC_ALL to default locale"));
227
228 /* If no real argument is given we have to print the contents of the
229 current locale definition variables. These are LANG and the LC_*. */
230 if (remaining == argc && show_keyword_name == 0 && show_category_name == 0)
231 {
232 show_locale_vars ();
233 exit (EXIT_SUCCESS);
234 }
235
236 /* Process all given names. */
237 while (remaining < argc)
238 show_info (argv[remaining++]);
239
240 exit (EXIT_SUCCESS);
241 }
242
243
244 /* Handle program arguments. */
245 static error_t
246 parse_opt (int key, char *arg, struct argp_state *state)
247 {
248 switch (key)
249 {
250 case 'a':
251 do_all = 1;
252 break;
253 case 'c':
254 show_category_name = 1;
255 break;
256 case 'm':
257 do_charmaps = 1;
258 break;
259 case 'k':
260 show_keyword_name = 1;
261 break;
262 case 'v':
263 verbose = 1;
264 break;
265 default:
266 return ARGP_ERR_UNKNOWN;
267 }
268 return 0;
269 }
270
271
272 static char *
273 more_help (int key, const char *text, void *input)
274 {
275 switch (key)
276 {
277 case ARGP_KEY_HELP_EXTRA:
278 /* We print some extra information. */
279 return xstrdup (gettext ("\
280 For bug reporting instructions, please see:\n\
281 <http://www.gnu.org/software/libc/bugs.html>.\n"));
282 default:
283 break;
284 }
285 return (char *) text;
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 "), "2004");
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
766 /* We have to show the contents of the environments determining the
767 locale. */
768 static void
769 show_locale_vars (void)
770 {
771 size_t cat_no;
772 const char *lcall = getenv ("LC_ALL");
773 const char *lang = getenv ("LANG") ? : "";
774
775 auto void get_source (const char *name);
776
777 void get_source (const char *name)
778 {
779 char *val = getenv (name);
780
781 if ((lcall ?: "")[0] != '\0' || val == NULL)
782 printf ("%s=\"%s\"\n", name,
783 (lcall ?: "")[0] ? lcall : (lang ?: "")[0] ? lang : "POSIX");
784 else
785 printf ("%s=%s\n", name, val);
786 }
787
788 /* LANG has to be the first value. */
789 printf ("LANG=%s\n", lang);
790
791 /* Now all categories in an unspecified order. */
792 for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
793 if (cat_no != LC_ALL)
794 get_source (category[cat_no].name);
795
796 /* The last is the LC_ALL value. */
797 printf ("LC_ALL=%s\n", lcall ? : "");
798 }
799
800
801 /* Show the information request for NAME. */
802 static void
803 show_info (const char *name)
804 {
805 size_t cat_no;
806
807 auto void print_item (struct cat_item *item);
808
809 void print_item (struct cat_item *item)
810 {
811 switch (item->value_type)
812 {
813 case string:
814 if (show_keyword_name)
815 printf ("%s=\"", item->name);
816 fputs (nl_langinfo (item->item_id) ? : "", stdout);
817 if (show_keyword_name)
818 putchar ('"');
819 putchar ('\n');
820 break;
821 case stringarray:
822 {
823 int cnt;
824 const char *val;
825
826 if (show_keyword_name)
827 printf ("%s=\"", item->name);
828
829 for (cnt = 0; cnt < item->max - 1; ++cnt)
830 {
831 val = nl_langinfo (item->item_id + cnt);
832 if (val != NULL)
833 fputs (val, stdout);
834 putchar (';');
835 }
836
837 val = nl_langinfo (item->item_id + cnt);
838 if (val != NULL)
839 fputs (val, stdout);
840
841 if (show_keyword_name)
842 putchar ('"');
843 putchar ('\n');
844 }
845 break;
846 case stringlist:
847 {
848 int first = 1;
849 const char *val = nl_langinfo (item->item_id) ? : "";
850 int cnt;
851
852 if (show_keyword_name)
853 printf ("%s=", item->name);
854
855 for (cnt = 0; cnt < item->max && *val != '\0'; ++cnt)
856 {
857 printf ("%s%s%s%s", first ? "" : ";",
858 show_keyword_name ? "\"" : "", val,
859 show_keyword_name ? "\"" : "");
860 val = strchr (val, '\0') + 1;
861 first = 0;
862 }
863 putchar ('\n');
864 }
865 break;
866 case byte:
867 {
868 const char *val = nl_langinfo (item->item_id);
869
870 if (show_keyword_name)
871 printf ("%s=", item->name);
872
873 if (val != NULL)
874 printf ("%d", *val == '\177' ? -1 : *val);
875 putchar ('\n');
876 }
877 break;
878 case bytearray:
879 {
880 const char *val = nl_langinfo (item->item_id);
881 int cnt = val ? strlen (val) : 0;
882
883 if (show_keyword_name)
884 printf ("%s=", item->name);
885
886 while (cnt > 1)
887 {
888 printf ("%d;", *val == '\177' ? -1 : *val);
889 --cnt;
890 ++val;
891 }
892
893 printf ("%d\n", cnt == 0 || *val == '\177' ? -1 : *val);
894 }
895 break;
896 case word:
897 {
898 union { unsigned int word; char *string; } val;
899 val.string = nl_langinfo (item->item_id);
900 if (show_keyword_name)
901 printf ("%s=", item->name);
902
903 printf ("%d\n", val.word);
904 }
905 break;
906 case wstring:
907 case wstringarray:
908 case wstringlist:
909 /* We don't print wide character information since the same
910 information is available in a multibyte string. */
911 default:
912 break;
913
914 }
915 }
916
917 for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
918 if (cat_no != LC_ALL)
919 {
920 size_t item_no;
921
922 if (strcmp (name, category[cat_no].name) == 0)
923 /* Print the whole category. */
924 {
925 if (show_category_name != 0)
926 puts (category[cat_no].name);
927
928 for (item_no = 0; item_no < category[cat_no].number; ++item_no)
929 print_item (&category[cat_no].item_desc[item_no]);
930
931 return;
932 }
933
934 for (item_no = 0; item_no < category[cat_no].number; ++item_no)
935 if (strcmp (name, category[cat_no].item_desc[item_no].name) == 0)
936 {
937 if (show_category_name != 0)
938 puts (category[cat_no].name);
939
940 print_item (&category[cat_no].item_desc[item_no]);
941 return;
942 }
943 }
944
945 /* The name is not a standard one.
946 For testing and perhaps advanced use allow some more symbols. */
947 locale_special (name, show_category_name, show_keyword_name);
948 }