[BZ #2510, BZ #2830, BZ #3137, BZ #3313, BZ #3426, BZ #3465, BZ #3480, BZ #3483,...
[glibc.git] / locale / programs / locale.c
blobcc3082d48a76e6edbd9542443058397d93e770f0
1 /* Implementation of the locale program according to POSIX 9945-2.
2 Copyright (C) 1995-1997, 1999-2006, 2007 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 version 2 as
8 published by the Free Software Foundation.
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, write to the Free Software Foundation,
17 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
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 <sys/mman.h>
40 #include <sys/stat.h>
42 #include "localeinfo.h"
43 #include "charmap-dir.h"
44 #include "../locarchive.h"
46 extern void *xmalloc (size_t __n);
47 extern char *xstrdup (const char *__str);
49 #define ARCHIVE_NAME LOCALEDIR "/locale-archive"
51 /* If set print the name of the category. */
52 static int show_category_name;
54 /* If set print the name of the item. */
55 static int show_keyword_name;
57 /* Print names of all available locales. */
58 static int do_all;
60 /* Print names of all available character maps. */
61 static int do_charmaps = 0;
63 /* Nonzero if verbose output is wanted. */
64 static int verbose;
66 /* Name and version of program. */
67 static void print_version (FILE *stream, struct argp_state *state);
68 void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
70 /* Definitions of arguments for argp functions. */
71 static const struct argp_option options[] =
73 { NULL, 0, NULL, 0, N_("System information:") },
74 { "all-locales", 'a', NULL, OPTION_NO_USAGE,
75 N_("Write names of available locales") },
76 { "charmaps", 'm', NULL, OPTION_NO_USAGE,
77 N_("Write names of available charmaps") },
78 { NULL, 0, NULL, 0, N_("Modify output format:") },
79 { "category-name", 'c', NULL, 0, N_("Write names of selected categories") },
80 { "keyword-name", 'k', NULL, 0, N_("Write names of selected keywords") },
81 { "verbose", 'v', NULL, 0, N_("Print more information") },
82 { NULL, 0, NULL, 0, NULL }
85 /* Short description of program. */
86 static const char doc[] = N_("Get locale-specific information.\v\
87 For bug reporting instructions, please see:\n\
88 <http://www.gnu.org/software/libc/bugs.html>.\n");
90 /* Strings for arguments in help texts. */
91 static const char args_doc[] = N_("NAME\n[-a|-m]");
93 /* Prototype for option handler. */
94 static error_t parse_opt (int key, char *arg, struct argp_state *state);
96 /* Data structure to communicate with argp functions. */
97 static struct argp argp =
99 options, parse_opt, args_doc, doc
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 /* Print the version information. */
270 static void
271 print_version (FILE *stream, struct argp_state *state)
273 fprintf (stream, "locale (GNU %s) %s\n", PACKAGE, VERSION);
274 fprintf (stream, gettext ("\
275 Copyright (C) %s Free Software Foundation, Inc.\n\
276 This is free software; see the source for copying conditions. There is NO\n\
277 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
278 "), "2007");
279 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
283 /* Simple action function which prints arguments as strings. */
284 static void
285 print_names (const void *nodep, VISIT value, int level)
287 if (value == postorder || value == leaf)
288 puts (*(char **) nodep);
292 static int
293 select_dirs (const struct dirent *dirent)
295 int result = 0;
297 if (strcmp (dirent->d_name, ".") != 0 && strcmp (dirent->d_name, "..") != 0)
299 mode_t mode = 0;
301 #ifdef _DIRENT_HAVE_D_TYPE
302 if (dirent->d_type != DT_UNKNOWN && dirent->d_type != DT_LNK)
303 mode = DTTOIF (dirent->d_type);
304 else
305 #endif
307 struct stat64 st;
308 char buf[sizeof (LOCALEDIR) + strlen (dirent->d_name) + 1];
310 stpcpy (stpcpy (stpcpy (buf, LOCALEDIR), "/"), dirent->d_name);
312 if (stat64 (buf, &st) == 0)
313 mode = st.st_mode;
316 result = S_ISDIR (mode);
319 return result;
323 static void
324 print_LC_IDENTIFICATION (void *mapped, size_t size)
326 /* Read the information from the file. */
327 struct
329 unsigned int magic;
330 unsigned int nstrings;
331 unsigned int strindex[0];
332 } *filedata = mapped;
334 if (filedata->magic == LIMAGIC (LC_IDENTIFICATION)
335 && (sizeof *filedata
336 + (filedata->nstrings
337 * sizeof (unsigned int))
338 <= size))
340 const char *str;
342 #define HANDLE(idx, name) \
343 str = ((char *) mapped \
344 + filedata->strindex[_NL_ITEM_INDEX (_NL_IDENTIFICATION_##idx)]); \
345 if (*str != '\0') \
346 printf ("%9s | %s\n", name, str)
347 HANDLE (TITLE, "title");
348 HANDLE (SOURCE, "source");
349 HANDLE (ADDRESS, "address");
350 HANDLE (CONTACT, "contact");
351 HANDLE (EMAIL, "email");
352 HANDLE (TEL, "telephone");
353 HANDLE (FAX, "fax");
354 HANDLE (LANGUAGE, "language");
355 HANDLE (TERRITORY, "territory");
356 HANDLE (AUDIENCE, "audience");
357 HANDLE (APPLICATION, "application");
358 HANDLE (ABBREVIATION, "abbreviation");
359 HANDLE (REVISION, "revision");
360 HANDLE (DATE, "date");
365 static void
366 print_LC_CTYPE (void *mapped, size_t size)
368 struct
370 unsigned int magic;
371 unsigned int nstrings;
372 unsigned int strindex[0];
373 } *filedata = mapped;
375 if (filedata->magic == LIMAGIC (LC_CTYPE)
376 && (sizeof *filedata
377 + (filedata->nstrings
378 * sizeof (unsigned int))
379 <= size))
381 const char *str;
383 str = ((char *) mapped
384 + filedata->strindex[_NL_ITEM_INDEX (_NL_CTYPE_CODESET_NAME)]);
385 if (*str != '\0')
386 printf (" codeset | %s\n", str);
391 /* Write the names of all available locales to stdout. We have some
392 sources of the information: the contents of the locale directory
393 and the locale.alias file. To avoid duplicates and print the
394 result is a reasonable order we put all entries is a search tree
395 and print them afterwards. */
396 static void
397 write_locales (void)
399 char linebuf[80];
400 void *all_data = NULL;
401 struct dirent **dirents;
402 int ndirents;
403 int cnt;
404 char *alias_path;
405 size_t alias_path_len;
406 char *entry;
407 int first_locale = 1;
409 #define PUT(name) tsearch (name, &all_data, \
410 (int (*) (const void *, const void *)) strcoll)
411 #define GET(name) tfind (name, &all_data, \
412 (int (*) (const void *, const void *)) strcoll)
414 /* `POSIX' locale is always available (POSIX.2 4.34.3). */
415 PUT ("POSIX");
416 /* And so is the "C" locale. */
417 PUT ("C");
419 memset (linebuf, '-', sizeof (linebuf) - 1);
420 linebuf[sizeof (linebuf) - 1] = '\0';
422 /* First scan the locale archive. */
423 if (write_archive_locales (&all_data, linebuf))
424 first_locale = 0;
426 /* Now we can look for all files in the directory. */
427 ndirents = scandir (LOCALEDIR, &dirents, select_dirs, alphasort);
428 for (cnt = 0; cnt < ndirents; ++cnt)
430 /* Test whether at least the LC_CTYPE data is there. Some
431 directories only contain translations. */
432 char buf[sizeof (LOCALEDIR) + strlen (dirents[cnt]->d_name)
433 + sizeof "/LC_IDENTIFICATION"];
434 char *enddir;
435 struct stat64 st;
437 stpcpy (enddir = stpcpy (stpcpy (stpcpy (buf, LOCALEDIR), "/"),
438 dirents[cnt]->d_name),
439 "/LC_IDENTIFICATION");
441 if (stat64 (buf, &st) == 0 && S_ISREG (st.st_mode))
443 if (verbose && GET (dirents[cnt]->d_name) == NULL)
445 /* Provide some nice output of all kinds of
446 information. */
447 int fd;
449 if (! first_locale)
450 putchar_unlocked ('\n');
451 first_locale = 0;
453 printf ("locale: %-15.15s directory: %.*s\n%s\n",
454 dirents[cnt]->d_name, (int) (enddir - buf), buf,
455 linebuf);
457 fd = open64 (buf, O_RDONLY);
458 if (fd != -1)
460 void *mapped = mmap64 (NULL, st.st_size, PROT_READ,
461 MAP_SHARED, fd, 0);
462 if (mapped != MAP_FAILED)
464 print_LC_IDENTIFICATION (mapped, st.st_size);
466 munmap (mapped, st.st_size);
469 close (fd);
471 /* Now try to get the charset information. */
472 strcpy (enddir, "/LC_CTYPE");
473 fd = open64 (buf, O_RDONLY);
474 if (fd != -1 && fstat64 (fd, &st) >= 0
475 && ((mapped = mmap64 (NULL, st.st_size, PROT_READ,
476 MAP_SHARED, fd, 0))
477 != MAP_FAILED))
479 print_LC_CTYPE (mapped, st.st_size);
481 munmap (mapped, st.st_size);
484 if (fd != -1)
485 close (fd);
489 /* If the verbose format is not selected we simply
490 collect the names. */
491 PUT (xstrdup (dirents[cnt]->d_name));
494 if (ndirents > 0)
495 free (dirents);
497 /* Now read the locale.alias files. */
498 if (argz_create_sep (LOCALE_ALIAS_PATH, ':', &alias_path, &alias_path_len))
499 error (1, errno, gettext ("while preparing output"));
501 entry = NULL;
502 while ((entry = argz_next (alias_path, alias_path_len, entry)))
504 static const char aliasfile[] = "/locale.alias";
505 FILE *fp;
506 char full_name[strlen (entry) + sizeof aliasfile];
508 stpcpy (stpcpy (full_name, entry), aliasfile);
509 fp = fopen (full_name, "rm");
510 if (fp == NULL)
511 /* Ignore non-existing files. */
512 continue;
514 /* No threads present. */
515 __fsetlocking (fp, FSETLOCKING_BYCALLER);
517 while (! feof_unlocked (fp))
519 /* It is a reasonable approach to use a fix buffer here
520 because
521 a) we are only interested in the first two fields
522 b) these fields must be usable as file names and so must
523 not be that long */
524 char buf[BUFSIZ];
525 char *alias;
526 char *value;
527 char *cp;
529 if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
530 /* EOF reached. */
531 break;
533 cp = buf;
534 /* Ignore leading white space. */
535 while (isspace (cp[0]) && cp[0] != '\n')
536 ++cp;
538 /* A leading '#' signals a comment line. */
539 if (cp[0] != '\0' && cp[0] != '#' && cp[0] != '\n')
541 alias = cp++;
542 while (cp[0] != '\0' && !isspace (cp[0]))
543 ++cp;
544 /* Terminate alias name. */
545 if (cp[0] != '\0')
546 *cp++ = '\0';
548 /* Now look for the beginning of the value. */
549 while (isspace (cp[0]))
550 ++cp;
552 if (cp[0] != '\0')
554 value = cp++;
555 while (cp[0] != '\0' && !isspace (cp[0]))
556 ++cp;
557 /* Terminate value. */
558 if (cp[0] == '\n')
560 /* This has to be done to make the following
561 test for the end of line possible. We are
562 looking for the terminating '\n' which do not
563 overwrite here. */
564 *cp++ = '\0';
565 *cp = '\n';
567 else if (cp[0] != '\0')
568 *cp++ = '\0';
570 /* Add the alias. */
571 if (! verbose && GET (value) != NULL)
572 PUT (xstrdup (alias));
576 /* Possibly not the whole line fits into the buffer.
577 Ignore the rest of the line. */
578 while (strchr (cp, '\n') == NULL)
580 cp = buf;
581 if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
582 /* Make sure the inner loop will be left. The outer
583 loop will exit at the `feof' test. */
584 *cp = '\n';
588 fclose (fp);
591 if (! verbose)
593 twalk (all_data, print_names);
598 struct nameent
600 char *name;
601 uint32_t locrec_offset;
605 static int
606 nameentcmp (const void *a, const void *b)
608 return strcoll (((const struct nameent *) a)->name,
609 ((const struct nameent *) b)->name);
613 static int
614 write_archive_locales (void **all_datap, char *linebuf)
616 struct stat64 st;
617 void *all_data = *all_datap;
618 size_t len = 0;
619 struct locarhead *head;
620 struct namehashent *namehashtab;
621 char *addr = MAP_FAILED;
622 int fd, ret = 0;
623 uint32_t cnt;
625 fd = open64 (ARCHIVE_NAME, O_RDONLY);
626 if (fd < 0)
627 return 0;
629 if (fstat64 (fd, &st) < 0 || st.st_size < sizeof (*head))
630 goto error_out;
632 len = st.st_size;
633 addr = mmap64 (NULL, len, PROT_READ, MAP_SHARED, fd, 0);
634 if (addr == MAP_FAILED)
635 goto error_out;
637 head = (struct locarhead *) addr;
638 if (head->namehash_offset + head->namehash_size > len
639 || head->string_offset + head->string_size > len
640 || head->locrectab_offset + head->locrectab_size > len
641 || head->sumhash_offset + head->sumhash_size > len)
642 goto error_out;
644 namehashtab = (struct namehashent *) (addr + head->namehash_offset);
645 if (! verbose)
647 for (cnt = 0; cnt < head->namehash_size; ++cnt)
648 if (namehashtab[cnt].locrec_offset != 0)
650 PUT (xstrdup (addr + namehashtab[cnt].name_offset));
651 ++ret;
654 else
656 struct nameent *names;
657 uint32_t used;
659 names = (struct nameent *) xmalloc (head->namehash_used
660 * sizeof (struct nameent));
661 for (cnt = used = 0; cnt < head->namehash_size; ++cnt)
662 if (namehashtab[cnt].locrec_offset != 0)
664 names[used].name = addr + namehashtab[cnt].name_offset;
665 names[used++].locrec_offset = namehashtab[cnt].locrec_offset;
668 /* Sort the names. */
669 qsort (names, used, sizeof (struct nameent), nameentcmp);
671 for (cnt = 0; cnt < used; ++cnt)
673 struct locrecent *locrec;
675 PUT (xstrdup (names[cnt].name));
677 if (cnt)
678 putchar_unlocked ('\n');
680 printf ("locale: %-15.15s archive: " ARCHIVE_NAME "\n%s\n",
681 names[cnt].name, linebuf);
683 locrec = (struct locrecent *) (addr + names[cnt].locrec_offset);
685 print_LC_IDENTIFICATION (addr
686 + locrec->record[LC_IDENTIFICATION].offset,
687 locrec->record[LC_IDENTIFICATION].len);
689 print_LC_CTYPE (addr + locrec->record[LC_CTYPE].offset,
690 locrec->record[LC_CTYPE].len);
693 ret = used;
696 error_out:
697 if (addr != MAP_FAILED)
698 munmap (addr, len);
699 close (fd);
700 *all_datap = all_data;
701 return ret;
705 /* Write the names of all available character maps to stdout. */
706 static void
707 write_charmaps (void)
709 void *all_data = NULL;
710 CHARMAP_DIR *dir;
711 const char *dirent;
713 /* Look for all files in the charmap directory. */
714 dir = charmap_opendir (CHARMAP_PATH);
715 if (dir == NULL)
716 return;
718 while ((dirent = charmap_readdir (dir)) != NULL)
720 char **aliases;
721 char **p;
723 PUT (xstrdup (dirent));
725 aliases = charmap_aliases (CHARMAP_PATH, dirent);
727 #if 0
728 /* Add the code_set_name and the aliases. */
729 for (p = aliases; *p; p++)
730 PUT (xstrdup (*p));
731 #else
732 /* Add the code_set_name only. Most aliases are obsolete. */
733 p = aliases;
734 if (*p)
735 PUT (xstrdup (*p));
736 #endif
738 charmap_free_aliases (aliases);
741 charmap_closedir (dir);
743 twalk (all_data, print_names);
747 /* We have to show the contents of the environments determining the
748 locale. */
749 static void
750 show_locale_vars (void)
752 size_t cat_no;
753 const char *lcall = getenv ("LC_ALL");
754 const char *lang = getenv ("LANG") ? : "";
756 auto void get_source (const char *name);
758 void get_source (const char *name)
760 char *val = getenv (name);
762 if ((lcall ?: "")[0] != '\0' || val == NULL)
763 printf ("%s=\"%s\"\n", name,
764 (lcall ?: "")[0] ? lcall : (lang ?: "")[0] ? lang : "POSIX");
765 else
766 printf ("%s=%s\n", name, val);
769 /* LANG has to be the first value. */
770 printf ("LANG=%s\n", lang);
772 /* Now all categories in an unspecified order. */
773 for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
774 if (cat_no != LC_ALL)
775 get_source (category[cat_no].name);
777 /* The last is the LC_ALL value. */
778 printf ("LC_ALL=%s\n", lcall ? : "");
782 /* Show the information request for NAME. */
783 static void
784 show_info (const char *name)
786 size_t cat_no;
788 auto void print_item (struct cat_item *item);
790 void print_item (struct cat_item *item)
792 switch (item->value_type)
794 case string:
795 if (show_keyword_name)
796 printf ("%s=\"", item->name);
797 fputs (nl_langinfo (item->item_id) ? : "", stdout);
798 if (show_keyword_name)
799 putchar ('"');
800 putchar ('\n');
801 break;
802 case stringarray:
804 int cnt;
805 const char *val;
807 if (show_keyword_name)
808 printf ("%s=\"", item->name);
810 for (cnt = 0; cnt < item->max - 1; ++cnt)
812 val = nl_langinfo (item->item_id + cnt);
813 if (val != NULL)
814 fputs (val, stdout);
815 putchar (';');
818 val = nl_langinfo (item->item_id + cnt);
819 if (val != NULL)
820 fputs (val, stdout);
822 if (show_keyword_name)
823 putchar ('"');
824 putchar ('\n');
826 break;
827 case stringlist:
829 int first = 1;
830 const char *val = nl_langinfo (item->item_id) ? : "";
831 int cnt;
833 if (show_keyword_name)
834 printf ("%s=", item->name);
836 for (cnt = 0; cnt < item->max && *val != '\0'; ++cnt)
838 printf ("%s%s%s%s", first ? "" : ";",
839 show_keyword_name ? "\"" : "", val,
840 show_keyword_name ? "\"" : "");
841 val = strchr (val, '\0') + 1;
842 first = 0;
844 putchar ('\n');
846 break;
847 case byte:
849 const char *val = nl_langinfo (item->item_id);
851 if (show_keyword_name)
852 printf ("%s=", item->name);
854 if (val != NULL)
855 printf ("%d", *val == '\177' ? -1 : *val);
856 putchar ('\n');
858 break;
859 case bytearray:
861 const char *val = nl_langinfo (item->item_id);
862 int cnt = val ? strlen (val) : 0;
864 if (show_keyword_name)
865 printf ("%s=", item->name);
867 while (cnt > 1)
869 printf ("%d;", *val == '\177' ? -1 : *val);
870 --cnt;
871 ++val;
874 printf ("%d\n", cnt == 0 || *val == '\177' ? -1 : *val);
876 break;
877 case word:
879 union { unsigned int word; char *string; } val;
880 val.string = nl_langinfo (item->item_id);
881 if (show_keyword_name)
882 printf ("%s=", item->name);
884 printf ("%d\n", val.word);
886 break;
887 case wstring:
888 case wstringarray:
889 case wstringlist:
890 /* We don't print wide character information since the same
891 information is available in a multibyte string. */
892 default:
893 break;
898 for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
899 if (cat_no != LC_ALL)
901 size_t item_no;
903 if (strcmp (name, category[cat_no].name) == 0)
904 /* Print the whole category. */
906 if (show_category_name != 0)
907 puts (category[cat_no].name);
909 for (item_no = 0; item_no < category[cat_no].number; ++item_no)
910 print_item (&category[cat_no].item_desc[item_no]);
912 return;
915 for (item_no = 0; item_no < category[cat_no].number; ++item_no)
916 if (strcmp (name, category[cat_no].item_desc[item_no].name) == 0)
918 if (show_category_name != 0)
919 puts (category[cat_no].name);
921 print_item (&category[cat_no].item_desc[item_no]);
922 return;
926 /* The name is not a standard one.
927 For testing and perhaps advanced use allow some more symbols. */
928 locale_special (name, show_category_name, show_keyword_name);