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