1 /* Implementation of the locale program according to POSIX 9945-2.
2    Copyright (C) 1995-2022 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4 
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published
7    by the Free Software Foundation; version 2 of the License, or
8    (at your option) any later version.
9 
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14 
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, see <https://www.gnu.org/licenses/>.  */
17 
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21 
22 #include <argp.h>
23 #include <argz.h>
24 #include <dirent.h>
25 #include <errno.h>
26 #include <error.h>
27 #include <fcntl.h>
28 #include <langinfo.h>
29 #include <libintl.h>
30 #include <limits.h>
31 #include <locale.h>
32 #include <search.h>
33 #include <stdio.h>
34 #include <stdio_ext.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <stdint.h>
39 #include <sys/mman.h>
40 #include <sys/stat.h>
41 
42 #include "record-status.h"
43 #include "localeinfo.h"
44 #include "charmap-dir.h"
45 #include "../locarchive.h"
46 #include <programs/xmalloc.h>
47 
48 #define ARCHIVE_NAME COMPLOCALEDIR "/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 /* Name and version of program.  */
63 static void print_version (FILE *stream, struct argp_state *state);
64 void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
65 
66 /* Definitions of arguments for argp functions.  */
67 static const struct argp_option options[] =
68 {
69   { NULL, 0, NULL, 0, N_("System information:") },
70   { "all-locales", 'a', NULL, OPTION_NO_USAGE,
71     N_("Write names of available locales") },
72   { "charmaps", 'm', NULL, OPTION_NO_USAGE,
73     N_("Write names of available charmaps") },
74   { NULL, 0, NULL, 0, N_("Modify output format:") },
75   { "category-name", 'c', NULL, 0, N_("Write names of selected categories") },
76   { "keyword-name", 'k', NULL, 0, N_("Write names of selected keywords") },
77   { "verbose", 'v', NULL, 0, N_("Print more information") },
78   { NULL, 0, NULL, 0, NULL }
79 };
80 
81 /* Short description of program.  */
82 static const char doc[] = N_("Get locale-specific information.");
83 
84 /* Strings for arguments in help texts.  */
85 static const char args_doc[] = N_("NAME\n[-a|-m]");
86 
87 /* Prototype for option handler.  */
88 static error_t parse_opt (int key, char *arg, struct argp_state *state);
89 
90 /* Function to print some extra text in the help message.  */
91 static char *more_help (int key, const char *text, void *input);
92 
93 /* Data structure to communicate with argp functions.  */
94 static struct argp argp =
95 {
96   options, parse_opt, args_doc, doc, NULL, more_help
97 };
98 
99 
100 /* We don't have these constants defined because we don't use them.  Give
101    default values.  */
102 #define CTYPE_MB_CUR_MIN 0
103 #define CTYPE_MB_CUR_MAX 0
104 #define CTYPE_HASH_SIZE 0
105 #define CTYPE_HASH_LAYERS 0
106 #define CTYPE_CLASS 0
107 #define CTYPE_TOUPPER_EB 0
108 #define CTYPE_TOLOWER_EB 0
109 #define CTYPE_TOUPPER_EL 0
110 #define CTYPE_TOLOWER_EL 0
111 
112 /* Definition of the data structure which represents a category and its
113    items.  */
114 struct category
115 {
116   int cat_id;
117   const char *name;
118   size_t number;
119   struct cat_item
120   {
121     int item_id;
122     const char *name;
123     enum { std, opt } status;
124     enum value_type value_type;
125     int min;
126     int max;
127   } *item_desc;
128 };
129 
130 /* Simple helper macro.  */
131 #define NELEMS(arr) ((sizeof (arr)) / (sizeof (arr[0])))
132 
133 /* For some tricky stuff.  */
134 #define NO_PAREN(Item, More...) Item, ## More
135 
136 /* We have all categories defined in `categories.def'.  Now construct
137    the description and data structure used for all categories.  */
138 #define DEFINE_ELEMENT(Item, More...) { Item, ## More },
139 #define DEFINE_CATEGORY(category, name, items, postload) \
140     static struct cat_item category##_desc[] =				      \
141       {									      \
142 	NO_PAREN items							      \
143       };
144 
145 #include "categories.def"
146 #undef DEFINE_CATEGORY
147 
148 static struct category category[] =
149   {
150 #define DEFINE_CATEGORY(category, name, items, postload) \
151     [category] = { _NL_NUM_##category, name, NELEMS (category##_desc),	      \
152 		   category##_desc },
153 #include "categories.def"
154 #undef DEFINE_CATEGORY
155   };
156 #define NCATEGORIES NELEMS (category)
157 
158 
159 /* Automatically set variable.  */
160 extern const char *__progname;
161 
162 /* helper function for extended name handling.  */
163 extern void locale_special (const char *name, int show_category_name,
164 			    int show_keyword_name);
165 
166 /* Prototypes for local functions.  */
167 static void print_LC_IDENTIFICATION (void *mapped, size_t size);
168 static void print_LC_CTYPE (void *mapped, size_t size);
169 static void write_locales (void);
170 static int nameentcmp (const void *a, const void *b);
171 static int write_archive_locales (void **all_datap, char *linebuf);
172 static void write_charmaps (void);
173 static void show_locale_vars (void);
174 static void show_info (const char *name);
175 static void try_setlocale (int category, const char *category_name);
176 static char *quote_string (const char *input);
177 static void setlocale_diagnostics (void);
178 
179 
180 int
main(int argc,char * argv[])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   try_setlocale (LC_CTYPE, "LC_CTYPE");
192   try_setlocale (LC_MESSAGES, "LC_MESSAGES");
193 
194   /* Initialize the message catalog.  */
195   textdomain (PACKAGE);
196 
197   /* Parse and process arguments.  */
198   argp_parse (&argp, argc, argv, 0, &remaining, NULL);
199 
200   /* `-a' requests the names of all available locales.  */
201   if (do_all != 0)
202     {
203       setlocale_diagnostics ();
204       try_setlocale (LC_COLLATE, "LC_COLLATE");
205       write_locales ();
206       exit (EXIT_SUCCESS);
207     }
208 
209   /* `m' requests the names of all available charmaps.  The names can be
210      used for the -f argument to localedef(1).  */
211   if (do_charmaps != 0)
212     {
213       setlocale_diagnostics ();
214       write_charmaps ();
215       exit (EXIT_SUCCESS);
216     }
217 
218   /* Specific information about the current locale are requested.
219      Change to this locale now.  */
220   try_setlocale (LC_ALL, "LC_ALL");
221   setlocale_diagnostics ();
222 
223   /* If no real argument is given we have to print the contents of the
224      current locale definition variables.  These are LANG and the LC_*.  */
225   if (remaining == argc && show_keyword_name == 0 && show_category_name == 0)
226     {
227       show_locale_vars ();
228       exit (EXIT_SUCCESS);
229     }
230 
231   /* Process all given names.  */
232   while (remaining <  argc)
233     show_info (argv[remaining++]);
234 
235   exit (EXIT_SUCCESS);
236 }
237 
238 
239 /* Handle program arguments.  */
240 static error_t
parse_opt(int key,char * arg,struct argp_state * state)241 parse_opt (int key, char *arg, struct argp_state *state)
242 {
243   switch (key)
244     {
245     case 'a':
246       do_all = 1;
247       break;
248     case 'c':
249       show_category_name = 1;
250       break;
251     case 'm':
252       do_charmaps = 1;
253       break;
254     case 'k':
255       show_keyword_name = 1;
256       break;
257     case 'v':
258       verbose = 1;
259       break;
260     default:
261       return ARGP_ERR_UNKNOWN;
262     }
263   return 0;
264 }
265 
266 
267 static char *
more_help(int key,const char * text,void * input)268 more_help (int key, const char *text, void *input)
269 {
270   char *tp = NULL;
271   switch (key)
272     {
273     case ARGP_KEY_HELP_EXTRA:
274       /* We print some extra information.  */
275       if (asprintf (&tp, gettext ("\
276 For bug reporting instructions, please see:\n\
277 %s.\n"), REPORT_BUGS_TO) < 0)
278 	return NULL;
279       return tp;
280     default:
281       break;
282     }
283   return (char *) text;
284 }
285 
286 
287 /* Print the version information.  */
288 static void
print_version(FILE * stream,struct argp_state * state)289 print_version (FILE *stream, struct argp_state *state)
290 {
291   fprintf (stream, "locale %s%s\n", PKGVERSION, VERSION);
292   fprintf (stream, gettext ("\
293 Copyright (C) %s Free Software Foundation, Inc.\n\
294 This is free software; see the source for copying conditions.  There is NO\n\
295 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
296 "), "2022");
297   fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
298 }
299 
300 
301 /* Simple action function which prints arguments as strings.  */
302 static void
print_names(const void * nodep,VISIT value,int level)303 print_names (const void *nodep, VISIT value, int level)
304 {
305   if (value == postorder || value == leaf)
306     puts (*(char **) nodep);
307 }
308 
309 
310 static int
select_dirs(const struct dirent * dirent)311 select_dirs (const struct dirent *dirent)
312 {
313   int result = 0;
314 
315   if (strcmp (dirent->d_name, ".") != 0 && strcmp (dirent->d_name, "..") != 0)
316     {
317       mode_t mode = 0;
318 
319       if (dirent->d_type != DT_UNKNOWN && dirent->d_type != DT_LNK)
320 	mode = DTTOIF (dirent->d_type);
321       else
322 	{
323 	  struct stat64 st;
324 	  char buf[sizeof (COMPLOCALEDIR)
325 		   + strlen (dirent->d_name) + 1];
326 
327 	  stpcpy (stpcpy (stpcpy (buf, COMPLOCALEDIR), "/"),
328 		  dirent->d_name);
329 
330 	  if (stat64 (buf, &st) == 0)
331 	    mode = st.st_mode;
332 	}
333 
334       result = S_ISDIR (mode);
335     }
336 
337   return result;
338 }
339 
340 
341 static void
print_LC_IDENTIFICATION(void * mapped,size_t size)342 print_LC_IDENTIFICATION (void *mapped, size_t size)
343 {
344   /* Read the information from the file.  */
345   struct
346     {
347       unsigned int magic;
348       unsigned int nstrings;
349       unsigned int strindex[0];
350     } *filedata = mapped;
351 
352   if (filedata->magic == LIMAGIC (LC_IDENTIFICATION)
353       && (sizeof *filedata
354 	  + (filedata->nstrings
355 	     * sizeof (unsigned int))
356 	  <= size))
357     {
358       const char *str;
359 
360 #define HANDLE(idx, name) \
361   str = ((char *) mapped						      \
362 	 + filedata->strindex[_NL_ITEM_INDEX (_NL_IDENTIFICATION_##idx)]);    \
363   if (*str != '\0')							      \
364     printf ("%9s | %s\n", name, str)
365       HANDLE (TITLE, "title");
366       HANDLE (SOURCE, "source");
367       HANDLE (ADDRESS, "address");
368       HANDLE (CONTACT, "contact");
369       HANDLE (EMAIL, "email");
370       HANDLE (TEL, "telephone");
371       HANDLE (FAX, "fax");
372       HANDLE (LANGUAGE, "language");
373       HANDLE (TERRITORY, "territory");
374       HANDLE (AUDIENCE, "audience");
375       HANDLE (APPLICATION, "application");
376       HANDLE (ABBREVIATION, "abbreviation");
377       HANDLE (REVISION, "revision");
378       HANDLE (DATE, "date");
379     }
380 }
381 
382 
383 static void
print_LC_CTYPE(void * mapped,size_t size)384 print_LC_CTYPE (void *mapped, size_t size)
385 {
386   struct
387     {
388       unsigned int magic;
389       unsigned int nstrings;
390       unsigned int strindex[0];
391     } *filedata = mapped;
392 
393   if (filedata->magic == LIMAGIC (LC_CTYPE)
394       && (sizeof *filedata
395 	  + (filedata->nstrings
396 	     * sizeof (unsigned int))
397 	  <= size))
398     {
399       const char *str;
400 
401       str = ((char *) mapped
402 	     + filedata->strindex[_NL_ITEM_INDEX (_NL_CTYPE_CODESET_NAME)]);
403       if (*str != '\0')
404 	printf ("  codeset | %s\n", str);
405     }
406 }
407 
408 
409 /* Write the names of all available locales to stdout.  We have some
410    sources of the information: the contents of the locale directory
411    and the locale.alias file.  To avoid duplicates and print the
412    result is a reasonable order we put all entries is a search tree
413    and print them afterwards.  */
414 static void
write_locales(void)415 write_locales (void)
416 {
417   char linebuf[80];
418   void *all_data = NULL;
419   struct dirent **dirents;
420   int ndirents;
421   int cnt;
422   char *alias_path;
423   size_t alias_path_len;
424   char *entry;
425   int first_locale = 1;
426 
427 #define PUT(name) tsearch (name, &all_data, \
428 			   (int (*) (const void *, const void *)) strcoll)
429 #define GET(name) tfind (name, &all_data, \
430 			   (int (*) (const void *, const void *)) strcoll)
431 
432   /* `POSIX' locale is always available (POSIX.2 4.34.3).  */
433   PUT ("POSIX");
434   /* And so is the "C" locale.  */
435   PUT ("C");
436 
437   memset (linebuf, '-', sizeof (linebuf) - 1);
438   linebuf[sizeof (linebuf) - 1] = '\0';
439 
440   /* First scan the locale archive.  */
441   if (write_archive_locales (&all_data, linebuf))
442     first_locale = 0;
443 
444   /* Now we can look for all files in the directory.  */
445   ndirents = scandir (COMPLOCALEDIR, &dirents, select_dirs,
446 		      alphasort);
447   for (cnt = 0; cnt < ndirents; ++cnt)
448     {
449       /* Test whether at least the LC_CTYPE data is there.  Some
450 	 directories only contain translations.  */
451       char buf[sizeof (COMPLOCALEDIR)
452 	       + strlen (dirents[cnt]->d_name)
453 	       + sizeof "/LC_IDENTIFICATION"];
454       char *enddir;
455       struct stat64 st;
456 
457       stpcpy (enddir = stpcpy (stpcpy (stpcpy (buf,
458 					       COMPLOCALEDIR),
459 					       "/"),
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
nameentcmp(const void * a,const void * b)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
write_archive_locales(void ** all_datap,char * linebuf)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       free (names);
716 
717       ret = used;
718     }
719 
720 error_out:
721   if (addr != MAP_FAILED)
722     munmap (addr, len);
723   close (fd);
724   *all_datap = all_data;
725   return ret;
726 }
727 
728 
729 /* Write the names of all available character maps to stdout.  */
730 static void
write_charmaps(void)731 write_charmaps (void)
732 {
733   void *all_data = NULL;
734   CHARMAP_DIR *dir;
735   const char *dirent;
736 
737   /* Look for all files in the charmap directory.  */
738   dir = charmap_opendir (CHARMAP_PATH);
739   if (dir == NULL)
740     return;
741 
742   while ((dirent = charmap_readdir (dir)) != NULL)
743     {
744       char **aliases;
745       char **p;
746 
747       PUT (xstrdup (dirent));
748 
749       aliases = charmap_aliases (CHARMAP_PATH, dirent);
750 
751 #if 0
752       /* Add the code_set_name and the aliases.  */
753       for (p = aliases; *p; p++)
754 	PUT (xstrdup (*p));
755 #else
756       /* Add the code_set_name only.  Most aliases are obsolete.  */
757       p = aliases;
758       if (*p)
759 	PUT (xstrdup (*p));
760 #endif
761 
762       charmap_free_aliases (aliases);
763     }
764 
765   charmap_closedir (dir);
766 
767   twalk (all_data, print_names);
768 }
769 
770 /* Print a properly quoted assignment of NAME with VAL, using double
771    quotes iff DQUOTE is true.  */
772 static void
print_assignment(const char * name,const char * val,bool dquote)773 print_assignment (const char *name, const char *val, bool dquote)
774 {
775   printf ("%s=", name);
776   if (dquote)
777     putchar ('"');
778   while (*val != '\0')
779     {
780       size_t segment
781 	= strcspn (val, dquote ? "$`\"\\" : "~|&;<>()$`\\\"' \t\n");
782       printf ("%.*s", (int) segment, val);
783       val += segment;
784       if (*val == '\0')
785 	break;
786       putchar ('\\');
787       putchar (*val++);
788     }
789   if (dquote)
790     putchar ('"');
791   putchar ('\n');
792 }
793 
794 /* We have to show the contents of the environments determining the
795    locale.  */
796 static void
show_locale_vars(void)797 show_locale_vars (void)
798 {
799   const char *lcall = getenv ("LC_ALL") ?: "";
800   const char *lang = getenv ("LANG") ?: "";
801 
802   /* LANG has to be the first value.  */
803   print_assignment ("LANG", lang, false);
804 
805   /* Now all categories in an unspecified order.  */
806   for (size_t cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
807     if (cat_no != LC_ALL)
808       {
809 	const char *name = category[cat_no].name;
810 	const char *val = getenv (name);
811 
812 	if (lcall[0] != '\0' || val == NULL)
813 	  print_assignment (name,
814 			    lcall[0] != '\0' ? lcall
815 			    : lang[0] != '\0' ? lang
816 			    : "POSIX",
817 			    true);
818 	else
819 	  print_assignment (name, val, false);
820       }
821 
822   /* The last is the LC_ALL value.  */
823   print_assignment ("LC_ALL", lcall, false);
824 }
825 
826 
827 /* Subroutine of show_info, below.  */
828 static void
print_item(struct cat_item * item)829 print_item (struct cat_item *item)
830 {
831   switch (item->value_type)
832     {
833     case string:
834       if (show_keyword_name)
835 	printf ("%s=\"", item->name);
836       fputs (nl_langinfo (item->item_id) ? : "", stdout);
837       if (show_keyword_name)
838 	putchar ('"');
839       putchar ('\n');
840       break;
841     case stringarray:
842       {
843 	const char *val;
844 	int cnt;
845 
846 	if (show_keyword_name)
847 	  printf ("%s=\"", item->name);
848 
849 	for (cnt = 0; cnt < item->max - 1; ++cnt)
850 	  {
851 	    val = nl_langinfo (item->item_id + cnt);
852 	    if (val != NULL)
853 	      fputs (val, stdout);
854 	    putchar (';');
855 	  }
856 
857 	val = nl_langinfo (item->item_id + cnt);
858 	if (val != NULL)
859 	  fputs (val, stdout);
860 
861 	if (show_keyword_name)
862 	  putchar ('"');
863 	putchar ('\n');
864       }
865       break;
866     case stringlist:
867       {
868 	int first = 1;
869 	const char *val = nl_langinfo (item->item_id) ? : "";
870 
871 	if (show_keyword_name)
872 	  printf ("%s=", item->name);
873 
874 	for (int cnt = 0; cnt < item->max && *val != '\0'; ++cnt)
875 	  {
876 	    printf ("%s%s%s%s", first ? "" : ";",
877 		    show_keyword_name ? "\"" : "", val,
878 		    show_keyword_name ? "\"" : "");
879 	    val = strchr (val, '\0') + 1;
880 	    first = 0;
881 	  }
882 	putchar ('\n');
883       }
884       break;
885     case byte:
886       {
887 	const char *val = nl_langinfo (item->item_id);
888 
889 	if (show_keyword_name)
890 	  printf ("%s=", item->name);
891 
892 	if (val != NULL)
893 	  printf ("%d", *val == '\377' ? -1 : *val);
894 	putchar ('\n');
895       }
896       break;
897     case bytearray:
898       {
899 	const char *val = nl_langinfo (item->item_id);
900 	int cnt = val ? strlen (val) : 0;
901 
902 	if (show_keyword_name)
903 	  printf ("%s=", item->name);
904 
905 	while (cnt > 1)
906 	  {
907 	    printf ("%d;", *val == '\177' ? -1 : *val);
908 	    --cnt;
909 	    ++val;
910 	  }
911 
912 	printf ("%d\n", cnt == 0 || *val == '\177' ? -1 : *val);
913       }
914       break;
915     case word:
916       {
917 	union { unsigned int word; char *string; } val;
918 	val.string = nl_langinfo (item->item_id);
919 	if (show_keyword_name)
920 	  printf ("%s=", item->name);
921 
922 	printf ("%d\n", val.word);
923       }
924       break;
925     case wordarray:
926       {
927 	int first = 1;
928 	union { unsigned int *wordarray; char *string; } val;
929 
930 	val.string = nl_langinfo (item->item_id);
931 	if (show_keyword_name)
932 	  printf ("%s=", item->name);
933 
934 	for (int cnt = 0; cnt < item->max; ++cnt)
935 	  {
936 	    printf ("%s%d", first ? "" : ";", val.wordarray[cnt]);
937 	    first = 0;
938 	  }
939 	putchar ('\n');
940       }
941       break;
942     case wstring:
943     case wstringarray:
944     case wstringlist:
945       /* We don't print wide character information since the same
946 	 information is available in a multibyte string.  */
947     default:
948       break;
949     }
950 }
951 
952 /* Show the information request for NAME.  */
953 static void
show_info(const char * name)954 show_info (const char *name)
955 {
956   for (size_t cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
957     if (cat_no != LC_ALL)
958       {
959 	if (strcmp (name, category[cat_no].name) == 0)
960 	  /* Print the whole category.  */
961 	  {
962 	    if (show_category_name != 0)
963 	      puts (category[cat_no].name);
964 
965 	    for (size_t item_no = 0;
966 		 item_no < category[cat_no].number;
967 		 ++item_no)
968 	      print_item (&category[cat_no].item_desc[item_no]);
969 
970 	    return;
971 	  }
972 
973 	for (size_t item_no = 0; item_no < category[cat_no].number; ++item_no)
974 	  if (strcmp (name, category[cat_no].item_desc[item_no].name) == 0)
975 	    {
976 	      if (show_category_name != 0)
977 		puts (category[cat_no].name);
978 
979 	      print_item (&category[cat_no].item_desc[item_no]);
980 	      return;
981 	    }
982       }
983 
984   /* The name is not a standard one.
985      For testing and perhaps advanced use allow some more symbols.  */
986   locale_special (name, show_category_name, show_keyword_name);
987 }
988 
989 /* Set to true by try_setlocale if setlocale fails.  Used by
990    setlocale_diagnostics.  */
991 static bool setlocale_failed;
992 
993 /* Call setlocale, with non-fatal error reporting.  */
994 static void
try_setlocale(int category,const char * category_name)995 try_setlocale (int category, const char *category_name)
996 {
997   if (setlocale (category, "") == NULL)
998     {
999       error (0, errno, gettext ("Cannot set %s to default locale"),
1000 	     category_name);
1001       setlocale_failed = true;
1002     }
1003 }
1004 
1005 /* Return a quoted version of the passed string, or NULL on error.  */
1006 static char *
quote_string(const char * input)1007 quote_string (const char *input)
1008 {
1009   char *buffer;
1010   size_t length;
1011   FILE *stream = open_memstream (&buffer, &length);
1012   if (stream == NULL)
1013     return NULL;
1014 
1015   while (true)
1016     {
1017       unsigned char ch = *input++;
1018       if (ch == '\0')
1019 	break;
1020 
1021       /* Use C backslash escapes for those control characters for
1022          which they are defined.  */
1023       switch (ch)
1024         {
1025           case '\a':
1026             putc_unlocked ('\\', stream);
1027             putc_unlocked ('a', stream);
1028             break;
1029           case '\b':
1030             putc_unlocked ('\\', stream);
1031             putc_unlocked ('b', stream);
1032             break;
1033           case '\f':
1034             putc_unlocked ('\\', stream);
1035             putc_unlocked ('f', stream);
1036             break;
1037           case '\n':
1038             putc_unlocked ('\\', stream);
1039             putc_unlocked ('n', stream);
1040             break;
1041           case '\r':
1042             putc_unlocked ('\\', stream);
1043             putc_unlocked ('r', stream);
1044             break;
1045           case '\t':
1046             putc_unlocked ('\\', stream);
1047             putc_unlocked ('t', stream);
1048             break;
1049           case '\v':
1050             putc_unlocked ('\\', stream);
1051             putc_unlocked ('v', stream);
1052             break;
1053           case '\\':
1054           case '\'':
1055           case '\"':
1056             putc_unlocked ('\\', stream);
1057             putc_unlocked (ch, stream);
1058             break;
1059         default:
1060           if (ch < ' ' || ch > '~')
1061             /* Use octal sequences because they are fixed width,
1062                unlike hexadecimal sequences.  */
1063             fprintf (stream, "\\%03o", ch);
1064           else
1065             putc_unlocked (ch, stream);
1066         }
1067     }
1068 
1069   if (ferror (stream))
1070     {
1071       fclose (stream);
1072       free (buffer);
1073       return NULL;
1074     }
1075   if (fclose (stream) != 0)
1076     {
1077       free (buffer);
1078       return NULL;
1079     }
1080 
1081   return buffer;
1082 }
1083 
1084 /* Print additional information if there was a setlocale error (during
1085    try_setlocale).  */
1086 static void
setlocale_diagnostics(void)1087 setlocale_diagnostics (void)
1088 {
1089   if (setlocale_failed)
1090     {
1091       const char *locpath = getenv ("LOCPATH");
1092       if (locpath != NULL)
1093 	{
1094 	  char *quoted = quote_string (locpath);
1095 	  if (quoted != NULL)
1096 	    fprintf (stderr,
1097 		     gettext ("\
1098 warning: The LOCPATH variable is set to \"%s\"\n"),
1099 		     quoted);
1100 	  else
1101 	    fputs ("warning: The LOCPATH variable is set\n", stderr);
1102 	  free (quoted);
1103 	}
1104     }
1105 }
1106