1 /* Implementation of the locale program according to POSIX 9945-2.
2 Copyright (C) 1995-1997,1999,2000,2001,2002 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"
47 extern void *xmalloc (size_t __n);
48 extern char *xstrdup (const char *__str);
51 /* If set print the name of the category. */
52 static int show_category_name;
54 /* If set print the name of the item. */
55 static int show_keyword_name;
57 /* Print names of all available locales. */
60 /* Print names of all available character maps. */
61 static int do_charmaps = 0;
63 /* Nonzero if verbose output is wanted. */
66 /* Name and version of program. */
67 static void print_version (FILE *stream, struct argp_state *state);
68 void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
70 /* Definitions of arguments for argp functions. */
71 static const struct argp_option options[] =
73 { NULL, 0, NULL, 0, N_("System information:") },
74 { "all-locales", 'a', NULL, OPTION_NO_USAGE,
75 N_("Write names of available locales") },
76 { "charmaps", 'm', NULL, OPTION_NO_USAGE,
77 N_("Write names of available charmaps") },
78 { NULL, 0, NULL, 0, N_("Modify output format:") },
79 { "category-name", 'c', NULL, 0, N_("Write names of selected categories") },
80 { "keyword-name", 'k', NULL, 0, N_("Write names of selected keywords") },
81 { "verbose", 'v', NULL, 0, N_("Print more information") },
82 { NULL, 0, NULL, 0, NULL }
85 /* Short description of program. */
86 static const char doc[] = N_("Get locale-specific information.");
88 /* Strings for arguments in help texts. */
89 static const char args_doc[] = N_("NAME\n[-a|-m]");
91 /* Prototype for option handler. */
92 static error_t parse_opt (int key, char *arg, struct argp_state *state);
94 /* Function to print some extra text in the help message. */
95 static char *more_help (int key, const char *text, void *input);
97 /* Data structure to communicate with argp functions. */
98 static struct argp argp =
100 options, parse_opt, args_doc, doc, NULL, more_help
104 /* We don't have these constants defined because we don't use them. Give
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
127 enum { std, opt } status;
128 enum value_type value_type;
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[] = \
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), \
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 write_locales (void);
172 static void write_charmaps (void);
173 static void show_locale_vars (void);
174 static void show_info (const char *name);
178 main (int argc, char *argv[])
182 /* Set initial values for global variables. */
183 show_category_name = 0;
184 show_keyword_name = 0;
186 /* Set locale. Do not set LC_ALL because the other categories must
187 not be affected (according to POSIX.2). */
188 setlocale (LC_CTYPE, "");
189 setlocale (LC_MESSAGES, "");
191 /* Initialize the message catalog. */
192 textdomain (PACKAGE);
194 /* Parse and process arguments. */
195 argp_parse (&argp, argc, argv, 0, &remaining, NULL);
197 /* `-a' requests the names of all available locales. */
200 setlocale (LC_COLLATE, "");
205 /* `m' requests the names of all available charmaps. The names can be
206 used for the -f argument to localedef(1). */
207 if (do_charmaps != 0)
213 /* Specific information about the current locale are requested.
214 Change to this locale now. */
215 setlocale (LC_ALL, "");
217 /* If no real argument is given we have to print the contents of the
218 current locale definition variables. These are LANG and the LC_*. */
219 if (remaining == argc && show_keyword_name == 0 && show_category_name == 0)
225 /* Process all given names. */
226 while (remaining < argc)
227 show_info (argv[remaining++]);
233 /* Handle program arguments. */
235 parse_opt (int key, char *arg, struct argp_state *state)
243 show_category_name = 1;
249 show_keyword_name = 1;
255 return ARGP_ERR_UNKNOWN;
262 more_help (int key, const char *text, void *input)
266 case ARGP_KEY_HELP_EXTRA:
267 /* We print some extra information. */
268 return xstrdup (gettext ("\
269 Report bugs using the `glibcbug' script to <bugs@gnu.org>.\n"));
273 return (char *) text;
276 /* Print the version information. */
278 print_version (FILE *stream, struct argp_state *state)
280 fprintf (stream, "locale (GNU %s) %s\n", PACKAGE, VERSION);
281 fprintf (stream, gettext ("\
282 Copyright (C) %s Free Software Foundation, Inc.\n\
283 This is free software; see the source for copying conditions. There is NO\n\
284 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
286 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
290 /* Simple action function which prints arguments as strings. */
292 print_names (const void *nodep, VISIT value, int level)
294 if (value == postorder || value == leaf)
295 puts (*(char **) nodep);
300 select_dirs (const struct dirent *dirent)
304 if (strcmp (dirent->d_name, ".") != 0 && strcmp (dirent->d_name, "..") != 0)
308 #ifdef _DIRENT_HAVE_D_TYPE
309 if (dirent->d_type != DT_UNKNOWN && dirent->d_type != DT_LNK)
310 mode = DTTOIF (dirent->d_type);
315 char buf[sizeof (LOCALEDIR) + strlen (dirent->d_name) + 1];
317 stpcpy (stpcpy (stpcpy (buf, LOCALEDIR), "/"), dirent->d_name);
319 if (stat64 (buf, &st) == 0)
323 result = S_ISDIR (mode);
330 /* Write the names of all available locales to stdout. We have some
331 sources of the information: the contents of the locale directory
332 and the locale.alias file. To avoid duplicates and print the
333 result is a reasonable order we put all entries is a search tree
334 and print them afterwards. */
339 void *all_data = NULL;
340 struct dirent **dirents;
344 size_t alias_path_len;
346 int first_locale = 1;
348 #define PUT(name) tsearch (name, &all_data, \
349 (int (*) (const void *, const void *)) strcoll)
350 #define GET(name) tfind (name, &all_data, \
351 (int (*) (const void *, const void *)) strcoll)
353 /* `POSIX' locale is always available (POSIX.2 4.34.3). */
355 /* And so is the "C" locale. */
358 memset (linebuf, '-', sizeof (linebuf) - 1);
359 linebuf[sizeof (linebuf) - 1] = '\0';
361 /* Now we can look for all files in the directory. */
362 ndirents = scandir (LOCALEDIR, &dirents, select_dirs, alphasort);
363 for (cnt = 0; cnt < ndirents; ++cnt)
365 /* Test whether at least the LC_CTYPE data is there. Some
366 directories only contain translations. */
367 char buf[sizeof (LOCALEDIR) + strlen (dirents[cnt]->d_name)
368 + sizeof "/LC_IDENTIFICATION"];
372 stpcpy (enddir = stpcpy (stpcpy (stpcpy (buf, LOCALEDIR), "/"),
373 dirents[cnt]->d_name),
374 "/LC_IDENTIFICATION");
376 if (stat64 (buf, &st) == 0 && S_ISREG (st.st_mode))
380 /* Provide some nice output of all kinds of
385 putchar_unlocked ('\n');
388 printf ("locale: %-15.15s directory: %.*s\n%s\n",
389 dirents[cnt]->d_name, (int) (enddir - buf), buf,
392 fd = open64 (buf, O_RDONLY);
395 void *mapped = mmap64 (NULL, st.st_size, PROT_READ,
397 if (mapped != MAP_FAILED)
399 /* Read the information from the file. */
403 unsigned int nstrings;
404 unsigned int strindex[0];
405 } *filedata = mapped;
407 if (filedata->magic == LIMAGIC (LC_IDENTIFICATION)
409 + (filedata->nstrings
410 * sizeof (unsigned int))
411 <= (size_t) st.st_size))
415 #define HANDLE(idx, name) \
416 str = ((char *) mapped \
417 + filedata->strindex[_NL_ITEM_INDEX (_NL_IDENTIFICATION_##idx)]); \
419 printf ("%9s | %s\n", name, str)
420 HANDLE (TITLE, "title");
421 HANDLE (SOURCE, "source");
422 HANDLE (ADDRESS, "address");
423 HANDLE (CONTACT, "contact");
424 HANDLE (EMAIL, "email");
425 HANDLE (TEL, "telephone");
427 HANDLE (LANGUAGE, "language");
428 HANDLE (TERRITORY, "territory");
429 HANDLE (AUDIENCE, "audience");
430 HANDLE (APPLICATION, "application");
431 HANDLE (ABBREVIATION, "abbreviation");
432 HANDLE (REVISION, "revision");
433 HANDLE (DATE, "date");
436 munmap (mapped, st.st_size);
441 /* Now try to get the charset information. */
442 strcpy (enddir, "/LC_CTYPE");
443 fd = open64 (buf, O_RDONLY);
444 if (fd != -1 && fstat64 (fd, &st) >= 0
445 && ((mapped = mmap64 (NULL, st.st_size, PROT_READ,
452 unsigned int nstrings;
453 unsigned int strindex[0];
454 } *filedata = mapped;
456 if (filedata->magic == LIMAGIC (LC_CTYPE)
458 + (filedata->nstrings
459 * sizeof (unsigned int))
460 <= (size_t) st.st_size))
464 str = ((char *) mapped
465 + filedata->strindex[_NL_ITEM_INDEX (_NL_CTYPE_CODESET_NAME)]);
467 printf (" codeset | %s\n", str);
470 munmap (mapped, st.st_size);
478 /* If the verbose format is not selected we simply
479 collect the names. */
480 PUT (xstrdup (dirents[cnt]->d_name));
486 /* Now read the locale.alias files. */
487 if (argz_create_sep (LOCALE_ALIAS_PATH, ':', &alias_path, &alias_path_len))
488 error (1, errno, gettext ("while preparing output"));
491 while ((entry = argz_next (alias_path, alias_path_len, entry)))
493 static const char aliasfile[] = "/locale.alias";
495 char full_name[strlen (entry) + sizeof aliasfile];
497 stpcpy (stpcpy (full_name, entry), aliasfile);
498 fp = fopen (full_name, "r");
500 /* Ignore non-existing files. */
503 /* No threads present. */
504 __fsetlocking (fp, FSETLOCKING_BYCALLER);
506 while (! feof_unlocked (fp))
508 /* It is a reasonable approach to use a fix buffer here
510 a) we are only interested in the first two fields
511 b) these fields must be usable as file names and so must
518 if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
523 /* Ignore leading white space. */
524 while (isspace (cp[0]) && cp[0] != '\n')
527 /* A leading '#' signals a comment line. */
528 if (cp[0] != '\0' && cp[0] != '#' && cp[0] != '\n')
531 while (cp[0] != '\0' && !isspace (cp[0]))
533 /* Terminate alias name. */
537 /* Now look for the beginning of the value. */
538 while (isspace (cp[0]))
544 while (cp[0] != '\0' && !isspace (cp[0]))
546 /* Terminate value. */
549 /* This has to be done to make the following
550 test for the end of line possible. We are
551 looking for the terminating '\n' which do not
556 else if (cp[0] != '\0')
560 if (! verbose && GET (value) != NULL)
561 PUT (xstrdup (alias));
565 /* Possibly not the whole line fits into the buffer.
566 Ignore the rest of the line. */
567 while (strchr (cp, '\n') == NULL)
570 if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
571 /* Make sure the inner loop will be left. The outer
572 loop will exit at the `feof' test. */
582 twalk (all_data, print_names);
587 /* Write the names of all available character maps to stdout. */
589 write_charmaps (void)
591 void *all_data = NULL;
595 /* Look for all files in the charmap directory. */
596 dir = charmap_opendir (CHARMAP_PATH);
600 while ((dirent = charmap_readdir (dir)) != NULL)
605 PUT (xstrdup (dirent));
607 aliases = charmap_aliases (CHARMAP_PATH, dirent);
610 /* Add the code_set_name and the aliases. */
611 for (p = aliases; *p; p++)
614 /* Add the code_set_name only. Most aliases are obsolete. */
620 charmap_free_aliases (aliases);
623 charmap_closedir (dir);
625 twalk (all_data, print_names);
629 /* We have to show the contents of the environments determining the
632 show_locale_vars (void)
635 const char *lcall = getenv ("LC_ALL");
636 const char *lang = getenv ("LANG") ? : "POSIX";
638 auto void get_source (const char *name);
640 void get_source (const char *name)
642 char *val = getenv (name);
644 if ((lcall ?: "")[0] != '\0' || val == NULL)
645 printf ("%s=\"%s\"\n", name, (lcall ?: "")[0] ? lcall : lang);
647 printf ("%s=%s\n", name, val);
650 /* LANG has to be the first value. */
651 printf ("LANG=%s\n", lang);
653 /* Now all categories in an unspecified order. */
654 for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
655 if (cat_no != LC_ALL)
656 get_source (category[cat_no].name);
658 /* The last is the LC_ALL value. */
659 printf ("LC_ALL=%s\n", lcall ? : "");
663 /* Show the information request for NAME. */
665 show_info (const char *name)
669 auto void print_item (struct cat_item *item);
671 void print_item (struct cat_item *item)
673 switch (item->value_type)
676 if (show_keyword_name)
677 printf ("%s=\"", item->name);
678 fputs (nl_langinfo (item->item_id) ? : "", stdout);
679 if (show_keyword_name)
688 if (show_keyword_name)
689 printf ("%s=\"", item->name);
691 for (cnt = 0; cnt < item->max - 1; ++cnt)
693 val = nl_langinfo (item->item_id + cnt);
699 val = nl_langinfo (item->item_id + cnt);
703 if (show_keyword_name)
711 const char *val = nl_langinfo (item->item_id) ? : "";
714 if (show_keyword_name)
715 printf ("%s=", item->name);
717 for (cnt = 0; cnt < item->max && *val != '\0'; ++cnt)
719 printf ("%s%s%s%s", first ? "" : ";",
720 show_keyword_name ? "\"" : "", val,
721 show_keyword_name ? "\"" : "");
722 val = strchr (val, '\0') + 1;
730 const char *val = nl_langinfo (item->item_id);
732 if (show_keyword_name)
733 printf ("%s=", item->name);
736 printf ("%d", *val == CHAR_MAX ? -1 : *val);
742 const char *val = nl_langinfo (item->item_id);
743 int cnt = val ? strlen (val) : 0;
745 if (show_keyword_name)
746 printf ("%s=", item->name);
750 printf ("%d;", *val == CHAR_MAX ? -1 : *val);
755 printf ("%d\n", cnt == 0 || *val == CHAR_MAX ? -1 : *val);
761 (unsigned int) (unsigned long int) nl_langinfo (item->item_id);
762 if (show_keyword_name)
763 printf ("%s=", item->name);
765 printf ("%d\n", val);
771 /* We don't print wide character information since the same
772 information is available in a multibyte string. */
779 for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
780 if (cat_no != LC_ALL)
784 if (strcmp (name, category[cat_no].name) == 0)
785 /* Print the whole category. */
787 if (show_category_name != 0)
788 puts (category[cat_no].name);
790 for (item_no = 0; item_no < category[cat_no].number; ++item_no)
791 print_item (&category[cat_no].item_desc[item_no]);
796 for (item_no = 0; item_no < category[cat_no].number; ++item_no)
797 if (strcmp (name, category[cat_no].item_desc[item_no].name) == 0)
799 if (show_category_name != 0)
800 puts (category[cat_no].name);
802 print_item (&category[cat_no].item_desc[item_no]);
807 /* The name is not a standard one.
808 For testing and perhaps advanced use allow some more symbols. */
809 locale_special (name, show_category_name, show_keyword_name);