Smack: add the execute lable to ldconfig
[platform/upstream/glibc.git] / locale / programs / locale.c
1 /* Implementation of the locale program according to POSIX 9945-2.
2    Copyright (C) 1995-2015 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 "), "2015");
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   const char *lcall = getenv ("LC_ALL") ?: "";
796   const char *lang = getenv ("LANG") ?: "";
797
798   /* LANG has to be the first value.  */
799   print_assignment ("LANG", lang, false);
800
801   /* Now all categories in an unspecified order.  */
802   for (size_t cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
803     if (cat_no != LC_ALL)
804       {
805         const char *name = category[cat_no].name;
806         const char *val = getenv (name);
807
808         if (lcall[0] != '\0' || val == NULL)
809           print_assignment (name,
810                             lcall[0] != '\0' ? lcall
811                             : lang[0] != '\0' ? lang
812                             : "POSIX",
813                             true);
814         else
815           print_assignment (name, val, false);
816       }
817
818   /* The last is the LC_ALL value.  */
819   print_assignment ("LC_ALL", lcall, false);
820 }
821
822
823 /* Subroutine of show_info, below.  */
824 static void
825 print_item (struct cat_item *item)
826 {
827   switch (item->value_type)
828     {
829     case string:
830       if (show_keyword_name)
831         printf ("%s=\"", item->name);
832       fputs (nl_langinfo (item->item_id) ? : "", stdout);
833       if (show_keyword_name)
834         putchar ('"');
835       putchar ('\n');
836       break;
837     case stringarray:
838       {
839         const char *val;
840         int cnt;
841
842         if (show_keyword_name)
843           printf ("%s=\"", item->name);
844
845         for (cnt = 0; cnt < item->max - 1; ++cnt)
846           {
847             val = nl_langinfo (item->item_id + cnt);
848             if (val != NULL)
849               fputs (val, stdout);
850             putchar (';');
851           }
852
853         val = nl_langinfo (item->item_id + cnt);
854         if (val != NULL)
855           fputs (val, stdout);
856
857         if (show_keyword_name)
858           putchar ('"');
859         putchar ('\n');
860       }
861       break;
862     case stringlist:
863       {
864         int first = 1;
865         const char *val = nl_langinfo (item->item_id) ? : "";
866
867         if (show_keyword_name)
868           printf ("%s=", item->name);
869
870         for (int cnt = 0; cnt < item->max && *val != '\0'; ++cnt)
871           {
872             printf ("%s%s%s%s", first ? "" : ";",
873                     show_keyword_name ? "\"" : "", val,
874                     show_keyword_name ? "\"" : "");
875             val = strchr (val, '\0') + 1;
876             first = 0;
877           }
878         putchar ('\n');
879       }
880       break;
881     case byte:
882       {
883         const char *val = nl_langinfo (item->item_id);
884
885         if (show_keyword_name)
886           printf ("%s=", item->name);
887
888         if (val != NULL)
889           printf ("%d", *val == '\377' ? -1 : *val);
890         putchar ('\n');
891       }
892       break;
893     case bytearray:
894       {
895         const char *val = nl_langinfo (item->item_id);
896         int cnt = val ? strlen (val) : 0;
897
898         if (show_keyword_name)
899           printf ("%s=", item->name);
900
901         while (cnt > 1)
902           {
903             printf ("%d;", *val == '\177' ? -1 : *val);
904             --cnt;
905             ++val;
906           }
907
908         printf ("%d\n", cnt == 0 || *val == '\177' ? -1 : *val);
909       }
910       break;
911     case word:
912       {
913         union { unsigned int word; char *string; } val;
914         val.string = nl_langinfo (item->item_id);
915         if (show_keyword_name)
916           printf ("%s=", item->name);
917
918         printf ("%d\n", val.word);
919       }
920       break;
921     case wordarray:
922       {
923         int first = 1;
924         union { unsigned int *wordarray; char *string; } val;
925
926         val.string = nl_langinfo (item->item_id);
927         if (show_keyword_name)
928           printf ("%s=", item->name);
929
930         for (int cnt = 0; cnt < item->max; ++cnt)
931           {
932             printf ("%s%d", first ? "" : ";", val.wordarray[cnt]);
933             first = 0;
934           }
935         putchar ('\n');
936       }
937       break;
938     case wstring:
939     case wstringarray:
940     case wstringlist:
941       /* We don't print wide character information since the same
942          information is available in a multibyte string.  */
943     default:
944       break;
945     }
946 }
947
948 /* Show the information request for NAME.  */
949 static void
950 show_info (const char *name)
951 {
952   for (size_t cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
953     if (cat_no != LC_ALL)
954       {
955         if (strcmp (name, category[cat_no].name) == 0)
956           /* Print the whole category.  */
957           {
958             if (show_category_name != 0)
959               puts (category[cat_no].name);
960
961             for (size_t item_no = 0;
962                  item_no < category[cat_no].number;
963                  ++item_no)
964               print_item (&category[cat_no].item_desc[item_no]);
965
966             return;
967           }
968
969         for (size_t item_no = 0; item_no < category[cat_no].number; ++item_no)
970           if (strcmp (name, category[cat_no].item_desc[item_no].name) == 0)
971             {
972               if (show_category_name != 0)
973                 puts (category[cat_no].name);
974
975               print_item (&category[cat_no].item_desc[item_no]);
976               return;
977             }
978       }
979
980   /* The name is not a standard one.
981      For testing and perhaps advanced use allow some more symbols.  */
982   locale_special (name, show_category_name, show_keyword_name);
983 }