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.
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
37 #include <stdio_ext.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. */
62 /* Print names of all available character maps. */
63 static int do_charmaps = 0;
65 /* Nonzero if verbose output is wanted. */
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.");
90 /* Strings for arguments in help texts. */
91 static const char args_doc[] = N_("NAME\n[-a|-m]");
93 /* Prototype for option handler. */
94 static error_t parse_opt (int key, char *arg, struct argp_state *state);
96 /* Function to print some extra text in the help message. */
97 static char *more_help (int key, const char *text, void *input);
99 /* Data structure to communicate with argp functions. */
100 static struct argp argp =
102 options, parse_opt, args_doc, doc, NULL, more_help
106 /* We don't have these constants defined because we don't use them. Give
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
118 /* Definition of the data structure which represents a category and its
129 enum { std, opt } status;
130 enum value_type value_type;
136 /* Simple helper macro. */
137 #define NELEMS(arr) ((sizeof (arr)) / (sizeof (arr[0])))
139 /* For some tricky stuff. */
140 #define NO_PAREN(Item, More...) Item, ## More
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[] = \
151 #include "categories.def"
152 #undef DEFINE_CATEGORY
154 static struct category category[] =
156 #define DEFINE_CATEGORY(category, name, items, postload) \
157 [category] = { _NL_NUM_##category, name, NELEMS (category##_desc), \
159 #include "categories.def"
160 #undef DEFINE_CATEGORY
162 #define NCATEGORIES NELEMS (category)
165 /* Automatically set variable. */
166 extern const char *__progname;
168 /* helper function for extended name handling. */
169 extern void locale_special (const char *name, int show_category_name,
170 int show_keyword_name);
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);
184 main (int argc, char *argv[])
188 /* Set initial values for global variables. */
189 show_category_name = 0;
190 show_keyword_name = 0;
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, "");
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. */
206 setlocale (LC_COLLATE, "");
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)
219 /* Specific information about the current locale are requested.
220 Change to this locale now. */
221 setlocale (LC_ALL, "");
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)
231 /* Process all given names. */
232 while (remaining < argc)
233 show_info (argv[remaining++]);
239 /* Handle program arguments. */
241 parse_opt (int key, char *arg, struct argp_state *state)
249 show_category_name = 1;
255 show_keyword_name = 1;
261 return ARGP_ERR_UNKNOWN;
268 more_help (int key, const char *text, void *input)
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"));
279 return (char *) text;
282 /* Print the version information. */
284 print_version (FILE *stream, struct argp_state *state)
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\
292 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
296 /* Simple action function which prints arguments as strings. */
298 print_names (const void *nodep, VISIT value, int level)
300 if (value == postorder || value == leaf)
301 puts (*(char **) nodep);
306 select_dirs (const struct dirent *dirent)
310 if (strcmp (dirent->d_name, ".") != 0 && strcmp (dirent->d_name, "..") != 0)
314 #ifdef _DIRENT_HAVE_D_TYPE
315 if (dirent->d_type != DT_UNKNOWN && dirent->d_type != DT_LNK)
316 mode = DTTOIF (dirent->d_type);
321 char buf[sizeof (LOCALEDIR) + strlen (dirent->d_name) + 1];
323 stpcpy (stpcpy (stpcpy (buf, LOCALEDIR), "/"), dirent->d_name);
325 if (stat64 (buf, &st) == 0)
329 result = S_ISDIR (mode);
337 print_LC_IDENTIFICATION (void *mapped, size_t size)
339 /* Read the information from the file. */
343 unsigned int nstrings;
344 unsigned int strindex[0];
345 } *filedata = mapped;
347 if (filedata->magic == LIMAGIC (LC_IDENTIFICATION)
349 + (filedata->nstrings
350 * sizeof (unsigned int))
355 #define HANDLE(idx, name) \
356 str = ((char *) mapped \
357 + filedata->strindex[_NL_ITEM_INDEX (_NL_IDENTIFICATION_##idx)]); \
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");
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");
379 print_LC_CTYPE (void *mapped, size_t size)
384 unsigned int nstrings;
385 unsigned int strindex[0];
386 } *filedata = mapped;
388 if (filedata->magic == LIMAGIC (LC_CTYPE)
390 + (filedata->nstrings
391 * sizeof (unsigned int))
396 str = ((char *) mapped
397 + filedata->strindex[_NL_ITEM_INDEX (_NL_CTYPE_CODESET_NAME)]);
399 printf (" codeset | %s\n", str);
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. */
413 void *all_data = NULL;
414 struct dirent **dirents;
418 size_t alias_path_len;
420 int first_locale = 1;
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)
427 /* `POSIX' locale is always available (POSIX.2 4.34.3). */
429 /* And so is the "C" locale. */
432 memset (linebuf, '-', sizeof (linebuf) - 1);
433 linebuf[sizeof (linebuf) - 1] = '\0';
435 /* First scan the locale archive. */
436 if (write_archive_locales (&all_data, linebuf))
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)
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"];
450 stpcpy (enddir = stpcpy (stpcpy (stpcpy (buf, LOCALEDIR), "/"),
451 dirents[cnt]->d_name),
452 "/LC_IDENTIFICATION");
454 if (stat64 (buf, &st) == 0 && S_ISREG (st.st_mode))
456 if (verbose && GET (dirents[cnt]->d_name) == NULL)
458 /* Provide some nice output of all kinds of
463 putchar_unlocked ('\n');
466 printf ("locale: %-15.15s directory: %.*s\n%s\n",
467 dirents[cnt]->d_name, (int) (enddir - buf), buf,
470 fd = open64 (buf, O_RDONLY);
473 void *mapped = mmap64 (NULL, st.st_size, PROT_READ,
475 if (mapped != MAP_FAILED)
477 print_LC_IDENTIFICATION (mapped, st.st_size);
479 munmap (mapped, st.st_size);
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,
492 print_LC_CTYPE (mapped, st.st_size);
494 munmap (mapped, st.st_size);
502 /* If the verbose format is not selected we simply
503 collect the names. */
504 PUT (xstrdup (dirents[cnt]->d_name));
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"));
515 while ((entry = argz_next (alias_path, alias_path_len, entry)))
517 static const char aliasfile[] = "/locale.alias";
519 char full_name[strlen (entry) + sizeof aliasfile];
521 stpcpy (stpcpy (full_name, entry), aliasfile);
522 fp = fopen (full_name, "rm");
524 /* Ignore non-existing files. */
527 /* No threads present. */
528 __fsetlocking (fp, FSETLOCKING_BYCALLER);
530 while (! feof_unlocked (fp))
532 /* It is a reasonable approach to use a fix buffer here
534 a) we are only interested in the first two fields
535 b) these fields must be usable as file names and so must
542 if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
547 /* Ignore leading white space. */
548 while (isspace (cp[0]) && cp[0] != '\n')
551 /* A leading '#' signals a comment line. */
552 if (cp[0] != '\0' && cp[0] != '#' && cp[0] != '\n')
555 while (cp[0] != '\0' && !isspace (cp[0]))
557 /* Terminate alias name. */
561 /* Now look for the beginning of the value. */
562 while (isspace (cp[0]))
568 while (cp[0] != '\0' && !isspace (cp[0]))
570 /* Terminate value. */
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
580 else if (cp[0] != '\0')
584 if (! verbose && GET (value) != NULL)
585 PUT (xstrdup (alias));
589 /* Possibly not the whole line fits into the buffer.
590 Ignore the rest of the line. */
591 while (strchr (cp, '\n') == NULL)
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. */
606 twalk (all_data, print_names);
614 uint32_t locrec_offset;
619 nameentcmp (const void *a, const void *b)
621 return strcoll (((const struct nameent *) a)->name,
622 ((const struct nameent *) b)->name);
627 write_archive_locales (void **all_datap, char *linebuf)
630 void *all_data = *all_datap;
632 struct locarhead *head;
633 struct namehashent *namehashtab;
634 char *addr = MAP_FAILED;
638 fd = open64 (ARCHIVE_NAME, O_RDONLY);
642 if (fstat64 (fd, &st) < 0 || st.st_size < sizeof (*head))
646 addr = mmap64 (NULL, len, PROT_READ, MAP_SHARED, fd, 0);
647 if (addr == MAP_FAILED)
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)
657 namehashtab = (struct namehashent *) (addr + head->namehash_offset);
660 for (cnt = 0; cnt < head->namehash_size; ++cnt)
661 if (namehashtab[cnt].locrec_offset != 0)
663 PUT (xstrdup (addr + namehashtab[cnt].name_offset));
669 struct nameent *names;
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)
677 names[used].name = addr + namehashtab[cnt].name_offset;
678 names[used++].locrec_offset = namehashtab[cnt].locrec_offset;
681 /* Sort the names. */
682 qsort (names, used, sizeof (struct nameent), nameentcmp);
684 for (cnt = 0; cnt < used; ++cnt)
686 struct locrecent *locrec;
688 PUT (xstrdup (names[cnt].name));
691 putchar_unlocked ('\n');
693 printf ("locale: %-15.15s archive: " ARCHIVE_NAME "\n%s\n",
694 names[cnt].name, linebuf);
696 locrec = (struct locrecent *) (addr + names[cnt].locrec_offset);
698 print_LC_IDENTIFICATION (addr
699 + locrec->record[LC_IDENTIFICATION].offset,
700 locrec->record[LC_IDENTIFICATION].len);
702 print_LC_CTYPE (addr + locrec->record[LC_CTYPE].offset,
703 locrec->record[LC_CTYPE].len);
710 if (addr != MAP_FAILED)
713 *all_datap = all_data;
718 /* Write the names of all available character maps to stdout. */
720 write_charmaps (void)
722 void *all_data = NULL;
726 /* Look for all files in the charmap directory. */
727 dir = charmap_opendir (CHARMAP_PATH);
731 while ((dirent = charmap_readdir (dir)) != NULL)
736 PUT (xstrdup (dirent));
738 aliases = charmap_aliases (CHARMAP_PATH, dirent);
741 /* Add the code_set_name and the aliases. */
742 for (p = aliases; *p; p++)
745 /* Add the code_set_name only. Most aliases are obsolete. */
751 charmap_free_aliases (aliases);
754 charmap_closedir (dir);
756 twalk (all_data, print_names);
760 /* We have to show the contents of the environments determining the
763 show_locale_vars (void)
766 const char *lcall = getenv ("LC_ALL");
767 const char *lang = getenv ("LANG") ? : "POSIX";
769 auto void get_source (const char *name);
771 void get_source (const char *name)
773 char *val = getenv (name);
775 if ((lcall ?: "")[0] != '\0' || val == NULL)
776 printf ("%s=\"%s\"\n", name, (lcall ?: "")[0] ? lcall : lang);
778 printf ("%s=%s\n", name, val);
781 /* LANG has to be the first value. */
782 printf ("LANG=%s\n", lang);
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);
789 /* The last is the LC_ALL value. */
790 printf ("LC_ALL=%s\n", lcall ? : "");
794 /* Show the information request for NAME. */
796 show_info (const char *name)
800 auto void print_item (struct cat_item *item);
802 void print_item (struct cat_item *item)
804 switch (item->value_type)
807 if (show_keyword_name)
808 printf ("%s=\"", item->name);
809 fputs (nl_langinfo (item->item_id) ? : "", stdout);
810 if (show_keyword_name)
819 if (show_keyword_name)
820 printf ("%s=\"", item->name);
822 for (cnt = 0; cnt < item->max - 1; ++cnt)
824 val = nl_langinfo (item->item_id + cnt);
830 val = nl_langinfo (item->item_id + cnt);
834 if (show_keyword_name)
842 const char *val = nl_langinfo (item->item_id) ? : "";
845 if (show_keyword_name)
846 printf ("%s=", item->name);
848 for (cnt = 0; cnt < item->max && *val != '\0'; ++cnt)
850 printf ("%s%s%s%s", first ? "" : ";",
851 show_keyword_name ? "\"" : "", val,
852 show_keyword_name ? "\"" : "");
853 val = strchr (val, '\0') + 1;
861 const char *val = nl_langinfo (item->item_id);
863 if (show_keyword_name)
864 printf ("%s=", item->name);
867 printf ("%d", *val == '\177' ? -1 : *val);
873 const char *val = nl_langinfo (item->item_id);
874 int cnt = val ? strlen (val) : 0;
876 if (show_keyword_name)
877 printf ("%s=", item->name);
881 printf ("%d;", *val == '\177' ? -1 : *val);
886 printf ("%d\n", cnt == 0 || *val == '\177' ? -1 : *val);
892 (unsigned int) (unsigned long int) nl_langinfo (item->item_id);
893 if (show_keyword_name)
894 printf ("%s=", item->name);
896 printf ("%d\n", val);
902 /* We don't print wide character information since the same
903 information is available in a multibyte string. */
910 for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
911 if (cat_no != LC_ALL)
915 if (strcmp (name, category[cat_no].name) == 0)
916 /* Print the whole category. */
918 if (show_category_name != 0)
919 puts (category[cat_no].name);
921 for (item_no = 0; item_no < category[cat_no].number; ++item_no)
922 print_item (&category[cat_no].item_desc[item_no]);
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)
930 if (show_category_name != 0)
931 puts (category[cat_no].name);
933 print_item (&category[cat_no].item_desc[item_no]);
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);