2.9
[glibc/nacl-glibc.git] / locale / programs / locale.c
blob5f770a1816a59548c2486bbd320e215ce2abe950
1 /* Implementation of the locale program according to POSIX 9945-2.
2 Copyright (C) 1995-1997, 1999-2007, 2008 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, write to the Free Software Foundation,
18 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
20 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif
24 #include <argp.h>
25 #include <argz.h>
26 #include <dirent.h>
27 #include <errno.h>
28 #include <error.h>
29 #include <fcntl.h>
30 #include <langinfo.h>
31 #include <libintl.h>
32 #include <limits.h>
33 #include <locale.h>
34 #include <search.h>
35 #include <stdio.h>
36 #include <stdio_ext.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
40 #include <sys/mman.h>
41 #include <sys/stat.h>
43 #include "localeinfo.h"
44 #include "charmap-dir.h"
45 #include "../locarchive.h"
47 extern void *xmalloc (size_t __n);
48 extern char *xstrdup (const char *__str);
50 #define ARCHIVE_NAME LOCALEDIR "/locale-archive"
52 /* If set print the name of the category. */
53 static int show_category_name;
55 /* If set print the name of the item. */
56 static int show_keyword_name;
58 /* Print names of all available locales. */
59 static int do_all;
61 /* Print names of all available character maps. */
62 static int do_charmaps = 0;
64 /* Nonzero if verbose output is wanted. */
65 static int verbose;
67 /* Name and version of program. */
68 static void print_version (FILE *stream, struct argp_state *state);
69 void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
71 /* Definitions of arguments for argp functions. */
72 static const struct argp_option options[] =
74 { NULL, 0, NULL, 0, N_("System information:") },
75 { "all-locales", 'a', NULL, OPTION_NO_USAGE,
76 N_("Write names of available locales") },
77 { "charmaps", 'm', NULL, OPTION_NO_USAGE,
78 N_("Write names of available charmaps") },
79 { NULL, 0, NULL, 0, N_("Modify output format:") },
80 { "category-name", 'c', NULL, 0, N_("Write names of selected categories") },
81 { "keyword-name", 'k', NULL, 0, N_("Write names of selected keywords") },
82 { "verbose", 'v', NULL, 0, N_("Print more information") },
83 { NULL, 0, NULL, 0, NULL }
86 /* Short description of program. */
87 static const char doc[] = N_("Get locale-specific information.\v\
88 For bug reporting instructions, please see:\n\
89 <http://www.gnu.org/software/libc/bugs.html>.\n");
91 /* Strings for arguments in help texts. */
92 static const char args_doc[] = N_("NAME\n[-a|-m]");
94 /* Prototype for option handler. */
95 static error_t parse_opt (int key, char *arg, struct argp_state *state);
97 /* Data structure to communicate with argp functions. */
98 static struct argp argp =
100 options, parse_opt, args_doc, doc
104 /* We don't have these constants defined because we don't use them. Give
105 default values. */
106 #define CTYPE_MB_CUR_MIN 0
107 #define CTYPE_MB_CUR_MAX 0
108 #define CTYPE_HASH_SIZE 0
109 #define CTYPE_HASH_LAYERS 0
110 #define CTYPE_CLASS 0
111 #define CTYPE_TOUPPER_EB 0
112 #define CTYPE_TOLOWER_EB 0
113 #define CTYPE_TOUPPER_EL 0
114 #define CTYPE_TOLOWER_EL 0
116 /* Definition of the data structure which represents a category and its
117 items. */
118 struct category
120 int cat_id;
121 const char *name;
122 size_t number;
123 struct cat_item
125 int item_id;
126 const char *name;
127 enum { std, opt } status;
128 enum value_type value_type;
129 int min;
130 int max;
131 } *item_desc;
134 /* Simple helper macro. */
135 #define NELEMS(arr) ((sizeof (arr)) / (sizeof (arr[0])))
137 /* For some tricky stuff. */
138 #define NO_PAREN(Item, More...) Item, ## More
140 /* We have all categories defined in `categories.def'. Now construct
141 the description and data structure used for all categories. */
142 #define DEFINE_ELEMENT(Item, More...) { Item, ## More },
143 #define DEFINE_CATEGORY(category, name, items, postload) \
144 static struct cat_item category##_desc[] = \
146 NO_PAREN items \
149 #include "categories.def"
150 #undef DEFINE_CATEGORY
152 static struct category category[] =
154 #define DEFINE_CATEGORY(category, name, items, postload) \
155 [category] = { _NL_NUM_##category, name, NELEMS (category##_desc), \
156 category##_desc },
157 #include "categories.def"
158 #undef DEFINE_CATEGORY
160 #define NCATEGORIES NELEMS (category)
163 /* Automatically set variable. */
164 extern const char *__progname;
166 /* helper function for extended name handling. */
167 extern void locale_special (const char *name, int show_category_name,
168 int show_keyword_name);
170 /* Prototypes for local functions. */
171 static void print_LC_IDENTIFICATION (void *mapped, size_t size);
172 static void print_LC_CTYPE (void *mapped, size_t size);
173 static void write_locales (void);
174 static int nameentcmp (const void *a, const void *b);
175 static int write_archive_locales (void **all_datap, char *linebuf);
176 static void write_charmaps (void);
177 static void show_locale_vars (void);
178 static void show_info (const char *name);
182 main (int argc, char *argv[])
184 int remaining;
186 /* Set initial values for global variables. */
187 show_category_name = 0;
188 show_keyword_name = 0;
190 /* Set locale. Do not set LC_ALL because the other categories must
191 not be affected (according to POSIX.2). */
192 if (setlocale (LC_CTYPE, "") == NULL)
193 error (0, errno, gettext ("Cannot set LC_CTYPE to default locale"));
194 if (setlocale (LC_MESSAGES, "") == NULL)
195 error (0, errno, gettext ("Cannot set LC_MESSAGES to default locale"));
197 /* Initialize the message catalog. */
198 textdomain (PACKAGE);
200 /* Parse and process arguments. */
201 argp_parse (&argp, argc, argv, 0, &remaining, NULL);
203 /* `-a' requests the names of all available locales. */
204 if (do_all != 0)
206 if (setlocale (LC_COLLATE, "") == NULL)
207 error (0, errno,
208 gettext ("Cannot set LC_COLLATE to default locale"));
209 write_locales ();
210 exit (EXIT_SUCCESS);
213 /* `m' requests the names of all available charmaps. The names can be
214 used for the -f argument to localedef(1). */
215 if (do_charmaps != 0)
217 write_charmaps ();
218 exit (EXIT_SUCCESS);
221 /* Specific information about the current locale are requested.
222 Change to this locale now. */
223 if (setlocale (LC_ALL, "") == NULL)
224 error (0, errno, gettext ("Cannot set LC_ALL to default locale"));
226 /* If no real argument is given we have to print the contents of the
227 current locale definition variables. These are LANG and the LC_*. */
228 if (remaining == argc && show_keyword_name == 0 && show_category_name == 0)
230 show_locale_vars ();
231 exit (EXIT_SUCCESS);
234 /* Process all given names. */
235 while (remaining < argc)
236 show_info (argv[remaining++]);
238 exit (EXIT_SUCCESS);
242 /* Handle program arguments. */
243 static error_t
244 parse_opt (int key, char *arg, struct argp_state *state)
246 switch (key)
248 case 'a':
249 do_all = 1;
250 break;
251 case 'c':
252 show_category_name = 1;
253 break;
254 case 'm':
255 do_charmaps = 1;
256 break;
257 case 'k':
258 show_keyword_name = 1;
259 break;
260 case 'v':
261 verbose = 1;
262 break;
263 default:
264 return ARGP_ERR_UNKNOWN;
266 return 0;
270 /* Print the version information. */
271 static void
272 print_version (FILE *stream, struct argp_state *state)
274 fprintf (stream, "locale (GNU %s) %s\n", PACKAGE, VERSION);
275 fprintf (stream, gettext ("\
276 Copyright (C) %s Free Software Foundation, Inc.\n\
277 This is free software; see the source for copying conditions. There is NO\n\
278 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
279 "), "2008");
280 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
284 /* Simple action function which prints arguments as strings. */
285 static void
286 print_names (const void *nodep, VISIT value, int level)
288 if (value == postorder || value == leaf)
289 puts (*(char **) nodep);
293 static int
294 select_dirs (const struct dirent *dirent)
296 int result = 0;
298 if (strcmp (dirent->d_name, ".") != 0 && strcmp (dirent->d_name, "..") != 0)
300 mode_t mode = 0;
302 #ifdef _DIRENT_HAVE_D_TYPE
303 if (dirent->d_type != DT_UNKNOWN && dirent->d_type != DT_LNK)
304 mode = DTTOIF (dirent->d_type);
305 else
306 #endif
308 struct stat64 st;
309 char buf[sizeof (LOCALEDIR) + strlen (dirent->d_name) + 1];
311 stpcpy (stpcpy (stpcpy (buf, LOCALEDIR), "/"), dirent->d_name);
313 if (stat64 (buf, &st) == 0)
314 mode = st.st_mode;
317 result = S_ISDIR (mode);
320 return result;
324 static void
325 print_LC_IDENTIFICATION (void *mapped, size_t size)
327 /* Read the information from the file. */
328 struct
330 unsigned int magic;
331 unsigned int nstrings;
332 unsigned int strindex[0];
333 } *filedata = mapped;
335 if (filedata->magic == LIMAGIC (LC_IDENTIFICATION)
336 && (sizeof *filedata
337 + (filedata->nstrings
338 * sizeof (unsigned int))
339 <= size))
341 const char *str;
343 #define HANDLE(idx, name) \
344 str = ((char *) mapped \
345 + filedata->strindex[_NL_ITEM_INDEX (_NL_IDENTIFICATION_##idx)]); \
346 if (*str != '\0') \
347 printf ("%9s | %s\n", name, str)
348 HANDLE (TITLE, "title");
349 HANDLE (SOURCE, "source");
350 HANDLE (ADDRESS, "address");
351 HANDLE (CONTACT, "contact");
352 HANDLE (EMAIL, "email");
353 HANDLE (TEL, "telephone");
354 HANDLE (FAX, "fax");
355 HANDLE (LANGUAGE, "language");
356 HANDLE (TERRITORY, "territory");
357 HANDLE (AUDIENCE, "audience");
358 HANDLE (APPLICATION, "application");
359 HANDLE (ABBREVIATION, "abbreviation");
360 HANDLE (REVISION, "revision");
361 HANDLE (DATE, "date");
366 static void
367 print_LC_CTYPE (void *mapped, size_t size)
369 struct
371 unsigned int magic;
372 unsigned int nstrings;
373 unsigned int strindex[0];
374 } *filedata = mapped;
376 if (filedata->magic == LIMAGIC (LC_CTYPE)
377 && (sizeof *filedata
378 + (filedata->nstrings
379 * sizeof (unsigned int))
380 <= size))
382 const char *str;
384 str = ((char *) mapped
385 + filedata->strindex[_NL_ITEM_INDEX (_NL_CTYPE_CODESET_NAME)]);
386 if (*str != '\0')
387 printf (" codeset | %s\n", str);
392 /* Write the names of all available locales to stdout. We have some
393 sources of the information: the contents of the locale directory
394 and the locale.alias file. To avoid duplicates and print the
395 result is a reasonable order we put all entries is a search tree
396 and print them afterwards. */
397 static void
398 write_locales (void)
400 char linebuf[80];
401 void *all_data = NULL;
402 struct dirent **dirents;
403 int ndirents;
404 int cnt;
405 char *alias_path;
406 size_t alias_path_len;
407 char *entry;
408 int first_locale = 1;
410 #define PUT(name) tsearch (name, &all_data, \
411 (int (*) (const void *, const void *)) strcoll)
412 #define GET(name) tfind (name, &all_data, \
413 (int (*) (const void *, const void *)) strcoll)
415 /* `POSIX' locale is always available (POSIX.2 4.34.3). */
416 PUT ("POSIX");
417 /* And so is the "C" locale. */
418 PUT ("C");
420 memset (linebuf, '-', sizeof (linebuf) - 1);
421 linebuf[sizeof (linebuf) - 1] = '\0';
423 /* First scan the locale archive. */
424 if (write_archive_locales (&all_data, linebuf))
425 first_locale = 0;
427 /* Now we can look for all files in the directory. */
428 ndirents = scandir (LOCALEDIR, &dirents, select_dirs, alphasort);
429 for (cnt = 0; cnt < ndirents; ++cnt)
431 /* Test whether at least the LC_CTYPE data is there. Some
432 directories only contain translations. */
433 char buf[sizeof (LOCALEDIR) + strlen (dirents[cnt]->d_name)
434 + sizeof "/LC_IDENTIFICATION"];
435 char *enddir;
436 struct stat64 st;
438 stpcpy (enddir = stpcpy (stpcpy (stpcpy (buf, LOCALEDIR), "/"),
439 dirents[cnt]->d_name),
440 "/LC_IDENTIFICATION");
442 if (stat64 (buf, &st) == 0 && S_ISREG (st.st_mode))
444 if (verbose && GET (dirents[cnt]->d_name) == NULL)
446 /* Provide some nice output of all kinds of
447 information. */
448 int fd;
450 if (! first_locale)
451 putchar_unlocked ('\n');
452 first_locale = 0;
454 printf ("locale: %-15.15s directory: %.*s\n%s\n",
455 dirents[cnt]->d_name, (int) (enddir - buf), buf,
456 linebuf);
458 fd = open64 (buf, O_RDONLY);
459 if (fd != -1)
461 void *mapped = mmap64 (NULL, st.st_size, PROT_READ,
462 MAP_SHARED, fd, 0);
463 if (mapped != MAP_FAILED)
465 print_LC_IDENTIFICATION (mapped, st.st_size);
467 munmap (mapped, st.st_size);
470 close (fd);
472 /* Now try to get the charset information. */
473 strcpy (enddir, "/LC_CTYPE");
474 fd = open64 (buf, O_RDONLY);
475 if (fd != -1 && fstat64 (fd, &st) >= 0
476 && ((mapped = mmap64 (NULL, st.st_size, PROT_READ,
477 MAP_SHARED, fd, 0))
478 != MAP_FAILED))
480 print_LC_CTYPE (mapped, st.st_size);
482 munmap (mapped, st.st_size);
485 if (fd != -1)
486 close (fd);
490 /* If the verbose format is not selected we simply
491 collect the names. */
492 PUT (xstrdup (dirents[cnt]->d_name));
495 if (ndirents > 0)
496 free (dirents);
498 /* Now read the locale.alias files. */
499 if (argz_create_sep (LOCALE_ALIAS_PATH, ':', &alias_path, &alias_path_len))
500 error (1, errno, gettext ("while preparing output"));
502 entry = NULL;
503 while ((entry = argz_next (alias_path, alias_path_len, entry)))
505 static const char aliasfile[] = "/locale.alias";
506 FILE *fp;
507 char full_name[strlen (entry) + sizeof aliasfile];
509 stpcpy (stpcpy (full_name, entry), aliasfile);
510 fp = fopen (full_name, "rm");
511 if (fp == NULL)
512 /* Ignore non-existing files. */
513 continue;
515 /* No threads present. */
516 __fsetlocking (fp, FSETLOCKING_BYCALLER);
518 while (! feof_unlocked (fp))
520 /* It is a reasonable approach to use a fix buffer here
521 because
522 a) we are only interested in the first two fields
523 b) these fields must be usable as file names and so must
524 not be that long */
525 char buf[BUFSIZ];
526 char *alias;
527 char *value;
528 char *cp;
530 if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
531 /* EOF reached. */
532 break;
534 cp = buf;
535 /* Ignore leading white space. */
536 while (isspace (cp[0]) && cp[0] != '\n')
537 ++cp;
539 /* A leading '#' signals a comment line. */
540 if (cp[0] != '\0' && cp[0] != '#' && cp[0] != '\n')
542 alias = cp++;
543 while (cp[0] != '\0' && !isspace (cp[0]))
544 ++cp;
545 /* Terminate alias name. */
546 if (cp[0] != '\0')
547 *cp++ = '\0';
549 /* Now look for the beginning of the value. */
550 while (isspace (cp[0]))
551 ++cp;
553 if (cp[0] != '\0')
555 value = cp++;
556 while (cp[0] != '\0' && !isspace (cp[0]))
557 ++cp;
558 /* Terminate value. */
559 if (cp[0] == '\n')
561 /* This has to be done to make the following
562 test for the end of line possible. We are
563 looking for the terminating '\n' which do not
564 overwrite here. */
565 *cp++ = '\0';
566 *cp = '\n';
568 else if (cp[0] != '\0')
569 *cp++ = '\0';
571 /* Add the alias. */
572 if (! verbose && GET (value) != NULL)
573 PUT (xstrdup (alias));
577 /* Possibly not the whole line fits into the buffer.
578 Ignore the rest of the line. */
579 while (strchr (cp, '\n') == NULL)
581 cp = buf;
582 if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
583 /* Make sure the inner loop will be left. The outer
584 loop will exit at the `feof' test. */
585 *cp = '\n';
589 fclose (fp);
592 if (! verbose)
594 twalk (all_data, print_names);
599 struct nameent
601 char *name;
602 uint32_t locrec_offset;
606 static int
607 nameentcmp (const void *a, const void *b)
609 return strcoll (((const struct nameent *) a)->name,
610 ((const struct nameent *) b)->name);
614 static int
615 write_archive_locales (void **all_datap, char *linebuf)
617 struct stat64 st;
618 void *all_data = *all_datap;
619 size_t len = 0;
620 struct locarhead *head;
621 struct namehashent *namehashtab;
622 char *addr = MAP_FAILED;
623 int fd, ret = 0;
624 uint32_t cnt;
626 fd = open64 (ARCHIVE_NAME, O_RDONLY);
627 if (fd < 0)
628 return 0;
630 if (fstat64 (fd, &st) < 0 || st.st_size < sizeof (*head))
631 goto error_out;
633 len = st.st_size;
634 addr = mmap64 (NULL, len, PROT_READ, MAP_SHARED, fd, 0);
635 if (addr == MAP_FAILED)
636 goto error_out;
638 head = (struct locarhead *) addr;
639 if (head->namehash_offset + head->namehash_size > len
640 || head->string_offset + head->string_size > len
641 || head->locrectab_offset + head->locrectab_size > len
642 || head->sumhash_offset + head->sumhash_size > len)
643 goto error_out;
645 namehashtab = (struct namehashent *) (addr + head->namehash_offset);
646 if (! verbose)
648 for (cnt = 0; cnt < head->namehash_size; ++cnt)
649 if (namehashtab[cnt].locrec_offset != 0)
651 PUT (xstrdup (addr + namehashtab[cnt].name_offset));
652 ++ret;
655 else
657 struct nameent *names;
658 uint32_t used;
660 names = (struct nameent *) xmalloc (head->namehash_used
661 * sizeof (struct nameent));
662 for (cnt = used = 0; cnt < head->namehash_size; ++cnt)
663 if (namehashtab[cnt].locrec_offset != 0)
665 names[used].name = addr + namehashtab[cnt].name_offset;
666 names[used++].locrec_offset = namehashtab[cnt].locrec_offset;
669 /* Sort the names. */
670 qsort (names, used, sizeof (struct nameent), nameentcmp);
672 for (cnt = 0; cnt < used; ++cnt)
674 struct locrecent *locrec;
676 PUT (xstrdup (names[cnt].name));
678 if (cnt)
679 putchar_unlocked ('\n');
681 printf ("locale: %-15.15s archive: " ARCHIVE_NAME "\n%s\n",
682 names[cnt].name, linebuf);
684 locrec = (struct locrecent *) (addr + names[cnt].locrec_offset);
686 print_LC_IDENTIFICATION (addr
687 + locrec->record[LC_IDENTIFICATION].offset,
688 locrec->record[LC_IDENTIFICATION].len);
690 print_LC_CTYPE (addr + locrec->record[LC_CTYPE].offset,
691 locrec->record[LC_CTYPE].len);
694 ret = used;
697 error_out:
698 if (addr != MAP_FAILED)
699 munmap (addr, len);
700 close (fd);
701 *all_datap = all_data;
702 return ret;
706 /* Write the names of all available character maps to stdout. */
707 static void
708 write_charmaps (void)
710 void *all_data = NULL;
711 CHARMAP_DIR *dir;
712 const char *dirent;
714 /* Look for all files in the charmap directory. */
715 dir = charmap_opendir (CHARMAP_PATH);
716 if (dir == NULL)
717 return;
719 while ((dirent = charmap_readdir (dir)) != NULL)
721 char **aliases;
722 char **p;
724 PUT (xstrdup (dirent));
726 aliases = charmap_aliases (CHARMAP_PATH, dirent);
728 #if 0
729 /* Add the code_set_name and the aliases. */
730 for (p = aliases; *p; p++)
731 PUT (xstrdup (*p));
732 #else
733 /* Add the code_set_name only. Most aliases are obsolete. */
734 p = aliases;
735 if (*p)
736 PUT (xstrdup (*p));
737 #endif
739 charmap_free_aliases (aliases);
742 charmap_closedir (dir);
744 twalk (all_data, print_names);
748 /* We have to show the contents of the environments determining the
749 locale. */
750 static void
751 show_locale_vars (void)
753 size_t cat_no;
754 const char *lcall = getenv ("LC_ALL");
755 const char *lang = getenv ("LANG") ? : "";
757 auto void get_source (const char *name);
759 void get_source (const char *name)
761 char *val = getenv (name);
763 if ((lcall ?: "")[0] != '\0' || val == NULL)
764 printf ("%s=\"%s\"\n", name,
765 (lcall ?: "")[0] ? lcall : (lang ?: "")[0] ? lang : "POSIX");
766 else
767 printf ("%s=%s\n", name, val);
770 /* LANG has to be the first value. */
771 printf ("LANG=%s\n", lang);
773 /* Now all categories in an unspecified order. */
774 for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
775 if (cat_no != LC_ALL)
776 get_source (category[cat_no].name);
778 /* The last is the LC_ALL value. */
779 printf ("LC_ALL=%s\n", lcall ? : "");
783 /* Show the information request for NAME. */
784 static void
785 show_info (const char *name)
787 size_t cat_no;
789 auto void print_item (struct cat_item *item);
791 void print_item (struct cat_item *item)
793 switch (item->value_type)
795 case string:
796 if (show_keyword_name)
797 printf ("%s=\"", item->name);
798 fputs (nl_langinfo (item->item_id) ? : "", stdout);
799 if (show_keyword_name)
800 putchar ('"');
801 putchar ('\n');
802 break;
803 case stringarray:
805 int cnt;
806 const char *val;
808 if (show_keyword_name)
809 printf ("%s=\"", item->name);
811 for (cnt = 0; cnt < item->max - 1; ++cnt)
813 val = nl_langinfo (item->item_id + cnt);
814 if (val != NULL)
815 fputs (val, stdout);
816 putchar (';');
819 val = nl_langinfo (item->item_id + cnt);
820 if (val != NULL)
821 fputs (val, stdout);
823 if (show_keyword_name)
824 putchar ('"');
825 putchar ('\n');
827 break;
828 case stringlist:
830 int first = 1;
831 const char *val = nl_langinfo (item->item_id) ? : "";
832 int cnt;
834 if (show_keyword_name)
835 printf ("%s=", item->name);
837 for (cnt = 0; cnt < item->max && *val != '\0'; ++cnt)
839 printf ("%s%s%s%s", first ? "" : ";",
840 show_keyword_name ? "\"" : "", val,
841 show_keyword_name ? "\"" : "");
842 val = strchr (val, '\0') + 1;
843 first = 0;
845 putchar ('\n');
847 break;
848 case byte:
850 const char *val = nl_langinfo (item->item_id);
852 if (show_keyword_name)
853 printf ("%s=", item->name);
855 if (val != NULL)
856 printf ("%d", *val == '\177' ? -1 : *val);
857 putchar ('\n');
859 break;
860 case bytearray:
862 const char *val = nl_langinfo (item->item_id);
863 int cnt = val ? strlen (val) : 0;
865 if (show_keyword_name)
866 printf ("%s=", item->name);
868 while (cnt > 1)
870 printf ("%d;", *val == '\177' ? -1 : *val);
871 --cnt;
872 ++val;
875 printf ("%d\n", cnt == 0 || *val == '\177' ? -1 : *val);
877 break;
878 case word:
880 union { unsigned int word; char *string; } val;
881 val.string = nl_langinfo (item->item_id);
882 if (show_keyword_name)
883 printf ("%s=", item->name);
885 printf ("%d\n", val.word);
887 break;
888 case wstring:
889 case wstringarray:
890 case wstringlist:
891 /* We don't print wide character information since the same
892 information is available in a multibyte string. */
893 default:
894 break;
899 for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
900 if (cat_no != LC_ALL)
902 size_t item_no;
904 if (strcmp (name, category[cat_no].name) == 0)
905 /* Print the whole category. */
907 if (show_category_name != 0)
908 puts (category[cat_no].name);
910 for (item_no = 0; item_no < category[cat_no].number; ++item_no)
911 print_item (&category[cat_no].item_desc[item_no]);
913 return;
916 for (item_no = 0; item_no < category[cat_no].number; ++item_no)
917 if (strcmp (name, category[cat_no].item_desc[item_no].name) == 0)
919 if (show_category_name != 0)
920 puts (category[cat_no].name);
922 print_item (&category[cat_no].item_desc[item_no]);
923 return;
927 /* The name is not a standard one.
928 For testing and perhaps advanced use allow some more symbols. */
929 locale_special (name, show_category_name, show_keyword_name);