(hsearch_r): Add back ensurance that hval is not zero.
[glibc/history.git] / locale / programs / locale.c
blob77262b7d1c3c49e520ef95b4693db607c4127a05
1 /* Implementation of the locale program according to POSIX 9945-2.
2 Copyright (C) 1995-1997, 1999-2008, 2009 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.");
89 /* Strings for arguments in help texts. */
90 static const char args_doc[] = N_("NAME\n[-a|-m]");
92 /* Prototype for option handler. */
93 static error_t parse_opt (int key, char *arg, struct argp_state *state);
95 /* Function to print some extra text in the help message. */
96 static char *more_help (int key, const char *text, void *input);
98 /* Data structure to communicate with argp functions. */
99 static struct argp argp =
101 options, parse_opt, args_doc, doc, NULL, more_help
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 static char *
272 more_help (int key, const char *text, void *input)
274 switch (key)
276 case ARGP_KEY_HELP_EXTRA:
277 /* We print some extra information. */
278 return strdup (gettext ("\
279 For bug reporting instructions, please see:\n\
280 <http://www.gnu.org/software/libc/bugs.html>.\n"));
281 default:
282 break;
284 return (char *) text;
288 /* Print the version information. */
289 static void
290 print_version (FILE *stream, struct argp_state *state)
292 fprintf (stream, "locale (GNU %s) %s\n", PACKAGE, VERSION);
293 fprintf (stream, gettext ("\
294 Copyright (C) %s Free Software Foundation, Inc.\n\
295 This is free software; see the source for copying conditions. There is NO\n\
296 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
297 "), "2009");
298 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
302 /* Simple action function which prints arguments as strings. */
303 static void
304 print_names (const void *nodep, VISIT value, int level)
306 if (value == postorder || value == leaf)
307 puts (*(char **) nodep);
311 static int
312 select_dirs (const struct dirent *dirent)
314 int result = 0;
316 if (strcmp (dirent->d_name, ".") != 0 && strcmp (dirent->d_name, "..") != 0)
318 mode_t mode = 0;
320 #ifdef _DIRENT_HAVE_D_TYPE
321 if (dirent->d_type != DT_UNKNOWN && dirent->d_type != DT_LNK)
322 mode = DTTOIF (dirent->d_type);
323 else
324 #endif
326 struct stat64 st;
327 char buf[sizeof (LOCALEDIR) + strlen (dirent->d_name) + 1];
329 stpcpy (stpcpy (stpcpy (buf, LOCALEDIR), "/"), dirent->d_name);
331 if (stat64 (buf, &st) == 0)
332 mode = st.st_mode;
335 result = S_ISDIR (mode);
338 return result;
342 static void
343 print_LC_IDENTIFICATION (void *mapped, size_t size)
345 /* Read the information from the file. */
346 struct
348 unsigned int magic;
349 unsigned int nstrings;
350 unsigned int strindex[0];
351 } *filedata = mapped;
353 if (filedata->magic == LIMAGIC (LC_IDENTIFICATION)
354 && (sizeof *filedata
355 + (filedata->nstrings
356 * sizeof (unsigned int))
357 <= size))
359 const char *str;
361 #define HANDLE(idx, name) \
362 str = ((char *) mapped \
363 + filedata->strindex[_NL_ITEM_INDEX (_NL_IDENTIFICATION_##idx)]); \
364 if (*str != '\0') \
365 printf ("%9s | %s\n", name, str)
366 HANDLE (TITLE, "title");
367 HANDLE (SOURCE, "source");
368 HANDLE (ADDRESS, "address");
369 HANDLE (CONTACT, "contact");
370 HANDLE (EMAIL, "email");
371 HANDLE (TEL, "telephone");
372 HANDLE (FAX, "fax");
373 HANDLE (LANGUAGE, "language");
374 HANDLE (TERRITORY, "territory");
375 HANDLE (AUDIENCE, "audience");
376 HANDLE (APPLICATION, "application");
377 HANDLE (ABBREVIATION, "abbreviation");
378 HANDLE (REVISION, "revision");
379 HANDLE (DATE, "date");
384 static void
385 print_LC_CTYPE (void *mapped, size_t size)
387 struct
389 unsigned int magic;
390 unsigned int nstrings;
391 unsigned int strindex[0];
392 } *filedata = mapped;
394 if (filedata->magic == LIMAGIC (LC_CTYPE)
395 && (sizeof *filedata
396 + (filedata->nstrings
397 * sizeof (unsigned int))
398 <= size))
400 const char *str;
402 str = ((char *) mapped
403 + filedata->strindex[_NL_ITEM_INDEX (_NL_CTYPE_CODESET_NAME)]);
404 if (*str != '\0')
405 printf (" codeset | %s\n", str);
410 /* Write the names of all available locales to stdout. We have some
411 sources of the information: the contents of the locale directory
412 and the locale.alias file. To avoid duplicates and print the
413 result is a reasonable order we put all entries is a search tree
414 and print them afterwards. */
415 static void
416 write_locales (void)
418 char linebuf[80];
419 void *all_data = NULL;
420 struct dirent **dirents;
421 int ndirents;
422 int cnt;
423 char *alias_path;
424 size_t alias_path_len;
425 char *entry;
426 int first_locale = 1;
428 #define PUT(name) tsearch (name, &all_data, \
429 (int (*) (const void *, const void *)) strcoll)
430 #define GET(name) tfind (name, &all_data, \
431 (int (*) (const void *, const void *)) strcoll)
433 /* `POSIX' locale is always available (POSIX.2 4.34.3). */
434 PUT ("POSIX");
435 /* And so is the "C" locale. */
436 PUT ("C");
438 memset (linebuf, '-', sizeof (linebuf) - 1);
439 linebuf[sizeof (linebuf) - 1] = '\0';
441 /* First scan the locale archive. */
442 if (write_archive_locales (&all_data, linebuf))
443 first_locale = 0;
445 /* Now we can look for all files in the directory. */
446 ndirents = scandir (LOCALEDIR, &dirents, select_dirs, alphasort);
447 for (cnt = 0; cnt < ndirents; ++cnt)
449 /* Test whether at least the LC_CTYPE data is there. Some
450 directories only contain translations. */
451 char buf[sizeof (LOCALEDIR) + strlen (dirents[cnt]->d_name)
452 + sizeof "/LC_IDENTIFICATION"];
453 char *enddir;
454 struct stat64 st;
456 stpcpy (enddir = stpcpy (stpcpy (stpcpy (buf, LOCALEDIR), "/"),
457 dirents[cnt]->d_name),
458 "/LC_IDENTIFICATION");
460 if (stat64 (buf, &st) == 0 && S_ISREG (st.st_mode))
462 if (verbose && GET (dirents[cnt]->d_name) == NULL)
464 /* Provide some nice output of all kinds of
465 information. */
466 int fd;
468 if (! first_locale)
469 putchar_unlocked ('\n');
470 first_locale = 0;
472 printf ("locale: %-15.15s directory: %.*s\n%s\n",
473 dirents[cnt]->d_name, (int) (enddir - buf), buf,
474 linebuf);
476 fd = open64 (buf, O_RDONLY);
477 if (fd != -1)
479 void *mapped = mmap64 (NULL, st.st_size, PROT_READ,
480 MAP_SHARED, fd, 0);
481 if (mapped != MAP_FAILED)
483 print_LC_IDENTIFICATION (mapped, st.st_size);
485 munmap (mapped, st.st_size);
488 close (fd);
490 /* Now try to get the charset information. */
491 strcpy (enddir, "/LC_CTYPE");
492 fd = open64 (buf, O_RDONLY);
493 if (fd != -1 && fstat64 (fd, &st) >= 0
494 && ((mapped = mmap64 (NULL, st.st_size, PROT_READ,
495 MAP_SHARED, fd, 0))
496 != MAP_FAILED))
498 print_LC_CTYPE (mapped, st.st_size);
500 munmap (mapped, st.st_size);
503 if (fd != -1)
504 close (fd);
508 /* If the verbose format is not selected we simply
509 collect the names. */
510 PUT (xstrdup (dirents[cnt]->d_name));
513 if (ndirents > 0)
514 free (dirents);
516 /* Now read the locale.alias files. */
517 if (argz_create_sep (LOCALE_ALIAS_PATH, ':', &alias_path, &alias_path_len))
518 error (1, errno, gettext ("while preparing output"));
520 entry = NULL;
521 while ((entry = argz_next (alias_path, alias_path_len, entry)))
523 static const char aliasfile[] = "/locale.alias";
524 FILE *fp;
525 char full_name[strlen (entry) + sizeof aliasfile];
527 stpcpy (stpcpy (full_name, entry), aliasfile);
528 fp = fopen (full_name, "rm");
529 if (fp == NULL)
530 /* Ignore non-existing files. */
531 continue;
533 /* No threads present. */
534 __fsetlocking (fp, FSETLOCKING_BYCALLER);
536 while (! feof_unlocked (fp))
538 /* It is a reasonable approach to use a fix buffer here
539 because
540 a) we are only interested in the first two fields
541 b) these fields must be usable as file names and so must
542 not be that long */
543 char buf[BUFSIZ];
544 char *alias;
545 char *value;
546 char *cp;
548 if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
549 /* EOF reached. */
550 break;
552 cp = buf;
553 /* Ignore leading white space. */
554 while (isspace (cp[0]) && cp[0] != '\n')
555 ++cp;
557 /* A leading '#' signals a comment line. */
558 if (cp[0] != '\0' && cp[0] != '#' && cp[0] != '\n')
560 alias = cp++;
561 while (cp[0] != '\0' && !isspace (cp[0]))
562 ++cp;
563 /* Terminate alias name. */
564 if (cp[0] != '\0')
565 *cp++ = '\0';
567 /* Now look for the beginning of the value. */
568 while (isspace (cp[0]))
569 ++cp;
571 if (cp[0] != '\0')
573 value = cp++;
574 while (cp[0] != '\0' && !isspace (cp[0]))
575 ++cp;
576 /* Terminate value. */
577 if (cp[0] == '\n')
579 /* This has to be done to make the following
580 test for the end of line possible. We are
581 looking for the terminating '\n' which do not
582 overwrite here. */
583 *cp++ = '\0';
584 *cp = '\n';
586 else if (cp[0] != '\0')
587 *cp++ = '\0';
589 /* Add the alias. */
590 if (! verbose && GET (value) != NULL)
591 PUT (xstrdup (alias));
595 /* Possibly not the whole line fits into the buffer.
596 Ignore the rest of the line. */
597 while (strchr (cp, '\n') == NULL)
599 cp = buf;
600 if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
601 /* Make sure the inner loop will be left. The outer
602 loop will exit at the `feof' test. */
603 *cp = '\n';
607 fclose (fp);
610 if (! verbose)
612 twalk (all_data, print_names);
617 struct nameent
619 char *name;
620 uint32_t locrec_offset;
624 static int
625 nameentcmp (const void *a, const void *b)
627 return strcoll (((const struct nameent *) a)->name,
628 ((const struct nameent *) b)->name);
632 static int
633 write_archive_locales (void **all_datap, char *linebuf)
635 struct stat64 st;
636 void *all_data = *all_datap;
637 size_t len = 0;
638 struct locarhead *head;
639 struct namehashent *namehashtab;
640 char *addr = MAP_FAILED;
641 int fd, ret = 0;
642 uint32_t cnt;
644 fd = open64 (ARCHIVE_NAME, O_RDONLY);
645 if (fd < 0)
646 return 0;
648 if (fstat64 (fd, &st) < 0 || st.st_size < sizeof (*head))
649 goto error_out;
651 len = st.st_size;
652 addr = mmap64 (NULL, len, PROT_READ, MAP_SHARED, fd, 0);
653 if (addr == MAP_FAILED)
654 goto error_out;
656 head = (struct locarhead *) addr;
657 if (head->namehash_offset + head->namehash_size > len
658 || head->string_offset + head->string_size > len
659 || head->locrectab_offset + head->locrectab_size > len
660 || head->sumhash_offset + head->sumhash_size > len)
661 goto error_out;
663 namehashtab = (struct namehashent *) (addr + head->namehash_offset);
664 if (! verbose)
666 for (cnt = 0; cnt < head->namehash_size; ++cnt)
667 if (namehashtab[cnt].locrec_offset != 0)
669 PUT (xstrdup (addr + namehashtab[cnt].name_offset));
670 ++ret;
673 else
675 struct nameent *names;
676 uint32_t used;
678 names = (struct nameent *) xmalloc (head->namehash_used
679 * sizeof (struct nameent));
680 for (cnt = used = 0; cnt < head->namehash_size; ++cnt)
681 if (namehashtab[cnt].locrec_offset != 0)
683 names[used].name = addr + namehashtab[cnt].name_offset;
684 names[used++].locrec_offset = namehashtab[cnt].locrec_offset;
687 /* Sort the names. */
688 qsort (names, used, sizeof (struct nameent), nameentcmp);
690 for (cnt = 0; cnt < used; ++cnt)
692 struct locrecent *locrec;
694 PUT (xstrdup (names[cnt].name));
696 if (cnt)
697 putchar_unlocked ('\n');
699 printf ("locale: %-15.15s archive: " ARCHIVE_NAME "\n%s\n",
700 names[cnt].name, linebuf);
702 locrec = (struct locrecent *) (addr + names[cnt].locrec_offset);
704 print_LC_IDENTIFICATION (addr
705 + locrec->record[LC_IDENTIFICATION].offset,
706 locrec->record[LC_IDENTIFICATION].len);
708 print_LC_CTYPE (addr + locrec->record[LC_CTYPE].offset,
709 locrec->record[LC_CTYPE].len);
712 ret = used;
715 error_out:
716 if (addr != MAP_FAILED)
717 munmap (addr, len);
718 close (fd);
719 *all_datap = all_data;
720 return ret;
724 /* Write the names of all available character maps to stdout. */
725 static void
726 write_charmaps (void)
728 void *all_data = NULL;
729 CHARMAP_DIR *dir;
730 const char *dirent;
732 /* Look for all files in the charmap directory. */
733 dir = charmap_opendir (CHARMAP_PATH);
734 if (dir == NULL)
735 return;
737 while ((dirent = charmap_readdir (dir)) != NULL)
739 char **aliases;
740 char **p;
742 PUT (xstrdup (dirent));
744 aliases = charmap_aliases (CHARMAP_PATH, dirent);
746 #if 0
747 /* Add the code_set_name and the aliases. */
748 for (p = aliases; *p; p++)
749 PUT (xstrdup (*p));
750 #else
751 /* Add the code_set_name only. Most aliases are obsolete. */
752 p = aliases;
753 if (*p)
754 PUT (xstrdup (*p));
755 #endif
757 charmap_free_aliases (aliases);
760 charmap_closedir (dir);
762 twalk (all_data, print_names);
766 /* We have to show the contents of the environments determining the
767 locale. */
768 static void
769 show_locale_vars (void)
771 size_t cat_no;
772 const char *lcall = getenv ("LC_ALL");
773 const char *lang = getenv ("LANG") ? : "";
775 auto void get_source (const char *name);
777 void get_source (const char *name)
779 char *val = getenv (name);
781 if ((lcall ?: "")[0] != '\0' || val == NULL)
782 printf ("%s=\"%s\"\n", name,
783 (lcall ?: "")[0] ? lcall : (lang ?: "")[0] ? lang : "POSIX");
784 else
785 printf ("%s=%s\n", name, val);
788 /* LANG has to be the first value. */
789 printf ("LANG=%s\n", lang);
791 /* Now all categories in an unspecified order. */
792 for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
793 if (cat_no != LC_ALL)
794 get_source (category[cat_no].name);
796 /* The last is the LC_ALL value. */
797 printf ("LC_ALL=%s\n", lcall ? : "");
801 /* Show the information request for NAME. */
802 static void
803 show_info (const char *name)
805 size_t cat_no;
807 auto void print_item (struct cat_item *item);
809 void print_item (struct cat_item *item)
811 switch (item->value_type)
813 case string:
814 if (show_keyword_name)
815 printf ("%s=\"", item->name);
816 fputs (nl_langinfo (item->item_id) ? : "", stdout);
817 if (show_keyword_name)
818 putchar ('"');
819 putchar ('\n');
820 break;
821 case stringarray:
823 int cnt;
824 const char *val;
826 if (show_keyword_name)
827 printf ("%s=\"", item->name);
829 for (cnt = 0; cnt < item->max - 1; ++cnt)
831 val = nl_langinfo (item->item_id + cnt);
832 if (val != NULL)
833 fputs (val, stdout);
834 putchar (';');
837 val = nl_langinfo (item->item_id + cnt);
838 if (val != NULL)
839 fputs (val, stdout);
841 if (show_keyword_name)
842 putchar ('"');
843 putchar ('\n');
845 break;
846 case stringlist:
848 int first = 1;
849 const char *val = nl_langinfo (item->item_id) ? : "";
850 int cnt;
852 if (show_keyword_name)
853 printf ("%s=", item->name);
855 for (cnt = 0; cnt < item->max && *val != '\0'; ++cnt)
857 printf ("%s%s%s%s", first ? "" : ";",
858 show_keyword_name ? "\"" : "", val,
859 show_keyword_name ? "\"" : "");
860 val = strchr (val, '\0') + 1;
861 first = 0;
863 putchar ('\n');
865 break;
866 case byte:
868 const char *val = nl_langinfo (item->item_id);
870 if (show_keyword_name)
871 printf ("%s=", item->name);
873 if (val != NULL)
874 printf ("%d", *val == '\177' ? -1 : *val);
875 putchar ('\n');
877 break;
878 case bytearray:
880 const char *val = nl_langinfo (item->item_id);
881 int cnt = val ? strlen (val) : 0;
883 if (show_keyword_name)
884 printf ("%s=", item->name);
886 while (cnt > 1)
888 printf ("%d;", *val == '\177' ? -1 : *val);
889 --cnt;
890 ++val;
893 printf ("%d\n", cnt == 0 || *val == '\177' ? -1 : *val);
895 break;
896 case word:
898 union { unsigned int word; char *string; } val;
899 val.string = nl_langinfo (item->item_id);
900 if (show_keyword_name)
901 printf ("%s=", item->name);
903 printf ("%d\n", val.word);
905 break;
906 case wstring:
907 case wstringarray:
908 case wstringlist:
909 /* We don't print wide character information since the same
910 information is available in a multibyte string. */
911 default:
912 break;
917 for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
918 if (cat_no != LC_ALL)
920 size_t item_no;
922 if (strcmp (name, category[cat_no].name) == 0)
923 /* Print the whole category. */
925 if (show_category_name != 0)
926 puts (category[cat_no].name);
928 for (item_no = 0; item_no < category[cat_no].number; ++item_no)
929 print_item (&category[cat_no].item_desc[item_no]);
931 return;
934 for (item_no = 0; item_no < category[cat_no].number; ++item_no)
935 if (strcmp (name, category[cat_no].item_desc[item_no].name) == 0)
937 if (show_category_name != 0)
938 puts (category[cat_no].name);
940 print_item (&category[cat_no].item_desc[item_no]);
941 return;
945 /* The name is not a standard one.
946 For testing and perhaps advanced use allow some more symbols. */
947 locale_special (name, show_category_name, show_keyword_name);