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 <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) \
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) \
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(1).  */
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.org>.\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 "), "2000");
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           {
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"];
344             struct stat st;
345
346             stpcpy (stpcpy (stpcpy (stpcpy (buf, LOCALEDIR), "/"),
347                             dirent->d_name),
348                     "/LC_CTYPE");
349
350             if (stat (buf, &st) == 0 && S_ISREG (st.st_mode))
351               PUT (strdup (dirent->d_name));
352           }
353       }
354
355   closedir (dir);
356
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"));
360
361   entry = NULL;
362   while ((entry = argz_next (alias_path, alias_path_len, entry)))
363     {
364       static const char aliasfile[] = "/locale.alias";
365       FILE *fp;
366       char full_name[strlen (entry) + sizeof aliasfile];
367
368       stpcpy (stpcpy (full_name, entry), aliasfile);
369       fp = fopen (full_name, "r");
370       if (fp == NULL)
371         /* Ignore non-existing files.  */
372         continue;
373
374       while (! feof (fp))
375         {
376           /* It is a reasonable approach to use a fix buffer here
377              because
378              a) we are only interested in the first two fields
379              b) these fields must be usable as file names and so must
380                 not be that long  */
381           char buf[BUFSIZ];
382           char *alias;
383           char *value;
384           char *cp;
385
386           if (fgets (buf, BUFSIZ, fp) == NULL)
387             /* EOF reached.  */
388             break;
389
390           cp = buf;
391           /* Ignore leading white space.  */
392           while (isspace (cp[0]) && cp[0] != '\n')
393             ++cp;
394
395           /* A leading '#' signals a comment line.  */
396           if (cp[0] != '\0' && cp[0] != '#' && cp[0] != '\n')
397             {
398               alias = cp++;
399               while (cp[0] != '\0' && !isspace (cp[0]))
400                 ++cp;
401               /* Terminate alias name.  */
402               if (cp[0] != '\0')
403                 *cp++ = '\0';
404
405               /* Now look for the beginning of the value.  */
406               while (isspace (cp[0]))
407                 ++cp;
408
409               if (cp[0] != '\0')
410                 {
411                   value = cp++;
412                   while (cp[0] != '\0' && !isspace (cp[0]))
413                     ++cp;
414                   /* Terminate value.  */
415                   if (cp[0] == '\n')
416                     {
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
420                          overwrite here.  */
421                       *cp++ = '\0';
422                       *cp = '\n';
423                     }
424                   else if (cp[0] != '\0')
425                     *cp++ = '\0';
426
427                   /* Add the alias.  */
428                   PUT (strdup (alias));
429                 }
430             }
431
432           /* Possibly not the whole line fits into the buffer.
433              Ignore the rest of the line.  */
434           while (strchr (cp, '\n') == NULL)
435             {
436               cp = buf;
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.  */
440                 *cp = '\n';
441             }
442         }
443
444       fclose (fp);
445     }
446
447   twalk (all_data, print_names);
448 }
449
450
451 /* Write the names of all available character maps to stdout.  */
452 static void
453 write_charmaps (void)
454 {
455   void *all_data = NULL;
456   DIR *dir;
457   struct dirent *dirent;
458
459   dir = opendir (CHARMAP_PATH);
460   if (dir == NULL)
461     {
462       error (1, errno, gettext ("cannot read character map directory `%s'"),
463              CHARMAP_PATH);
464       return;
465     }
466
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)
471       {
472         char *buf = NULL;
473         mode_t mode;
474
475 #ifdef _DIRENT_HAVE_D_TYPE
476         if (dirent->d_type != DT_UNKNOWN)
477           mode = DTTOIF (dirent->d_type);
478         else
479 #endif
480           {
481             struct stat st;
482
483             buf = alloca (sizeof (CHARMAP_PATH) + strlen (dirent->d_name) + 1);
484
485             stpcpy (stpcpy (stpcpy (buf, CHARMAP_PATH), "/"), dirent->d_name);
486
487             if (stat (buf, &st) < 0)
488               continue;
489             mode = st.st_mode;
490           }
491
492         if (S_ISREG (mode))
493           {
494             FILE *fp;
495
496             PUT (strdup (dirent->d_name));
497
498             /* Read the file and learn about the code set name.  */
499             if (buf == NULL)
500               {
501                 buf = alloca (sizeof (CHARMAP_PATH)
502                               + strlen (dirent->d_name) + 1);
503
504                 stpcpy (stpcpy (stpcpy (buf, CHARMAP_PATH), "/"),
505                         dirent->d_name);
506               }
507
508             fp = fopen (buf, "r");
509             if (fp != NULL)
510               {
511                 char *name = NULL;
512
513                 while (!feof (fp))
514                   {
515                     char junk[BUFSIZ];
516
517                     if (fscanf (fp, " <code_set_name> %as", &name) == 1)
518                       break;
519
520                     while (fgets (junk, sizeof junk, fp) != NULL
521                            && strchr (junk, '\n') == NULL)
522                       continue;
523                   }
524
525                 fclose (fp);
526
527                 if (name != NULL)
528                   PUT (name);
529               }
530           }
531       }
532
533   closedir (dir);
534
535   twalk (all_data, print_names);
536 }
537
538
539 /* We have to show the contents of the environments determining the
540    locale.  */
541 static void
542 show_locale_vars (void)
543 {
544   size_t cat_no;
545   const char *lcall = getenv ("LC_ALL");
546   const char *lang = getenv ("LANG") ? : "POSIX";
547
548   void get_source (const char *name)
549     {
550       char *val = getenv (name);
551
552       if ((lcall ?: "")[0] != '\0' || val == NULL)
553         printf ("%s=\"%s\"\n", name, (lcall ?: "")[0] ? lcall : lang);
554       else
555         printf ("%s=%s\n", name, val);
556     }
557
558   /* LANG has to be the first value.  */
559   printf ("LANG=%s\n", lang);
560
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);
565
566   /* The last is the LC_ALL value.  */
567   printf ("LC_ALL=%s\n", lcall ? : "");
568 }
569
570
571 /* Some of the "string" we print contain non-printable characters.  We
572    encode them here.  */
573 static void
574 print_escaped (const char *string)
575 {
576   const unsigned char *ch;
577
578   ch = string;
579   while ('\0' != *ch)
580     {
581       if (isprint (*ch))
582         putchar (*ch);
583       else
584         printf("<0x%02x>", *ch);
585       ++ch;
586     }
587 }
588
589
590 /* Show the information request for NAME.  */
591 static void
592 show_info (const char *name)
593 {
594   size_t cat_no;
595
596   void print_item (struct cat_item *item)
597     {
598       if (show_keyword_name != 0)
599         printf ("%s=", item->name);
600
601       switch (item->value_type)
602         {
603         case string:
604           if (show_keyword_name)
605             putchar ('"');
606           print_escaped (nl_langinfo (item->item_id) ? : "");
607           if (show_keyword_name)
608             putchar ('"');
609           break;
610         case stringarray:
611           {
612             int cnt;
613             const char *val;
614
615             if (show_keyword_name)
616               putchar ('"');
617
618             for (cnt = 0; cnt < item->max - 1; ++cnt)
619               {
620                 val = nl_langinfo (item->item_id + cnt);
621                 if (val != NULL)
622                   print_escaped (val);
623                 putchar (';');
624               }
625
626             val = nl_langinfo (item->item_id + cnt);
627             if (val != NULL)
628               print_escaped (val);
629
630             if (show_keyword_name)
631               putchar ('"');
632           }
633           break;
634         case stringlist:
635           {
636             int first = 1;
637             const char *val = nl_langinfo (item->item_id) ? : "";
638
639             while (*val != '\0')
640               {
641                 printf ("%s%s%s%s", first ? "" : ";",
642                         show_keyword_name ? "\"" : "", val,
643                         show_keyword_name ? "\"" : "");
644                 val = strchr (val, '\0') + 1;
645                 first = 0;
646               }
647           }
648           break;
649         case byte:
650           {
651             const char *val = nl_langinfo (item->item_id);
652
653             if (val != NULL)
654               printf ("%d", *val == CHAR_MAX ? -1 : *val);
655           }
656           break;
657         case bytearray:
658           {
659             const char *val = nl_langinfo (item->item_id);
660             int cnt = val ? strlen (val) : 0;
661
662             while (cnt > 1)
663               {
664                 printf ("%d;", *val == CHAR_MAX ? -1 : *val);
665                 --cnt;
666                 ++val;
667               }
668
669             printf ("%d", cnt == 0 || *val == CHAR_MAX ? -1 : *val);
670           }
671           break;
672         case word:
673           {
674             unsigned int val =
675               (unsigned int) (unsigned long int) nl_langinfo (item->item_id);
676             printf ("%d", val);
677           }
678           break;
679         default:
680         }
681       putchar ('\n');
682     }
683
684   for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
685     if (cat_no != LC_ALL)
686       {
687         size_t item_no;
688
689         if (strcmp (name, category[cat_no].name) == 0)
690           /* Print the whole category.  */
691           {
692             if (show_category_name != 0)
693               puts (category[cat_no].name);
694
695             for (item_no = 0; item_no < category[cat_no].number; ++item_no)
696               print_item (&category[cat_no].item_desc[item_no]);
697
698             return;
699           }
700
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)
703             {
704               if (show_category_name != 0)
705                 puts (category[cat_no].name);
706
707               print_item (&category[cat_no].item_desc[item_no]);
708               return;
709             }
710       }
711
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);
715 }