Update.
[platform/upstream/glibc.git] / locale / programs / locale.c
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.
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 <sys/stat.h>
39
40 #include "localeinfo.h"
41 #include "charmap-dir.h"
42
43 extern char *xstrdup (const char *__str);
44
45
46 /* If set print the name of the category.  */
47 static int show_category_name;
48
49 /* If set print the name of the item.  */
50 static int show_keyword_name;
51
52 /* Print names of all available locales.  */
53 static int do_all;
54
55 /* Print names of all available character maps.  */
56 static int do_charmaps = 0;
57
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;
61
62 /* Definitions of arguments for argp functions.  */
63 static const struct argp_option options[] =
64 {
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 }
74 };
75
76 /* Short description of program.  */
77 static const char doc[] = N_("Get locale-specific information.");
78
79 /* Strings for arguments in help texts.  */
80 static const char args_doc[] = N_("NAME\n[-a|-m]");
81
82 /* Prototype for option handler.  */
83 static error_t parse_opt (int key, char *arg, struct argp_state *state);
84
85 /* Function to print some extra text in the help message.  */
86 static char *more_help (int key, const char *text, void *input);
87
88 /* Data structure to communicate with argp functions.  */
89 static struct argp argp =
90 {
91   options, parse_opt, args_doc, doc, NULL, more_help
92 };
93
94
95 /* We don't have these constants defined because we don't use them.  Give
96    default values.  */
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
106
107 /* Definition of the data structure which represents a category and its
108    items.  */
109 struct category
110 {
111   int cat_id;
112   const char *name;
113   size_t number;
114   struct cat_item
115   {
116     int item_id;
117     const char *name;
118     enum { std, opt } status;
119     enum value_type value_type;
120     int min;
121     int max;
122   } *item_desc;
123 };
124
125 /* Simple helper macro.  */
126 #define NELEMS(arr) ((sizeof (arr)) / (sizeof (arr[0])))
127
128 /* For some tricky stuff.  */
129 #define NO_PAREN(Item, More...) Item, ## More
130
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[] =                                \
136       {                                                                       \
137         NO_PAREN items                                                        \
138       };
139
140 #include "categories.def"
141 #undef DEFINE_CATEGORY
142
143 static struct category category[] =
144   {
145 #define DEFINE_CATEGORY(category, name, items, postload) \
146     [category] = { _NL_NUM_##category, name, NELEMS (category##_desc),        \
147                    category##_desc },
148 #include "categories.def"
149 #undef DEFINE_CATEGORY
150   };
151 #define NCATEGORIES NELEMS (category)
152
153
154 /* Automatically set variable.  */
155 extern const char *__progname;
156
157 /* helper function for extended name handling.  */
158 extern void locale_special (const char *name, int show_category_name,
159                             int show_keyword_name);
160
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);
166
167
168 int
169 main (int argc, char *argv[])
170 {
171   int remaining;
172
173   /* Set initial values for global variables.  */
174   show_category_name = 0;
175   show_keyword_name = 0;
176
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, "");
181
182   /* Initialize the message catalog.  */
183   textdomain (PACKAGE);
184
185   /* Parse and process arguments.  */
186   argp_parse (&argp, argc, argv, 0, &remaining, NULL);
187
188   /* `-a' requests the names of all available locales.  */
189   if (do_all != 0)
190     {
191       setlocale (LC_COLLATE, "");
192       write_locales ();
193       exit (EXIT_SUCCESS);
194     }
195
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)
199     {
200       write_charmaps ();
201       exit (EXIT_SUCCESS);
202     }
203
204   /* Specific information about the current locale are requested.
205      Change to this locale now.  */
206   setlocale (LC_ALL, "");
207
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)
211     {
212       show_locale_vars ();
213       exit (EXIT_SUCCESS);
214     }
215
216   /* Process all given names.  */
217   while (remaining <  argc)
218     show_info (argv[remaining++]);
219
220   exit (EXIT_SUCCESS);
221 }
222
223
224 /* Handle program arguments.  */
225 static error_t
226 parse_opt (int key, char *arg, struct argp_state *state)
227 {
228   switch (key)
229     {
230     case 'a':
231       do_all = 1;
232       break;
233     case 'c':
234       show_category_name = 1;
235       break;
236     case 'm':
237       do_charmaps = 1;
238       break;
239     case 'k':
240       show_keyword_name = 1;
241       break;
242     default:
243       return ARGP_ERR_UNKNOWN;
244     }
245   return 0;
246 }
247
248
249 static char *
250 more_help (int key, const char *text, void *input)
251 {
252   switch (key)
253     {
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"));
258     default:
259       break;
260     }
261   return (char *) text;
262 }
263
264 /* Print the version information.  */
265 static void
266 print_version (FILE *stream, struct argp_state *state)
267 {
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\
273 "), "2000");
274   fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
275 }
276
277
278 /* Simple action function which prints arguments as strings.  */
279 static void
280 print_names (const void *nodep, VISIT value, int level)
281 {
282   if (value == postorder || value == leaf)
283     puts (*(char **) nodep);
284 }
285
286
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.  */
292 static void
293 write_locales (void)
294 {
295   void *all_data = NULL;
296   DIR *dir;
297   struct dirent *dirent;
298   char *alias_path;
299   size_t alias_path_len;
300   char *entry;
301
302 #define PUT(name) tsearch ((name), &all_data, \
303                            (int (*) (const void *, const void *)) strcoll)
304
305   dir = opendir (LOCALEDIR);
306   if (dir == NULL)
307     {
308       error (1, errno, gettext ("cannot read locale directory `%s'"),
309              LOCALEDIR);
310       return;
311     }
312
313   /* `POSIX' locale is always available (POSIX.2 4.34.3).  */
314   PUT ("POSIX");
315   /* And so is the "C" locale.  */
316   PUT ("C");
317
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)
322       {
323         mode_t mode;
324 #ifdef _DIRENT_HAVE_D_TYPE
325         if (dirent->d_type != DT_UNKNOWN && dirent->d_type != DT_LNK)
326           mode = DTTOIF (dirent->d_type);
327         else
328 #endif
329           {
330             struct stat st;
331             char buf[sizeof (LOCALEDIR) + strlen (dirent->d_name) + 1];
332
333             stpcpy (stpcpy (stpcpy (buf, LOCALEDIR), "/"), dirent->d_name);
334
335             if (stat (buf, &st) < 0)
336               continue;
337             mode = st.st_mode;
338           }
339
340         if (S_ISDIR (mode))
341           {
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"];
346             struct stat st;
347
348             stpcpy (stpcpy (stpcpy (stpcpy (buf, LOCALEDIR), "/"),
349                             dirent->d_name),
350                     "/LC_CTYPE");
351
352             if (stat (buf, &st) == 0 && S_ISREG (st.st_mode))
353               PUT (xstrdup (dirent->d_name));
354           }
355       }
356
357   closedir (dir);
358
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"));
362
363   entry = NULL;
364   while ((entry = argz_next (alias_path, alias_path_len, entry)))
365     {
366       static const char aliasfile[] = "/locale.alias";
367       FILE *fp;
368       char full_name[strlen (entry) + sizeof aliasfile];
369
370       stpcpy (stpcpy (full_name, entry), aliasfile);
371       fp = fopen (full_name, "r");
372       if (fp == NULL)
373         /* Ignore non-existing files.  */
374         continue;
375
376       while (! feof (fp))
377         {
378           /* It is a reasonable approach to use a fix buffer here
379              because
380              a) we are only interested in the first two fields
381              b) these fields must be usable as file names and so must
382                 not be that long  */
383           char buf[BUFSIZ];
384           char *alias;
385           char *value;
386           char *cp;
387
388           if (fgets (buf, BUFSIZ, fp) == NULL)
389             /* EOF reached.  */
390             break;
391
392           cp = buf;
393           /* Ignore leading white space.  */
394           while (isspace (cp[0]) && cp[0] != '\n')
395             ++cp;
396
397           /* A leading '#' signals a comment line.  */
398           if (cp[0] != '\0' && cp[0] != '#' && cp[0] != '\n')
399             {
400               alias = cp++;
401               while (cp[0] != '\0' && !isspace (cp[0]))
402                 ++cp;
403               /* Terminate alias name.  */
404               if (cp[0] != '\0')
405                 *cp++ = '\0';
406
407               /* Now look for the beginning of the value.  */
408               while (isspace (cp[0]))
409                 ++cp;
410
411               if (cp[0] != '\0')
412                 {
413                   value = cp++;
414                   while (cp[0] != '\0' && !isspace (cp[0]))
415                     ++cp;
416                   /* Terminate value.  */
417                   if (cp[0] == '\n')
418                     {
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
422                          overwrite here.  */
423                       *cp++ = '\0';
424                       *cp = '\n';
425                     }
426                   else if (cp[0] != '\0')
427                     *cp++ = '\0';
428
429                   /* Add the alias.  */
430                   PUT (xstrdup (alias));
431                 }
432             }
433
434           /* Possibly not the whole line fits into the buffer.
435              Ignore the rest of the line.  */
436           while (strchr (cp, '\n') == NULL)
437             {
438               cp = buf;
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.  */
442                 *cp = '\n';
443             }
444         }
445
446       fclose (fp);
447     }
448
449   twalk (all_data, print_names);
450 }
451
452
453 /* Write the names of all available character maps to stdout.  */
454 static void
455 write_charmaps (void)
456 {
457   void *all_data = NULL;
458   CHARMAP_DIR *dir;
459   const char *dirent;
460
461   /* Look for all files in the charmap directory.  */
462   dir = charmap_opendir (CHARMAP_PATH);
463   if (dir == NULL)
464     return;
465
466   while ((dirent = charmap_readdir (dir)) != NULL)
467     {
468       char **aliases;
469       char **p;
470
471       PUT (xstrdup (dirent));
472
473       aliases = charmap_aliases (CHARMAP_PATH, dirent);
474
475 #if 0
476       /* Add the code_set_name and the aliases.  */
477       for (p = aliases; *p; p++)
478         PUT (xstrdup (*p));
479 #else
480       /* Add the code_set_name only.  Most aliases are obsolete.  */
481       p = aliases;
482       if (*p)
483         PUT (xstrdup (*p));
484 #endif
485
486       charmap_free_aliases (aliases);
487     }
488
489   charmap_closedir (dir);
490
491   twalk (all_data, print_names);
492 }
493
494
495 /* We have to show the contents of the environments determining the
496    locale.  */
497 static void
498 show_locale_vars (void)
499 {
500   size_t cat_no;
501   const char *lcall = getenv ("LC_ALL");
502   const char *lang = getenv ("LANG") ? : "POSIX";
503
504   void get_source (const char *name)
505     {
506       char *val = getenv (name);
507
508       if ((lcall ?: "")[0] != '\0' || val == NULL)
509         printf ("%s=\"%s\"\n", name, (lcall ?: "")[0] ? lcall : lang);
510       else
511         printf ("%s=%s\n", name, val);
512     }
513
514   /* LANG has to be the first value.  */
515   printf ("LANG=%s\n", lang);
516
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);
521
522   /* The last is the LC_ALL value.  */
523   printf ("LC_ALL=%s\n", lcall ? : "");
524 }
525
526
527 /* Some of the "string" we print contain non-printable characters.  We
528    encode them here.  */
529 static void
530 print_escaped (const char *string)
531 {
532   const unsigned char *ch;
533
534   ch = string;
535   while ('\0' != *ch)
536     {
537       if (isprint (*ch))
538         putchar (*ch);
539       else
540         printf("<0x%02x>", *ch);
541       ++ch;
542     }
543 }
544
545
546 /* Show the information request for NAME.  */
547 static void
548 show_info (const char *name)
549 {
550   size_t cat_no;
551
552   void print_item (struct cat_item *item)
553     {
554       if (show_keyword_name != 0)
555         printf ("%s=", item->name);
556
557       switch (item->value_type)
558         {
559         case string:
560           if (show_keyword_name)
561             putchar ('"');
562           print_escaped (nl_langinfo (item->item_id) ? : "");
563           if (show_keyword_name)
564             putchar ('"');
565           break;
566         case stringarray:
567           {
568             int cnt;
569             const char *val;
570
571             if (show_keyword_name)
572               putchar ('"');
573
574             for (cnt = 0; cnt < item->max - 1; ++cnt)
575               {
576                 val = nl_langinfo (item->item_id + cnt);
577                 if (val != NULL)
578                   print_escaped (val);
579                 putchar (';');
580               }
581
582             val = nl_langinfo (item->item_id + cnt);
583             if (val != NULL)
584               print_escaped (val);
585
586             if (show_keyword_name)
587               putchar ('"');
588           }
589           break;
590         case stringlist:
591           {
592             int first = 1;
593             const char *val = nl_langinfo (item->item_id) ? : "";
594
595             while (*val != '\0')
596               {
597                 printf ("%s%s%s%s", first ? "" : ";",
598                         show_keyword_name ? "\"" : "", val,
599                         show_keyword_name ? "\"" : "");
600                 val = strchr (val, '\0') + 1;
601                 first = 0;
602               }
603           }
604           break;
605         case byte:
606           {
607             const char *val = nl_langinfo (item->item_id);
608
609             if (val != NULL)
610               printf ("%d", *val == CHAR_MAX ? -1 : *val);
611           }
612           break;
613         case bytearray:
614           {
615             const char *val = nl_langinfo (item->item_id);
616             int cnt = val ? strlen (val) : 0;
617
618             while (cnt > 1)
619               {
620                 printf ("%d;", *val == CHAR_MAX ? -1 : *val);
621                 --cnt;
622                 ++val;
623               }
624
625             printf ("%d", cnt == 0 || *val == CHAR_MAX ? -1 : *val);
626           }
627           break;
628         case word:
629           {
630             unsigned int val =
631               (unsigned int) (unsigned long int) nl_langinfo (item->item_id);
632             printf ("%d", val);
633           }
634           break;
635         default:
636         }
637       putchar ('\n');
638     }
639
640   for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
641     if (cat_no != LC_ALL)
642       {
643         size_t item_no;
644
645         if (strcmp (name, category[cat_no].name) == 0)
646           /* Print the whole category.  */
647           {
648             if (show_category_name != 0)
649               puts (category[cat_no].name);
650
651             for (item_no = 0; item_no < category[cat_no].number; ++item_no)
652               print_item (&category[cat_no].item_desc[item_no]);
653
654             return;
655           }
656
657         for (item_no = 0; item_no < category[cat_no].number; ++item_no)
658           if (strcmp (name, category[cat_no].item_desc[item_no].name) == 0)
659             {
660               if (show_category_name != 0)
661                 puts (category[cat_no].name);
662
663               print_item (&category[cat_no].item_desc[item_no]);
664               return;
665             }
666       }
667
668   /* The name is not a standard one.
669      For testing and perhaps advanced use allow some more symbols.  */
670   locale_special (name, show_category_name, show_keyword_name);
671 }