Update.
[platform/upstream/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 Report bugs using the `glibcbug' script to <bugs@gnu.org>.\n"));
281     default:
282       break;
283     }
284   return (char *) text;
285 }
286
287 /* Print the version information.  */
288 static void
289 print_version (FILE *stream, struct argp_state *state)
290 {
291   fprintf (stream, "locale (GNU %s) %s\n", PACKAGE, VERSION);
292   fprintf (stream, gettext ("\
293 Copyright (C) %s Free Software Foundation, Inc.\n\
294 This is free software; see the source for copying conditions.  There is NO\n\
295 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
296 "), "2004");
297   fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
298 }
299
300
301 /* Simple action function which prints arguments as strings.  */
302 static void
303 print_names (const void *nodep, VISIT value, int level)
304 {
305   if (value == postorder || value == leaf)
306     puts (*(char **) nodep);
307 }
308
309
310 static int
311 select_dirs (const struct dirent *dirent)
312 {
313   int result = 0;
314
315   if (strcmp (dirent->d_name, ".") != 0 && strcmp (dirent->d_name, "..") != 0)
316     {
317       mode_t mode = 0;
318
319 #ifdef _DIRENT_HAVE_D_TYPE
320       if (dirent->d_type != DT_UNKNOWN && dirent->d_type != DT_LNK)
321         mode = DTTOIF (dirent->d_type);
322       else
323 #endif
324         {
325           struct stat64 st;
326           char buf[sizeof (LOCALEDIR) + strlen (dirent->d_name) + 1];
327
328           stpcpy (stpcpy (stpcpy (buf, LOCALEDIR), "/"), dirent->d_name);
329
330           if (stat64 (buf, &st) == 0)
331             mode = st.st_mode;
332         }
333
334       result = S_ISDIR (mode);
335     }
336
337   return result;
338 }
339
340
341 static void
342 print_LC_IDENTIFICATION (void *mapped, size_t size)
343 {
344   /* Read the information from the file.  */
345   struct
346     {
347       unsigned int magic;
348       unsigned int nstrings;
349       unsigned int strindex[0];
350     } *filedata = mapped;
351
352   if (filedata->magic == LIMAGIC (LC_IDENTIFICATION)
353       && (sizeof *filedata
354           + (filedata->nstrings
355              * sizeof (unsigned int))
356           <= size))
357     {
358       const char *str;
359
360 #define HANDLE(idx, name) \
361   str = ((char *) mapped                                                      \
362          + filedata->strindex[_NL_ITEM_INDEX (_NL_IDENTIFICATION_##idx)]);    \
363   if (*str != '\0')                                                           \
364     printf ("%9s | %s\n", name, str)
365       HANDLE (TITLE, "title");
366       HANDLE (SOURCE, "source");
367       HANDLE (ADDRESS, "address");
368       HANDLE (CONTACT, "contact");
369       HANDLE (EMAIL, "email");
370       HANDLE (TEL, "telephone");
371       HANDLE (FAX, "fax");
372       HANDLE (LANGUAGE, "language");
373       HANDLE (TERRITORY, "territory");
374       HANDLE (AUDIENCE, "audience");
375       HANDLE (APPLICATION, "application");
376       HANDLE (ABBREVIATION, "abbreviation");
377       HANDLE (REVISION, "revision");
378       HANDLE (DATE, "date");
379     }
380 }
381
382
383 static void
384 print_LC_CTYPE (void *mapped, size_t size)
385 {
386   struct
387     {
388       unsigned int magic;
389       unsigned int nstrings;
390       unsigned int strindex[0];
391     } *filedata = mapped;
392
393   if (filedata->magic == LIMAGIC (LC_CTYPE)
394       && (sizeof *filedata
395           + (filedata->nstrings
396              * sizeof (unsigned int))
397           <= size))
398     {
399       const char *str;
400
401       str = ((char *) mapped
402              + filedata->strindex[_NL_ITEM_INDEX (_NL_CTYPE_CODESET_NAME)]);
403       if (*str != '\0')
404         printf ("  codeset | %s\n", str);
405     }
406 }
407
408
409 /* Write the names of all available locales to stdout.  We have some
410    sources of the information: the contents of the locale directory
411    and the locale.alias file.  To avoid duplicates and print the
412    result is a reasonable order we put all entries is a search tree
413    and print them afterwards.  */
414 static void
415 write_locales (void)
416 {
417   char linebuf[80];
418   void *all_data = NULL;
419   struct dirent **dirents;
420   int ndirents;
421   int cnt;
422   char *alias_path;
423   size_t alias_path_len;
424   char *entry;
425   int first_locale = 1;
426
427 #define PUT(name) tsearch (name, &all_data, \
428                            (int (*) (const void *, const void *)) strcoll)
429 #define GET(name) tfind (name, &all_data, \
430                            (int (*) (const void *, const void *)) strcoll)
431
432   /* `POSIX' locale is always available (POSIX.2 4.34.3).  */
433   PUT ("POSIX");
434   /* And so is the "C" locale.  */
435   PUT ("C");
436
437   memset (linebuf, '-', sizeof (linebuf) - 1);
438   linebuf[sizeof (linebuf) - 1] = '\0';
439
440   /* First scan the locale archive.  */
441   if (write_archive_locales (&all_data, linebuf))
442     first_locale = 0;
443
444   /* Now we can look for all files in the directory.  */
445   ndirents = scandir (LOCALEDIR, &dirents, select_dirs, alphasort);
446   for (cnt = 0; cnt < ndirents; ++cnt)
447     {
448       /* Test whether at least the LC_CTYPE data is there.  Some
449          directories only contain translations.  */
450       char buf[sizeof (LOCALEDIR) + strlen (dirents[cnt]->d_name)
451               + sizeof "/LC_IDENTIFICATION"];
452       char *enddir;
453       struct stat64 st;
454
455       stpcpy (enddir = stpcpy (stpcpy (stpcpy (buf, LOCALEDIR), "/"),
456                                dirents[cnt]->d_name),
457               "/LC_IDENTIFICATION");
458
459       if (stat64 (buf, &st) == 0 && S_ISREG (st.st_mode))
460         {
461           if (verbose && GET (dirents[cnt]->d_name) == NULL)
462             {
463               /* Provide some nice output of all kinds of
464                  information.  */
465               int fd;
466
467               if (! first_locale)
468                 putchar_unlocked ('\n');
469               first_locale = 0;
470
471               printf ("locale: %-15.15s directory: %.*s\n%s\n",
472                       dirents[cnt]->d_name, (int) (enddir - buf), buf,
473                       linebuf);
474
475               fd = open64 (buf, O_RDONLY);
476               if (fd != -1)
477                 {
478                   void *mapped = mmap64 (NULL, st.st_size, PROT_READ,
479                                          MAP_SHARED, fd, 0);
480                   if (mapped != MAP_FAILED)
481                     {
482                       print_LC_IDENTIFICATION (mapped, st.st_size);
483
484                       munmap (mapped, st.st_size);
485                     }
486
487                   close (fd);
488
489                   /* Now try to get the charset information.  */
490                   strcpy (enddir, "/LC_CTYPE");
491                   fd = open64 (buf, O_RDONLY);
492                   if (fd != -1 && fstat64 (fd, &st) >= 0
493                       && ((mapped = mmap64 (NULL, st.st_size, PROT_READ,
494                                             MAP_SHARED, fd, 0))
495                           != MAP_FAILED))
496                     {
497                       print_LC_CTYPE (mapped, st.st_size);
498
499                       munmap (mapped, st.st_size);
500                     }
501
502                   if (fd != -1)
503                     close (fd);
504                 }
505             }
506
507           /* If the verbose format is not selected we simply
508              collect the names.  */
509           PUT (xstrdup (dirents[cnt]->d_name));
510         }
511     }
512   if (ndirents > 0)
513     free (dirents);
514
515   /* Now read the locale.alias files.  */
516   if (argz_create_sep (LOCALE_ALIAS_PATH, ':', &alias_path, &alias_path_len))
517     error (1, errno, gettext ("while preparing output"));
518
519   entry = NULL;
520   while ((entry = argz_next (alias_path, alias_path_len, entry)))
521     {
522       static const char aliasfile[] = "/locale.alias";
523       FILE *fp;
524       char full_name[strlen (entry) + sizeof aliasfile];
525
526       stpcpy (stpcpy (full_name, entry), aliasfile);
527       fp = fopen (full_name, "rm");
528       if (fp == NULL)
529         /* Ignore non-existing files.  */
530         continue;
531
532       /* No threads present.  */
533       __fsetlocking (fp, FSETLOCKING_BYCALLER);
534
535       while (! feof_unlocked (fp))
536         {
537           /* It is a reasonable approach to use a fix buffer here
538              because
539              a) we are only interested in the first two fields
540              b) these fields must be usable as file names and so must
541                 not be that long  */
542           char buf[BUFSIZ];
543           char *alias;
544           char *value;
545           char *cp;
546
547           if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
548             /* EOF reached.  */
549             break;
550
551           cp = buf;
552           /* Ignore leading white space.  */
553           while (isspace (cp[0]) && cp[0] != '\n')
554             ++cp;
555
556           /* A leading '#' signals a comment line.  */
557           if (cp[0] != '\0' && cp[0] != '#' && cp[0] != '\n')
558             {
559               alias = cp++;
560               while (cp[0] != '\0' && !isspace (cp[0]))
561                 ++cp;
562               /* Terminate alias name.  */
563               if (cp[0] != '\0')
564                 *cp++ = '\0';
565
566               /* Now look for the beginning of the value.  */
567               while (isspace (cp[0]))
568                 ++cp;
569
570               if (cp[0] != '\0')
571                 {
572                   value = cp++;
573                   while (cp[0] != '\0' && !isspace (cp[0]))
574                     ++cp;
575                   /* Terminate value.  */
576                   if (cp[0] == '\n')
577                     {
578                       /* This has to be done to make the following
579                          test for the end of line possible.  We are
580                          looking for the terminating '\n' which do not
581                          overwrite here.  */
582                       *cp++ = '\0';
583                       *cp = '\n';
584                     }
585                   else if (cp[0] != '\0')
586                     *cp++ = '\0';
587
588                   /* Add the alias.  */
589                   if (! verbose && GET (value) != NULL)
590                     PUT (xstrdup (alias));
591                 }
592             }
593
594           /* Possibly not the whole line fits into the buffer.
595              Ignore the rest of the line.  */
596           while (strchr (cp, '\n') == NULL)
597             {
598               cp = buf;
599               if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
600                 /* Make sure the inner loop will be left.  The outer
601                    loop will exit at the `feof' test.  */
602                 *cp = '\n';
603             }
604         }
605
606       fclose (fp);
607     }
608
609   if (! verbose)
610     {
611       twalk (all_data, print_names);
612     }
613 }
614
615
616 struct nameent
617 {
618   char *name;
619   uint32_t locrec_offset;
620 };
621
622
623 static int
624 nameentcmp (const void *a, const void *b)
625 {
626   return strcoll (((const struct nameent *) a)->name,
627                   ((const struct nameent *) b)->name);
628 }
629
630
631 static int
632 write_archive_locales (void **all_datap, char *linebuf)
633 {
634   struct stat64 st;
635   void *all_data = *all_datap;
636   size_t len = 0;
637   struct locarhead *head;
638   struct namehashent *namehashtab;
639   char *addr = MAP_FAILED;
640   int fd, ret = 0;
641   uint32_t cnt;
642
643   fd = open64 (ARCHIVE_NAME, O_RDONLY);
644   if (fd < 0)
645     return 0;
646
647   if (fstat64 (fd, &st) < 0 || st.st_size < sizeof (*head))
648     goto error_out;
649
650   len = st.st_size;
651   addr = mmap64 (NULL, len, PROT_READ, MAP_SHARED, fd, 0);
652   if (addr == MAP_FAILED)
653     goto error_out;
654
655   head = (struct locarhead *) addr;
656   if (head->namehash_offset + head->namehash_size > len
657       || head->string_offset + head->string_size > len
658       || head->locrectab_offset + head->locrectab_size > len
659       || head->sumhash_offset + head->sumhash_size > len)
660     goto error_out;
661
662   namehashtab = (struct namehashent *) (addr + head->namehash_offset);
663   if (! verbose)
664     {
665       for (cnt = 0; cnt < head->namehash_size; ++cnt)
666         if (namehashtab[cnt].locrec_offset != 0)
667           {
668             PUT (xstrdup (addr + namehashtab[cnt].name_offset));
669             ++ret;
670           }
671     }
672   else
673     {
674       struct nameent *names;
675       uint32_t used;
676
677       names = (struct nameent *) xmalloc (head->namehash_used
678                                           * sizeof (struct nameent));
679       for (cnt = used = 0; cnt < head->namehash_size; ++cnt)
680         if (namehashtab[cnt].locrec_offset != 0)
681           {
682             names[used].name = addr + namehashtab[cnt].name_offset;
683             names[used++].locrec_offset = namehashtab[cnt].locrec_offset;
684           }
685
686       /* Sort the names.  */
687       qsort (names, used, sizeof (struct nameent), nameentcmp);
688
689       for (cnt = 0; cnt < used; ++cnt)
690         {
691           struct locrecent *locrec;
692
693           PUT (xstrdup (names[cnt].name));
694
695           if (cnt)
696             putchar_unlocked ('\n');
697
698           printf ("locale: %-15.15s archive: " ARCHIVE_NAME "\n%s\n",
699                   names[cnt].name, linebuf);
700
701           locrec = (struct locrecent *) (addr + names[cnt].locrec_offset);
702
703           print_LC_IDENTIFICATION (addr
704                                    + locrec->record[LC_IDENTIFICATION].offset,
705                                    locrec->record[LC_IDENTIFICATION].len);
706
707           print_LC_CTYPE (addr + locrec->record[LC_CTYPE].offset,
708                           locrec->record[LC_CTYPE].len);
709         }
710
711       ret = used;
712     }
713
714 error_out:
715   if (addr != MAP_FAILED)
716     munmap (addr, len);
717   close (fd);
718   *all_datap = all_data;
719   return ret;
720 }
721
722
723 /* Write the names of all available character maps to stdout.  */
724 static void
725 write_charmaps (void)
726 {
727   void *all_data = NULL;
728   CHARMAP_DIR *dir;
729   const char *dirent;
730
731   /* Look for all files in the charmap directory.  */
732   dir = charmap_opendir (CHARMAP_PATH);
733   if (dir == NULL)
734     return;
735
736   while ((dirent = charmap_readdir (dir)) != NULL)
737     {
738       char **aliases;
739       char **p;
740
741       PUT (xstrdup (dirent));
742
743       aliases = charmap_aliases (CHARMAP_PATH, dirent);
744
745 #if 0
746       /* Add the code_set_name and the aliases.  */
747       for (p = aliases; *p; p++)
748         PUT (xstrdup (*p));
749 #else
750       /* Add the code_set_name only.  Most aliases are obsolete.  */
751       p = aliases;
752       if (*p)
753         PUT (xstrdup (*p));
754 #endif
755
756       charmap_free_aliases (aliases);
757     }
758
759   charmap_closedir (dir);
760
761   twalk (all_data, print_names);
762 }
763
764
765 /* We have to show the contents of the environments determining the
766    locale.  */
767 static void
768 show_locale_vars (void)
769 {
770   size_t cat_no;
771   const char *lcall = getenv ("LC_ALL");
772   const char *lang = getenv ("LANG") ? : "";
773
774   auto void get_source (const char *name);
775
776   void get_source (const char *name)
777     {
778       char *val = getenv (name);
779
780       if ((lcall ?: "")[0] != '\0' || val == NULL)
781         printf ("%s=\"%s\"\n", name,
782                 (lcall ?: "")[0] ? lcall : (lang ?: "")[0] ? lang : "POSIX");
783       else
784         printf ("%s=%s\n", name, val);
785     }
786
787   /* LANG has to be the first value.  */
788   printf ("LANG=%s\n", lang);
789
790   /* Now all categories in an unspecified order.  */
791   for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
792     if (cat_no != LC_ALL)
793       get_source (category[cat_no].name);
794
795   /* The last is the LC_ALL value.  */
796   printf ("LC_ALL=%s\n", lcall ? : "");
797 }
798
799
800 /* Show the information request for NAME.  */
801 static void
802 show_info (const char *name)
803 {
804   size_t cat_no;
805
806   auto void print_item (struct cat_item *item);
807
808   void print_item (struct cat_item *item)
809     {
810       switch (item->value_type)
811         {
812         case string:
813           if (show_keyword_name)
814             printf ("%s=\"", item->name);
815           fputs (nl_langinfo (item->item_id) ? : "", stdout);
816           if (show_keyword_name)
817             putchar ('"');
818           putchar ('\n');
819           break;
820         case stringarray:
821           {
822             int cnt;
823             const char *val;
824
825             if (show_keyword_name)
826               printf ("%s=\"", item->name);
827
828             for (cnt = 0; cnt < item->max - 1; ++cnt)
829               {
830                 val = nl_langinfo (item->item_id + cnt);
831                 if (val != NULL)
832                   fputs (val, stdout);
833                 putchar (';');
834               }
835
836             val = nl_langinfo (item->item_id + cnt);
837             if (val != NULL)
838               fputs (val, stdout);
839
840             if (show_keyword_name)
841               putchar ('"');
842             putchar ('\n');
843           }
844           break;
845         case stringlist:
846           {
847             int first = 1;
848             const char *val = nl_langinfo (item->item_id) ? : "";
849             int cnt;
850
851             if (show_keyword_name)
852               printf ("%s=", item->name);
853
854             for (cnt = 0; cnt < item->max && *val != '\0'; ++cnt)
855               {
856                 printf ("%s%s%s%s", first ? "" : ";",
857                         show_keyword_name ? "\"" : "", val,
858                         show_keyword_name ? "\"" : "");
859                 val = strchr (val, '\0') + 1;
860                 first = 0;
861               }
862             putchar ('\n');
863           }
864           break;
865         case byte:
866           {
867             const char *val = nl_langinfo (item->item_id);
868
869             if (show_keyword_name)
870               printf ("%s=", item->name);
871
872             if (val != NULL)
873               printf ("%d", *val == '\177' ? -1 : *val);
874             putchar ('\n');
875           }
876           break;
877         case bytearray:
878           {
879             const char *val = nl_langinfo (item->item_id);
880             int cnt = val ? strlen (val) : 0;
881
882             if (show_keyword_name)
883               printf ("%s=", item->name);
884
885             while (cnt > 1)
886               {
887                 printf ("%d;", *val == '\177' ? -1 : *val);
888                 --cnt;
889                 ++val;
890               }
891
892             printf ("%d\n", cnt == 0 || *val == '\177' ? -1 : *val);
893           }
894           break;
895         case word:
896           {
897             union { unsigned int word; char *string; } val;
898             val.string = nl_langinfo (item->item_id);
899             if (show_keyword_name)
900               printf ("%s=", item->name);
901
902             printf ("%d\n", val.word);
903           }
904           break;
905         case wstring:
906         case wstringarray:
907         case wstringlist:
908           /* We don't print wide character information since the same
909              information is available in a multibyte string.  */
910         default:
911           break;
912
913         }
914     }
915
916   for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
917     if (cat_no != LC_ALL)
918       {
919         size_t item_no;
920
921         if (strcmp (name, category[cat_no].name) == 0)
922           /* Print the whole category.  */
923           {
924             if (show_category_name != 0)
925               puts (category[cat_no].name);
926
927             for (item_no = 0; item_no < category[cat_no].number; ++item_no)
928               print_item (&category[cat_no].item_desc[item_no]);
929
930             return;
931           }
932
933         for (item_no = 0; item_no < category[cat_no].number; ++item_no)
934           if (strcmp (name, category[cat_no].item_desc[item_no].name) == 0)
935             {
936               if (show_category_name != 0)
937                 puts (category[cat_no].name);
938
939               print_item (&category[cat_no].item_desc[item_no]);
940               return;
941             }
942       }
943
944   /* The name is not a standard one.
945      For testing and perhaps advanced use allow some more symbols.  */
946   locale_special (name, show_category_name, show_keyword_name);
947 }