Copyright 2001.
[kopensolaris-gnu/glibc.git] / locale / programs / locale.c
1 /* Implementation of the locale program according to POSIX 9945-2.
2    Copyright (C) 1995-1997, 1999, 2000, 2001 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 "), "2001");
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   auto void get_source (const char *name);
505
506   void get_source (const char *name)
507     {
508       char *val = getenv (name);
509
510       if ((lcall ?: "")[0] != '\0' || val == NULL)
511         printf ("%s=\"%s\"\n", name, (lcall ?: "")[0] ? lcall : lang);
512       else
513         printf ("%s=%s\n", name, val);
514     }
515
516   /* LANG has to be the first value.  */
517   printf ("LANG=%s\n", lang);
518
519   /* Now all categories in an unspecified order.  */
520   for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
521     if (cat_no != LC_ALL)
522       get_source (category[cat_no].name);
523
524   /* The last is the LC_ALL value.  */
525   printf ("LC_ALL=%s\n", lcall ? : "");
526 }
527
528
529 /* Show the information request for NAME.  */
530 static void
531 show_info (const char *name)
532 {
533   size_t cat_no;
534
535   auto void print_item (struct cat_item *item);
536
537   void print_item (struct cat_item *item)
538     {
539       switch (item->value_type)
540         {
541         case string:
542           if (show_keyword_name)
543             printf ("%s=\"", item->name);
544           fputs (nl_langinfo (item->item_id) ? : "", stdout);
545           if (show_keyword_name)
546             putchar ('"');
547           putchar ('\n');
548           break;
549         case stringarray:
550           {
551             int cnt;
552             const char *val;
553
554             if (show_keyword_name)
555               printf ("%s=\"", item->name);
556
557             for (cnt = 0; cnt < item->max - 1; ++cnt)
558               {
559                 val = nl_langinfo (item->item_id + cnt);
560                 if (val != NULL)
561                   fputs (val, stdout);
562                 putchar (';');
563               }
564
565             val = nl_langinfo (item->item_id + cnt);
566             if (val != NULL)
567               fputs (val, stdout);
568
569             if (show_keyword_name)
570               putchar ('"');
571             putchar ('\n');
572           }
573           break;
574         case stringlist:
575           {
576             int first = 1;
577             const char *val = nl_langinfo (item->item_id) ? : "";
578             int cnt;
579
580             if (show_keyword_name)
581               printf ("%s=", item->name);
582
583             for (cnt = 0; cnt < item->max && *val != '\0'; ++cnt)
584               {
585                 printf ("%s%s%s%s", first ? "" : ";",
586                         show_keyword_name ? "\"" : "", val,
587                         show_keyword_name ? "\"" : "");
588                 val = strchr (val, '\0') + 1;
589                 first = 0;
590               }
591             putchar ('\n');
592           }
593           break;
594         case byte:
595           {
596             const char *val = nl_langinfo (item->item_id);
597
598             if (show_keyword_name)
599               printf ("%s=", item->name);
600
601             if (val != NULL)
602               printf ("%d", *val == CHAR_MAX ? -1 : *val);
603             putchar ('\n');
604           }
605           break;
606         case bytearray:
607           {
608             const char *val = nl_langinfo (item->item_id);
609             int cnt = val ? strlen (val) : 0;
610
611             if (show_keyword_name)
612               printf ("%s=", item->name);
613
614             while (cnt > 1)
615               {
616                 printf ("%d;", *val == CHAR_MAX ? -1 : *val);
617                 --cnt;
618                 ++val;
619               }
620
621             printf ("%d\n", cnt == 0 || *val == CHAR_MAX ? -1 : *val);
622           }
623           break;
624         case word:
625           {
626             unsigned int val =
627               (unsigned int) (unsigned long int) nl_langinfo (item->item_id);
628             if (show_keyword_name)
629               printf ("%s=", item->name);
630
631             printf ("%d\n", val);
632           }
633           break;
634         case wstring:
635         case wstringarray:
636         case wstringlist:
637           /* We don't print wide character information since the same
638              information is available in a multibyte string.  */
639         default:
640           break;
641
642         }
643     }
644
645   for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
646     if (cat_no != LC_ALL)
647       {
648         size_t item_no;
649
650         if (strcmp (name, category[cat_no].name) == 0)
651           /* Print the whole category.  */
652           {
653             if (show_category_name != 0)
654               puts (category[cat_no].name);
655
656             for (item_no = 0; item_no < category[cat_no].number; ++item_no)
657               print_item (&category[cat_no].item_desc[item_no]);
658
659             return;
660           }
661
662         for (item_no = 0; item_no < category[cat_no].number; ++item_no)
663           if (strcmp (name, category[cat_no].item_desc[item_no].name) == 0)
664             {
665               if (show_category_name != 0)
666                 puts (category[cat_no].name);
667
668               print_item (&category[cat_no].item_desc[item_no]);
669               return;
670             }
671       }
672
673   /* The name is not a standard one.
674      For testing and perhaps advanced use allow some more symbols.  */
675   locale_special (name, show_category_name, show_keyword_name);
676 }