]> sourceware.org Git - glibc.git/blame - locale/programs/locale.c
Update.
[glibc.git] / locale / programs / locale.c
CommitLineData
5107cf1d 1/* Implementation of the locale program according to POSIX 9945-2.
df4ef2ab 2 Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc.
6d52618b
UD
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
2b83a2a4 5
6d52618b
UD
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.
2b83a2a4 10
6d52618b
UD
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.
2b83a2a4 15
6d52618b
UD
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. */
2b83a2a4 20
299a95b9
RM
21#ifdef HAVE_CONFIG_H
22# include <config.h>
23#endif
24
1fb05e3d
UD
25#include <argp.h>
26#include <argz.h>
2b83a2a4 27#include <dirent.h>
1fb05e3d 28#include <errno.h>
299a95b9 29#include <error.h>
2b83a2a4
RM
30#include <langinfo.h>
31#include <libintl.h>
32#include <limits.h>
33#include <locale.h>
1fb05e3d 34#include <search.h>
2b83a2a4
RM
35#include <stdio.h>
36#include <stdlib.h>
a1470b6f 37#include <string.h>
1fb05e3d
UD
38#include <unistd.h>
39#include <sys/stat.h>
2b83a2a4 40
19bc17a9 41#include "localeinfo.h"
2b83a2a4
RM
42
43
2b83a2a4
RM
44/* If set print the name of the category. */
45static int show_category_name;
46
47/* If set print the name of the item. */
48static int show_keyword_name;
49
1fb05e3d
UD
50/* Print names of all available locales. */
51static int do_all;
52
53/* Print names of all available character maps. */
54static int do_charmaps = 0;
55
56/* Name and version of program. */
57static void print_version (FILE *stream, struct argp_state *state);
58void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
59
60/* Definitions of arguments for argp functions. */
5a97622d 61static const struct argp_option options[] =
19bc17a9 62{
5a97622d
UD
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") },
1fb05e3d
UD
71 { NULL, 0, NULL, 0, NULL }
72};
73
74/* Short description of program. */
5a97622d 75static const char doc[] = N_("Get locale-specific information.");
1fb05e3d
UD
76
77/* Strings for arguments in help texts. */
5a97622d 78static const char args_doc[] = N_("NAME\n[-a|-m]");
1fb05e3d
UD
79
80/* Prototype for option handler. */
81static error_t parse_opt (int key, char *arg, struct argp_state *state);
82
83/* Function to print some extra text in the help message. */
84static char *more_help (int key, const char *text, void *input);
85
86/* Data structure to communicate with argp functions. */
87static struct argp argp =
88{
89 options, parse_opt, args_doc, doc, NULL, more_help
19bc17a9 90};
2b83a2a4
RM
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
19bc17a9 104
299a95b9
RM
105/* Definition of the data structure which represents a category and its
106 items. */
107struct category
19bc17a9 108{
299a95b9 109 int cat_id;
19bc17a9 110 const char *name;
299a95b9
RM
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;
19bc17a9
RM
121};
122
299a95b9
RM
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
2b83a2a4
RM
128
129/* We have all categories defined in `categories.def'. Now construct
130 the description and data structure used for all categories. */
299a95b9 131#define DEFINE_ELEMENT(Item, More...) { Item, ## More },
2b83a2a4
RM
132#define DEFINE_CATEGORY(category, name, items, postload, in, check, out) \
133 static struct cat_item category##_desc[] = \
134 { \
135 NO_PAREN items \
136 };
137
299a95b9 138#include "categories.def"
2b83a2a4
RM
139#undef DEFINE_CATEGORY
140
141static struct category category[] =
142 {
143#define DEFINE_CATEGORY(category, name, items, postload, in, check, out) \
036cc82f
RM
144 [category] = { _NL_NUM_##category, name, NELEMS (category##_desc), \
145 category##_desc },
299a95b9 146#include "categories.def"
2b83a2a4
RM
147#undef DEFINE_CATEGORY
148 };
149#define NCATEGORIES NELEMS (category)
150
151
036cc82f
RM
152/* Automatically set variable. */
153extern const char *__progname;
154
155/* helper function for extended name handling. */
156extern void locale_special (const char *name, int show_category_name,
157 int show_keyword_name);
158
2b83a2a4 159/* Prototypes for local functions. */
2b83a2a4
RM
160static void write_locales (void);
161static void write_charmaps (void);
162static void show_locale_vars (void);
163static void show_info (const char *name);
2b83a2a4
RM
164
165
166int
167main (int argc, char *argv[])
168{
2f6d1f1b
UD
169 int remaining;
170
bba7bb78 171 /* Set initial values for global variables. */
2b83a2a4
RM
172 show_category_name = 0;
173 show_keyword_name = 0;
174
175 /* Set locale. Do not set LC_ALL because the other categories must
6d52618b 176 not be affected (according to POSIX.2). */
2b83a2a4
RM
177 setlocale (LC_CTYPE, "");
178 setlocale (LC_MESSAGES, "");
179
180 /* Initialize the message catalog. */
181 textdomain (PACKAGE);
182
1fb05e3d 183 /* Parse and process arguments. */
2f6d1f1b 184 argp_parse (&argp, argc, argv, 0, &remaining, NULL);
2b83a2a4 185
2b83a2a4
RM
186 /* `-a' requests the names of all available locales. */
187 if (do_all != 0)
188 {
1fb05e3d 189 setlocale (LC_COLLATE, "");
2b83a2a4
RM
190 write_locales ();
191 exit (EXIT_SUCCESS);
192 }
193
194 /* `m' requests the names of all available charmaps. The names can be
8f2ece69 195 used for the -f argument to localedef(1). */
2b83a2a4
RM
196 if (do_charmaps != 0)
197 {
198 write_charmaps ();
199 exit (EXIT_SUCCESS);
200 }
201
76060ec0
RM
202 /* Specific information about the current locale are requested.
203 Change to this locale now. */
204 setlocale (LC_ALL, "");
205
2b83a2a4
RM
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_*. */
2f6d1f1b 208 if (remaining == argc && show_keyword_name == 0 && show_category_name == 0)
2b83a2a4
RM
209 {
210 show_locale_vars ();
211 exit (EXIT_SUCCESS);
212 }
213
214 /* Process all given names. */
2f6d1f1b
UD
215 while (remaining < argc)
216 show_info (argv[remaining++]);
2b83a2a4
RM
217
218 exit (EXIT_SUCCESS);
219}
220
221
1fb05e3d
UD
222/* Handle program arguments. */
223static error_t
224parse_opt (int key, char *arg, struct argp_state *state)
2b83a2a4 225{
1fb05e3d 226 switch (key)
fafaa44e 227 {
1fb05e3d
UD
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;
fafaa44e 242 }
1fb05e3d
UD
243 return 0;
244}
245
2b83a2a4 246
1fb05e3d
UD
247static char *
248more_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 ("\
f2ea0f5b 255Report bugs using the `glibcbug' script to <bugs@gnu.org>.\n"));
1fb05e3d
UD
256 default:
257 break;
258 }
259 return (char *) text;
260}
261
262/* Print the version information. */
263static void
264print_version (FILE *stream, struct argp_state *state)
265{
266 fprintf (stream, "locale (GNU %s) %s\n", PACKAGE, VERSION);
267 fprintf (stream, gettext ("\
268Copyright (C) %s Free Software Foundation, Inc.\n\
269This is free software; see the source for copying conditions. There is NO\n\
270warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
271"), "1995, 1996, 1997");
272 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
2b83a2a4
RM
273}
274
275
1fb05e3d
UD
276/* Simple action function which prints arguments as strings. */
277static void
278print_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. */
2b83a2a4
RM
290static void
291write_locales (void)
292{
1fb05e3d 293 void *all_data = NULL;
2b83a2a4
RM
294 DIR *dir;
295 struct dirent *dirent;
1fb05e3d
UD
296 char *alias_path;
297 size_t alias_path_len;
298 char *entry;
2b83a2a4 299
1fb05e3d
UD
300#define PUT(name) tsearch ((name), &all_data, \
301 (int (*) (const void *, const void *)) strcoll)
2b83a2a4 302
1fb05e3d 303 dir = opendir (LOCALEDIR);
2b83a2a4
RM
304 if (dir == NULL)
305 {
306 error (1, errno, gettext ("cannot read locale directory `%s'"),
1fb05e3d 307 LOCALEDIR);
2b83a2a4
RM
308 return;
309 }
310
1fb05e3d
UD
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
2b83a2a4
RM
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)
1fb05e3d
UD
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 }
2b83a2a4
RM
341
342 closedir (dir);
1fb05e3d
UD
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]))
380 ++cp;
381
382 /* A leading '#' signals a comment line. */
383 if (cp[0] != '\0' && cp[0] != '#')
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);
2b83a2a4
RM
435}
436
437
438/* Write the names of all available character maps to stdout. */
439static void
440write_charmaps (void)
441{
1fb05e3d 442 void *all_data = NULL;
2b83a2a4
RM
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)
1fb05e3d 458 {
51702635 459 char *buf = NULL;
1fb05e3d 460 mode_t mode;
51702635 461
1fb05e3d
UD
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;
51702635
UD
469
470 buf = alloca (sizeof (CHARMAP_PATH) + strlen (dirent->d_name) + 1);
1fb05e3d
UD
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))
51702635
UD
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
1f205a47
UD
507 while (fgets (junk, sizeof junk, fp) != NULL
508 && strchr (junk, '\n') == NULL)
509 continue;
51702635
UD
510 }
511
512 fclose (fp);
513
514 if (name != NULL)
515 PUT (name);
516 }
517 }
1fb05e3d 518 }
2b83a2a4
RM
519
520 closedir (dir);
1fb05e3d
UD
521
522 twalk (all_data, print_names);
2b83a2a4
RM
523}
524
525
526/* We have to show the contents of the environments determining the
527 locale. */
528static void
529show_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
51702635
UD
539 if ((lcall ?: "")[0] != '\0' || val == NULL)
540 printf ("%s=\"%s\"\n", name, (lcall ?: "")[0] ? lcall : lang);
2b83a2a4
RM
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 get_source (category[cat_no].name);
551
552 /* The last is the LC_ALL value. */
553 printf ("LC_ALL=%s\n", lcall ? : "");
554}
555
556
8f2ece69
UD
557/* Some of the "string" we print contain non-printable characters. We
558 encode them here. */
559static void
560print_escaped (const char *string)
561{
562 const unsigned char *ch;
563
564 ch = string;
565 while ('\0' != *ch)
566 {
567 if (isprint (*ch))
568 putchar (*ch);
569 else
570 printf("<0x%02x>", *ch);
571 ++ch;
572 }
573}
574
575
2b83a2a4
RM
576/* Show the information request for NAME. */
577static void
578show_info (const char *name)
579{
580 size_t cat_no;
581
582 void print_item (struct cat_item *item)
583 {
584 if (show_keyword_name != 0)
585 printf ("%s=", item->name);
586
587 switch (item->value_type)
588 {
589 case string:
8f2ece69
UD
590 if (show_keyword_name)
591 putchar ('"');
592 print_escaped (nl_langinfo (item->item_id) ? : "");
593 if (show_keyword_name)
594 putchar ('"');
2b83a2a4
RM
595 break;
596 case stringarray:
597 {
598 int cnt;
599 const char *val;
600
601 if (show_keyword_name)
602 putchar ('"');
603
604 for (cnt = 0; cnt < item->max - 1; ++cnt)
605 {
606 val = nl_langinfo (item->item_id + cnt);
8f2ece69
UD
607 if (val != NULL)
608 print_escaped (val);
609 putchar (';');
2b83a2a4
RM
610 }
611
612 val = nl_langinfo (item->item_id + cnt);
8f2ece69
UD
613 if (val != NULL)
614 print_escaped (val);
2b83a2a4
RM
615
616 if (show_keyword_name)
617 putchar ('"');
618 }
619 break;
51702635
UD
620 case stringlist:
621 {
622 int first = 1;
623 const char *val = nl_langinfo (item->item_id) ? : "";
624
625 while (*val != '\0')
626 {
627 printf ("%s%s%s%s", first ? "" : ";",
628 show_keyword_name ? "\"" : "", val,
629 show_keyword_name ? "\"" : "");
630 val = strchr (val, '\0') + 1;
631 first = 0;
632 }
633 }
634 break;
2b83a2a4
RM
635 case byte:
636 {
637 const char *val = nl_langinfo (item->item_id);
638
639 if (val != NULL)
640 printf ("%d", *val == CHAR_MAX ? -1 : *val);
641 }
642 break;
643 case bytearray:
644 {
645 const char *val = nl_langinfo (item->item_id);
646 int cnt = val ? strlen (val) : 0;
647
648 while (cnt > 1)
649 {
650 printf ("%d;", *val == CHAR_MAX ? -1 : *val);
651 --cnt;
652 ++val;
653 }
654
655 printf ("%d", cnt == 0 || *val == CHAR_MAX ? -1 : *val);
656 }
657 break;
299a95b9
RM
658 case word:
659 {
660 unsigned int val = (unsigned int) nl_langinfo (item->item_id);
661 printf ("%d", val);
662 }
663 break;
2b83a2a4
RM
664 default:
665 }
666 putchar ('\n');
667 }
668
669 for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
670 {
671 size_t item_no;
672
2b83a2a4
RM
673 if (strcmp (name, category[cat_no].name) == 0)
674 /* Print the whole category. */
675 {
676 if (show_category_name != 0)
677 puts (category[cat_no].name);
678
679 for (item_no = 0; item_no < category[cat_no].number; ++item_no)
680 print_item (&category[cat_no].item_desc[item_no]);
681
682 return;
683 }
19bc17a9 684
2b83a2a4
RM
685 for (item_no = 0; item_no < category[cat_no].number; ++item_no)
686 if (strcmp (name, category[cat_no].item_desc[item_no].name) == 0)
687 {
688 if (show_category_name != 0)
689 puts (category[cat_no].name);
690
691 print_item (&category[cat_no].item_desc[item_no]);
692 return;
693 }
694 }
036cc82f 695
6d52618b
UD
696 /* The name is not a standard one.
697 For testing and perhaps advanced use allow some more symbols. */
036cc82f 698 locale_special (name, show_category_name, show_keyword_name);
2b83a2a4 699}
This page took 0.13054 seconds and 5 git commands to generate.