1 /* Implementation of the locale program according to POSIX 1003.2.
2 Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 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, in, check, out) \
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, in, check, out) \
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[])
169 /* Set initial values for global variables. */
170 show_category_name = 0;
171 show_keyword_name = 0;
173 /* Set locale. Do not set LC_ALL because the other categories must
174 not be affected (according to POSIX.2). */
175 setlocale (LC_CTYPE, "");
176 setlocale (LC_MESSAGES, "");
178 /* Initialize the message catalog. */
179 textdomain (PACKAGE);
181 /* Parse and process arguments. */
182 argp_parse (&argp, argc, argv, 0, 0, NULL);
184 /* `-a' requests the names of all available locales. */
187 setlocale (LC_COLLATE, "");
192 /* `m' requests the names of all available charmaps. The names can be
193 used for the -f argument to localedef(3). */
194 if (do_charmaps != 0)
200 /* Specific information about the current locale are requested.
201 Change to this locale now. */
202 setlocale (LC_ALL, "");
204 /* If no real argument is given we have to print the contents of the
205 current locale definition variables. These are LANG and the LC_*. */
206 if (optind == argc && show_keyword_name == 0 && show_category_name == 0)
212 /* Process all given names. */
213 while (optind < argc)
214 show_info (argv[optind++]);
220 /* Handle program arguments. */
222 parse_opt (int key, char *arg, struct argp_state *state)
230 show_category_name = 1;
236 show_keyword_name = 1;
239 return ARGP_ERR_UNKNOWN;
246 more_help (int key, const char *text, void *input)
250 case ARGP_KEY_HELP_EXTRA:
251 /* We print some extra information. */
252 return strdup (gettext ("\
253 Report bugs using the `glibcbug' script to <bugs@gnu.ai.mit.edu>.\n"));
257 return (char *) text;
260 /* Print the version information. */
262 print_version (FILE *stream, struct argp_state *state)
264 fprintf (stream, "locale (GNU %s) %s\n", PACKAGE, VERSION);
265 fprintf (stream, gettext ("\
266 Copyright (C) %s Free Software Foundation, Inc.\n\
267 This is free software; see the source for copying conditions. There is NO\n\
268 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
269 "), "1995, 1996, 1997");
270 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
274 /* Simple action function which prints arguments as strings. */
276 print_names (const void *nodep, VISIT value, int level)
278 if (value == postorder || value == leaf)
279 puts (*(char **) nodep);
283 /* Write the names of all available locales to stdout. We have some
284 sources of the information: the contents of the locale directory
285 and the locale.alias file. To avoid duplicates and print the
286 result is a reasonable order we put all entries is a search tree
287 and print them afterwards. */
291 void *all_data = NULL;
293 struct dirent *dirent;
295 size_t alias_path_len;
298 #define PUT(name) tsearch ((name), &all_data, \
299 (int (*) (const void *, const void *)) strcoll)
301 dir = opendir (LOCALEDIR);
304 error (1, errno, gettext ("cannot read locale directory `%s'"),
309 /* `POSIX' locale is always available (POSIX.2 4.34.3). */
311 /* And so is the "C" locale. */
314 /* Now we can look for all files in the directory. */
315 while ((dirent = readdir (dir)) != NULL)
316 if (strcmp (dirent->d_name, ".") != 0
317 && strcmp (dirent->d_name, "..") != 0)
320 #ifdef _DIRENT_HAVE_D_TYPE
321 if (dirent->d_type != DT_UNKNOWN)
322 mode = DTTOIF (dirent->d_type);
327 char buf[sizeof (LOCALEDIR) + strlen (dirent->d_name) + 1];
329 stpcpy (stpcpy (stpcpy (buf, LOCALEDIR), "/"), dirent->d_name);
331 if (stat (buf, &st) < 0)
337 PUT (strdup (dirent->d_name));
342 /* Now read the locale.alias files. */
343 if (argz_create_sep (LOCALE_ALIAS_PATH, ':', &alias_path, &alias_path_len))
344 error (1, errno, gettext ("while preparing output"));
347 while ((entry = argz_next (alias_path, alias_path_len, entry)))
349 static const char aliasfile[] = "/locale.alias";
351 char full_name[strlen (entry) + sizeof aliasfile];
353 stpcpy (stpcpy (full_name, entry), aliasfile);
354 fp = fopen (full_name, "r");
356 /* Ignore non-existing files. */
361 /* It is a reasonable approach to use a fix buffer here
363 a) we are only interested in the first two fields
364 b) these fields must be usable as file names and so must
371 if (fgets (buf, BUFSIZ, fp) == NULL)
376 /* Ignore leading white space. */
377 while (isspace (cp[0]))
380 /* A leading '#' signals a comment line. */
381 if (cp[0] != '\0' && cp[0] != '#')
384 while (cp[0] != '\0' && !isspace (cp[0]))
386 /* Terminate alias name. */
390 /* Now look for the beginning of the value. */
391 while (isspace (cp[0]))
397 while (cp[0] != '\0' && !isspace (cp[0]))
399 /* Terminate value. */
402 /* This has to be done to make the following
403 test for the end of line possible. We are
404 looking for the terminating '\n' which do not
409 else if (cp[0] != '\0')
413 PUT (strdup (alias));
417 /* Possibly not the whole line fits into the buffer.
418 Ignore the rest of the line. */
419 while (strchr (cp, '\n') == NULL)
422 if (fgets (buf, BUFSIZ, fp) == NULL)
423 /* Make sure the inner loop will be left. The outer
424 loop will exit at the `feof' test. */
432 twalk (all_data, print_names);
436 /* Write the names of all available character maps to stdout. */
438 write_charmaps (void)
440 void *all_data = NULL;
442 struct dirent *dirent;
444 dir = opendir (CHARMAP_PATH);
447 error (1, errno, gettext ("cannot read character map directory `%s'"),
452 /* Now we can look for all files in the directory. */
453 while ((dirent = readdir (dir)) != NULL)
454 if (strcmp (dirent->d_name, ".") != 0
455 && strcmp (dirent->d_name, "..") != 0)
458 #ifdef _DIRENT_HAVE_D_TYPE
459 if (dirent->d_type != DT_UNKNOWN)
460 mode = DTTOIF (dirent->d_type);
465 char buf[sizeof (CHARMAP_PATH) + strlen (dirent->d_name) + 1];
467 stpcpy (stpcpy (stpcpy (buf, CHARMAP_PATH), "/"), dirent->d_name);
469 if (stat (buf, &st) < 0)
475 PUT (strdup (dirent->d_name));
480 twalk (all_data, print_names);
484 /* We have to show the contents of the environments determining the
487 show_locale_vars (void)
490 const char *lcall = getenv ("LC_ALL");
491 const char *lang = getenv ("LANG") ? : "POSIX";
493 void get_source (const char *name)
495 char *val = getenv (name);
497 if (lcall != NULL || val == NULL)
498 printf ("%s=\"%s\"\n", name, lcall ? : lang);
500 printf ("%s=%s\n", name, val);
503 /* LANG has to be the first value. */
504 printf ("LANG=%s\n", lang);
506 /* Now all categories in an unspecified order. */
507 for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
508 get_source (category[cat_no].name);
510 /* The last is the LC_ALL value. */
511 printf ("LC_ALL=%s\n", lcall ? : "");
515 /* Show the information request for NAME. */
517 show_info (const char *name)
521 void print_item (struct cat_item *item)
523 if (show_keyword_name != 0)
524 printf ("%s=", item->name);
526 switch (item->value_type)
529 printf ("%s%s%s", show_keyword_name ? "\"" : "",
530 nl_langinfo (item->item_id) ? : "",
531 show_keyword_name ? "\"" : "");
538 if (show_keyword_name)
541 for (cnt = 0; cnt < item->max - 1; ++cnt)
543 val = nl_langinfo (item->item_id + cnt);
544 printf ("%s;", val ? : "");
547 val = nl_langinfo (item->item_id + cnt);
548 printf ("%s", val ? : "");
550 if (show_keyword_name)
556 const char *val = nl_langinfo (item->item_id);
559 printf ("%d", *val == CHAR_MAX ? -1 : *val);
564 const char *val = nl_langinfo (item->item_id);
565 int cnt = val ? strlen (val) : 0;
569 printf ("%d;", *val == CHAR_MAX ? -1 : *val);
574 printf ("%d", cnt == 0 || *val == CHAR_MAX ? -1 : *val);
579 unsigned int val = (unsigned int) nl_langinfo (item->item_id);
588 for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
592 if (strcmp (name, category[cat_no].name) == 0)
593 /* Print the whole category. */
595 if (show_category_name != 0)
596 puts (category[cat_no].name);
598 for (item_no = 0; item_no < category[cat_no].number; ++item_no)
599 print_item (&category[cat_no].item_desc[item_no]);
604 for (item_no = 0; item_no < category[cat_no].number; ++item_no)
605 if (strcmp (name, category[cat_no].item_desc[item_no].name) == 0)
607 if (show_category_name != 0)
608 puts (category[cat_no].name);
610 print_item (&category[cat_no].item_desc[item_no]);
615 /* The name is not a standard one.
616 For testing and perhaps advanced use allow some more symbols. */
617 locale_special (name, show_category_name, show_keyword_name);