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