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