]> sourceware.org Git - glibc.git/blob - locale/programs/locale.c
Update.
[glibc.git] / locale / programs / locale.c
1 /* Implementation of the locale program according to POSIX 9945-2.
2 Copyright (C) 1995-1997, 1999-2002, 2003 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1995.
5
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.
10
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.
15
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. */
20
21 #ifdef HAVE_CONFIG_H
22 # include <config.h>
23 #endif
24
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>
43
44 #include "localeinfo.h"
45 #include "charmap-dir.h"
46 #include "../locarchive.h"
47
48 extern void *xmalloc (size_t __n);
49 extern char *xstrdup (const char *__str);
50
51 #define ARCHIVE_NAME LOCALEDIR "/locale-archive"
52
53 /* If set print the name of the category. */
54 static int show_category_name;
55
56 /* If set print the name of the item. */
57 static int show_keyword_name;
58
59 /* Print names of all available locales. */
60 static int do_all;
61
62 /* Print names of all available character maps. */
63 static int do_charmaps = 0;
64
65 /* Nonzero if verbose output is wanted. */
66 static int verbose;
67
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;
71
72 /* Definitions of arguments for argp functions. */
73 static const struct argp_option options[] =
74 {
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 }
85 };
86
87 /* Short description of program. */
88 static const char doc[] = N_("Get locale-specific information.");
89
90 /* Strings for arguments in help texts. */
91 static const char args_doc[] = N_("NAME\n[-a|-m]");
92
93 /* Prototype for option handler. */
94 static error_t parse_opt (int key, char *arg, struct argp_state *state);
95
96 /* Function to print some extra text in the help message. */
97 static char *more_help (int key, const char *text, void *input);
98
99 /* Data structure to communicate with argp functions. */
100 static struct argp argp =
101 {
102 options, parse_opt, args_doc, doc, NULL, more_help
103 };
104
105
106 /* We don't have these constants defined because we don't use them. Give
107 default values. */
108 #define CTYPE_MB_CUR_MIN 0
109 #define CTYPE_MB_CUR_MAX 0
110 #define CTYPE_HASH_SIZE 0
111 #define CTYPE_HASH_LAYERS 0
112 #define CTYPE_CLASS 0
113 #define CTYPE_TOUPPER_EB 0
114 #define CTYPE_TOLOWER_EB 0
115 #define CTYPE_TOUPPER_EL 0
116 #define CTYPE_TOLOWER_EL 0
117
118 /* Definition of the data structure which represents a category and its
119 items. */
120 struct category
121 {
122 int cat_id;
123 const char *name;
124 size_t number;
125 struct cat_item
126 {
127 int item_id;
128 const char *name;
129 enum { std, opt } status;
130 enum value_type value_type;
131 int min;
132 int max;
133 } *item_desc;
134 };
135
136 /* Simple helper macro. */
137 #define NELEMS(arr) ((sizeof (arr)) / (sizeof (arr[0])))
138
139 /* For some tricky stuff. */
140 #define NO_PAREN(Item, More...) Item, ## More
141
142 /* We have all categories defined in `categories.def'. Now construct
143 the description and data structure used for all categories. */
144 #define DEFINE_ELEMENT(Item, More...) { Item, ## More },
145 #define DEFINE_CATEGORY(category, name, items, postload) \
146 static struct cat_item category##_desc[] = \
147 { \
148 NO_PAREN items \
149 };
150
151 #include "categories.def"
152 #undef DEFINE_CATEGORY
153
154 static struct category category[] =
155 {
156 #define DEFINE_CATEGORY(category, name, items, postload) \
157 [category] = { _NL_NUM_##category, name, NELEMS (category##_desc), \
158 category##_desc },
159 #include "categories.def"
160 #undef DEFINE_CATEGORY
161 };
162 #define NCATEGORIES NELEMS (category)
163
164
165 /* Automatically set variable. */
166 extern const char *__progname;
167
168 /* helper function for extended name handling. */
169 extern void locale_special (const char *name, int show_category_name,
170 int show_keyword_name);
171
172 /* Prototypes for local functions. */
173 static void print_LC_IDENTIFICATION (void *mapped, size_t size);
174 static void print_LC_CTYPE (void *mapped, size_t size);
175 static void write_locales (void);
176 static int nameentcmp (const void *a, const void *b);
177 static int write_archive_locales (void **all_datap, char *linebuf);
178 static void write_charmaps (void);
179 static void show_locale_vars (void);
180 static void show_info (const char *name);
181
182
183 int
184 main (int argc, char *argv[])
185 {
186 int remaining;
187
188 /* Set initial values for global variables. */
189 show_category_name = 0;
190 show_keyword_name = 0;
191
192 /* Set locale. Do not set LC_ALL because the other categories must
193 not be affected (according to POSIX.2). */
194 setlocale (LC_CTYPE, "");
195 setlocale (LC_MESSAGES, "");
196
197 /* Initialize the message catalog. */
198 textdomain (PACKAGE);
199
200 /* Parse and process arguments. */
201 argp_parse (&argp, argc, argv, 0, &remaining, NULL);
202
203 /* `-a' requests the names of all available locales. */
204 if (do_all != 0)
205 {
206 setlocale (LC_COLLATE, "");
207 write_locales ();
208 exit (EXIT_SUCCESS);
209 }
210
211 /* `m' requests the names of all available charmaps. The names can be
212 used for the -f argument to localedef(1). */
213 if (do_charmaps != 0)
214 {
215 write_charmaps ();
216 exit (EXIT_SUCCESS);
217 }
218
219 /* Specific information about the current locale are requested.
220 Change to this locale now. */
221 setlocale (LC_ALL, "");
222
223 /* If no real argument is given we have to print the contents of the
224 current locale definition variables. These are LANG and the LC_*. */
225 if (remaining == argc && show_keyword_name == 0 && show_category_name == 0)
226 {
227 show_locale_vars ();
228 exit (EXIT_SUCCESS);
229 }
230
231 /* Process all given names. */
232 while (remaining < argc)
233 show_info (argv[remaining++]);
234
235 exit (EXIT_SUCCESS);
236 }
237
238
239 /* Handle program arguments. */
240 static error_t
241 parse_opt (int key, char *arg, struct argp_state *state)
242 {
243 switch (key)
244 {
245 case 'a':
246 do_all = 1;
247 break;
248 case 'c':
249 show_category_name = 1;
250 break;
251 case 'm':
252 do_charmaps = 1;
253 break;
254 case 'k':
255 show_keyword_name = 1;
256 break;
257 case 'v':
258 verbose = 1;
259 break;
260 default:
261 return ARGP_ERR_UNKNOWN;
262 }
263 return 0;
264 }
265
266
267 static char *
268 more_help (int key, const char *text, void *input)
269 {
270 switch (key)
271 {
272 case ARGP_KEY_HELP_EXTRA:
273 /* We print some extra information. */
274 return xstrdup (gettext ("\
275 Report bugs using the `glibcbug' script to <bugs@gnu.org>.\n"));
276 default:
277 break;
278 }
279 return (char *) text;
280 }
281
282 /* Print the version information. */
283 static void
284 print_version (FILE *stream, struct argp_state *state)
285 {
286 fprintf (stream, "locale (GNU %s) %s\n", PACKAGE, VERSION);
287 fprintf (stream, gettext ("\
288 Copyright (C) %s Free Software Foundation, Inc.\n\
289 This is free software; see the source for copying conditions. There is NO\n\
290 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
291 "), "2003");
292 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
293 }
294
295
296 /* Simple action function which prints arguments as strings. */
297 static void
298 print_names (const void *nodep, VISIT value, int level)
299 {
300 if (value == postorder || value == leaf)
301 puts (*(char **) nodep);
302 }
303
304
305 static int
306 select_dirs (const struct dirent *dirent)
307 {
308 int result = 0;
309
310 if (strcmp (dirent->d_name, ".") != 0 && strcmp (dirent->d_name, "..") != 0)
311 {
312 mode_t mode = 0;
313
314 #ifdef _DIRENT_HAVE_D_TYPE
315 if (dirent->d_type != DT_UNKNOWN && dirent->d_type != DT_LNK)
316 mode = DTTOIF (dirent->d_type);
317 else
318 #endif
319 {
320 struct stat64 st;
321 char buf[sizeof (LOCALEDIR) + strlen (dirent->d_name) + 1];
322
323 stpcpy (stpcpy (stpcpy (buf, LOCALEDIR), "/"), dirent->d_name);
324
325 if (stat64 (buf, &st) == 0)
326 mode = st.st_mode;
327 }
328
329 result = S_ISDIR (mode);
330 }
331
332 return result;
333 }
334
335
336 static void
337 print_LC_IDENTIFICATION (void *mapped, size_t size)
338 {
339 /* Read the information from the file. */
340 struct
341 {
342 unsigned int magic;
343 unsigned int nstrings;
344 unsigned int strindex[0];
345 } *filedata = mapped;
346
347 if (filedata->magic == LIMAGIC (LC_IDENTIFICATION)
348 && (sizeof *filedata
349 + (filedata->nstrings
350 * sizeof (unsigned int))
351 <= size))
352 {
353 const char *str;
354
355 #define HANDLE(idx, name) \
356 str = ((char *) mapped \
357 + filedata->strindex[_NL_ITEM_INDEX (_NL_IDENTIFICATION_##idx)]); \
358 if (*str != '\0') \
359 printf ("%9s | %s\n", name, str)
360 HANDLE (TITLE, "title");
361 HANDLE (SOURCE, "source");
362 HANDLE (ADDRESS, "address");
363 HANDLE (CONTACT, "contact");
364 HANDLE (EMAIL, "email");
365 HANDLE (TEL, "telephone");
366 HANDLE (FAX, "fax");
367 HANDLE (LANGUAGE, "language");
368 HANDLE (TERRITORY, "territory");
369 HANDLE (AUDIENCE, "audience");
370 HANDLE (APPLICATION, "application");
371 HANDLE (ABBREVIATION, "abbreviation");
372 HANDLE (REVISION, "revision");
373 HANDLE (DATE, "date");
374 }
375 }
376
377
378 static void
379 print_LC_CTYPE (void *mapped, size_t size)
380 {
381 struct
382 {
383 unsigned int magic;
384 unsigned int nstrings;
385 unsigned int strindex[0];
386 } *filedata = mapped;
387
388 if (filedata->magic == LIMAGIC (LC_CTYPE)
389 && (sizeof *filedata
390 + (filedata->nstrings
391 * sizeof (unsigned int))
392 <= size))
393 {
394 const char *str;
395
396 str = ((char *) mapped
397 + filedata->strindex[_NL_ITEM_INDEX (_NL_CTYPE_CODESET_NAME)]);
398 if (*str != '\0')
399 printf (" codeset | %s\n", str);
400 }
401 }
402
403
404 /* Write the names of all available locales to stdout. We have some
405 sources of the information: the contents of the locale directory
406 and the locale.alias file. To avoid duplicates and print the
407 result is a reasonable order we put all entries is a search tree
408 and print them afterwards. */
409 static void
410 write_locales (void)
411 {
412 char linebuf[80];
413 void *all_data = NULL;
414 struct dirent **dirents;
415 int ndirents;
416 int cnt;
417 char *alias_path;
418 size_t alias_path_len;
419 char *entry;
420 int first_locale = 1;
421
422 #define PUT(name) tsearch (name, &all_data, \
423 (int (*) (const void *, const void *)) strcoll)
424 #define GET(name) tfind (name, &all_data, \
425 (int (*) (const void *, const void *)) strcoll)
426
427 /* `POSIX' locale is always available (POSIX.2 4.34.3). */
428 PUT ("POSIX");
429 /* And so is the "C" locale. */
430 PUT ("C");
431
432 memset (linebuf, '-', sizeof (linebuf) - 1);
433 linebuf[sizeof (linebuf) - 1] = '\0';
434
435 /* First scan the locale archive. */
436 if (write_archive_locales (&all_data, linebuf))
437 first_locale = 0;
438
439 /* Now we can look for all files in the directory. */
440 ndirents = scandir (LOCALEDIR, &dirents, select_dirs, alphasort);
441 for (cnt = 0; cnt < ndirents; ++cnt)
442 {
443 /* Test whether at least the LC_CTYPE data is there. Some
444 directories only contain translations. */
445 char buf[sizeof (LOCALEDIR) + strlen (dirents[cnt]->d_name)
446 + sizeof "/LC_IDENTIFICATION"];
447 char *enddir;
448 struct stat64 st;
449
450 stpcpy (enddir = stpcpy (stpcpy (stpcpy (buf, LOCALEDIR), "/"),
451 dirents[cnt]->d_name),
452 "/LC_IDENTIFICATION");
453
454 if (stat64 (buf, &st) == 0 && S_ISREG (st.st_mode))
455 {
456 if (verbose && GET (dirents[cnt]->d_name) == NULL)
457 {
458 /* Provide some nice output of all kinds of
459 information. */
460 int fd;
461
462 if (! first_locale)
463 putchar_unlocked ('\n');
464 first_locale = 0;
465
466 printf ("locale: %-15.15s directory: %.*s\n%s\n",
467 dirents[cnt]->d_name, (int) (enddir - buf), buf,
468 linebuf);
469
470 fd = open64 (buf, O_RDONLY);
471 if (fd != -1)
472 {
473 void *mapped = mmap64 (NULL, st.st_size, PROT_READ,
474 MAP_SHARED, fd, 0);
475 if (mapped != MAP_FAILED)
476 {
477 print_LC_IDENTIFICATION (mapped, st.st_size);
478
479 munmap (mapped, st.st_size);
480 }
481
482 close (fd);
483
484 /* Now try to get the charset information. */
485 strcpy (enddir, "/LC_CTYPE");
486 fd = open64 (buf, O_RDONLY);
487 if (fd != -1 && fstat64 (fd, &st) >= 0
488 && ((mapped = mmap64 (NULL, st.st_size, PROT_READ,
489 MAP_SHARED, fd, 0))
490 != MAP_FAILED))
491 {
492 print_LC_CTYPE (mapped, st.st_size);
493
494 munmap (mapped, st.st_size);
495 }
496
497 if (fd != -1)
498 close (fd);
499 }
500 }
501
502 /* If the verbose format is not selected we simply
503 collect the names. */
504 PUT (xstrdup (dirents[cnt]->d_name));
505 }
506 }
507 if (ndirents > 0)
508 free (dirents);
509
510 /* Now read the locale.alias files. */
511 if (argz_create_sep (LOCALE_ALIAS_PATH, ':', &alias_path, &alias_path_len))
512 error (1, errno, gettext ("while preparing output"));
513
514 entry = NULL;
515 while ((entry = argz_next (alias_path, alias_path_len, entry)))
516 {
517 static const char aliasfile[] = "/locale.alias";
518 FILE *fp;
519 char full_name[strlen (entry) + sizeof aliasfile];
520
521 stpcpy (stpcpy (full_name, entry), aliasfile);
522 fp = fopen (full_name, "rm");
523 if (fp == NULL)
524 /* Ignore non-existing files. */
525 continue;
526
527 /* No threads present. */
528 __fsetlocking (fp, FSETLOCKING_BYCALLER);
529
530 while (! feof_unlocked (fp))
531 {
532 /* It is a reasonable approach to use a fix buffer here
533 because
534 a) we are only interested in the first two fields
535 b) these fields must be usable as file names and so must
536 not be that long */
537 char buf[BUFSIZ];
538 char *alias;
539 char *value;
540 char *cp;
541
542 if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
543 /* EOF reached. */
544 break;
545
546 cp = buf;
547 /* Ignore leading white space. */
548 while (isspace (cp[0]) && cp[0] != '\n')
549 ++cp;
550
551 /* A leading '#' signals a comment line. */
552 if (cp[0] != '\0' && cp[0] != '#' && cp[0] != '\n')
553 {
554 alias = cp++;
555 while (cp[0] != '\0' && !isspace (cp[0]))
556 ++cp;
557 /* Terminate alias name. */
558 if (cp[0] != '\0')
559 *cp++ = '\0';
560
561 /* Now look for the beginning of the value. */
562 while (isspace (cp[0]))
563 ++cp;
564
565 if (cp[0] != '\0')
566 {
567 value = cp++;
568 while (cp[0] != '\0' && !isspace (cp[0]))
569 ++cp;
570 /* Terminate value. */
571 if (cp[0] == '\n')
572 {
573 /* This has to be done to make the following
574 test for the end of line possible. We are
575 looking for the terminating '\n' which do not
576 overwrite here. */
577 *cp++ = '\0';
578 *cp = '\n';
579 }
580 else if (cp[0] != '\0')
581 *cp++ = '\0';
582
583 /* Add the alias. */
584 if (! verbose && GET (value) != NULL)
585 PUT (xstrdup (alias));
586 }
587 }
588
589 /* Possibly not the whole line fits into the buffer.
590 Ignore the rest of the line. */
591 while (strchr (cp, '\n') == NULL)
592 {
593 cp = buf;
594 if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
595 /* Make sure the inner loop will be left. The outer
596 loop will exit at the `feof' test. */
597 *cp = '\n';
598 }
599 }
600
601 fclose (fp);
602 }
603
604 if (! verbose)
605 {
606 twalk (all_data, print_names);
607 }
608 }
609
610
611 struct nameent
612 {
613 char *name;
614 uint32_t locrec_offset;
615 };
616
617
618 static int
619 nameentcmp (const void *a, const void *b)
620 {
621 return strcoll (((const struct nameent *) a)->name,
622 ((const struct nameent *) b)->name);
623 }
624
625
626 static int
627 write_archive_locales (void **all_datap, char *linebuf)
628 {
629 struct stat64 st;
630 void *all_data = *all_datap;
631 size_t len = 0;
632 struct locarhead *head;
633 struct namehashent *namehashtab;
634 char *addr = MAP_FAILED;
635 int fd, ret = 0;
636 uint32_t cnt;
637
638 fd = open64 (ARCHIVE_NAME, O_RDONLY);
639 if (fd < 0)
640 return 0;
641
642 if (fstat64 (fd, &st) < 0 || st.st_size < sizeof (*head))
643 goto error_out;
644
645 len = st.st_size;
646 addr = mmap64 (NULL, len, PROT_READ, MAP_SHARED, fd, 0);
647 if (addr == MAP_FAILED)
648 goto error_out;
649
650 head = (struct locarhead *) addr;
651 if (head->namehash_offset + head->namehash_size > len
652 || head->string_offset + head->string_size > len
653 || head->locrectab_offset + head->locrectab_size > len
654 || head->sumhash_offset + head->sumhash_size > len)
655 goto error_out;
656
657 namehashtab = (struct namehashent *) (addr + head->namehash_offset);
658 if (! verbose)
659 {
660 for (cnt = 0; cnt < head->namehash_size; ++cnt)
661 if (namehashtab[cnt].locrec_offset != 0)
662 {
663 PUT (xstrdup (addr + namehashtab[cnt].name_offset));
664 ++ret;
665 }
666 }
667 else
668 {
669 struct nameent *names;
670 uint32_t used;
671
672 names = (struct nameent *) xmalloc (head->namehash_used
673 * sizeof (struct nameent));
674 for (cnt = used = 0; cnt < head->namehash_size; ++cnt)
675 if (namehashtab[cnt].locrec_offset != 0)
676 {
677 names[used].name = addr + namehashtab[cnt].name_offset;
678 names[used++].locrec_offset = namehashtab[cnt].locrec_offset;
679 }
680
681 /* Sort the names. */
682 qsort (names, used, sizeof (struct nameent), nameentcmp);
683
684 for (cnt = 0; cnt < used; ++cnt)
685 {
686 struct locrecent *locrec;
687
688 PUT (xstrdup (names[cnt].name));
689
690 if (cnt)
691 putchar_unlocked ('\n');
692
693 printf ("locale: %-15.15s archive: " ARCHIVE_NAME "\n%s\n",
694 names[cnt].name, linebuf);
695
696 locrec = (struct locrecent *) (addr + names[cnt].locrec_offset);
697
698 print_LC_IDENTIFICATION (addr
699 + locrec->record[LC_IDENTIFICATION].offset,
700 locrec->record[LC_IDENTIFICATION].len);
701
702 print_LC_CTYPE (addr + locrec->record[LC_CTYPE].offset,
703 locrec->record[LC_CTYPE].len);
704 }
705
706 ret = used;
707 }
708
709 error_out:
710 if (addr != MAP_FAILED)
711 munmap (addr, len);
712 close (fd);
713 *all_datap = all_data;
714 return ret;
715 }
716
717
718 /* Write the names of all available character maps to stdout. */
719 static void
720 write_charmaps (void)
721 {
722 void *all_data = NULL;
723 CHARMAP_DIR *dir;
724 const char *dirent;
725
726 /* Look for all files in the charmap directory. */
727 dir = charmap_opendir (CHARMAP_PATH);
728 if (dir == NULL)
729 return;
730
731 while ((dirent = charmap_readdir (dir)) != NULL)
732 {
733 char **aliases;
734 char **p;
735
736 PUT (xstrdup (dirent));
737
738 aliases = charmap_aliases (CHARMAP_PATH, dirent);
739
740 #if 0
741 /* Add the code_set_name and the aliases. */
742 for (p = aliases; *p; p++)
743 PUT (xstrdup (*p));
744 #else
745 /* Add the code_set_name only. Most aliases are obsolete. */
746 p = aliases;
747 if (*p)
748 PUT (xstrdup (*p));
749 #endif
750
751 charmap_free_aliases (aliases);
752 }
753
754 charmap_closedir (dir);
755
756 twalk (all_data, print_names);
757 }
758
759
760 /* We have to show the contents of the environments determining the
761 locale. */
762 static void
763 show_locale_vars (void)
764 {
765 size_t cat_no;
766 const char *lcall = getenv ("LC_ALL");
767 const char *lang = getenv ("LANG") ? : "POSIX";
768
769 auto void get_source (const char *name);
770
771 void get_source (const char *name)
772 {
773 char *val = getenv (name);
774
775 if ((lcall ?: "")[0] != '\0' || val == NULL)
776 printf ("%s=\"%s\"\n", name, (lcall ?: "")[0] ? lcall : lang);
777 else
778 printf ("%s=%s\n", name, val);
779 }
780
781 /* LANG has to be the first value. */
782 printf ("LANG=%s\n", lang);
783
784 /* Now all categories in an unspecified order. */
785 for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
786 if (cat_no != LC_ALL)
787 get_source (category[cat_no].name);
788
789 /* The last is the LC_ALL value. */
790 printf ("LC_ALL=%s\n", lcall ? : "");
791 }
792
793
794 /* Show the information request for NAME. */
795 static void
796 show_info (const char *name)
797 {
798 size_t cat_no;
799
800 auto void print_item (struct cat_item *item);
801
802 void print_item (struct cat_item *item)
803 {
804 switch (item->value_type)
805 {
806 case string:
807 if (show_keyword_name)
808 printf ("%s=\"", item->name);
809 fputs (nl_langinfo (item->item_id) ? : "", stdout);
810 if (show_keyword_name)
811 putchar ('"');
812 putchar ('\n');
813 break;
814 case stringarray:
815 {
816 int cnt;
817 const char *val;
818
819 if (show_keyword_name)
820 printf ("%s=\"", item->name);
821
822 for (cnt = 0; cnt < item->max - 1; ++cnt)
823 {
824 val = nl_langinfo (item->item_id + cnt);
825 if (val != NULL)
826 fputs (val, stdout);
827 putchar (';');
828 }
829
830 val = nl_langinfo (item->item_id + cnt);
831 if (val != NULL)
832 fputs (val, stdout);
833
834 if (show_keyword_name)
835 putchar ('"');
836 putchar ('\n');
837 }
838 break;
839 case stringlist:
840 {
841 int first = 1;
842 const char *val = nl_langinfo (item->item_id) ? : "";
843 int cnt;
844
845 if (show_keyword_name)
846 printf ("%s=", item->name);
847
848 for (cnt = 0; cnt < item->max && *val != '\0'; ++cnt)
849 {
850 printf ("%s%s%s%s", first ? "" : ";",
851 show_keyword_name ? "\"" : "", val,
852 show_keyword_name ? "\"" : "");
853 val = strchr (val, '\0') + 1;
854 first = 0;
855 }
856 putchar ('\n');
857 }
858 break;
859 case byte:
860 {
861 const char *val = nl_langinfo (item->item_id);
862
863 if (show_keyword_name)
864 printf ("%s=", item->name);
865
866 if (val != NULL)
867 printf ("%d", *val == '\177' ? -1 : *val);
868 putchar ('\n');
869 }
870 break;
871 case bytearray:
872 {
873 const char *val = nl_langinfo (item->item_id);
874 int cnt = val ? strlen (val) : 0;
875
876 if (show_keyword_name)
877 printf ("%s=", item->name);
878
879 while (cnt > 1)
880 {
881 printf ("%d;", *val == '\177' ? -1 : *val);
882 --cnt;
883 ++val;
884 }
885
886 printf ("%d\n", cnt == 0 || *val == '\177' ? -1 : *val);
887 }
888 break;
889 case word:
890 {
891 unsigned int val =
892 (unsigned int) (unsigned long int) nl_langinfo (item->item_id);
893 if (show_keyword_name)
894 printf ("%s=", item->name);
895
896 printf ("%d\n", val);
897 }
898 break;
899 case wstring:
900 case wstringarray:
901 case wstringlist:
902 /* We don't print wide character information since the same
903 information is available in a multibyte string. */
904 default:
905 break;
906
907 }
908 }
909
910 for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
911 if (cat_no != LC_ALL)
912 {
913 size_t item_no;
914
915 if (strcmp (name, category[cat_no].name) == 0)
916 /* Print the whole category. */
917 {
918 if (show_category_name != 0)
919 puts (category[cat_no].name);
920
921 for (item_no = 0; item_no < category[cat_no].number; ++item_no)
922 print_item (&category[cat_no].item_desc[item_no]);
923
924 return;
925 }
926
927 for (item_no = 0; item_no < category[cat_no].number; ++item_no)
928 if (strcmp (name, category[cat_no].item_desc[item_no].name) == 0)
929 {
930 if (show_category_name != 0)
931 puts (category[cat_no].name);
932
933 print_item (&category[cat_no].item_desc[item_no]);
934 return;
935 }
936 }
937
938 /* The name is not a standard one.
939 For testing and perhaps advanced use allow some more symbols. */
940 locale_special (name, show_category_name, show_keyword_name);
941 }
This page took 0.081053 seconds and 5 git commands to generate.