support: Include <limits.h> for NAME_MAX use in temp_file.c
[glibc.git] / locale / programs / locale.c
blobc7ee1874e8838e2cdab6fd083a4bfff6e4829048
1 /* Implementation of the locale program according to POSIX 9945-2.
2 Copyright (C) 1995-2024 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
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.
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.
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/>. */
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
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>
42 #include "record-status.h"
43 #include "localeinfo.h"
44 #include "charmap-dir.h"
45 #include "../locarchive.h"
46 #include <programs/xmalloc.h>
48 #define ARCHIVE_NAME COMPLOCALEDIR "/locale-archive"
50 /* If set print the name of the category. */
51 static int show_category_name;
53 /* If set print the name of the item. */
54 static int show_keyword_name;
56 /* Print names of all available locales. */
57 static int do_all;
59 /* Print names of all available character maps. */
60 static int do_charmaps = 0;
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;
66 /* Definitions of arguments for argp functions. */
67 static const struct argp_option options[] =
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 }
81 /* Short description of program. */
82 static const char doc[] = N_("Get locale-specific information.");
84 /* Strings for arguments in help texts. */
85 static const char args_doc[] = N_("NAME\n[-a|-m]");
87 /* Prototype for option handler. */
88 static error_t parse_opt (int key, char *arg, struct argp_state *state);
90 /* Function to print some extra text in the help message. */
91 static char *more_help (int key, const char *text, void *input);
93 /* Data structure to communicate with argp functions. */
94 static struct argp argp =
96 options, parse_opt, args_doc, doc, NULL, more_help
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
112 /* Definition of the data structure which represents a category and its
113 items. */
114 struct category
116 int cat_id;
117 const char *name;
118 size_t number;
119 struct cat_item
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;
130 /* Simple helper macro. */
131 #define NELEMS(arr) ((sizeof (arr)) / (sizeof (arr[0])))
133 /* For some tricky stuff. */
134 #define NO_PAREN(Item, More...) Item, ## More
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[] = \
142 NO_PAREN items \
145 #include "categories.def"
146 #undef DEFINE_CATEGORY
148 static struct category category[] =
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
156 #define NCATEGORIES NELEMS (category)
159 /* Automatically set variable. */
160 extern const char *__progname;
162 /* helper function for extended name handling. */
163 extern void locale_special (const char *name, int show_category_name,
164 int show_keyword_name);
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);
181 main (int argc, char *argv[])
183 int remaining;
185 /* Set initial values for global variables. */
186 show_category_name = 0;
187 show_keyword_name = 0;
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");
194 /* Initialize the message catalog. */
195 textdomain (PACKAGE);
197 /* Parse and process arguments. */
198 argp_parse (&argp, argc, argv, 0, &remaining, NULL);
200 /* `-a' requests the names of all available locales. */
201 if (do_all != 0)
203 setlocale_diagnostics ();
204 try_setlocale (LC_COLLATE, "LC_COLLATE");
205 write_locales ();
206 exit (EXIT_SUCCESS);
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)
213 setlocale_diagnostics ();
214 write_charmaps ();
215 exit (EXIT_SUCCESS);
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 ();
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)
227 show_locale_vars ();
228 exit (EXIT_SUCCESS);
231 /* Process all given names. */
232 while (remaining < argc)
233 show_info (argv[remaining++]);
235 exit (EXIT_SUCCESS);
239 /* Handle program arguments. */
240 static error_t
241 parse_opt (int key, char *arg, struct argp_state *state)
243 switch (key)
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;
263 return 0;
267 static char *
268 more_help (int key, const char *text, void *input)
270 char *tp = NULL;
271 switch (key)
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;
283 return (char *) text;
287 /* Print the version information. */
288 static void
289 print_version (FILE *stream, struct argp_state *state)
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 "), "2024");
297 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
301 /* Simple action function which prints arguments as strings. */
302 static void
303 print_names (const void *nodep, VISIT value, int level)
305 if (value == postorder || value == leaf)
306 puts (*(char **) nodep);
310 static int
311 select_dirs (const struct dirent *dirent)
313 int result = 0;
315 if (strcmp (dirent->d_name, ".") != 0 && strcmp (dirent->d_name, "..") != 0)
317 mode_t mode = 0;
319 if (dirent->d_type != DT_UNKNOWN && dirent->d_type != DT_LNK)
320 mode = DTTOIF (dirent->d_type);
321 else
323 struct stat64 st;
324 char buf[sizeof (COMPLOCALEDIR)
325 + strlen (dirent->d_name) + 1];
327 stpcpy (stpcpy (stpcpy (buf, COMPLOCALEDIR), "/"),
328 dirent->d_name);
330 if (stat64 (buf, &st) == 0)
331 mode = st.st_mode;
334 result = S_ISDIR (mode);
337 return result;
341 static void
342 print_LC_IDENTIFICATION (void *mapped, size_t size)
344 /* Read the information from the file. */
345 struct
347 unsigned int magic;
348 unsigned int nstrings;
349 unsigned int strindex[0];
350 } *filedata = mapped;
352 if (filedata->magic == LIMAGIC (LC_IDENTIFICATION)
353 && (sizeof *filedata
354 + (filedata->nstrings
355 * sizeof (unsigned int))
356 <= size))
358 const char *str;
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");
383 static void
384 print_LC_CTYPE (void *mapped, size_t size)
386 struct
388 unsigned int magic;
389 unsigned int nstrings;
390 unsigned int strindex[0];
391 } *filedata = mapped;
393 if (filedata->magic == LIMAGIC (LC_CTYPE)
394 && (sizeof *filedata
395 + (filedata->nstrings
396 * sizeof (unsigned int))
397 <= size))
399 const char *str;
401 str = ((char *) mapped
402 + filedata->strindex[_NL_ITEM_INDEX (_NL_CTYPE_CODESET_NAME)]);
403 if (*str != '\0')
404 printf (" codeset | %s\n", str);
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
415 write_locales (void)
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;
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)
432 /* `POSIX' locale is always available (POSIX.2 4.34.3). */
433 PUT ("POSIX");
434 /* And so is the "C" locale. */
435 PUT ("C");
437 memset (linebuf, '-', sizeof (linebuf) - 1);
438 linebuf[sizeof (linebuf) - 1] = '\0';
440 /* First scan the locale archive. */
441 if (write_archive_locales (&all_data, linebuf))
442 first_locale = 0;
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)
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;
457 stpcpy (enddir = stpcpy (stpcpy (stpcpy (buf,
458 COMPLOCALEDIR),
459 "/"),
460 dirents[cnt]->d_name),
461 "/LC_IDENTIFICATION");
463 if (stat64 (buf, &st) == 0 && S_ISREG (st.st_mode))
465 if (verbose && GET (dirents[cnt]->d_name) == NULL)
467 /* Provide some nice output of all kinds of
468 information. */
469 int fd;
471 if (! first_locale)
472 putchar_unlocked ('\n');
473 first_locale = 0;
475 printf ("locale: %-15.15s directory: %.*s\n%s\n",
476 dirents[cnt]->d_name, (int) (enddir - buf), buf,
477 linebuf);
479 fd = open64 (buf, O_RDONLY);
480 if (fd != -1)
482 void *mapped = mmap64 (NULL, st.st_size, PROT_READ,
483 MAP_SHARED, fd, 0);
484 if (mapped != MAP_FAILED)
486 print_LC_IDENTIFICATION (mapped, st.st_size);
488 munmap (mapped, st.st_size);
491 close (fd);
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))
501 print_LC_CTYPE (mapped, st.st_size);
503 munmap (mapped, st.st_size);
506 if (fd != -1)
507 close (fd);
511 /* If the verbose format is not selected we simply
512 collect the names. */
513 PUT (xstrdup (dirents[cnt]->d_name));
516 if (ndirents > 0)
517 free (dirents);
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"));
523 entry = NULL;
524 while ((entry = argz_next (alias_path, alias_path_len, entry)))
526 static const char aliasfile[] = "/locale.alias";
527 FILE *fp;
528 char full_name[strlen (entry) + sizeof aliasfile];
530 stpcpy (stpcpy (full_name, entry), aliasfile);
531 fp = fopen (full_name, "rm");
532 if (fp == NULL)
533 /* Ignore non-existing files. */
534 continue;
536 /* No threads present. */
537 __fsetlocking (fp, FSETLOCKING_BYCALLER);
539 while (! feof_unlocked (fp))
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;
551 if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
552 /* EOF reached. */
553 break;
555 cp = buf;
556 /* Ignore leading white space. */
557 while (isspace (cp[0]) && cp[0] != '\n')
558 ++cp;
560 /* A leading '#' signals a comment line. */
561 if (cp[0] != '\0' && cp[0] != '#' && cp[0] != '\n')
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';
570 /* Now look for the beginning of the value. */
571 while (isspace (cp[0]))
572 ++cp;
574 if (cp[0] != '\0')
576 value = cp++;
577 while (cp[0] != '\0' && !isspace (cp[0]))
578 ++cp;
579 /* Terminate value. */
580 if (cp[0] == '\n')
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';
589 else if (cp[0] != '\0')
590 *cp++ = '\0';
592 /* Add the alias. */
593 if (! verbose && GET (value) != NULL)
594 PUT (xstrdup (alias));
598 /* Possibly not the whole line fits into the buffer.
599 Ignore the rest of the line. */
600 while (strchr (cp, '\n') == NULL)
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';
610 fclose (fp);
613 if (! verbose)
615 twalk (all_data, print_names);
620 struct nameent
622 char *name;
623 uint32_t locrec_offset;
627 static int
628 nameentcmp (const void *a, const void *b)
630 return strcoll (((const struct nameent *) a)->name,
631 ((const struct nameent *) b)->name);
635 static int
636 write_archive_locales (void **all_datap, char *linebuf)
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;
647 fd = open64 (ARCHIVE_NAME, O_RDONLY);
648 if (fd < 0)
649 return 0;
651 if (fstat64 (fd, &st) < 0 || st.st_size < sizeof (*head))
652 goto error_out;
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;
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;
666 namehashtab = (struct namehashent *) (addr + head->namehash_offset);
667 if (! verbose)
669 for (cnt = 0; cnt < head->namehash_size; ++cnt)
670 if (namehashtab[cnt].locrec_offset != 0)
672 PUT (xstrdup (addr + namehashtab[cnt].name_offset));
673 ++ret;
676 else
678 struct nameent *names;
679 uint32_t used;
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)
686 names[used].name = addr + namehashtab[cnt].name_offset;
687 names[used++].locrec_offset = namehashtab[cnt].locrec_offset;
690 /* Sort the names. */
691 qsort (names, used, sizeof (struct nameent), nameentcmp);
693 for (cnt = 0; cnt < used; ++cnt)
695 struct locrecent *locrec;
697 PUT (xstrdup (names[cnt].name));
699 if (cnt)
700 putchar_unlocked ('\n');
702 printf ("locale: %-15.15s archive: " ARCHIVE_NAME "\n%s\n",
703 names[cnt].name, linebuf);
705 locrec = (struct locrecent *) (addr + names[cnt].locrec_offset);
707 print_LC_IDENTIFICATION (addr
708 + locrec->record[LC_IDENTIFICATION].offset,
709 locrec->record[LC_IDENTIFICATION].len);
711 print_LC_CTYPE (addr + locrec->record[LC_CTYPE].offset,
712 locrec->record[LC_CTYPE].len);
715 free (names);
717 ret = used;
720 error_out:
721 if (addr != MAP_FAILED)
722 munmap (addr, len);
723 close (fd);
724 *all_datap = all_data;
725 return ret;
729 /* Write the names of all available character maps to stdout. */
730 static void
731 write_charmaps (void)
733 void *all_data = NULL;
734 CHARMAP_DIR *dir;
735 const char *dirent;
737 /* Look for all files in the charmap directory. */
738 dir = charmap_opendir (CHARMAP_PATH);
739 if (dir == NULL)
740 return;
742 while ((dirent = charmap_readdir (dir)) != NULL)
744 char **aliases;
745 char **p;
747 PUT (xstrdup (dirent));
749 aliases = charmap_aliases (CHARMAP_PATH, dirent);
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
762 charmap_free_aliases (aliases);
765 charmap_closedir (dir);
767 twalk (all_data, print_names);
770 /* Print a properly quoted assignment of NAME with VAL, using double
771 quotes iff DQUOTE is true. */
772 static void
773 print_assignment (const char *name, const char *val, bool dquote)
775 printf ("%s=", name);
776 if (dquote)
777 putchar ('"');
778 while (*val != '\0')
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++);
789 if (dquote)
790 putchar ('"');
791 putchar ('\n');
794 /* We have to show the contents of the environments determining the
795 locale. */
796 static void
797 show_locale_vars (void)
799 const char *lcall = getenv ("LC_ALL") ?: "";
800 const char *lang = getenv ("LANG") ?: "";
802 /* LANG has to be the first value. */
803 print_assignment ("LANG", lang, false);
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)
809 const char *name = category[cat_no].name;
810 const char *val = getenv (name);
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);
822 /* The last is the LC_ALL value. */
823 print_assignment ("LC_ALL", lcall, false);
827 /* Subroutine of show_info, below. */
828 static void
829 print_item (struct cat_item *item)
831 switch (item->value_type)
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:
843 const char *val;
844 int cnt;
846 if (show_keyword_name)
847 printf ("%s=\"", item->name);
849 for (cnt = 0; cnt < item->max - 1; ++cnt)
851 val = nl_langinfo (item->item_id + cnt);
852 if (val != NULL)
853 fputs (val, stdout);
854 putchar (';');
857 val = nl_langinfo (item->item_id + cnt);
858 if (val != NULL)
859 fputs (val, stdout);
861 if (show_keyword_name)
862 putchar ('"');
863 putchar ('\n');
865 break;
866 case stringlist:
868 int first = 1;
869 const char *val = nl_langinfo (item->item_id) ? : "";
871 if (show_keyword_name)
872 printf ("%s=", item->name);
874 for (int cnt = 0; cnt < item->max && *val != '\0'; ++cnt)
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;
882 putchar ('\n');
884 break;
885 case byte:
887 const char *val = nl_langinfo (item->item_id);
889 if (show_keyword_name)
890 printf ("%s=", item->name);
892 if (val != NULL)
893 printf ("%d", *val == '\377' ? -1 : *val);
894 putchar ('\n');
896 break;
897 case bytearray:
899 const char *val = nl_langinfo (item->item_id);
900 int cnt = val ? strlen (val) : 0;
902 if (show_keyword_name)
903 printf ("%s=", item->name);
905 while (cnt > 1)
907 printf ("%d;", *val == '\177' ? -1 : *val);
908 --cnt;
909 ++val;
912 printf ("%d\n", cnt == 0 || *val == '\177' ? -1 : *val);
914 break;
915 case word:
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);
922 printf ("%d\n", val.word);
924 break;
925 case wordarray:
927 int first = 1;
928 union { unsigned int *wordarray; char *string; } val;
930 val.string = nl_langinfo (item->item_id);
931 if (show_keyword_name)
932 printf ("%s=", item->name);
934 for (int cnt = 0; cnt < item->max; ++cnt)
936 printf ("%s%d", first ? "" : ";", val.wordarray[cnt]);
937 first = 0;
939 putchar ('\n');
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;
952 /* Show the information request for NAME. */
953 static void
954 show_info (const char *name)
956 for (size_t cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
957 if (cat_no != LC_ALL)
959 if (strcmp (name, category[cat_no].name) == 0)
960 /* Print the whole category. */
962 if (show_category_name != 0)
963 puts (category[cat_no].name);
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]);
970 return;
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)
976 if (show_category_name != 0)
977 puts (category[cat_no].name);
979 print_item (&category[cat_no].item_desc[item_no]);
980 return;
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);
989 /* Set to true by try_setlocale if setlocale fails. Used by
990 setlocale_diagnostics. */
991 static bool setlocale_failed;
993 /* Call setlocale, with non-fatal error reporting. */
994 static void
995 try_setlocale (int category, const char *category_name)
997 if (setlocale (category, "") == NULL)
999 error (0, errno, gettext ("Cannot set %s to default locale"),
1000 category_name);
1001 setlocale_failed = true;
1005 /* Return a quoted version of the passed string, or NULL on error. */
1006 static char *
1007 quote_string (const char *input)
1009 char *buffer;
1010 size_t length;
1011 FILE *stream = open_memstream (&buffer, &length);
1012 if (stream == NULL)
1013 return NULL;
1015 while (true)
1017 unsigned char ch = *input++;
1018 if (ch == '\0')
1019 break;
1021 /* Use C backslash escapes for those control characters for
1022 which they are defined. */
1023 switch (ch)
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);
1069 if (ferror (stream))
1071 fclose (stream);
1072 free (buffer);
1073 return NULL;
1075 if (fclose (stream) != 0)
1077 free (buffer);
1078 return NULL;
1081 return buffer;
1084 /* Print additional information if there was a setlocale error (during
1085 try_setlocale). */
1086 static void
1087 setlocale_diagnostics (void)
1089 if (setlocale_failed)
1091 const char *locpath = getenv ("LOCPATH");
1092 if (locpath != NULL)
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);