update from main archive 970225
[platform/upstream/glibc.git] / locale / programs / locale.c
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.
5
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.
10
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.
15
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.  */
20
21 #ifdef HAVE_CONFIG_H
22 # include <config.h>
23 #endif
24
25 #include <argp.h>
26 #include <argz.h>
27 #include <dirent.h>
28 #include <errno.h>
29 #include <error.h>
30 #include <langinfo.h>
31 #include <libintl.h>
32 #include <limits.h>
33 #include <locale.h>
34 #include <search.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39 #include <sys/stat.h>
40
41 #include "localeinfo.h"
42
43
44 /* If set print the name of the category.  */
45 static int show_category_name;
46
47 /* If set print the name of the item.  */
48 static int show_keyword_name;
49
50 /* Print names of all available locales.  */
51 static int do_all;
52
53 /* Print names of all available character maps.  */
54 static int do_charmaps = 0;
55
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;
59
60 /* Definitions of arguments for argp functions.  */
61 static const struct argp_option options[] =
62 {
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 }
72 };
73
74 /* Short description of program.  */
75 static const char doc[] = N_("Get locale-specific information.");
76
77 /* Strings for arguments in help texts.  */
78 static const char args_doc[] = N_("NAME\n[-a|-m]");
79
80 /* Prototype for option handler.  */
81 static error_t parse_opt (int key, char *arg, struct argp_state *state);
82
83 /* Function to print some extra text in the help message.  */
84 static char *more_help (int key, const char *text, void *input);
85
86 /* Data structure to communicate with argp functions.  */
87 static struct argp argp =
88 {
89   options, parse_opt, args_doc, doc, NULL, more_help
90 };
91
92
93 /* We don't have these constants defined because we don't use them.  Give
94    default values.  */
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
99 #define CTYPE_CLASS 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
104
105 /* Definition of the data structure which represents a category and its
106    items.  */
107 struct category
108 {
109   int cat_id;
110   const char *name;
111   size_t number;
112   struct cat_item
113   {
114     int item_id;
115     const char *name;
116     enum { std, opt } status;
117     enum value_type value_type;
118     int min;
119     int max;
120   } *item_desc;
121 };
122
123 /* Simple helper macro.  */
124 #define NELEMS(arr) ((sizeof (arr)) / (sizeof (arr[0])))
125
126 /* For some tricky stuff.  */
127 #define NO_PAREN(Item, More...) Item, ## More
128
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[] =                                \
134       {                                                                       \
135         NO_PAREN items                                                        \
136       };
137
138 #include "categories.def"
139 #undef DEFINE_CATEGORY
140
141 static struct category category[] =
142   {
143 #define DEFINE_CATEGORY(category, name, items, postload, in, check, out)      \
144     [category] = { _NL_NUM_##category, name, NELEMS (category##_desc),        \
145                    category##_desc },
146 #include "categories.def"
147 #undef DEFINE_CATEGORY
148   };
149 #define NCATEGORIES NELEMS (category)
150
151
152 /* Automatically set variable.  */
153 extern const char *__progname;
154
155 /* helper function for extended name handling.  */
156 extern void locale_special (const char *name, int show_category_name,
157                             int show_keyword_name);
158
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);
164
165
166 int
167 main (int argc, char *argv[])
168 {
169   int remaining;
170
171   /* Set initial values for global variables.  */
172   show_category_name = 0;
173   show_keyword_name = 0;
174
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, "");
179
180   /* Initialize the message catalog.  */
181   textdomain (PACKAGE);
182
183   /* Parse and process arguments.  */
184   argp_parse (&argp, argc, argv, 0, &remaining, NULL);
185
186   /* `-a' requests the names of all available locales.  */
187   if (do_all != 0)
188     {
189       setlocale (LC_COLLATE, "");
190       write_locales ();
191       exit (EXIT_SUCCESS);
192     }
193
194   /* `m' requests the names of all available charmaps.  The names can be
195      used for the -f argument to localedef(3).  */
196   if (do_charmaps != 0)
197     {
198       write_charmaps ();
199       exit (EXIT_SUCCESS);
200     }
201
202   /* Specific information about the current locale are requested.
203      Change to this locale now.  */
204   setlocale (LC_ALL, "");
205
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)
209     {
210       show_locale_vars ();
211       exit (EXIT_SUCCESS);
212     }
213
214   /* Process all given names.  */
215   while (remaining <  argc)
216     show_info (argv[remaining++]);
217
218   exit (EXIT_SUCCESS);
219 }
220
221
222 /* Handle program arguments.  */
223 static error_t
224 parse_opt (int key, char *arg, struct argp_state *state)
225 {
226   switch (key)
227     {
228     case 'a':
229       do_all = 1;
230       break;
231     case 'c':
232       show_category_name = 1;
233       break;
234     case 'm':
235       do_charmaps = 1;
236       break;
237     case 'k':
238       show_keyword_name = 1;
239       break;
240     default:
241       return ARGP_ERR_UNKNOWN;
242     }
243   return 0;
244 }
245
246
247 static char *
248 more_help (int key, const char *text, void *input)
249 {
250   switch (key)
251     {
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.ai.mit.edu>.\n"));
256     default:
257       break;
258     }
259   return (char *) text;
260 }
261
262 /* Print the version information.  */
263 static void
264 print_version (FILE *stream, struct argp_state *state)
265 {
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\
271 "), "1995, 1996, 1997");
272   fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
273 }
274
275
276 /* Simple action function which prints arguments as strings.  */
277 static void
278 print_names (const void *nodep, VISIT value, int level)
279 {
280   if (value == postorder || value == leaf)
281     puts (*(char **) nodep);
282 }
283
284
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.  */
290 static void
291 write_locales (void)
292 {
293   void *all_data = NULL;
294   DIR *dir;
295   struct dirent *dirent;
296   char *alias_path;
297   size_t alias_path_len;
298   char *entry;
299
300 #define PUT(name) tsearch ((name), &all_data, \
301                            (int (*) (const void *, const void *)) strcoll)
302
303   dir = opendir (LOCALEDIR);
304   if (dir == NULL)
305     {
306       error (1, errno, gettext ("cannot read locale directory `%s'"),
307              LOCALEDIR);
308       return;
309     }
310
311   /* `POSIX' locale is always available (POSIX.2 4.34.3).  */
312   PUT ("POSIX");
313   /* And so is the "C" locale.  */
314   PUT ("C");
315
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)
320       {
321         mode_t mode;
322 #ifdef _DIRENT_HAVE_D_TYPE
323         if (dirent->d_type != DT_UNKNOWN)
324           mode = DTTOIF (dirent->d_type);
325         else
326 #endif
327           {
328             struct stat st;
329             char buf[sizeof (LOCALEDIR) + strlen (dirent->d_name) + 1];
330
331             stpcpy (stpcpy (stpcpy (buf, LOCALEDIR), "/"), dirent->d_name);
332
333             if (stat (buf, &st) < 0)
334               continue;
335             mode = st.st_mode;
336           }
337
338         if (S_ISDIR (mode))
339           PUT (strdup (dirent->d_name));
340       }
341
342   closedir (dir);
343
344   /* Now read the locale.alias files.  */
345   if (argz_create_sep (LOCALE_ALIAS_PATH, ':', &alias_path, &alias_path_len))
346     error (1, errno, gettext ("while preparing output"));
347
348   entry = NULL;
349   while ((entry = argz_next (alias_path, alias_path_len, entry)))
350     {
351       static const char aliasfile[] = "/locale.alias";
352       FILE *fp;
353       char full_name[strlen (entry) + sizeof aliasfile];
354
355       stpcpy (stpcpy (full_name, entry), aliasfile);
356       fp = fopen (full_name, "r");
357       if (fp == NULL)
358         /* Ignore non-existing files.  */
359         continue;
360
361       while (! feof (fp))
362         {
363           /* It is a reasonable approach to use a fix buffer here
364              because
365              a) we are only interested in the first two fields
366              b) these fields must be usable as file names and so must
367                 not be that long  */
368           char buf[BUFSIZ];
369           char *alias;
370           char *value;
371           char *cp;
372
373           if (fgets (buf, BUFSIZ, fp) == NULL)
374             /* EOF reached.  */
375             break;
376
377           cp = buf;
378           /* Ignore leading white space.  */
379           while (isspace (cp[0]))
380             ++cp;
381
382           /* A leading '#' signals a comment line.  */
383           if (cp[0] != '\0' && cp[0] != '#')
384             {
385               alias = cp++;
386               while (cp[0] != '\0' && !isspace (cp[0]))
387                 ++cp;
388               /* Terminate alias name.  */
389               if (cp[0] != '\0')
390                 *cp++ = '\0';
391
392               /* Now look for the beginning of the value.  */
393               while (isspace (cp[0]))
394                 ++cp;
395
396               if (cp[0] != '\0')
397                 {
398                   value = cp++;
399                   while (cp[0] != '\0' && !isspace (cp[0]))
400                     ++cp;
401                   /* Terminate value.  */
402                   if (cp[0] == '\n')
403                     {
404                       /* This has to be done to make the following
405                          test for the end of line possible.  We are
406                          looking for the terminating '\n' which do not
407                          overwrite here.  */
408                       *cp++ = '\0';
409                       *cp = '\n';
410                     }
411                   else if (cp[0] != '\0')
412                     *cp++ = '\0';
413
414                   /* Add the alias.  */
415                   PUT (strdup (alias));
416                 }
417             }
418
419           /* Possibly not the whole line fits into the buffer.
420              Ignore the rest of the line.  */
421           while (strchr (cp, '\n') == NULL)
422             {
423               cp = buf;
424               if (fgets (buf, BUFSIZ, fp) == NULL)
425                 /* Make sure the inner loop will be left.  The outer
426                    loop will exit at the `feof' test.  */
427                 *cp = '\n';
428             }
429         }
430
431       fclose (fp);
432     }
433
434   twalk (all_data, print_names);
435 }
436
437
438 /* Write the names of all available character maps to stdout.  */
439 static void
440 write_charmaps (void)
441 {
442   void *all_data = NULL;
443   DIR *dir;
444   struct dirent *dirent;
445
446   dir = opendir (CHARMAP_PATH);
447   if (dir == NULL)
448     {
449       error (1, errno, gettext ("cannot read character map directory `%s'"),
450              CHARMAP_PATH);
451       return;
452     }
453
454   /* Now we can look for all files in the directory.  */
455   while ((dirent = readdir (dir)) != NULL)
456     if (strcmp (dirent->d_name, ".") != 0
457         && strcmp (dirent->d_name, "..") != 0)
458       {
459         mode_t mode;
460 #ifdef _DIRENT_HAVE_D_TYPE
461         if (dirent->d_type != DT_UNKNOWN)
462           mode = DTTOIF (dirent->d_type);
463         else
464 #endif
465           {
466             struct stat st;
467             char buf[sizeof (CHARMAP_PATH) + strlen (dirent->d_name) + 1];
468
469             stpcpy (stpcpy (stpcpy (buf, CHARMAP_PATH), "/"), dirent->d_name);
470
471             if (stat (buf, &st) < 0)
472               continue;
473             mode = st.st_mode;
474           }
475
476         if (S_ISREG (mode))
477           PUT (strdup (dirent->d_name));
478       }
479
480   closedir (dir);
481
482   twalk (all_data, print_names);
483 }
484
485
486 /* We have to show the contents of the environments determining the
487    locale.  */
488 static void
489 show_locale_vars (void)
490 {
491   size_t cat_no;
492   const char *lcall = getenv ("LC_ALL");
493   const char *lang = getenv ("LANG") ? : "POSIX";
494
495   void get_source (const char *name)
496     {
497       char *val = getenv (name);
498
499       if (lcall != NULL || val == NULL)
500         printf ("%s=\"%s\"\n", name, lcall ? : lang);
501       else
502         printf ("%s=%s\n", name, val);
503     }
504
505   /* LANG has to be the first value.  */
506   printf ("LANG=%s\n", lang);
507
508   /* Now all categories in an unspecified order.  */
509   for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
510     get_source (category[cat_no].name);
511
512   /* The last is the LC_ALL value.  */
513   printf ("LC_ALL=%s\n", lcall ? : "");
514 }
515
516
517 /* Show the information request for NAME.  */
518 static void
519 show_info (const char *name)
520 {
521   size_t cat_no;
522
523   void print_item (struct cat_item *item)
524     {
525       if (show_keyword_name != 0)
526         printf ("%s=", item->name);
527
528       switch (item->value_type)
529         {
530         case string:
531           printf ("%s%s%s", show_keyword_name ? "\"" : "",
532                   nl_langinfo (item->item_id) ? : "",
533                   show_keyword_name ? "\"" : "");
534           break;
535         case stringarray:
536           {
537             int cnt;
538             const char *val;
539
540             if (show_keyword_name)
541               putchar ('"');
542
543             for (cnt = 0; cnt < item->max - 1; ++cnt)
544               {
545                 val = nl_langinfo (item->item_id + cnt);
546                 printf ("%s;", val ? : "");
547               }
548
549             val = nl_langinfo (item->item_id + cnt);
550             printf ("%s", val ? : "");
551
552             if (show_keyword_name)
553               putchar ('"');
554           }
555           break;
556         case byte:
557           {
558             const char *val = nl_langinfo (item->item_id);
559
560             if (val != NULL)
561               printf ("%d", *val == CHAR_MAX ? -1 : *val);
562           }
563           break;
564         case bytearray:
565           {
566             const char *val = nl_langinfo (item->item_id);
567             int cnt = val ? strlen (val) : 0;
568
569             while (cnt > 1)
570               {
571                 printf ("%d;", *val == CHAR_MAX ? -1 : *val);
572                 --cnt;
573                 ++val;
574               }
575
576             printf ("%d", cnt == 0 || *val == CHAR_MAX ? -1 : *val);
577           }
578           break;
579         case word:
580           {
581             unsigned int val = (unsigned int) nl_langinfo (item->item_id);
582             printf ("%d", val);
583           }
584           break;
585         default:
586         }
587       putchar ('\n');
588     }
589
590   for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
591     {
592       size_t item_no;
593
594       if (strcmp (name, category[cat_no].name) == 0)
595         /* Print the whole category.  */
596         {
597           if (show_category_name != 0)
598             puts (category[cat_no].name);
599
600           for (item_no = 0; item_no < category[cat_no].number; ++item_no)
601             print_item (&category[cat_no].item_desc[item_no]);
602
603           return;
604         }
605
606       for (item_no = 0; item_no < category[cat_no].number; ++item_no)
607         if (strcmp (name, category[cat_no].item_desc[item_no].name) == 0)
608           {
609             if (show_category_name != 0)
610               puts (category[cat_no].name);
611
612             print_item (&category[cat_no].item_desc[item_no]);
613             return;
614           }
615     }
616
617   /* The name is not a standard one.
618      For testing and perhaps advanced use allow some more symbols.  */
619   locale_special (name, show_category_name, show_keyword_name);
620 }