(add_to_readlist): Take locale pointer as extra parameter from which
[kopensolaris-gnu/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           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]) && cp[0] != '\n')
380             ++cp;
381
382           /* A leading '#' signals a comment line.  */
383           if (cp[0] != '\0' && cp[0] != '#' && cp[0] != '\n')
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         char *buf = NULL;
460         mode_t mode;
461
462 #ifdef _DIRENT_HAVE_D_TYPE
463         if (dirent->d_type != DT_UNKNOWN)
464           mode = DTTOIF (dirent->d_type);
465         else
466 #endif
467           {
468             struct stat st;
469
470             buf = alloca (sizeof (CHARMAP_PATH) + strlen (dirent->d_name) + 1);
471
472             stpcpy (stpcpy (stpcpy (buf, CHARMAP_PATH), "/"), dirent->d_name);
473
474             if (stat (buf, &st) < 0)
475               continue;
476             mode = st.st_mode;
477           }
478
479         if (S_ISREG (mode))
480           {
481             FILE *fp;
482
483             PUT (strdup (dirent->d_name));
484
485             /* Read the file and learn about the code set name.  */
486             if (buf == NULL)
487               {
488                 buf = alloca (sizeof (CHARMAP_PATH)
489                               + strlen (dirent->d_name) + 1);
490
491                 stpcpy (stpcpy (stpcpy (buf, CHARMAP_PATH), "/"),
492                         dirent->d_name);
493               }
494
495             fp = fopen (buf, "r");
496             if (fp != NULL)
497               {
498                 char *name = NULL;
499
500                 while (!feof (fp))
501                   {
502                     char junk[BUFSIZ];
503
504                     if (fscanf (fp, " <code_set_name> %as", &name) == 1)
505                       break;
506
507                     while (fgets (junk, sizeof junk, fp) != NULL
508                            && strchr (junk, '\n') == NULL)
509                       continue;
510                   }
511
512                 fclose (fp);
513
514                 if (name != NULL)
515                   PUT (name);
516               }
517           }
518       }
519
520   closedir (dir);
521
522   twalk (all_data, print_names);
523 }
524
525
526 /* We have to show the contents of the environments determining the
527    locale.  */
528 static void
529 show_locale_vars (void)
530 {
531   size_t cat_no;
532   const char *lcall = getenv ("LC_ALL");
533   const char *lang = getenv ("LANG") ? : "POSIX";
534
535   void get_source (const char *name)
536     {
537       char *val = getenv (name);
538
539       if ((lcall ?: "")[0] != '\0' || val == NULL)
540         printf ("%s=\"%s\"\n", name, (lcall ?: "")[0] ? lcall : lang);
541       else
542         printf ("%s=%s\n", name, val);
543     }
544
545   /* LANG has to be the first value.  */
546   printf ("LANG=%s\n", lang);
547
548   /* Now all categories in an unspecified order.  */
549   for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
550     if (cat_no != LC_ALL)
551       get_source (category[cat_no].name);
552
553   /* The last is the LC_ALL value.  */
554   printf ("LC_ALL=%s\n", lcall ? : "");
555 }
556
557
558 /* Some of the "string" we print contain non-printable characters.  We
559    encode them here.  */
560 static void
561 print_escaped (const char *string)
562 {
563   const unsigned char *ch;
564
565   ch = string;
566   while ('\0' != *ch)
567     {
568       if (isprint (*ch))
569         putchar (*ch);
570       else
571         printf("<0x%02x>", *ch);
572       ++ch;
573     }
574 }
575
576
577 /* Show the information request for NAME.  */
578 static void
579 show_info (const char *name)
580 {
581   size_t cat_no;
582
583   void print_item (struct cat_item *item)
584     {
585       if (show_keyword_name != 0)
586         printf ("%s=", item->name);
587
588       switch (item->value_type)
589         {
590         case string:
591           if (show_keyword_name)
592             putchar ('"');
593           print_escaped (nl_langinfo (item->item_id) ? : "");
594           if (show_keyword_name)
595             putchar ('"');
596           break;
597         case stringarray:
598           {
599             int cnt;
600             const char *val;
601
602             if (show_keyword_name)
603               putchar ('"');
604
605             for (cnt = 0; cnt < item->max - 1; ++cnt)
606               {
607                 val = nl_langinfo (item->item_id + cnt);
608                 if (val != NULL)
609                   print_escaped (val);
610                 putchar (';');
611               }
612
613             val = nl_langinfo (item->item_id + cnt);
614             if (val != NULL)
615               print_escaped (val);
616
617             if (show_keyword_name)
618               putchar ('"');
619           }
620           break;
621         case stringlist:
622           {
623             int first = 1;
624             const char *val = nl_langinfo (item->item_id) ? : "";
625
626             while (*val != '\0')
627               {
628                 printf ("%s%s%s%s", first ? "" : ";",
629                         show_keyword_name ? "\"" : "", val,
630                         show_keyword_name ? "\"" : "");
631                 val = strchr (val, '\0') + 1;
632                 first = 0;
633               }
634           }
635           break;
636         case byte:
637           {
638             const char *val = nl_langinfo (item->item_id);
639
640             if (val != NULL)
641               printf ("%d", *val == CHAR_MAX ? -1 : *val);
642           }
643           break;
644         case bytearray:
645           {
646             const char *val = nl_langinfo (item->item_id);
647             int cnt = val ? strlen (val) : 0;
648
649             while (cnt > 1)
650               {
651                 printf ("%d;", *val == CHAR_MAX ? -1 : *val);
652                 --cnt;
653                 ++val;
654               }
655
656             printf ("%d", cnt == 0 || *val == CHAR_MAX ? -1 : *val);
657           }
658           break;
659         case word:
660           {
661             unsigned int val =
662               (unsigned int) (unsigned long int) nl_langinfo (item->item_id);
663             printf ("%d", val);
664           }
665           break;
666         default:
667         }
668       putchar ('\n');
669     }
670
671   for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
672     if (cat_no != LC_ALL)
673       {
674         size_t item_no;
675
676         if (strcmp (name, category[cat_no].name) == 0)
677           /* Print the whole category.  */
678           {
679             if (show_category_name != 0)
680               puts (category[cat_no].name);
681
682             for (item_no = 0; item_no < category[cat_no].number; ++item_no)
683               print_item (&category[cat_no].item_desc[item_no]);
684
685             return;
686           }
687
688         for (item_no = 0; item_no < category[cat_no].number; ++item_no)
689           if (strcmp (name, category[cat_no].item_desc[item_no].name) == 0)
690             {
691               if (show_category_name != 0)
692                 puts (category[cat_no].name);
693
694               print_item (&category[cat_no].item_desc[item_no]);
695               return;
696             }
697       }
698
699   /* The name is not a standard one.
700      For testing and perhaps advanced use allow some more symbols.  */
701   locale_special (name, show_category_name, show_keyword_name);
702 }