1 /* Implementation of the locale program according to POSIX 9945-2.
2 Copyright (C) 1995-1997, 1999, 2000, 2001 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 Library General Public License as
8 published by the Free Software Foundation; either version 2 of the
9 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 Library General Public License for more details.
16 You should have received a copy of the GNU Library General Public
17 License along with the GNU C Library; see the file COPYING.LIB. If not,
18 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA. */
43 #include "localeinfo.h"
44 #include "charmap-dir.h"
46 extern void *xmalloc (size_t __n);
47 extern char *xstrdup (const char *__str);
50 /* If set print the name of the category. */
51 static int show_category_name;
53 /* If set print the name of the item. */
54 static int show_keyword_name;
56 /* Print names of all available locales. */
59 /* Print names of all available character maps. */
60 static int do_charmaps = 0;
62 /* Nonzero if verbose output is wanted. */
65 /* Name and version of program. */
66 static void print_version (FILE *stream, struct argp_state *state);
67 void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
69 /* Definitions of arguments for argp functions. */
70 static const struct argp_option options[] =
72 { NULL, 0, NULL, 0, N_("System information:") },
73 { "all-locales", 'a', NULL, OPTION_NO_USAGE,
74 N_("Write names of available locales") },
75 { "charmaps", 'm', NULL, OPTION_NO_USAGE,
76 N_("Write names of available charmaps") },
77 { NULL, 0, NULL, 0, N_("Modify output format:") },
78 { "category-name", 'c', NULL, 0, N_("Write names of selected categories") },
79 { "keyword-name", 'k', NULL, 0, N_("Write names of selected keywords") },
80 { "verbose", 'v', NULL, 0, N_("Print more information") },
81 { NULL, 0, NULL, 0, NULL }
84 /* Short description of program. */
85 static const char doc[] = N_("Get locale-specific information.");
87 /* Strings for arguments in help texts. */
88 static const char args_doc[] = N_("NAME\n[-a|-m]");
90 /* Prototype for option handler. */
91 static error_t parse_opt (int key, char *arg, struct argp_state *state);
93 /* Function to print some extra text in the help message. */
94 static char *more_help (int key, const char *text, void *input);
96 /* Data structure to communicate with argp functions. */
97 static struct argp argp =
99 options, parse_opt, args_doc, doc, NULL, more_help
103 /* We don't have these constants defined because we don't use them. Give
105 #define CTYPE_MB_CUR_MIN 0
106 #define CTYPE_MB_CUR_MAX 0
107 #define CTYPE_HASH_SIZE 0
108 #define CTYPE_HASH_LAYERS 0
109 #define CTYPE_CLASS 0
110 #define CTYPE_TOUPPER_EB 0
111 #define CTYPE_TOLOWER_EB 0
112 #define CTYPE_TOUPPER_EL 0
113 #define CTYPE_TOLOWER_EL 0
115 /* Definition of the data structure which represents a category and its
126 enum { std, opt } status;
127 enum value_type value_type;
133 /* Simple helper macro. */
134 #define NELEMS(arr) ((sizeof (arr)) / (sizeof (arr[0])))
136 /* For some tricky stuff. */
137 #define NO_PAREN(Item, More...) Item, ## More
139 /* We have all categories defined in `categories.def'. Now construct
140 the description and data structure used for all categories. */
141 #define DEFINE_ELEMENT(Item, More...) { Item, ## More },
142 #define DEFINE_CATEGORY(category, name, items, postload) \
143 static struct cat_item category##_desc[] = \
148 #include "categories.def"
149 #undef DEFINE_CATEGORY
151 static struct category category[] =
153 #define DEFINE_CATEGORY(category, name, items, postload) \
154 [category] = { _NL_NUM_##category, name, NELEMS (category##_desc), \
156 #include "categories.def"
157 #undef DEFINE_CATEGORY
159 #define NCATEGORIES NELEMS (category)
162 /* Automatically set variable. */
163 extern const char *__progname;
165 /* helper function for extended name handling. */
166 extern void locale_special (const char *name, int show_category_name,
167 int show_keyword_name);
169 /* Prototypes for local functions. */
170 static void write_locales (void);
171 static void write_charmaps (void);
172 static void show_locale_vars (void);
173 static void show_info (const char *name);
177 main (int argc, char *argv[])
181 /* Set initial values for global variables. */
182 show_category_name = 0;
183 show_keyword_name = 0;
185 /* Set locale. Do not set LC_ALL because the other categories must
186 not be affected (according to POSIX.2). */
187 setlocale (LC_CTYPE, "");
188 setlocale (LC_MESSAGES, "");
190 /* Initialize the message catalog. */
191 textdomain (PACKAGE);
193 /* Parse and process arguments. */
194 argp_parse (&argp, argc, argv, 0, &remaining, NULL);
196 /* `-a' requests the names of all available locales. */
199 setlocale (LC_COLLATE, "");
204 /* `m' requests the names of all available charmaps. The names can be
205 used for the -f argument to localedef(1). */
206 if (do_charmaps != 0)
212 /* Specific information about the current locale are requested.
213 Change to this locale now. */
214 setlocale (LC_ALL, "");
216 /* If no real argument is given we have to print the contents of the
217 current locale definition variables. These are LANG and the LC_*. */
218 if (remaining == argc && show_keyword_name == 0 && show_category_name == 0)
224 /* Process all given names. */
225 while (remaining < argc)
226 show_info (argv[remaining++]);
232 /* Handle program arguments. */
234 parse_opt (int key, char *arg, struct argp_state *state)
242 show_category_name = 1;
248 show_keyword_name = 1;
254 return ARGP_ERR_UNKNOWN;
261 more_help (int key, const char *text, void *input)
265 case ARGP_KEY_HELP_EXTRA:
266 /* We print some extra information. */
267 return xstrdup (gettext ("\
268 Report bugs using the `glibcbug' script to <bugs@gnu.org>.\n"));
272 return (char *) text;
275 /* Print the version information. */
277 print_version (FILE *stream, struct argp_state *state)
279 fprintf (stream, "locale (GNU %s) %s\n", PACKAGE, VERSION);
280 fprintf (stream, gettext ("\
281 Copyright (C) %s Free Software Foundation, Inc.\n\
282 This is free software; see the source for copying conditions. There is NO\n\
283 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
285 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
289 /* Simple action function which prints arguments as strings. */
291 print_names (const void *nodep, VISIT value, int level)
293 if (value == postorder || value == leaf)
294 puts (*(char **) nodep);
298 /* Write the names of all available locales to stdout. We have some
299 sources of the information: the contents of the locale directory
300 and the locale.alias file. To avoid duplicates and print the
301 result is a reasonable order we put all entries is a search tree
302 and print them afterwards. */
307 void *all_data = NULL;
309 struct dirent *dirent;
311 size_t alias_path_len;
313 int first_locale = 1;
315 #define PUT(name) tsearch (name, &all_data, \
316 (int (*) (const void *, const void *)) strcoll)
318 /* Now read the locale.alias files. */
319 if (argz_create_sep (LOCALE_ALIAS_PATH, ':', &alias_path, &alias_path_len))
320 error (1, errno, gettext ("while preparing output"));
323 while ((entry = argz_next (alias_path, alias_path_len, entry)))
325 static const char aliasfile[] = "/locale.alias";
327 char full_name[strlen (entry) + sizeof aliasfile];
329 stpcpy (stpcpy (full_name, entry), aliasfile);
330 fp = fopen (full_name, "r");
332 /* Ignore non-existing files. */
337 /* It is a reasonable approach to use a fix buffer here
339 a) we are only interested in the first two fields
340 b) these fields must be usable as file names and so must
347 if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
352 /* Ignore leading white space. */
353 while (isspace (cp[0]) && cp[0] != '\n')
356 /* A leading '#' signals a comment line. */
357 if (cp[0] != '\0' && cp[0] != '#' && cp[0] != '\n')
360 while (cp[0] != '\0' && !isspace (cp[0]))
362 /* Terminate alias name. */
366 /* Now look for the beginning of the value. */
367 while (isspace (cp[0]))
373 while (cp[0] != '\0' && !isspace (cp[0]))
375 /* Terminate value. */
378 /* This has to be done to make the following
379 test for the end of line possible. We are
380 looking for the terminating '\n' which do not
385 else if (cp[0] != '\0')
390 PUT (xstrdup (alias));
394 /* Possibly not the whole line fits into the buffer.
395 Ignore the rest of the line. */
396 while (strchr (cp, '\n') == NULL)
399 if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
400 /* Make sure the inner loop will be left. The outer
401 loop will exit at the `feof' test. */
409 /* This is the directory with the locale files. */
410 dir = opendir (LOCALEDIR);
413 error (1, errno, gettext ("cannot read locale directory `%s'"),
418 memset (linebuf, '-', sizeof (linebuf) - 1);
419 linebuf[sizeof (linebuf) - 1] = '\0';
421 /* Now we can look for all files in the directory. */
422 while ((dirent = readdir (dir)) != NULL)
423 if (strcmp (dirent->d_name, ".") != 0
424 && strcmp (dirent->d_name, "..") != 0)
427 #ifdef _DIRENT_HAVE_D_TYPE
428 if (dirent->d_type != DT_UNKNOWN && dirent->d_type != DT_LNK)
429 mode = DTTOIF (dirent->d_type);
434 char buf[sizeof (LOCALEDIR) + strlen (dirent->d_name) + 1];
436 stpcpy (stpcpy (stpcpy (buf, LOCALEDIR), "/"), dirent->d_name);
438 if (stat64 (buf, &st) < 0)
445 /* Test whether at least the LC_CTYPE data is there. Some
446 directories only contain translations. */
447 char buf[sizeof (LOCALEDIR) + strlen (dirent->d_name)
448 + sizeof "/LC_IDENTIFICATION"];
452 stpcpy (enddir = stpcpy (stpcpy (stpcpy (buf, LOCALEDIR), "/"),
454 "/LC_IDENTIFICATION");
456 if (stat64 (buf, &st) == 0 && S_ISREG (st.st_mode))
460 /* Provide some nice output of all kinds of
465 putchar_unlocked ('\n');
468 printf ("locale: %-15.15s directory: %.*s\n%s\n",
469 dirent->d_name, (int) (enddir - buf), buf,
472 fd = open64 (buf, O_RDONLY);
475 void *mapped = mmap64 (NULL, st.st_size, PROT_READ,
477 if (mapped != MAP_FAILED)
479 /* Read the information from the file. */
483 unsigned int nstrings;
484 unsigned int strindex[0];
485 } *filedata = mapped;
487 if (filedata->magic == LIMAGIC (LC_IDENTIFICATION)
489 + (filedata->nstrings
490 * sizeof (unsigned int))
491 <= (size_t) st.st_size))
495 #define HANDLE(idx, name) \
496 str = ((char *) mapped \
497 + filedata->strindex[_NL_ITEM_INDEX (_NL_IDENTIFICATION_##idx)]); \
499 printf ("%9s | %s\n", name, str)
500 HANDLE (TITLE, "title");
501 HANDLE (SOURCE, "source");
502 HANDLE (ADDRESS, "address");
503 HANDLE (CONTACT, "contact");
504 HANDLE (EMAIL, "email");
505 HANDLE (TEL, "telephone");
507 HANDLE (LANGUAGE, "language");
508 HANDLE (TERRITORY, "territory");
509 HANDLE (AUDIENCE, "audience");
510 HANDLE (APPLICATION, "application");
511 HANDLE (ABBREVIATION, "abbreviation");
512 HANDLE (REVISION, "revision");
513 HANDLE (DATE, "date");
516 munmap (mapped, st.st_size);
521 /* Now try to get the charset information. */
522 strcpy (enddir, "/LC_CTYPE");
523 fd = open64 (buf, O_RDONLY);
524 if (fd != -1 && fstat64 (fd, &st) >= 0
525 && ((mapped = mmap64 (NULL, st.st_size, PROT_READ,
532 unsigned int nstrings;
533 unsigned int strindex[0];
534 } *filedata = mapped;
536 if (filedata->magic == LIMAGIC (LC_CTYPE)
538 + (filedata->nstrings
539 * sizeof (unsigned int))
540 <= (size_t) st.st_size))
544 str = ((char *) mapped
545 + filedata->strindex[_NL_ITEM_INDEX (_NL_CTYPE_CODESET_NAME)]);
547 printf (" codeset | %s\n", str);
550 munmap (mapped, st.st_size);
558 /* If the verbose format is not selected we simply
559 collect the names. */
560 PUT (xstrdup (dirent->d_name));
569 /* `POSIX' locale is always available (POSIX.2 4.34.3). */
571 /* And so is the "C" locale. */
574 twalk (all_data, print_names);
579 /* Write the names of all available character maps to stdout. */
581 write_charmaps (void)
583 void *all_data = NULL;
587 /* Look for all files in the charmap directory. */
588 dir = charmap_opendir (CHARMAP_PATH);
592 while ((dirent = charmap_readdir (dir)) != NULL)
597 PUT (xstrdup (dirent));
599 aliases = charmap_aliases (CHARMAP_PATH, dirent);
602 /* Add the code_set_name and the aliases. */
603 for (p = aliases; *p; p++)
606 /* Add the code_set_name only. Most aliases are obsolete. */
612 charmap_free_aliases (aliases);
615 charmap_closedir (dir);
617 twalk (all_data, print_names);
621 /* We have to show the contents of the environments determining the
624 show_locale_vars (void)
627 const char *lcall = getenv ("LC_ALL");
628 const char *lang = getenv ("LANG") ? : "POSIX";
630 auto void get_source (const char *name);
632 void get_source (const char *name)
634 char *val = getenv (name);
636 if ((lcall ?: "")[0] != '\0' || val == NULL)
637 printf ("%s=\"%s\"\n", name, (lcall ?: "")[0] ? lcall : lang);
639 printf ("%s=%s\n", name, val);
642 /* LANG has to be the first value. */
643 printf ("LANG=%s\n", lang);
645 /* Now all categories in an unspecified order. */
646 for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
647 if (cat_no != LC_ALL)
648 get_source (category[cat_no].name);
650 /* The last is the LC_ALL value. */
651 printf ("LC_ALL=%s\n", lcall ? : "");
655 /* Show the information request for NAME. */
657 show_info (const char *name)
661 auto void print_item (struct cat_item *item);
663 void print_item (struct cat_item *item)
665 switch (item->value_type)
668 if (show_keyword_name)
669 printf ("%s=\"", item->name);
670 fputs (nl_langinfo (item->item_id) ? : "", stdout);
671 if (show_keyword_name)
680 if (show_keyword_name)
681 printf ("%s=\"", item->name);
683 for (cnt = 0; cnt < item->max - 1; ++cnt)
685 val = nl_langinfo (item->item_id + cnt);
691 val = nl_langinfo (item->item_id + cnt);
695 if (show_keyword_name)
703 const char *val = nl_langinfo (item->item_id) ? : "";
706 if (show_keyword_name)
707 printf ("%s=", item->name);
709 for (cnt = 0; cnt < item->max && *val != '\0'; ++cnt)
711 printf ("%s%s%s%s", first ? "" : ";",
712 show_keyword_name ? "\"" : "", val,
713 show_keyword_name ? "\"" : "");
714 val = strchr (val, '\0') + 1;
722 const char *val = nl_langinfo (item->item_id);
724 if (show_keyword_name)
725 printf ("%s=", item->name);
728 printf ("%d", *val == CHAR_MAX ? -1 : *val);
734 const char *val = nl_langinfo (item->item_id);
735 int cnt = val ? strlen (val) : 0;
737 if (show_keyword_name)
738 printf ("%s=", item->name);
742 printf ("%d;", *val == CHAR_MAX ? -1 : *val);
747 printf ("%d\n", cnt == 0 || *val == CHAR_MAX ? -1 : *val);
753 (unsigned int) (unsigned long int) nl_langinfo (item->item_id);
754 if (show_keyword_name)
755 printf ("%s=", item->name);
757 printf ("%d\n", val);
763 /* We don't print wide character information since the same
764 information is available in a multibyte string. */
771 for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
772 if (cat_no != LC_ALL)
776 if (strcmp (name, category[cat_no].name) == 0)
777 /* Print the whole category. */
779 if (show_category_name != 0)
780 puts (category[cat_no].name);
782 for (item_no = 0; item_no < category[cat_no].number; ++item_no)
783 print_item (&category[cat_no].item_desc[item_no]);
788 for (item_no = 0; item_no < category[cat_no].number; ++item_no)
789 if (strcmp (name, category[cat_no].item_desc[item_no].name) == 0)
791 if (show_category_name != 0)
792 puts (category[cat_no].name);
794 print_item (&category[cat_no].item_desc[item_no]);
799 /* The name is not a standard one.
800 For testing and perhaps advanced use allow some more symbols. */
801 locale_special (name, show_category_name, show_keyword_name);