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