Support mcount/gprof test with GCC defaulting to PIE
[glibc.git] / locale / programs / locale.c
blob941290089b18bc2b7009636d60d65888fd50b7eb
1 /* Implementation of the locale program according to POSIX 9945-2.
2 Copyright (C) 1995-2017 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1995.
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.
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.
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/>. */
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
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>
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 /* Nonzero if verbose output is wanted. */
63 static int verbose;
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;
69 /* Definitions of arguments for argp functions. */
70 static const struct argp_option options[] =
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 }
84 /* Short description of program. */
85 static const char doc[] = N_("Get locale-specific information.");
87 /* Strings for arguments in help texts. */
88 static const char args_doc[] = N_("NAME\n[-a|-m]");
90 /* Prototype for option handler. */
91 static error_t parse_opt (int key, char *arg, struct argp_state *state);
93 /* Function to print some extra text in the help message. */
94 static char *more_help (int key, const char *text, void *input);
96 /* Data structure to communicate with argp functions. */
97 static struct argp argp =
99 options, parse_opt, args_doc, doc, NULL, more_help
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
115 /* Definition of the data structure which represents a category and its
116 items. */
117 struct category
119 int cat_id;
120 const char *name;
121 size_t number;
122 struct cat_item
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;
133 /* Simple helper macro. */
134 #define NELEMS(arr) ((sizeof (arr)) / (sizeof (arr[0])))
136 /* For some tricky stuff. */
137 #define NO_PAREN(Item, More...) Item, ## More
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[] = \
145 NO_PAREN items \
148 #include "categories.def"
149 #undef DEFINE_CATEGORY
151 static struct category category[] =
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
159 #define NCATEGORIES NELEMS (category)
162 /* Automatically set variable. */
163 extern const char *__progname;
165 /* helper function for extended name handling. */
166 extern void locale_special (const char *name, int show_category_name,
167 int show_keyword_name);
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);
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 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"));
196 /* Initialize the message catalog. */
197 textdomain (PACKAGE);
199 /* Parse and process arguments. */
200 argp_parse (&argp, argc, argv, 0, &remaining, NULL);
202 /* `-a' requests the names of all available locales. */
203 if (do_all != 0)
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);
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)
216 write_charmaps ();
217 exit (EXIT_SUCCESS);
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"));
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)
229 show_locale_vars ();
230 exit (EXIT_SUCCESS);
233 /* Process all given names. */
234 while (remaining < argc)
235 show_info (argv[remaining++]);
237 exit (EXIT_SUCCESS);
241 /* Handle program arguments. */
242 static error_t
243 parse_opt (int key, char *arg, struct argp_state *state)
245 switch (key)
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;
265 return 0;
269 static char *
270 more_help (int key, const char *text, void *input)
272 char *tp = NULL;
273 switch (key)
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;
285 return (char *) text;
289 /* Print the version information. */
290 static void
291 print_version (FILE *stream, struct argp_state *state)
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 "), "2017");
299 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
303 /* Simple action function which prints arguments as strings. */
304 static void
305 print_names (const void *nodep, VISIT value, int level)
307 if (value == postorder || value == leaf)
308 puts (*(char **) nodep);
312 static int
313 select_dirs (const struct dirent *dirent)
315 int result = 0;
317 if (strcmp (dirent->d_name, ".") != 0 && strcmp (dirent->d_name, "..") != 0)
319 mode_t mode = 0;
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
327 struct stat64 st;
328 char buf[sizeof (COMPLOCALEDIR)
329 + strlen (dirent->d_name) + 1];
331 stpcpy (stpcpy (stpcpy (buf, COMPLOCALEDIR), "/"),
332 dirent->d_name);
334 if (stat64 (buf, &st) == 0)
335 mode = st.st_mode;
338 result = S_ISDIR (mode);
341 return result;
345 static void
346 print_LC_IDENTIFICATION (void *mapped, size_t size)
348 /* Read the information from the file. */
349 struct
351 unsigned int magic;
352 unsigned int nstrings;
353 unsigned int strindex[0];
354 } *filedata = mapped;
356 if (filedata->magic == LIMAGIC (LC_IDENTIFICATION)
357 && (sizeof *filedata
358 + (filedata->nstrings
359 * sizeof (unsigned int))
360 <= size))
362 const char *str;
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");
387 static void
388 print_LC_CTYPE (void *mapped, size_t size)
390 struct
392 unsigned int magic;
393 unsigned int nstrings;
394 unsigned int strindex[0];
395 } *filedata = mapped;
397 if (filedata->magic == LIMAGIC (LC_CTYPE)
398 && (sizeof *filedata
399 + (filedata->nstrings
400 * sizeof (unsigned int))
401 <= size))
403 const char *str;
405 str = ((char *) mapped
406 + filedata->strindex[_NL_ITEM_INDEX (_NL_CTYPE_CODESET_NAME)]);
407 if (*str != '\0')
408 printf (" codeset | %s\n", str);
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)
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;
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)
436 /* `POSIX' locale is always available (POSIX.2 4.34.3). */
437 PUT ("POSIX");
438 /* And so is the "C" locale. */
439 PUT ("C");
441 memset (linebuf, '-', sizeof (linebuf) - 1);
442 linebuf[sizeof (linebuf) - 1] = '\0';
444 /* First scan the locale archive. */
445 if (write_archive_locales (&all_data, linebuf))
446 first_locale = 0;
448 /* Now we can look for all files in the directory. */
449 ndirents = scandir (COMPLOCALEDIR, &dirents, select_dirs,
450 alphasort);
451 for (cnt = 0; cnt < ndirents; ++cnt)
453 /* Test whether at least the LC_CTYPE data is there. Some
454 directories only contain translations. */
455 char buf[sizeof (COMPLOCALEDIR)
456 + strlen (dirents[cnt]->d_name)
457 + sizeof "/LC_IDENTIFICATION"];
458 char *enddir;
459 struct stat64 st;
461 stpcpy (enddir = stpcpy (stpcpy (stpcpy (buf,
462 COMPLOCALEDIR),
463 "/"),
464 dirents[cnt]->d_name),
465 "/LC_IDENTIFICATION");
467 if (stat64 (buf, &st) == 0 && S_ISREG (st.st_mode))
469 if (verbose && GET (dirents[cnt]->d_name) == NULL)
471 /* Provide some nice output of all kinds of
472 information. */
473 int fd;
475 if (! first_locale)
476 putchar_unlocked ('\n');
477 first_locale = 0;
479 printf ("locale: %-15.15s directory: %.*s\n%s\n",
480 dirents[cnt]->d_name, (int) (enddir - buf), buf,
481 linebuf);
483 fd = open64 (buf, O_RDONLY);
484 if (fd != -1)
486 void *mapped = mmap64 (NULL, st.st_size, PROT_READ,
487 MAP_SHARED, fd, 0);
488 if (mapped != MAP_FAILED)
490 print_LC_IDENTIFICATION (mapped, st.st_size);
492 munmap (mapped, st.st_size);
495 close (fd);
497 /* Now try to get the charset information. */
498 strcpy (enddir, "/LC_CTYPE");
499 fd = open64 (buf, O_RDONLY);
500 if (fd != -1 && fstat64 (fd, &st) >= 0
501 && ((mapped = mmap64 (NULL, st.st_size, PROT_READ,
502 MAP_SHARED, fd, 0))
503 != MAP_FAILED))
505 print_LC_CTYPE (mapped, st.st_size);
507 munmap (mapped, st.st_size);
510 if (fd != -1)
511 close (fd);
515 /* If the verbose format is not selected we simply
516 collect the names. */
517 PUT (xstrdup (dirents[cnt]->d_name));
520 if (ndirents > 0)
521 free (dirents);
523 /* Now read the locale.alias files. */
524 if (argz_create_sep (LOCALE_ALIAS_PATH, ':', &alias_path, &alias_path_len))
525 error (1, errno, gettext ("while preparing output"));
527 entry = NULL;
528 while ((entry = argz_next (alias_path, alias_path_len, entry)))
530 static const char aliasfile[] = "/locale.alias";
531 FILE *fp;
532 char full_name[strlen (entry) + sizeof aliasfile];
534 stpcpy (stpcpy (full_name, entry), aliasfile);
535 fp = fopen (full_name, "rm");
536 if (fp == NULL)
537 /* Ignore non-existing files. */
538 continue;
540 /* No threads present. */
541 __fsetlocking (fp, FSETLOCKING_BYCALLER);
543 while (! feof_unlocked (fp))
545 /* It is a reasonable approach to use a fix buffer here
546 because
547 a) we are only interested in the first two fields
548 b) these fields must be usable as file names and so must
549 not be that long */
550 char buf[BUFSIZ];
551 char *alias;
552 char *value;
553 char *cp;
555 if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
556 /* EOF reached. */
557 break;
559 cp = buf;
560 /* Ignore leading white space. */
561 while (isspace (cp[0]) && cp[0] != '\n')
562 ++cp;
564 /* A leading '#' signals a comment line. */
565 if (cp[0] != '\0' && cp[0] != '#' && cp[0] != '\n')
567 alias = cp++;
568 while (cp[0] != '\0' && !isspace (cp[0]))
569 ++cp;
570 /* Terminate alias name. */
571 if (cp[0] != '\0')
572 *cp++ = '\0';
574 /* Now look for the beginning of the value. */
575 while (isspace (cp[0]))
576 ++cp;
578 if (cp[0] != '\0')
580 value = cp++;
581 while (cp[0] != '\0' && !isspace (cp[0]))
582 ++cp;
583 /* Terminate value. */
584 if (cp[0] == '\n')
586 /* This has to be done to make the following
587 test for the end of line possible. We are
588 looking for the terminating '\n' which do not
589 overwrite here. */
590 *cp++ = '\0';
591 *cp = '\n';
593 else if (cp[0] != '\0')
594 *cp++ = '\0';
596 /* Add the alias. */
597 if (! verbose && GET (value) != NULL)
598 PUT (xstrdup (alias));
602 /* Possibly not the whole line fits into the buffer.
603 Ignore the rest of the line. */
604 while (strchr (cp, '\n') == NULL)
606 cp = buf;
607 if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
608 /* Make sure the inner loop will be left. The outer
609 loop will exit at the `feof' test. */
610 *cp = '\n';
614 fclose (fp);
617 if (! verbose)
619 twalk (all_data, print_names);
624 struct nameent
626 char *name;
627 uint32_t locrec_offset;
631 static int
632 nameentcmp (const void *a, const void *b)
634 return strcoll (((const struct nameent *) a)->name,
635 ((const struct nameent *) b)->name);
639 static int
640 write_archive_locales (void **all_datap, char *linebuf)
642 struct stat64 st;
643 void *all_data = *all_datap;
644 size_t len = 0;
645 struct locarhead *head;
646 struct namehashent *namehashtab;
647 char *addr = MAP_FAILED;
648 int fd, ret = 0;
649 uint32_t cnt;
651 fd = open64 (ARCHIVE_NAME, O_RDONLY);
652 if (fd < 0)
653 return 0;
655 if (fstat64 (fd, &st) < 0 || st.st_size < sizeof (*head))
656 goto error_out;
658 len = st.st_size;
659 addr = mmap64 (NULL, len, PROT_READ, MAP_SHARED, fd, 0);
660 if (addr == MAP_FAILED)
661 goto error_out;
663 head = (struct locarhead *) addr;
664 if (head->namehash_offset + head->namehash_size > len
665 || head->string_offset + head->string_size > len
666 || head->locrectab_offset + head->locrectab_size > len
667 || head->sumhash_offset + head->sumhash_size > len)
668 goto error_out;
670 namehashtab = (struct namehashent *) (addr + head->namehash_offset);
671 if (! verbose)
673 for (cnt = 0; cnt < head->namehash_size; ++cnt)
674 if (namehashtab[cnt].locrec_offset != 0)
676 PUT (xstrdup (addr + namehashtab[cnt].name_offset));
677 ++ret;
680 else
682 struct nameent *names;
683 uint32_t used;
685 names = (struct nameent *) xmalloc (head->namehash_used
686 * sizeof (struct nameent));
687 for (cnt = used = 0; cnt < head->namehash_size; ++cnt)
688 if (namehashtab[cnt].locrec_offset != 0)
690 names[used].name = addr + namehashtab[cnt].name_offset;
691 names[used++].locrec_offset = namehashtab[cnt].locrec_offset;
694 /* Sort the names. */
695 qsort (names, used, sizeof (struct nameent), nameentcmp);
697 for (cnt = 0; cnt < used; ++cnt)
699 struct locrecent *locrec;
701 PUT (xstrdup (names[cnt].name));
703 if (cnt)
704 putchar_unlocked ('\n');
706 printf ("locale: %-15.15s archive: " ARCHIVE_NAME "\n%s\n",
707 names[cnt].name, linebuf);
709 locrec = (struct locrecent *) (addr + names[cnt].locrec_offset);
711 print_LC_IDENTIFICATION (addr
712 + locrec->record[LC_IDENTIFICATION].offset,
713 locrec->record[LC_IDENTIFICATION].len);
715 print_LC_CTYPE (addr + locrec->record[LC_CTYPE].offset,
716 locrec->record[LC_CTYPE].len);
719 ret = used;
722 error_out:
723 if (addr != MAP_FAILED)
724 munmap (addr, len);
725 close (fd);
726 *all_datap = all_data;
727 return ret;
731 /* Write the names of all available character maps to stdout. */
732 static void
733 write_charmaps (void)
735 void *all_data = NULL;
736 CHARMAP_DIR *dir;
737 const char *dirent;
739 /* Look for all files in the charmap directory. */
740 dir = charmap_opendir (CHARMAP_PATH);
741 if (dir == NULL)
742 return;
744 while ((dirent = charmap_readdir (dir)) != NULL)
746 char **aliases;
747 char **p;
749 PUT (xstrdup (dirent));
751 aliases = charmap_aliases (CHARMAP_PATH, dirent);
753 #if 0
754 /* Add the code_set_name and the aliases. */
755 for (p = aliases; *p; p++)
756 PUT (xstrdup (*p));
757 #else
758 /* Add the code_set_name only. Most aliases are obsolete. */
759 p = aliases;
760 if (*p)
761 PUT (xstrdup (*p));
762 #endif
764 charmap_free_aliases (aliases);
767 charmap_closedir (dir);
769 twalk (all_data, print_names);
772 /* Print a properly quoted assignment of NAME with VAL, using double
773 quotes iff DQUOTE is true. */
774 static void
775 print_assignment (const char *name, const char *val, bool dquote)
777 printf ("%s=", name);
778 if (dquote)
779 putchar ('"');
780 while (*val != '\0')
782 size_t segment
783 = strcspn (val, dquote ? "$`\"\\" : "~|&;<>()$`\\\"' \t\n");
784 printf ("%.*s", (int) segment, val);
785 val += segment;
786 if (*val == '\0')
787 break;
788 putchar ('\\');
789 putchar (*val++);
791 if (dquote)
792 putchar ('"');
793 putchar ('\n');
796 /* We have to show the contents of the environments determining the
797 locale. */
798 static void
799 show_locale_vars (void)
801 const char *lcall = getenv ("LC_ALL") ?: "";
802 const char *lang = getenv ("LANG") ?: "";
804 /* LANG has to be the first value. */
805 print_assignment ("LANG", lang, false);
807 /* Now all categories in an unspecified order. */
808 for (size_t cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
809 if (cat_no != LC_ALL)
811 const char *name = category[cat_no].name;
812 const char *val = getenv (name);
814 if (lcall[0] != '\0' || val == NULL)
815 print_assignment (name,
816 lcall[0] != '\0' ? lcall
817 : lang[0] != '\0' ? lang
818 : "POSIX",
819 true);
820 else
821 print_assignment (name, val, false);
824 /* The last is the LC_ALL value. */
825 print_assignment ("LC_ALL", lcall, false);
829 /* Subroutine of show_info, below. */
830 static void
831 print_item (struct cat_item *item)
833 switch (item->value_type)
835 case string:
836 if (show_keyword_name)
837 printf ("%s=\"", item->name);
838 fputs (nl_langinfo (item->item_id) ? : "", stdout);
839 if (show_keyword_name)
840 putchar ('"');
841 putchar ('\n');
842 break;
843 case stringarray:
845 const char *val;
846 int cnt;
848 if (show_keyword_name)
849 printf ("%s=\"", item->name);
851 for (cnt = 0; cnt < item->max - 1; ++cnt)
853 val = nl_langinfo (item->item_id + cnt);
854 if (val != NULL)
855 fputs (val, stdout);
856 putchar (';');
859 val = nl_langinfo (item->item_id + cnt);
860 if (val != NULL)
861 fputs (val, stdout);
863 if (show_keyword_name)
864 putchar ('"');
865 putchar ('\n');
867 break;
868 case stringlist:
870 int first = 1;
871 const char *val = nl_langinfo (item->item_id) ? : "";
873 if (show_keyword_name)
874 printf ("%s=", item->name);
876 for (int cnt = 0; cnt < item->max && *val != '\0'; ++cnt)
878 printf ("%s%s%s%s", first ? "" : ";",
879 show_keyword_name ? "\"" : "", val,
880 show_keyword_name ? "\"" : "");
881 val = strchr (val, '\0') + 1;
882 first = 0;
884 putchar ('\n');
886 break;
887 case byte:
889 const char *val = nl_langinfo (item->item_id);
891 if (show_keyword_name)
892 printf ("%s=", item->name);
894 if (val != NULL)
895 printf ("%d", *val == '\377' ? -1 : *val);
896 putchar ('\n');
898 break;
899 case bytearray:
901 const char *val = nl_langinfo (item->item_id);
902 int cnt = val ? strlen (val) : 0;
904 if (show_keyword_name)
905 printf ("%s=", item->name);
907 while (cnt > 1)
909 printf ("%d;", *val == '\177' ? -1 : *val);
910 --cnt;
911 ++val;
914 printf ("%d\n", cnt == 0 || *val == '\177' ? -1 : *val);
916 break;
917 case word:
919 union { unsigned int word; char *string; } val;
920 val.string = nl_langinfo (item->item_id);
921 if (show_keyword_name)
922 printf ("%s=", item->name);
924 printf ("%d\n", val.word);
926 break;
927 case wordarray:
929 int first = 1;
930 union { unsigned int *wordarray; char *string; } val;
932 val.string = nl_langinfo (item->item_id);
933 if (show_keyword_name)
934 printf ("%s=", item->name);
936 for (int cnt = 0; cnt < item->max; ++cnt)
938 printf ("%s%d", first ? "" : ";", val.wordarray[cnt]);
939 first = 0;
941 putchar ('\n');
943 break;
944 case wstring:
945 case wstringarray:
946 case wstringlist:
947 /* We don't print wide character information since the same
948 information is available in a multibyte string. */
949 default:
950 break;
954 /* Show the information request for NAME. */
955 static void
956 show_info (const char *name)
958 for (size_t cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
959 if (cat_no != LC_ALL)
961 if (strcmp (name, category[cat_no].name) == 0)
962 /* Print the whole category. */
964 if (show_category_name != 0)
965 puts (category[cat_no].name);
967 for (size_t item_no = 0;
968 item_no < category[cat_no].number;
969 ++item_no)
970 print_item (&category[cat_no].item_desc[item_no]);
972 return;
975 for (size_t item_no = 0; item_no < category[cat_no].number; ++item_no)
976 if (strcmp (name, category[cat_no].item_desc[item_no].name) == 0)
978 if (show_category_name != 0)
979 puts (category[cat_no].name);
981 print_item (&category[cat_no].item_desc[item_no]);
982 return;
986 /* The name is not a standard one.
987 For testing and perhaps advanced use allow some more symbols. */
988 locale_special (name, show_category_name, show_keyword_name);