]> sourceware.org Git - glibc.git/blob - locale/programs/locale.c
Update.
[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 "), "1999");
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 {
673 size_t item_no;
674
675 if (strcmp (name, category[cat_no].name) == 0)
676 /* Print the whole category. */
677 {
678 if (show_category_name != 0)
679 puts (category[cat_no].name);
680
681 for (item_no = 0; item_no < category[cat_no].number; ++item_no)
682 print_item (&category[cat_no].item_desc[item_no]);
683
684 return;
685 }
686
687 for (item_no = 0; item_no < category[cat_no].number; ++item_no)
688 if (strcmp (name, category[cat_no].item_desc[item_no].name) == 0)
689 {
690 if (show_category_name != 0)
691 puts (category[cat_no].name);
692
693 print_item (&category[cat_no].item_desc[item_no]);
694 return;
695 }
696 }
697
698 /* The name is not a standard one.
699 For testing and perhaps advanced use allow some more symbols. */
700 locale_special (name, show_category_name, show_keyword_name);
701 }
This page took 0.068693 seconds and 5 git commands to generate.