1 /* Implementation of the locale program according to POSIX 9945-2.
2 Copyright (C) 1995, 1996, 1997, 1999, 2000 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. */
40 #include "localeinfo.h"
41 #include "charmap-dir.h"
43 extern char *xstrdup (const char *__str);
46 /* If set print the name of the category. */
47 static int show_category_name;
49 /* If set print the name of the item. */
50 static int show_keyword_name;
52 /* Print names of all available locales. */
55 /* Print names of all available character maps. */
56 static int do_charmaps = 0;
58 /* Name and version of program. */
59 static void print_version (FILE *stream, struct argp_state *state);
60 void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
62 /* Definitions of arguments for argp functions. */
63 static const struct argp_option options[] =
65 { NULL, 0, NULL, 0, N_("System information:") },
66 { "all-locales", 'a', NULL, OPTION_NO_USAGE,
67 N_("Write names of available locales") },
68 { "charmaps", 'm', NULL, OPTION_NO_USAGE,
69 N_("Write names of available charmaps") },
70 { NULL, 0, NULL, 0, N_("Modify output format:") },
71 { "category-name", 'c', NULL, 0, N_("Write names of selected categories") },
72 { "keyword-name", 'k', NULL, 0, N_("Write names of selected keywords") },
73 { NULL, 0, NULL, 0, NULL }
76 /* Short description of program. */
77 static const char doc[] = N_("Get locale-specific information.");
79 /* Strings for arguments in help texts. */
80 static const char args_doc[] = N_("NAME\n[-a|-m]");
82 /* Prototype for option handler. */
83 static error_t parse_opt (int key, char *arg, struct argp_state *state);
85 /* Function to print some extra text in the help message. */
86 static char *more_help (int key, const char *text, void *input);
88 /* Data structure to communicate with argp functions. */
89 static struct argp argp =
91 options, parse_opt, args_doc, doc, NULL, more_help
95 /* We don't have these constants defined because we don't use them. Give
97 #define CTYPE_MB_CUR_MIN 0
98 #define CTYPE_MB_CUR_MAX 0
99 #define CTYPE_HASH_SIZE 0
100 #define CTYPE_HASH_LAYERS 0
101 #define CTYPE_CLASS 0
102 #define CTYPE_TOUPPER_EB 0
103 #define CTYPE_TOLOWER_EB 0
104 #define CTYPE_TOUPPER_EL 0
105 #define CTYPE_TOLOWER_EL 0
107 /* Definition of the data structure which represents a category and its
118 enum { std, opt } status;
119 enum value_type value_type;
125 /* Simple helper macro. */
126 #define NELEMS(arr) ((sizeof (arr)) / (sizeof (arr[0])))
128 /* For some tricky stuff. */
129 #define NO_PAREN(Item, More...) Item, ## More
131 /* We have all categories defined in `categories.def'. Now construct
132 the description and data structure used for all categories. */
133 #define DEFINE_ELEMENT(Item, More...) { Item, ## More },
134 #define DEFINE_CATEGORY(category, name, items, postload) \
135 static struct cat_item category##_desc[] = \
140 #include "categories.def"
141 #undef DEFINE_CATEGORY
143 static struct category category[] =
145 #define DEFINE_CATEGORY(category, name, items, postload) \
146 [category] = { _NL_NUM_##category, name, NELEMS (category##_desc), \
148 #include "categories.def"
149 #undef DEFINE_CATEGORY
151 #define NCATEGORIES NELEMS (category)
154 /* Automatically set variable. */
155 extern const char *__progname;
157 /* helper function for extended name handling. */
158 extern void locale_special (const char *name, int show_category_name,
159 int show_keyword_name);
161 /* Prototypes for local functions. */
162 static void write_locales (void);
163 static void write_charmaps (void);
164 static void show_locale_vars (void);
165 static void show_info (const char *name);
169 main (int argc, char *argv[])
173 /* Set initial values for global variables. */
174 show_category_name = 0;
175 show_keyword_name = 0;
177 /* Set locale. Do not set LC_ALL because the other categories must
178 not be affected (according to POSIX.2). */
179 setlocale (LC_CTYPE, "");
180 setlocale (LC_MESSAGES, "");
182 /* Initialize the message catalog. */
183 textdomain (PACKAGE);
185 /* Parse and process arguments. */
186 argp_parse (&argp, argc, argv, 0, &remaining, NULL);
188 /* `-a' requests the names of all available locales. */
191 setlocale (LC_COLLATE, "");
196 /* `m' requests the names of all available charmaps. The names can be
197 used for the -f argument to localedef(1). */
198 if (do_charmaps != 0)
204 /* Specific information about the current locale are requested.
205 Change to this locale now. */
206 setlocale (LC_ALL, "");
208 /* If no real argument is given we have to print the contents of the
209 current locale definition variables. These are LANG and the LC_*. */
210 if (remaining == argc && show_keyword_name == 0 && show_category_name == 0)
216 /* Process all given names. */
217 while (remaining < argc)
218 show_info (argv[remaining++]);
224 /* Handle program arguments. */
226 parse_opt (int key, char *arg, struct argp_state *state)
234 show_category_name = 1;
240 show_keyword_name = 1;
243 return ARGP_ERR_UNKNOWN;
250 more_help (int key, const char *text, void *input)
254 case ARGP_KEY_HELP_EXTRA:
255 /* We print some extra information. */
256 return xstrdup (gettext ("\
257 Report bugs using the `glibcbug' script to <bugs@gnu.org>.\n"));
261 return (char *) text;
264 /* Print the version information. */
266 print_version (FILE *stream, struct argp_state *state)
268 fprintf (stream, "locale (GNU %s) %s\n", PACKAGE, VERSION);
269 fprintf (stream, gettext ("\
270 Copyright (C) %s Free Software Foundation, Inc.\n\
271 This is free software; see the source for copying conditions. There is NO\n\
272 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
274 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
278 /* Simple action function which prints arguments as strings. */
280 print_names (const void *nodep, VISIT value, int level)
282 if (value == postorder || value == leaf)
283 puts (*(char **) nodep);
287 /* Write the names of all available locales to stdout. We have some
288 sources of the information: the contents of the locale directory
289 and the locale.alias file. To avoid duplicates and print the
290 result is a reasonable order we put all entries is a search tree
291 and print them afterwards. */
295 void *all_data = NULL;
297 struct dirent *dirent;
299 size_t alias_path_len;
302 #define PUT(name) tsearch ((name), &all_data, \
303 (int (*) (const void *, const void *)) strcoll)
305 dir = opendir (LOCALEDIR);
308 error (1, errno, gettext ("cannot read locale directory `%s'"),
313 /* `POSIX' locale is always available (POSIX.2 4.34.3). */
315 /* And so is the "C" locale. */
318 /* Now we can look for all files in the directory. */
319 while ((dirent = readdir (dir)) != NULL)
320 if (strcmp (dirent->d_name, ".") != 0
321 && strcmp (dirent->d_name, "..") != 0)
324 #ifdef _DIRENT_HAVE_D_TYPE
325 if (dirent->d_type != DT_UNKNOWN && dirent->d_type != DT_LNK)
326 mode = DTTOIF (dirent->d_type);
331 char buf[sizeof (LOCALEDIR) + strlen (dirent->d_name) + 1];
333 stpcpy (stpcpy (stpcpy (buf, LOCALEDIR), "/"), dirent->d_name);
335 if (stat (buf, &st) < 0)
342 /* Test whether at least the LC_CTYPE data is there. Some
343 directories only contain translations. */
344 char buf[sizeof (LOCALEDIR) + strlen (dirent->d_name)
345 + sizeof "/LC_CTYPE"];
348 stpcpy (stpcpy (stpcpy (stpcpy (buf, LOCALEDIR), "/"),
352 if (stat (buf, &st) == 0 && S_ISREG (st.st_mode))
353 PUT (xstrdup (dirent->d_name));
359 /* Now read the locale.alias files. */
360 if (argz_create_sep (LOCALE_ALIAS_PATH, ':', &alias_path, &alias_path_len))
361 error (1, errno, gettext ("while preparing output"));
364 while ((entry = argz_next (alias_path, alias_path_len, entry)))
366 static const char aliasfile[] = "/locale.alias";
368 char full_name[strlen (entry) + sizeof aliasfile];
370 stpcpy (stpcpy (full_name, entry), aliasfile);
371 fp = fopen (full_name, "r");
373 /* Ignore non-existing files. */
378 /* It is a reasonable approach to use a fix buffer here
380 a) we are only interested in the first two fields
381 b) these fields must be usable as file names and so must
388 if (fgets (buf, BUFSIZ, fp) == NULL)
393 /* Ignore leading white space. */
394 while (isspace (cp[0]) && cp[0] != '\n')
397 /* A leading '#' signals a comment line. */
398 if (cp[0] != '\0' && cp[0] != '#' && cp[0] != '\n')
401 while (cp[0] != '\0' && !isspace (cp[0]))
403 /* Terminate alias name. */
407 /* Now look for the beginning of the value. */
408 while (isspace (cp[0]))
414 while (cp[0] != '\0' && !isspace (cp[0]))
416 /* Terminate value. */
419 /* This has to be done to make the following
420 test for the end of line possible. We are
421 looking for the terminating '\n' which do not
426 else if (cp[0] != '\0')
430 PUT (xstrdup (alias));
434 /* Possibly not the whole line fits into the buffer.
435 Ignore the rest of the line. */
436 while (strchr (cp, '\n') == NULL)
439 if (fgets (buf, BUFSIZ, fp) == NULL)
440 /* Make sure the inner loop will be left. The outer
441 loop will exit at the `feof' test. */
449 twalk (all_data, print_names);
453 /* Write the names of all available character maps to stdout. */
455 write_charmaps (void)
457 void *all_data = NULL;
461 /* Look for all files in the charmap directory. */
462 dir = charmap_opendir (CHARMAP_PATH);
466 while ((dirent = charmap_readdir (dir)) != NULL)
471 PUT (xstrdup (dirent));
473 aliases = charmap_aliases (CHARMAP_PATH, dirent);
476 /* Add the code_set_name and the aliases. */
477 for (p = aliases; *p; p++)
480 /* Add the code_set_name only. Most aliases are obsolete. */
486 charmap_free_aliases (aliases);
489 charmap_closedir (dir);
491 twalk (all_data, print_names);
495 /* We have to show the contents of the environments determining the
498 show_locale_vars (void)
501 const char *lcall = getenv ("LC_ALL");
502 const char *lang = getenv ("LANG") ? : "POSIX";
504 void get_source (const char *name)
506 char *val = getenv (name);
508 if ((lcall ?: "")[0] != '\0' || val == NULL)
509 printf ("%s=\"%s\"\n", name, (lcall ?: "")[0] ? lcall : lang);
511 printf ("%s=%s\n", name, val);
514 /* LANG has to be the first value. */
515 printf ("LANG=%s\n", lang);
517 /* Now all categories in an unspecified order. */
518 for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
519 if (cat_no != LC_ALL)
520 get_source (category[cat_no].name);
522 /* The last is the LC_ALL value. */
523 printf ("LC_ALL=%s\n", lcall ? : "");
527 /* Show the information request for NAME. */
529 show_info (const char *name)
533 void print_item (struct cat_item *item)
535 switch (item->value_type)
538 if (show_keyword_name)
539 printf ("%s=\"", item->name);
540 fputs (nl_langinfo (item->item_id) ? : "", stdout);
541 if (show_keyword_name)
550 if (show_keyword_name)
551 printf ("%s=\"", item->name);
553 for (cnt = 0; cnt < item->max - 1; ++cnt)
555 val = nl_langinfo (item->item_id + cnt);
561 val = nl_langinfo (item->item_id + cnt);
565 if (show_keyword_name)
573 const char *val = nl_langinfo (item->item_id) ? : "";
576 if (show_keyword_name)
577 printf ("%s=", item->name);
579 for (cnt = 0; cnt < item->max && *val != '\0'; ++cnt)
581 printf ("%s%s%s%s", first ? "" : ";",
582 show_keyword_name ? "\"" : "", val,
583 show_keyword_name ? "\"" : "");
584 val = strchr (val, '\0') + 1;
592 const char *val = nl_langinfo (item->item_id);
594 if (show_keyword_name)
595 printf ("%s=", item->name);
598 printf ("%d", *val == CHAR_MAX ? -1 : *val);
604 const char *val = nl_langinfo (item->item_id);
605 int cnt = val ? strlen (val) : 0;
607 if (show_keyword_name)
608 printf ("%s=", item->name);
612 printf ("%d;", *val == CHAR_MAX ? -1 : *val);
617 printf ("%d\n", cnt == 0 || *val == CHAR_MAX ? -1 : *val);
623 (unsigned int) (unsigned long int) nl_langinfo (item->item_id);
624 if (show_keyword_name)
625 printf ("%s=", item->name);
627 printf ("%d\n", val);
633 /* We don't print wide character information since the same
634 information is available in a multibyte string. */
641 for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
642 if (cat_no != LC_ALL)
646 if (strcmp (name, category[cat_no].name) == 0)
647 /* Print the whole category. */
649 if (show_category_name != 0)
650 puts (category[cat_no].name);
652 for (item_no = 0; item_no < category[cat_no].number; ++item_no)
653 print_item (&category[cat_no].item_desc[item_no]);
658 for (item_no = 0; item_no < category[cat_no].number; ++item_no)
659 if (strcmp (name, category[cat_no].item_desc[item_no].name) == 0)
661 if (show_category_name != 0)
662 puts (category[cat_no].name);
664 print_item (&category[cat_no].item_desc[item_no]);
669 /* The name is not a standard one.
670 For testing and perhaps advanced use allow some more symbols. */
671 locale_special (name, show_category_name, show_keyword_name);