Thu Jan 18 00:32:43 1996 Roland McGrath <roland@churchy.gnu.ai.mit.edu>
[kopensolaris-gnu/glibc.git] / locale / locale.c
1 /* Copyright (C) 1995 Free Software Foundation, Inc.
2
3 The GNU C Library is free software; you can redistribute it and/or
4 modify it under the terms of the GNU Library General Public License as
5 published by the Free Software Foundation; either version 2 of the
6 License, or (at your option) any later version.
7
8 The GNU C Library is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 Library General Public License for more details.
12
13 You should have received a copy of the GNU Library General Public
14 License along with the GNU C Library; see the file COPYING.LIB.  If
15 not, write to the Free Software Foundation, Inc., 675 Mass Ave,
16 Cambridge, MA 02139, USA.  */
17
18 #include <dirent.h>
19 #include <getopt.h>
20 #include <langinfo.h>
21 #include <libintl.h>
22 #include <limits.h>
23 #include <locale.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <errno.h>
28
29 #include "localedef.h"
30
31
32 /* If set dump C code describing the current locale.  */
33 static int do_dump;
34
35 /* If set print the name of the category.  */
36 static int show_category_name;
37
38 /* If set print the name of the item.  */
39 static int show_keyword_name;
40
41 /* Long options.  */
42 static const struct option long_options[] =
43   {
44     { "all-locales", no_argument, NULL, 'a' },
45     { "category-name", no_argument, &show_category_name, 1 },
46     { "charmaps", no_argument, NULL, 'm' },
47     { "dump", no_argument, &do_dump, 1 },
48     { "help", no_argument, NULL, 'h' },
49     { "keyword-name", no_argument, &show_keyword_name, 1 },
50     { "version", no_argument, NULL, 'v' },
51     { NULL, 0, NULL, 0 }
52   };
53
54
55 /* We don't have these constants defined because we don't use them.  Give
56    default values.  */
57 #define CTYPE_MB_CUR_MIN 0
58 #define CTYPE_MB_CUR_MAX 0
59 #define CTYPE_HASH_SIZE 0
60 #define CTYPE_HASH_LAYERS 0
61 #define CTYPE_CLASS 0
62 #define CTYPE_TOUPPER_EB 0
63 #define CTYPE_TOLOWER_EB 0
64 #define CTYPE_TOUPPER_EL 0
65 #define CTYPE_TOLOWER_EL 0
66  
67
68 /* We have all categories defined in `categories.def'.  Now construct
69    the description and data structure used for all categories.  */
70 #define DEFINE_CATEGORY(category, name, items, postload, in, check, out)      \
71     static struct cat_item category##_desc[] =                                \
72       {                                                                       \
73         NO_PAREN items                                                        \
74       };
75
76 #include "categories.def"
77 #undef DEFINE_CATEGORY
78
79 static struct category category[] =
80   {
81 #define DEFINE_CATEGORY(category, name, items, postload, in, check, out)      \
82     { _NL_NUM_##category, name, NELEMS (category##_desc) - 1,                 \
83       category##_desc, NULL, NULL, NULL, out },
84 #include "categories.def"
85 #undef DEFINE_CATEGORY
86   };
87 #define NCATEGORIES NELEMS (category)
88
89
90 /* Prototypes for local functions.  */
91 static void usage (int status) __attribute__ ((noreturn));
92 static void write_locales (void);
93 static void write_charmaps (void);
94 static void show_locale_vars (void);
95 static void show_info (const char *name);
96 static void dump_category (const char *name);
97
98
99 int
100 main (int argc, char *argv[])
101 {
102   int optchar;
103   int do_all = 0;
104   int do_help = 0;
105   int do_version = 0;
106   int do_charmaps = 0;
107
108   /* Set initial values for global varaibles.  */
109   do_dump = 0;
110   show_category_name = 0;
111   show_keyword_name = 0;
112
113   /* Set locale.  Do not set LC_ALL because the other categories must
114      not be affected (acccording to POSIX.2).  */
115   setlocale (LC_CTYPE, "");
116   setlocale (LC_MESSAGES, "");
117
118   /* Initialize the message catalog.  */
119   textdomain (PACKAGE);
120
121   while ((optchar = getopt_long (argc, argv, "achkmv", long_options, NULL))
122          != EOF)
123     switch (optchar)
124       {
125       case '\0':
126         break;
127       case 'a':
128         do_all = 1;
129         break;
130       case 'c':
131         show_category_name = 1;
132         break;
133       case 'h':
134         do_help = 1;
135         break;
136       case 'k':
137         show_keyword_name = 1;
138         break;
139       case 'm':
140         do_charmaps = 1;
141         break;
142       case 'v':
143         do_version = 1;
144         break;
145       default:
146         error (1, 0, gettext ("illegal option \"%s\""), optarg);
147         break;
148       }
149
150   /* Version information is requested.  */
151   if (do_version)
152     {
153       fprintf (stderr, "GNU %s %s\n", PACKAGE, VERSION);
154       exit (EXIT_SUCCESS);
155     }
156
157   /* Help is requested.  */
158   if (do_help)
159     usage (EXIT_SUCCESS);
160
161   /* Dump C code.  */
162   if (do_dump)
163     {
164       printf ("\
165 /* Generated by GNU %s %s.  */\n\
166 \n\
167 #include \"localeinfo.h\"\n", program_invocation_name, VERSION);
168
169       while (optind < argc)
170         dump_category (argv[optind++]);
171
172       exit (EXIT_SUCCESS);
173     }
174
175   /* `-a' requests the names of all available locales.  */
176   if (do_all != 0)
177     {
178       write_locales ();
179       exit (EXIT_SUCCESS);
180     }
181
182   /* `m' requests the names of all available charmaps.  The names can be
183      used for the -f argument to localedef(3).  */
184   if (do_charmaps != 0)
185     {
186       write_charmaps ();
187       exit (EXIT_SUCCESS);
188     }
189
190   /* If no real argument is given we have to print the contents of the
191      current locale definition variables.  These are LANG and the LC_*.  */
192   if (optind == argc && show_keyword_name == 0 && show_category_name == 0)
193     {
194       show_locale_vars ();
195       exit (EXIT_SUCCESS);
196     }
197
198   /* Process all given names.  */
199   while (optind <  argc)
200     show_info (argv[optind++]);
201
202   exit (EXIT_SUCCESS);
203 }
204
205
206 /* Display usage information and exit.  */
207 static void
208 usage(int status)
209 {
210   if (status != EXIT_SUCCESS)
211     fprintf (stderr, gettext ("Try `%s --help' for more information.\n"),
212              program_invocation_name);
213   else
214     printf(gettext ("\
215 Usage: %s [OPTION]... name\n\
216 Mandatory arguments to long options are mandatory for short options too.\n\
217   -h, --help            display this help and exit\n\
218   -v, --version         output version information and exit\n\
219 \n\
220   -a, --all-locales     write names of available locales\n\
221   -m, --charmaps        write names of available charmaps\n\
222 \n\
223   -c, --category-name   write names of selected categories\n\
224   -k, --keyword-name    write names of selected keywords\n\
225 \n\
226       --dump            dump C code describing the current locale\n\
227                         (this code can be used in the C library)\n\
228 "), program_invocation_name);
229
230   exit (status);
231 }
232
233
234 /* Write the names of all available locales to stdout.  */
235 static void
236 write_locales (void)
237 {
238   DIR *dir;
239   struct dirent *dirent;
240
241   /* `POSIX' locale is always available (POSIX.2 4.34.3).  */
242   puts ("POSIX");
243
244   dir = opendir (LOCALE_PATH);
245   if (dir == NULL)
246     {
247       error (1, errno, gettext ("cannot read locale directory `%s'"),
248              LOCALE_PATH);
249       return;
250     }
251
252   /* Now we can look for all files in the directory.  */
253   while ((dirent = readdir (dir)) != NULL)
254     if (strcmp (dirent->d_name, ".") != 0
255         && strcmp (dirent->d_name, "..") != 0)
256       puts (dirent->d_name);
257
258   closedir (dir);
259 }
260
261
262 /* Write the names of all available character maps to stdout.  */
263 static void
264 write_charmaps (void)
265 {
266   DIR *dir;
267   struct dirent *dirent;
268
269   dir = opendir (CHARMAP_PATH);
270   if (dir == NULL)
271     {
272       error (1, errno, gettext ("cannot read character map directory `%s'"),
273              CHARMAP_PATH);
274       return;
275     }
276
277   /* Now we can look for all files in the directory.  */
278   while ((dirent = readdir (dir)) != NULL)
279     if (strcmp (dirent->d_name, ".") != 0
280         && strcmp (dirent->d_name, "..") != 0)
281       puts (dirent->d_name);
282
283   closedir (dir);
284 }
285
286
287 /* We have to show the contents of the environments determining the
288    locale.  */
289 static void
290 show_locale_vars (void)
291 {
292   size_t cat_no;
293   const char *lcall = getenv ("LC_ALL");
294   const char *lang = getenv ("LANG") ? : "POSIX";
295
296   void get_source (const char *name)
297     {
298       char *val = getenv (name);
299
300       if (lcall != NULL || val == NULL)
301         printf ("%s=\"%s\"\n", name, lcall ? : lang);
302       else
303         printf ("%s=%s\n", name, val);
304     }
305
306   /* LANG has to be the first value.  */
307   printf ("LANG=%s\n", lang);
308
309   /* Now all categories in an unspecified order.  */
310   for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
311     get_source (category[cat_no].name);
312
313   /* The last is the LC_ALL value.  */
314   printf ("LC_ALL=%s\n", lcall ? : "");
315 }
316
317
318 /* Show the information request for NAME.  */
319 static void
320 show_info (const char *name)
321 {
322   size_t cat_no;
323
324   void print_item (struct cat_item *item)
325     {
326       if (show_keyword_name != 0)
327         printf ("%s=", item->name);
328
329       switch (item->value_type)
330         {
331         case string:
332           printf ("%s%s%s", show_keyword_name ? "\"" : "",
333                   nl_langinfo (item->item_id) ? : "",
334                   show_keyword_name ? "\"" : "");
335           break;
336         case stringarray:
337           {
338             int cnt;
339             const char *val;
340
341             if (show_keyword_name)
342               putchar ('"');
343
344             for (cnt = 0; cnt < item->max - 1; ++cnt)
345               {
346                 val = nl_langinfo (item->item_id + cnt);
347                 printf ("%s;", val ? : "");
348               }
349
350             val = nl_langinfo (item->item_id + cnt);
351             printf ("%s", val ? : "");
352
353             if (show_keyword_name)
354               putchar ('"');
355           }
356           break;
357         case byte:
358           {
359             const char *val = nl_langinfo (item->item_id);
360
361             if (val != NULL)
362               printf ("%d", *val == CHAR_MAX ? -1 : *val);
363           }
364           break;
365         case bytearray:
366           {
367             const char *val = nl_langinfo (item->item_id);
368             int cnt = val ? strlen (val) : 0;
369
370             while (cnt > 1)
371               {
372                 printf ("%d;", *val == CHAR_MAX ? -1 : *val);
373                 --cnt;
374                 ++val;
375               }
376
377             printf ("%d", cnt == 0 || *val == CHAR_MAX ? -1 : *val);
378           }
379           break;
380         default:
381         }
382       putchar ('\n');
383     }
384
385   for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
386     {
387       size_t item_no;
388
389       if (category[cat_no].outfct != NULL)
390         /* Categories which need special handling of the output are
391            not written.  This is especially for LC_CTYPE and LC_COLLATE.
392            It does not make sense to have this large number of cryptic
393            characters displayed.  */
394         continue;
395
396       if (strcmp (name, category[cat_no].name) == 0)
397         /* Print the whole category.  */
398         {
399           if (show_category_name != 0)
400             puts (category[cat_no].name);
401
402           for (item_no = 0; item_no < category[cat_no].number; ++item_no)
403             print_item (&category[cat_no].item_desc[item_no]);
404
405           return;
406         }
407       
408       for (item_no = 0; item_no < category[cat_no].number; ++item_no)
409         if (strcmp (name, category[cat_no].item_desc[item_no].name) == 0)
410           {
411             if (show_category_name != 0)
412               puts (category[cat_no].name);
413
414             print_item (&category[cat_no].item_desc[item_no]);
415             return;
416           }
417     }
418 }
419
420
421 static void
422 dump_category (const char *name)
423 {
424   char *locname;
425   size_t cat_no, item_no, nstrings;
426
427   for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
428     if (strcmp (name, category[cat_no].name) == 0)
429       break;
430
431   if (cat_no >= NCATEGORIES)
432     return;
433
434   /* The NAME specifies a correct locale category.  */
435   if (category[cat_no].outfct != NULL)
436     {
437       category[cat_no].outfct ();
438       return;
439     }
440
441   locname = (getenv ("LC_ALL") ?: getenv (name) ?:
442              getenv ("LANG") ?: (char *) "POSIX");
443
444   /* Determine the number of strings in advance.  */
445   nstrings = 0;
446   for (item_no = 0; item_no < category[cat_no].number; ++item_no)
447     switch (category[cat_no].item_desc[item_no].value_type)
448       {
449       case string:
450       case byte:
451       case bytearray:
452         ++nstrings;
453         break;
454       case stringarray:
455         nstrings += category[cat_no].item_desc[item_no].max;
456       default:
457       }
458
459   printf ("\nconst struct locale_data _nl_%s_%s =\n{\n"
460           "  NULL, 0, /* no file mapped */\n  %Zu,\n  {\n",
461           locname, name, nstrings);
462
463   for (item_no = 0; item_no < category[cat_no].number; ++item_no)
464     switch (category[cat_no].item_desc[item_no].value_type)
465       {
466       case string:
467         {
468           const char *val = nl_langinfo (
469             category[cat_no].item_desc[item_no].item_id);
470
471           if (val != NULL)
472             printf ("    \"%s\",\n", val);
473           else
474             puts ("    NULL,");
475         }
476         break;
477       case stringarray:
478         {
479           const char *val;
480           int cnt;
481
482           for (cnt = 0; cnt < category[cat_no].item_desc[item_no].max; ++cnt)
483             {
484               val = nl_langinfo (
485                 category[cat_no].item_desc[item_no].item_id + cnt);
486
487               if (val != NULL)
488                 printf ("    \"%s\",\n", val);
489               else
490                 puts ("    NULL,");
491             }
492         }
493         break;
494       case byte:
495         {
496           const char *val = nl_langinfo (
497             category[cat_no].item_desc[item_no].item_id);
498
499           if (val != NULL)
500             printf ("    \"\\%o\",\n",
501                     *(unsigned char *) val ? : UCHAR_MAX);
502           else
503             puts ("    NULL,");
504         }
505         break;
506       case bytearray:
507         {
508           const char *bytes = nl_langinfo (
509             category[cat_no].item_desc[item_no].item_id);
510
511           if (bytes != NULL)
512             {
513               fputs ("    \"", stdout);
514               if (*bytes != '\0')
515                 do
516                   printf ("\\%o", *(unsigned char *) bytes++);
517                 while (*bytes != '\0');
518               else
519                 printf ("\\%o", UCHAR_MAX);
520
521               puts ("\",");
522             }
523           else
524             puts ("    NULL,");
525         }
526         break;
527       default:
528         break;
529       }
530
531   puts ("  }\n};");
532 }
533
534 /*
535  * Local Variables:
536  *  mode:c
537  *  c-basic-offset:2
538  * End:
539  */