Update.
[glibc.git] / locale / programs / locale.c
blobadf99445789c1bc04ca30f4bdc0483f66234d963
1 /* Implementation of the locale program according to POSIX 9945-2.
2 Copyright (C) 1995-1997, 1999-2003, 2004 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1995.
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
11 The GNU C Library 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 GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; if not, write to the Free
18 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA. */
21 #ifdef HAVE_CONFIG_H
22 # include <config.h>
23 #endif
25 #include <argp.h>
26 #include <argz.h>
27 #include <dirent.h>
28 #include <errno.h>
29 #include <error.h>
30 #include <fcntl.h>
31 #include <langinfo.h>
32 #include <libintl.h>
33 #include <limits.h>
34 #include <locale.h>
35 #include <search.h>
36 #include <stdio.h>
37 #include <stdio_ext.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <unistd.h>
41 #include <sys/mman.h>
42 #include <sys/stat.h>
44 #include "localeinfo.h"
45 #include "charmap-dir.h"
46 #include "../locarchive.h"
48 extern void *xmalloc (size_t __n);
49 extern char *xstrdup (const char *__str);
51 #define ARCHIVE_NAME LOCALEDIR "/locale-archive"
53 /* If set print the name of the category. */
54 static int show_category_name;
56 /* If set print the name of the item. */
57 static int show_keyword_name;
59 /* Print names of all available locales. */
60 static int do_all;
62 /* Print names of all available character maps. */
63 static int do_charmaps = 0;
65 /* Nonzero if verbose output is wanted. */
66 static int verbose;
68 /* Name and version of program. */
69 static void print_version (FILE *stream, struct argp_state *state);
70 void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
72 /* Definitions of arguments for argp functions. */
73 static const struct argp_option options[] =
75 { NULL, 0, NULL, 0, N_("System information:") },
76 { "all-locales", 'a', NULL, OPTION_NO_USAGE,
77 N_("Write names of available locales") },
78 { "charmaps", 'm', NULL, OPTION_NO_USAGE,
79 N_("Write names of available charmaps") },
80 { NULL, 0, NULL, 0, N_("Modify output format:") },
81 { "category-name", 'c', NULL, 0, N_("Write names of selected categories") },
82 { "keyword-name", 'k', NULL, 0, N_("Write names of selected keywords") },
83 { "verbose", 'v', NULL, 0, N_("Print more information") },
84 { NULL, 0, NULL, 0, NULL }
87 /* Short description of program. */
88 static const char doc[] = N_("Get locale-specific information.\v\
89 For bug reporting instructions, please see:\n\
90 <http://www.gnu.org/software/libc/bugs.html>.\n");
92 /* Strings for arguments in help texts. */
93 static const char args_doc[] = N_("NAME\n[-a|-m]");
95 /* Prototype for option handler. */
96 static error_t parse_opt (int key, char *arg, struct argp_state *state);
98 /* Data structure to communicate with argp functions. */
99 static struct argp argp =
101 options, parse_opt, args_doc, doc
105 /* We don't have these constants defined because we don't use them. Give
106 default values. */
107 #define CTYPE_MB_CUR_MIN 0
108 #define CTYPE_MB_CUR_MAX 0
109 #define CTYPE_HASH_SIZE 0
110 #define CTYPE_HASH_LAYERS 0
111 #define CTYPE_CLASS 0
112 #define CTYPE_TOUPPER_EB 0
113 #define CTYPE_TOLOWER_EB 0
114 #define CTYPE_TOUPPER_EL 0
115 #define CTYPE_TOLOWER_EL 0
117 /* Definition of the data structure which represents a category and its
118 items. */
119 struct category
121 int cat_id;
122 const char *name;
123 size_t number;
124 struct cat_item
126 int item_id;
127 const char *name;
128 enum { std, opt } status;
129 enum value_type value_type;
130 int min;
131 int max;
132 } *item_desc;
135 /* Simple helper macro. */
136 #define NELEMS(arr) ((sizeof (arr)) / (sizeof (arr[0])))
138 /* For some tricky stuff. */
139 #define NO_PAREN(Item, More...) Item, ## More
141 /* We have all categories defined in `categories.def'. Now construct
142 the description and data structure used for all categories. */
143 #define DEFINE_ELEMENT(Item, More...) { Item, ## More },
144 #define DEFINE_CATEGORY(category, name, items, postload) \
145 static struct cat_item category##_desc[] = \
147 NO_PAREN items \
150 #include "categories.def"
151 #undef DEFINE_CATEGORY
153 static struct category category[] =
155 #define DEFINE_CATEGORY(category, name, items, postload) \
156 [category] = { _NL_NUM_##category, name, NELEMS (category##_desc), \
157 category##_desc },
158 #include "categories.def"
159 #undef DEFINE_CATEGORY
161 #define NCATEGORIES NELEMS (category)
164 /* Automatically set variable. */
165 extern const char *__progname;
167 /* helper function for extended name handling. */
168 extern void locale_special (const char *name, int show_category_name,
169 int show_keyword_name);
171 /* Prototypes for local functions. */
172 static void print_LC_IDENTIFICATION (void *mapped, size_t size);
173 static void print_LC_CTYPE (void *mapped, size_t size);
174 static void write_locales (void);
175 static int nameentcmp (const void *a, const void *b);
176 static int write_archive_locales (void **all_datap, char *linebuf);
177 static void write_charmaps (void);
178 static void show_locale_vars (void);
179 static void show_info (const char *name);
183 main (int argc, char *argv[])
185 int remaining;
187 /* Set initial values for global variables. */
188 show_category_name = 0;
189 show_keyword_name = 0;
191 /* Set locale. Do not set LC_ALL because the other categories must
192 not be affected (according to POSIX.2). */
193 if (setlocale (LC_CTYPE, "") == NULL)
194 error (0, errno, gettext ("Cannot set LC_CTYPE to default locale"));
195 if (setlocale (LC_MESSAGES, "") == NULL)
196 error (0, errno, gettext ("Cannot set LC_MESSAGES to default locale"));
198 /* Initialize the message catalog. */
199 textdomain (PACKAGE);
201 /* Parse and process arguments. */
202 argp_parse (&argp, argc, argv, 0, &remaining, NULL);
204 /* `-a' requests the names of all available locales. */
205 if (do_all != 0)
207 if (setlocale (LC_COLLATE, "") == NULL)
208 error (0, errno,
209 gettext ("Cannot set LC_COLLATE to default locale"));
210 write_locales ();
211 exit (EXIT_SUCCESS);
214 /* `m' requests the names of all available charmaps. The names can be
215 used for the -f argument to localedef(1). */
216 if (do_charmaps != 0)
218 write_charmaps ();
219 exit (EXIT_SUCCESS);
222 /* Specific information about the current locale are requested.
223 Change to this locale now. */
224 if (setlocale (LC_ALL, "") == NULL)
225 error (0, errno, gettext ("Cannot set LC_ALL to default locale"));
227 /* If no real argument is given we have to print the contents of the
228 current locale definition variables. These are LANG and the LC_*. */
229 if (remaining == argc && show_keyword_name == 0 && show_category_name == 0)
231 show_locale_vars ();
232 exit (EXIT_SUCCESS);
235 /* Process all given names. */
236 while (remaining < argc)
237 show_info (argv[remaining++]);
239 exit (EXIT_SUCCESS);
243 /* Handle program arguments. */
244 static error_t
245 parse_opt (int key, char *arg, struct argp_state *state)
247 switch (key)
249 case 'a':
250 do_all = 1;
251 break;
252 case 'c':
253 show_category_name = 1;
254 break;
255 case 'm':
256 do_charmaps = 1;
257 break;
258 case 'k':
259 show_keyword_name = 1;
260 break;
261 case 'v':
262 verbose = 1;
263 break;
264 default:
265 return ARGP_ERR_UNKNOWN;
267 return 0;
271 /* Print the version information. */
272 static void
273 print_version (FILE *stream, struct argp_state *state)
275 fprintf (stream, "locale (GNU %s) %s\n", PACKAGE, VERSION);
276 fprintf (stream, gettext ("\
277 Copyright (C) %s Free Software Foundation, Inc.\n\
278 This is free software; see the source for copying conditions. There is NO\n\
279 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
280 "), "2004");
281 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
285 /* Simple action function which prints arguments as strings. */
286 static void
287 print_names (const void *nodep, VISIT value, int level)
289 if (value == postorder || value == leaf)
290 puts (*(char **) nodep);
294 static int
295 select_dirs (const struct dirent *dirent)
297 int result = 0;
299 if (strcmp (dirent->d_name, ".") != 0 && strcmp (dirent->d_name, "..") != 0)
301 mode_t mode = 0;
303 #ifdef _DIRENT_HAVE_D_TYPE
304 if (dirent->d_type != DT_UNKNOWN && dirent->d_type != DT_LNK)
305 mode = DTTOIF (dirent->d_type);
306 else
307 #endif
309 struct stat64 st;
310 char buf[sizeof (LOCALEDIR) + strlen (dirent->d_name) + 1];
312 stpcpy (stpcpy (stpcpy (buf, LOCALEDIR), "/"), dirent->d_name);
314 if (stat64 (buf, &st) == 0)
315 mode = st.st_mode;
318 result = S_ISDIR (mode);
321 return result;
325 static void
326 print_LC_IDENTIFICATION (void *mapped, size_t size)
328 /* Read the information from the file. */
329 struct
331 unsigned int magic;
332 unsigned int nstrings;
333 unsigned int strindex[0];
334 } *filedata = mapped;
336 if (filedata->magic == LIMAGIC (LC_IDENTIFICATION)
337 && (sizeof *filedata
338 + (filedata->nstrings
339 * sizeof (unsigned int))
340 <= size))
342 const char *str;
344 #define HANDLE(idx, name) \
345 str = ((char *) mapped \
346 + filedata->strindex[_NL_ITEM_INDEX (_NL_IDENTIFICATION_##idx)]); \
347 if (*str != '\0') \
348 printf ("%9s | %s\n", name, str)
349 HANDLE (TITLE, "title");
350 HANDLE (SOURCE, "source");
351 HANDLE (ADDRESS, "address");
352 HANDLE (CONTACT, "contact");
353 HANDLE (EMAIL, "email");
354 HANDLE (TEL, "telephone");
355 HANDLE (FAX, "fax");
356 HANDLE (LANGUAGE, "language");
357 HANDLE (TERRITORY, "territory");
358 HANDLE (AUDIENCE, "audience");
359 HANDLE (APPLICATION, "application");
360 HANDLE (ABBREVIATION, "abbreviation");
361 HANDLE (REVISION, "revision");
362 HANDLE (DATE, "date");
367 static void
368 print_LC_CTYPE (void *mapped, size_t size)
370 struct
372 unsigned int magic;
373 unsigned int nstrings;
374 unsigned int strindex[0];
375 } *filedata = mapped;
377 if (filedata->magic == LIMAGIC (LC_CTYPE)
378 && (sizeof *filedata
379 + (filedata->nstrings
380 * sizeof (unsigned int))
381 <= size))
383 const char *str;
385 str = ((char *) mapped
386 + filedata->strindex[_NL_ITEM_INDEX (_NL_CTYPE_CODESET_NAME)]);
387 if (*str != '\0')
388 printf (" codeset | %s\n", str);
393 /* Write the names of all available locales to stdout. We have some
394 sources of the information: the contents of the locale directory
395 and the locale.alias file. To avoid duplicates and print the
396 result is a reasonable order we put all entries is a search tree
397 and print them afterwards. */
398 static void
399 write_locales (void)
401 char linebuf[80];
402 void *all_data = NULL;
403 struct dirent **dirents;
404 int ndirents;
405 int cnt;
406 char *alias_path;
407 size_t alias_path_len;
408 char *entry;
409 int first_locale = 1;
411 #define PUT(name) tsearch (name, &all_data, \
412 (int (*) (const void *, const void *)) strcoll)
413 #define GET(name) tfind (name, &all_data, \
414 (int (*) (const void *, const void *)) strcoll)
416 /* `POSIX' locale is always available (POSIX.2 4.34.3). */
417 PUT ("POSIX");
418 /* And so is the "C" locale. */
419 PUT ("C");
421 memset (linebuf, '-', sizeof (linebuf) - 1);
422 linebuf[sizeof (linebuf) - 1] = '\0';
424 /* First scan the locale archive. */
425 if (write_archive_locales (&all_data, linebuf))
426 first_locale = 0;
428 /* Now we can look for all files in the directory. */
429 ndirents = scandir (LOCALEDIR, &dirents, select_dirs, alphasort);
430 for (cnt = 0; cnt < ndirents; ++cnt)
432 /* Test whether at least the LC_CTYPE data is there. Some
433 directories only contain translations. */
434 char buf[sizeof (LOCALEDIR) + strlen (dirents[cnt]->d_name)
435 + sizeof "/LC_IDENTIFICATION"];
436 char *enddir;
437 struct stat64 st;
439 stpcpy (enddir = stpcpy (stpcpy (stpcpy (buf, LOCALEDIR), "/"),
440 dirents[cnt]->d_name),
441 "/LC_IDENTIFICATION");
443 if (stat64 (buf, &st) == 0 && S_ISREG (st.st_mode))
445 if (verbose && GET (dirents[cnt]->d_name) == NULL)
447 /* Provide some nice output of all kinds of
448 information. */
449 int fd;
451 if (! first_locale)
452 putchar_unlocked ('\n');
453 first_locale = 0;
455 printf ("locale: %-15.15s directory: %.*s\n%s\n",
456 dirents[cnt]->d_name, (int) (enddir - buf), buf,
457 linebuf);
459 fd = open64 (buf, O_RDONLY);
460 if (fd != -1)
462 void *mapped = mmap64 (NULL, st.st_size, PROT_READ,
463 MAP_SHARED, fd, 0);
464 if (mapped != MAP_FAILED)
466 print_LC_IDENTIFICATION (mapped, st.st_size);
468 munmap (mapped, st.st_size);
471 close (fd);
473 /* Now try to get the charset information. */
474 strcpy (enddir, "/LC_CTYPE");
475 fd = open64 (buf, O_RDONLY);
476 if (fd != -1 && fstat64 (fd, &st) >= 0
477 && ((mapped = mmap64 (NULL, st.st_size, PROT_READ,
478 MAP_SHARED, fd, 0))
479 != MAP_FAILED))
481 print_LC_CTYPE (mapped, st.st_size);
483 munmap (mapped, st.st_size);
486 if (fd != -1)
487 close (fd);
491 /* If the verbose format is not selected we simply
492 collect the names. */
493 PUT (xstrdup (dirents[cnt]->d_name));
496 if (ndirents > 0)
497 free (dirents);
499 /* Now read the locale.alias files. */
500 if (argz_create_sep (LOCALE_ALIAS_PATH, ':', &alias_path, &alias_path_len))
501 error (1, errno, gettext ("while preparing output"));
503 entry = NULL;
504 while ((entry = argz_next (alias_path, alias_path_len, entry)))
506 static const char aliasfile[] = "/locale.alias";
507 FILE *fp;
508 char full_name[strlen (entry) + sizeof aliasfile];
510 stpcpy (stpcpy (full_name, entry), aliasfile);
511 fp = fopen (full_name, "rm");
512 if (fp == NULL)
513 /* Ignore non-existing files. */
514 continue;
516 /* No threads present. */
517 __fsetlocking (fp, FSETLOCKING_BYCALLER);
519 while (! feof_unlocked (fp))
521 /* It is a reasonable approach to use a fix buffer here
522 because
523 a) we are only interested in the first two fields
524 b) these fields must be usable as file names and so must
525 not be that long */
526 char buf[BUFSIZ];
527 char *alias;
528 char *value;
529 char *cp;
531 if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
532 /* EOF reached. */
533 break;
535 cp = buf;
536 /* Ignore leading white space. */
537 while (isspace (cp[0]) && cp[0] != '\n')
538 ++cp;
540 /* A leading '#' signals a comment line. */
541 if (cp[0] != '\0' && cp[0] != '#' && cp[0] != '\n')
543 alias = cp++;
544 while (cp[0] != '\0' && !isspace (cp[0]))
545 ++cp;
546 /* Terminate alias name. */
547 if (cp[0] != '\0')
548 *cp++ = '\0';
550 /* Now look for the beginning of the value. */
551 while (isspace (cp[0]))
552 ++cp;
554 if (cp[0] != '\0')
556 value = cp++;
557 while (cp[0] != '\0' && !isspace (cp[0]))
558 ++cp;
559 /* Terminate value. */
560 if (cp[0] == '\n')
562 /* This has to be done to make the following
563 test for the end of line possible. We are
564 looking for the terminating '\n' which do not
565 overwrite here. */
566 *cp++ = '\0';
567 *cp = '\n';
569 else if (cp[0] != '\0')
570 *cp++ = '\0';
572 /* Add the alias. */
573 if (! verbose && GET (value) != NULL)
574 PUT (xstrdup (alias));
578 /* Possibly not the whole line fits into the buffer.
579 Ignore the rest of the line. */
580 while (strchr (cp, '\n') == NULL)
582 cp = buf;
583 if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
584 /* Make sure the inner loop will be left. The outer
585 loop will exit at the `feof' test. */
586 *cp = '\n';
590 fclose (fp);
593 if (! verbose)
595 twalk (all_data, print_names);
600 struct nameent
602 char *name;
603 uint32_t locrec_offset;
607 static int
608 nameentcmp (const void *a, const void *b)
610 return strcoll (((const struct nameent *) a)->name,
611 ((const struct nameent *) b)->name);
615 static int
616 write_archive_locales (void **all_datap, char *linebuf)
618 struct stat64 st;
619 void *all_data = *all_datap;
620 size_t len = 0;
621 struct locarhead *head;
622 struct namehashent *namehashtab;
623 char *addr = MAP_FAILED;
624 int fd, ret = 0;
625 uint32_t cnt;
627 fd = open64 (ARCHIVE_NAME, O_RDONLY);
628 if (fd < 0)
629 return 0;
631 if (fstat64 (fd, &st) < 0 || st.st_size < sizeof (*head))
632 goto error_out;
634 len = st.st_size;
635 addr = mmap64 (NULL, len, PROT_READ, MAP_SHARED, fd, 0);
636 if (addr == MAP_FAILED)
637 goto error_out;
639 head = (struct locarhead *) addr;
640 if (head->namehash_offset + head->namehash_size > len
641 || head->string_offset + head->string_size > len
642 || head->locrectab_offset + head->locrectab_size > len
643 || head->sumhash_offset + head->sumhash_size > len)
644 goto error_out;
646 namehashtab = (struct namehashent *) (addr + head->namehash_offset);
647 if (! verbose)
649 for (cnt = 0; cnt < head->namehash_size; ++cnt)
650 if (namehashtab[cnt].locrec_offset != 0)
652 PUT (xstrdup (addr + namehashtab[cnt].name_offset));
653 ++ret;
656 else
658 struct nameent *names;
659 uint32_t used;
661 names = (struct nameent *) xmalloc (head->namehash_used
662 * sizeof (struct nameent));
663 for (cnt = used = 0; cnt < head->namehash_size; ++cnt)
664 if (namehashtab[cnt].locrec_offset != 0)
666 names[used].name = addr + namehashtab[cnt].name_offset;
667 names[used++].locrec_offset = namehashtab[cnt].locrec_offset;
670 /* Sort the names. */
671 qsort (names, used, sizeof (struct nameent), nameentcmp);
673 for (cnt = 0; cnt < used; ++cnt)
675 struct locrecent *locrec;
677 PUT (xstrdup (names[cnt].name));
679 if (cnt)
680 putchar_unlocked ('\n');
682 printf ("locale: %-15.15s archive: " ARCHIVE_NAME "\n%s\n",
683 names[cnt].name, linebuf);
685 locrec = (struct locrecent *) (addr + names[cnt].locrec_offset);
687 print_LC_IDENTIFICATION (addr
688 + locrec->record[LC_IDENTIFICATION].offset,
689 locrec->record[LC_IDENTIFICATION].len);
691 print_LC_CTYPE (addr + locrec->record[LC_CTYPE].offset,
692 locrec->record[LC_CTYPE].len);
695 ret = used;
698 error_out:
699 if (addr != MAP_FAILED)
700 munmap (addr, len);
701 close (fd);
702 *all_datap = all_data;
703 return ret;
707 /* Write the names of all available character maps to stdout. */
708 static void
709 write_charmaps (void)
711 void *all_data = NULL;
712 CHARMAP_DIR *dir;
713 const char *dirent;
715 /* Look for all files in the charmap directory. */
716 dir = charmap_opendir (CHARMAP_PATH);
717 if (dir == NULL)
718 return;
720 while ((dirent = charmap_readdir (dir)) != NULL)
722 char **aliases;
723 char **p;
725 PUT (xstrdup (dirent));
727 aliases = charmap_aliases (CHARMAP_PATH, dirent);
729 #if 0
730 /* Add the code_set_name and the aliases. */
731 for (p = aliases; *p; p++)
732 PUT (xstrdup (*p));
733 #else
734 /* Add the code_set_name only. Most aliases are obsolete. */
735 p = aliases;
736 if (*p)
737 PUT (xstrdup (*p));
738 #endif
740 charmap_free_aliases (aliases);
743 charmap_closedir (dir);
745 twalk (all_data, print_names);
749 /* We have to show the contents of the environments determining the
750 locale. */
751 static void
752 show_locale_vars (void)
754 size_t cat_no;
755 const char *lcall = getenv ("LC_ALL");
756 const char *lang = getenv ("LANG") ? : "";
758 auto void get_source (const char *name);
760 void get_source (const char *name)
762 char *val = getenv (name);
764 if ((lcall ?: "")[0] != '\0' || val == NULL)
765 printf ("%s=\"%s\"\n", name,
766 (lcall ?: "")[0] ? lcall : (lang ?: "")[0] ? lang : "POSIX");
767 else
768 printf ("%s=%s\n", name, val);
771 /* LANG has to be the first value. */
772 printf ("LANG=%s\n", lang);
774 /* Now all categories in an unspecified order. */
775 for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
776 if (cat_no != LC_ALL)
777 get_source (category[cat_no].name);
779 /* The last is the LC_ALL value. */
780 printf ("LC_ALL=%s\n", lcall ? : "");
784 /* Show the information request for NAME. */
785 static void
786 show_info (const char *name)
788 size_t cat_no;
790 auto void print_item (struct cat_item *item);
792 void print_item (struct cat_item *item)
794 switch (item->value_type)
796 case string:
797 if (show_keyword_name)
798 printf ("%s=\"", item->name);
799 fputs (nl_langinfo (item->item_id) ? : "", stdout);
800 if (show_keyword_name)
801 putchar ('"');
802 putchar ('\n');
803 break;
804 case stringarray:
806 int cnt;
807 const char *val;
809 if (show_keyword_name)
810 printf ("%s=\"", item->name);
812 for (cnt = 0; cnt < item->max - 1; ++cnt)
814 val = nl_langinfo (item->item_id + cnt);
815 if (val != NULL)
816 fputs (val, stdout);
817 putchar (';');
820 val = nl_langinfo (item->item_id + cnt);
821 if (val != NULL)
822 fputs (val, stdout);
824 if (show_keyword_name)
825 putchar ('"');
826 putchar ('\n');
828 break;
829 case stringlist:
831 int first = 1;
832 const char *val = nl_langinfo (item->item_id) ? : "";
833 int cnt;
835 if (show_keyword_name)
836 printf ("%s=", item->name);
838 for (cnt = 0; cnt < item->max && *val != '\0'; ++cnt)
840 printf ("%s%s%s%s", first ? "" : ";",
841 show_keyword_name ? "\"" : "", val,
842 show_keyword_name ? "\"" : "");
843 val = strchr (val, '\0') + 1;
844 first = 0;
846 putchar ('\n');
848 break;
849 case byte:
851 const char *val = nl_langinfo (item->item_id);
853 if (show_keyword_name)
854 printf ("%s=", item->name);
856 if (val != NULL)
857 printf ("%d", *val == '\177' ? -1 : *val);
858 putchar ('\n');
860 break;
861 case bytearray:
863 const char *val = nl_langinfo (item->item_id);
864 int cnt = val ? strlen (val) : 0;
866 if (show_keyword_name)
867 printf ("%s=", item->name);
869 while (cnt > 1)
871 printf ("%d;", *val == '\177' ? -1 : *val);
872 --cnt;
873 ++val;
876 printf ("%d\n", cnt == 0 || *val == '\177' ? -1 : *val);
878 break;
879 case word:
881 union { unsigned int word; char *string; } val;
882 val.string = nl_langinfo (item->item_id);
883 if (show_keyword_name)
884 printf ("%s=", item->name);
886 printf ("%d\n", val.word);
888 break;
889 case wstring:
890 case wstringarray:
891 case wstringlist:
892 /* We don't print wide character information since the same
893 information is available in a multibyte string. */
894 default:
895 break;
900 for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
901 if (cat_no != LC_ALL)
903 size_t item_no;
905 if (strcmp (name, category[cat_no].name) == 0)
906 /* Print the whole category. */
908 if (show_category_name != 0)
909 puts (category[cat_no].name);
911 for (item_no = 0; item_no < category[cat_no].number; ++item_no)
912 print_item (&category[cat_no].item_desc[item_no]);
914 return;
917 for (item_no = 0; item_no < category[cat_no].number; ++item_no)
918 if (strcmp (name, category[cat_no].item_desc[item_no].name) == 0)
920 if (show_category_name != 0)
921 puts (category[cat_no].name);
923 print_item (&category[cat_no].item_desc[item_no]);
924 return;
928 /* The name is not a standard one.
929 For testing and perhaps advanced use allow some more symbols. */
930 locale_special (name, show_category_name, show_keyword_name);