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. */
41 #include "localeinfo.h"
44 /* If set print the name of the category. */
45 static int show_category_name;
47 /* If set print the name of the item. */
48 static int show_keyword_name;
50 /* Print names of all available locales. */
53 /* Print names of all available character maps. */
54 static int do_charmaps = 0;
56 /* Name and version of program. */
57 static void print_version (FILE *stream, struct argp_state *state);
58 void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
60 /* Definitions of arguments for argp functions. */
61 static const struct argp_option options[] =
63 { NULL, 0, NULL, 0, N_("System information:") },
64 { "all-locales", 'a', NULL, OPTION_NO_USAGE,
65 N_("Write names of available locales") },
66 { "charmaps", 'm', NULL, OPTION_NO_USAGE,
67 N_("Write names of available charmaps") },
68 { NULL, 0, NULL, 0, N_("Modify output format:") },
69 { "category-name", 'c', NULL, 0, N_("Write names of selected categories") },
70 { "keyword-name", 'k', NULL, 0, N_("Write names of selected keywords") },
71 { NULL, 0, NULL, 0, NULL }
74 /* Short description of program. */
75 static const char doc[] = N_("Get locale-specific information.");
77 /* Strings for arguments in help texts. */
78 static const char args_doc[] = N_("NAME\n[-a|-m]");
80 /* Prototype for option handler. */
81 static error_t parse_opt (int key, char *arg, struct argp_state *state);
83 /* Function to print some extra text in the help message. */
84 static char *more_help (int key, const char *text, void *input);
86 /* Data structure to communicate with argp functions. */
87 static struct argp argp =
89 options, parse_opt, args_doc, doc, NULL, more_help
93 /* We don't have these constants defined because we don't use them. Give
95 #define CTYPE_MB_CUR_MIN 0
96 #define CTYPE_MB_CUR_MAX 0
97 #define CTYPE_HASH_SIZE 0
98 #define CTYPE_HASH_LAYERS 0
100 #define CTYPE_TOUPPER_EB 0
101 #define CTYPE_TOLOWER_EB 0
102 #define CTYPE_TOUPPER_EL 0
103 #define CTYPE_TOLOWER_EL 0
105 /* Definition of the data structure which represents a category and its
116 enum { std, opt } status;
117 enum value_type value_type;
123 /* Simple helper macro. */
124 #define NELEMS(arr) ((sizeof (arr)) / (sizeof (arr[0])))
126 /* For some tricky stuff. */
127 #define NO_PAREN(Item, More...) Item, ## More
129 /* We have all categories defined in `categories.def'. Now construct
130 the description and data structure used for all categories. */
131 #define DEFINE_ELEMENT(Item, More...) { Item, ## More },
132 #define DEFINE_CATEGORY(category, name, items, postload) \
133 static struct cat_item category##_desc[] = \
138 #include "categories.def"
139 #undef DEFINE_CATEGORY
141 static struct category category[] =
143 #define DEFINE_CATEGORY(category, name, items, postload) \
144 [category] = { _NL_NUM_##category, name, NELEMS (category##_desc), \
146 #include "categories.def"
147 #undef DEFINE_CATEGORY
149 #define NCATEGORIES NELEMS (category)
152 /* Automatically set variable. */
153 extern const char *__progname;
155 /* helper function for extended name handling. */
156 extern void locale_special (const char *name, int show_category_name,
157 int show_keyword_name);
159 /* Prototypes for local functions. */
160 static void write_locales (void);
161 static void write_charmaps (void);
162 static void show_locale_vars (void);
163 static void show_info (const char *name);
167 main (int argc, char *argv[])
171 /* Set initial values for global variables. */
172 show_category_name = 0;
173 show_keyword_name = 0;
175 /* Set locale. Do not set LC_ALL because the other categories must
176 not be affected (according to POSIX.2). */
177 setlocale (LC_CTYPE, "");
178 setlocale (LC_MESSAGES, "");
180 /* Initialize the message catalog. */
181 textdomain (PACKAGE);
183 /* Parse and process arguments. */
184 argp_parse (&argp, argc, argv, 0, &remaining, NULL);
186 /* `-a' requests the names of all available locales. */
189 setlocale (LC_COLLATE, "");
194 /* `m' requests the names of all available charmaps. The names can be
195 used for the -f argument to localedef(1). */
196 if (do_charmaps != 0)
202 /* Specific information about the current locale are requested.
203 Change to this locale now. */
204 setlocale (LC_ALL, "");
206 /* If no real argument is given we have to print the contents of the
207 current locale definition variables. These are LANG and the LC_*. */
208 if (remaining == argc && show_keyword_name == 0 && show_category_name == 0)
214 /* Process all given names. */
215 while (remaining < argc)
216 show_info (argv[remaining++]);
222 /* Handle program arguments. */
224 parse_opt (int key, char *arg, struct argp_state *state)
232 show_category_name = 1;
238 show_keyword_name = 1;
241 return ARGP_ERR_UNKNOWN;
248 more_help (int key, const char *text, void *input)
252 case ARGP_KEY_HELP_EXTRA:
253 /* We print some extra information. */
254 return strdup (gettext ("\
255 Report bugs using the `glibcbug' script to <bugs@gnu.org>.\n"));
259 return (char *) text;
262 /* Print the version information. */
264 print_version (FILE *stream, struct argp_state *state)
266 fprintf (stream, "locale (GNU %s) %s\n", PACKAGE, VERSION);
267 fprintf (stream, gettext ("\
268 Copyright (C) %s Free Software Foundation, Inc.\n\
269 This is free software; see the source for copying conditions. There is NO\n\
270 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
272 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
276 /* Simple action function which prints arguments as strings. */
278 print_names (const void *nodep, VISIT value, int level)
280 if (value == postorder || value == leaf)
281 puts (*(char **) nodep);
285 /* Write the names of all available locales to stdout. We have some
286 sources of the information: the contents of the locale directory
287 and the locale.alias file. To avoid duplicates and print the
288 result is a reasonable order we put all entries is a search tree
289 and print them afterwards. */
293 void *all_data = NULL;
295 struct dirent *dirent;
297 size_t alias_path_len;
300 #define PUT(name) tsearch ((name), &all_data, \
301 (int (*) (const void *, const void *)) strcoll)
303 dir = opendir (LOCALEDIR);
306 error (1, errno, gettext ("cannot read locale directory `%s'"),
311 /* `POSIX' locale is always available (POSIX.2 4.34.3). */
313 /* And so is the "C" locale. */
316 /* Now we can look for all files in the directory. */
317 while ((dirent = readdir (dir)) != NULL)
318 if (strcmp (dirent->d_name, ".") != 0
319 && strcmp (dirent->d_name, "..") != 0)
322 #ifdef _DIRENT_HAVE_D_TYPE
323 if (dirent->d_type != DT_UNKNOWN)
324 mode = DTTOIF (dirent->d_type);
329 char buf[sizeof (LOCALEDIR) + strlen (dirent->d_name) + 1];
331 stpcpy (stpcpy (stpcpy (buf, LOCALEDIR), "/"), dirent->d_name);
333 if (stat (buf, &st) < 0)
340 /* Test whether at least the LC_CTYPE data is there. Some
341 directories only contain translations. */
342 char buf[sizeof (LOCALEDIR) + strlen (dirent->d_name)
343 + sizeof "/LC_CTYPE"];
346 stpcpy (stpcpy (stpcpy (stpcpy (buf, LOCALEDIR), "/"),
350 if (stat (buf, &st) == 0 && S_ISREG (st.st_mode))
351 PUT (strdup (dirent->d_name));
357 /* Now read the locale.alias files. */
358 if (argz_create_sep (LOCALE_ALIAS_PATH, ':', &alias_path, &alias_path_len))
359 error (1, errno, gettext ("while preparing output"));
362 while ((entry = argz_next (alias_path, alias_path_len, entry)))
364 static const char aliasfile[] = "/locale.alias";
366 char full_name[strlen (entry) + sizeof aliasfile];
368 stpcpy (stpcpy (full_name, entry), aliasfile);
369 fp = fopen (full_name, "r");
371 /* Ignore non-existing files. */
376 /* It is a reasonable approach to use a fix buffer here
378 a) we are only interested in the first two fields
379 b) these fields must be usable as file names and so must
386 if (fgets (buf, BUFSIZ, fp) == NULL)
391 /* Ignore leading white space. */
392 while (isspace (cp[0]) && cp[0] != '\n')
395 /* A leading '#' signals a comment line. */
396 if (cp[0] != '\0' && cp[0] != '#' && cp[0] != '\n')
399 while (cp[0] != '\0' && !isspace (cp[0]))
401 /* Terminate alias name. */
405 /* Now look for the beginning of the value. */
406 while (isspace (cp[0]))
412 while (cp[0] != '\0' && !isspace (cp[0]))
414 /* Terminate value. */
417 /* This has to be done to make the following
418 test for the end of line possible. We are
419 looking for the terminating '\n' which do not
424 else if (cp[0] != '\0')
428 PUT (strdup (alias));
432 /* Possibly not the whole line fits into the buffer.
433 Ignore the rest of the line. */
434 while (strchr (cp, '\n') == NULL)
437 if (fgets (buf, BUFSIZ, fp) == NULL)
438 /* Make sure the inner loop will be left. The outer
439 loop will exit at the `feof' test. */
447 twalk (all_data, print_names);
451 /* Write the names of all available character maps to stdout. */
453 write_charmaps (void)
455 void *all_data = NULL;
457 struct dirent *dirent;
459 dir = opendir (CHARMAP_PATH);
462 error (1, errno, gettext ("cannot read character map directory `%s'"),
467 /* Now we can look for all files in the directory. */
468 while ((dirent = readdir (dir)) != NULL)
469 if (strcmp (dirent->d_name, ".") != 0
470 && strcmp (dirent->d_name, "..") != 0)
475 #ifdef _DIRENT_HAVE_D_TYPE
476 if (dirent->d_type != DT_UNKNOWN)
477 mode = DTTOIF (dirent->d_type);
483 buf = alloca (sizeof (CHARMAP_PATH) + strlen (dirent->d_name) + 1);
485 stpcpy (stpcpy (stpcpy (buf, CHARMAP_PATH), "/"), dirent->d_name);
487 if (stat (buf, &st) < 0)
496 PUT (strdup (dirent->d_name));
498 /* Read the file and learn about the code set name. */
501 buf = alloca (sizeof (CHARMAP_PATH)
502 + strlen (dirent->d_name) + 1);
504 stpcpy (stpcpy (stpcpy (buf, CHARMAP_PATH), "/"),
508 fp = fopen (buf, "r");
517 if (fscanf (fp, " <code_set_name> %as", &name) == 1)
520 while (fgets (junk, sizeof junk, fp) != NULL
521 && strchr (junk, '\n') == NULL)
535 twalk (all_data, print_names);
539 /* We have to show the contents of the environments determining the
542 show_locale_vars (void)
545 const char *lcall = getenv ("LC_ALL");
546 const char *lang = getenv ("LANG") ? : "POSIX";
548 void get_source (const char *name)
550 char *val = getenv (name);
552 if ((lcall ?: "")[0] != '\0' || val == NULL)
553 printf ("%s=\"%s\"\n", name, (lcall ?: "")[0] ? lcall : lang);
555 printf ("%s=%s\n", name, val);
558 /* LANG has to be the first value. */
559 printf ("LANG=%s\n", lang);
561 /* Now all categories in an unspecified order. */
562 for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
563 if (cat_no != LC_ALL)
564 get_source (category[cat_no].name);
566 /* The last is the LC_ALL value. */
567 printf ("LC_ALL=%s\n", lcall ? : "");
571 /* Some of the "string" we print contain non-printable characters. We
574 print_escaped (const char *string)
576 const unsigned char *ch;
584 printf("<0x%02x>", *ch);
590 /* Show the information request for NAME. */
592 show_info (const char *name)
596 void print_item (struct cat_item *item)
598 if (show_keyword_name != 0)
599 printf ("%s=", item->name);
601 switch (item->value_type)
604 if (show_keyword_name)
606 print_escaped (nl_langinfo (item->item_id) ? : "");
607 if (show_keyword_name)
615 if (show_keyword_name)
618 for (cnt = 0; cnt < item->max - 1; ++cnt)
620 val = nl_langinfo (item->item_id + cnt);
626 val = nl_langinfo (item->item_id + cnt);
630 if (show_keyword_name)
637 const char *val = nl_langinfo (item->item_id) ? : "";
641 printf ("%s%s%s%s", first ? "" : ";",
642 show_keyword_name ? "\"" : "", val,
643 show_keyword_name ? "\"" : "");
644 val = strchr (val, '\0') + 1;
651 const char *val = nl_langinfo (item->item_id);
654 printf ("%d", *val == CHAR_MAX ? -1 : *val);
659 const char *val = nl_langinfo (item->item_id);
660 int cnt = val ? strlen (val) : 0;
664 printf ("%d;", *val == CHAR_MAX ? -1 : *val);
669 printf ("%d", cnt == 0 || *val == CHAR_MAX ? -1 : *val);
675 (unsigned int) (unsigned long int) nl_langinfo (item->item_id);
684 for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
685 if (cat_no != LC_ALL)
689 if (strcmp (name, category[cat_no].name) == 0)
690 /* Print the whole category. */
692 if (show_category_name != 0)
693 puts (category[cat_no].name);
695 for (item_no = 0; item_no < category[cat_no].number; ++item_no)
696 print_item (&category[cat_no].item_desc[item_no]);
701 for (item_no = 0; item_no < category[cat_no].number; ++item_no)
702 if (strcmp (name, category[cat_no].item_desc[item_no].name) == 0)
704 if (show_category_name != 0)
705 puts (category[cat_no].name);
707 print_item (&category[cat_no].item_desc[item_no]);
712 /* The name is not a standard one.
713 For testing and perhaps advanced use allow some more symbols. */
714 locale_special (name, show_category_name, show_keyword_name);