]> git.ipfire.org Git - thirdparty/glibc.git/blob - locale/programs/locale.c
Update to LGPL v2.1.
[thirdparty/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 Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the 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 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; if not, write to the Free
18 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 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 <fcntl.h>
31 #include <langinfo.h>
32 #include <libintl.h>
33 #include <limits.h>
34 #include <locale.h>
35 #include <search.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
40 #include <sys/mman.h>
41 #include <sys/stat.h>
42
43 #include "localeinfo.h"
44 #include "charmap-dir.h"
45
46 extern void *xmalloc (size_t __n);
47 extern char *xstrdup (const char *__str);
48
49
50 /* If set print the name of the category. */
51 static int show_category_name;
52
53 /* If set print the name of the item. */
54 static int show_keyword_name;
55
56 /* Print names of all available locales. */
57 static int do_all;
58
59 /* Print names of all available character maps. */
60 static int do_charmaps = 0;
61
62 /* Nonzero if verbose output is wanted. */
63 static int verbose;
64
65 /* Name and version of program. */
66 static void print_version (FILE *stream, struct argp_state *state);
67 void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
68
69 /* Definitions of arguments for argp functions. */
70 static const struct argp_option options[] =
71 {
72 { NULL, 0, NULL, 0, N_("System information:") },
73 { "all-locales", 'a', NULL, OPTION_NO_USAGE,
74 N_("Write names of available locales") },
75 { "charmaps", 'm', NULL, OPTION_NO_USAGE,
76 N_("Write names of available charmaps") },
77 { NULL, 0, NULL, 0, N_("Modify output format:") },
78 { "category-name", 'c', NULL, 0, N_("Write names of selected categories") },
79 { "keyword-name", 'k', NULL, 0, N_("Write names of selected keywords") },
80 { "verbose", 'v', NULL, 0, N_("Print more information") },
81 { NULL, 0, NULL, 0, NULL }
82 };
83
84 /* Short description of program. */
85 static const char doc[] = N_("Get locale-specific information.");
86
87 /* Strings for arguments in help texts. */
88 static const char args_doc[] = N_("NAME\n[-a|-m]");
89
90 /* Prototype for option handler. */
91 static error_t parse_opt (int key, char *arg, struct argp_state *state);
92
93 /* Function to print some extra text in the help message. */
94 static char *more_help (int key, const char *text, void *input);
95
96 /* Data structure to communicate with argp functions. */
97 static struct argp argp =
98 {
99 options, parse_opt, args_doc, doc, NULL, more_help
100 };
101
102
103 /* We don't have these constants defined because we don't use them. Give
104 default values. */
105 #define CTYPE_MB_CUR_MIN 0
106 #define CTYPE_MB_CUR_MAX 0
107 #define CTYPE_HASH_SIZE 0
108 #define CTYPE_HASH_LAYERS 0
109 #define CTYPE_CLASS 0
110 #define CTYPE_TOUPPER_EB 0
111 #define CTYPE_TOLOWER_EB 0
112 #define CTYPE_TOUPPER_EL 0
113 #define CTYPE_TOLOWER_EL 0
114
115 /* Definition of the data structure which represents a category and its
116 items. */
117 struct category
118 {
119 int cat_id;
120 const char *name;
121 size_t number;
122 struct cat_item
123 {
124 int item_id;
125 const char *name;
126 enum { std, opt } status;
127 enum value_type value_type;
128 int min;
129 int max;
130 } *item_desc;
131 };
132
133 /* Simple helper macro. */
134 #define NELEMS(arr) ((sizeof (arr)) / (sizeof (arr[0])))
135
136 /* For some tricky stuff. */
137 #define NO_PAREN(Item, More...) Item, ## More
138
139 /* We have all categories defined in `categories.def'. Now construct
140 the description and data structure used for all categories. */
141 #define DEFINE_ELEMENT(Item, More...) { Item, ## More },
142 #define DEFINE_CATEGORY(category, name, items, postload) \
143 static struct cat_item category##_desc[] = \
144 { \
145 NO_PAREN items \
146 };
147
148 #include "categories.def"
149 #undef DEFINE_CATEGORY
150
151 static struct category category[] =
152 {
153 #define DEFINE_CATEGORY(category, name, items, postload) \
154 [category] = { _NL_NUM_##category, name, NELEMS (category##_desc), \
155 category##_desc },
156 #include "categories.def"
157 #undef DEFINE_CATEGORY
158 };
159 #define NCATEGORIES NELEMS (category)
160
161
162 /* Automatically set variable. */
163 extern const char *__progname;
164
165 /* helper function for extended name handling. */
166 extern void locale_special (const char *name, int show_category_name,
167 int show_keyword_name);
168
169 /* Prototypes for local functions. */
170 static void write_locales (void);
171 static void write_charmaps (void);
172 static void show_locale_vars (void);
173 static void show_info (const char *name);
174
175
176 int
177 main (int argc, char *argv[])
178 {
179 int remaining;
180
181 /* Set initial values for global variables. */
182 show_category_name = 0;
183 show_keyword_name = 0;
184
185 /* Set locale. Do not set LC_ALL because the other categories must
186 not be affected (according to POSIX.2). */
187 setlocale (LC_CTYPE, "");
188 setlocale (LC_MESSAGES, "");
189
190 /* Initialize the message catalog. */
191 textdomain (PACKAGE);
192
193 /* Parse and process arguments. */
194 argp_parse (&argp, argc, argv, 0, &remaining, NULL);
195
196 /* `-a' requests the names of all available locales. */
197 if (do_all != 0)
198 {
199 setlocale (LC_COLLATE, "");
200 write_locales ();
201 exit (EXIT_SUCCESS);
202 }
203
204 /* `m' requests the names of all available charmaps. The names can be
205 used for the -f argument to localedef(1). */
206 if (do_charmaps != 0)
207 {
208 write_charmaps ();
209 exit (EXIT_SUCCESS);
210 }
211
212 /* Specific information about the current locale are requested.
213 Change to this locale now. */
214 setlocale (LC_ALL, "");
215
216 /* If no real argument is given we have to print the contents of the
217 current locale definition variables. These are LANG and the LC_*. */
218 if (remaining == argc && show_keyword_name == 0 && show_category_name == 0)
219 {
220 show_locale_vars ();
221 exit (EXIT_SUCCESS);
222 }
223
224 /* Process all given names. */
225 while (remaining < argc)
226 show_info (argv[remaining++]);
227
228 exit (EXIT_SUCCESS);
229 }
230
231
232 /* Handle program arguments. */
233 static error_t
234 parse_opt (int key, char *arg, struct argp_state *state)
235 {
236 switch (key)
237 {
238 case 'a':
239 do_all = 1;
240 break;
241 case 'c':
242 show_category_name = 1;
243 break;
244 case 'm':
245 do_charmaps = 1;
246 break;
247 case 'k':
248 show_keyword_name = 1;
249 break;
250 case 'v':
251 verbose = 1;
252 break;
253 default:
254 return ARGP_ERR_UNKNOWN;
255 }
256 return 0;
257 }
258
259
260 static char *
261 more_help (int key, const char *text, void *input)
262 {
263 switch (key)
264 {
265 case ARGP_KEY_HELP_EXTRA:
266 /* We print some extra information. */
267 return xstrdup (gettext ("\
268 Report bugs using the `glibcbug' script to <bugs@gnu.org>.\n"));
269 default:
270 break;
271 }
272 return (char *) text;
273 }
274
275 /* Print the version information. */
276 static void
277 print_version (FILE *stream, struct argp_state *state)
278 {
279 fprintf (stream, "locale (GNU %s) %s\n", PACKAGE, VERSION);
280 fprintf (stream, gettext ("\
281 Copyright (C) %s Free Software Foundation, Inc.\n\
282 This is free software; see the source for copying conditions. There is NO\n\
283 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
284 "), "2001");
285 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
286 }
287
288
289 /* Simple action function which prints arguments as strings. */
290 static void
291 print_names (const void *nodep, VISIT value, int level)
292 {
293 if (value == postorder || value == leaf)
294 puts (*(char **) nodep);
295 }
296
297
298 static int
299 select_dirs (const struct dirent *dirent)
300 {
301 int result = 0;
302
303 if (strcmp (dirent->d_name, ".") != 0 && strcmp (dirent->d_name, "..") != 0)
304 {
305 mode_t mode = 0;
306
307 #ifdef _DIRENT_HAVE_D_TYPE
308 if (dirent->d_type != DT_UNKNOWN && dirent->d_type != DT_LNK)
309 mode = DTTOIF (dirent->d_type);
310 else
311 #endif
312 {
313 struct stat64 st;
314 char buf[sizeof (LOCALEDIR) + strlen (dirent->d_name) + 1];
315
316 stpcpy (stpcpy (stpcpy (buf, LOCALEDIR), "/"), dirent->d_name);
317
318 if (stat64 (buf, &st) == 0)
319 mode = st.st_mode;
320 }
321
322 result = S_ISDIR (mode);
323 }
324
325 return result;
326 }
327
328
329 /* Write the names of all available locales to stdout. We have some
330 sources of the information: the contents of the locale directory
331 and the locale.alias file. To avoid duplicates and print the
332 result is a reasonable order we put all entries is a search tree
333 and print them afterwards. */
334 static void
335 write_locales (void)
336 {
337 char linebuf[80];
338 void *all_data = NULL;
339 struct dirent **dirents;
340 int ndirents;
341 int cnt;
342 char *alias_path;
343 size_t alias_path_len;
344 char *entry;
345 int first_locale = 1;
346
347 #define PUT(name) tsearch (name, &all_data, \
348 (int (*) (const void *, const void *)) strcoll)
349
350 /* Now read the locale.alias files. */
351 if (argz_create_sep (LOCALE_ALIAS_PATH, ':', &alias_path, &alias_path_len))
352 error (1, errno, gettext ("while preparing output"));
353
354 entry = NULL;
355 while ((entry = argz_next (alias_path, alias_path_len, entry)))
356 {
357 static const char aliasfile[] = "/locale.alias";
358 FILE *fp;
359 char full_name[strlen (entry) + sizeof aliasfile];
360
361 stpcpy (stpcpy (full_name, entry), aliasfile);
362 fp = fopen (full_name, "r");
363 if (fp == NULL)
364 /* Ignore non-existing files. */
365 continue;
366
367 while (! feof (fp))
368 {
369 /* It is a reasonable approach to use a fix buffer here
370 because
371 a) we are only interested in the first two fields
372 b) these fields must be usable as file names and so must
373 not be that long */
374 char buf[BUFSIZ];
375 char *alias;
376 char *value;
377 char *cp;
378
379 if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
380 /* EOF reached. */
381 break;
382
383 cp = buf;
384 /* Ignore leading white space. */
385 while (isspace (cp[0]) && cp[0] != '\n')
386 ++cp;
387
388 /* A leading '#' signals a comment line. */
389 if (cp[0] != '\0' && cp[0] != '#' && cp[0] != '\n')
390 {
391 alias = cp++;
392 while (cp[0] != '\0' && !isspace (cp[0]))
393 ++cp;
394 /* Terminate alias name. */
395 if (cp[0] != '\0')
396 *cp++ = '\0';
397
398 /* Now look for the beginning of the value. */
399 while (isspace (cp[0]))
400 ++cp;
401
402 if (cp[0] != '\0')
403 {
404 value = cp++;
405 while (cp[0] != '\0' && !isspace (cp[0]))
406 ++cp;
407 /* Terminate value. */
408 if (cp[0] == '\n')
409 {
410 /* This has to be done to make the following
411 test for the end of line possible. We are
412 looking for the terminating '\n' which do not
413 overwrite here. */
414 *cp++ = '\0';
415 *cp = '\n';
416 }
417 else if (cp[0] != '\0')
418 *cp++ = '\0';
419
420 /* Add the alias. */
421 if (! verbose)
422 PUT (xstrdup (alias));
423 }
424 }
425
426 /* Possibly not the whole line fits into the buffer.
427 Ignore the rest of the line. */
428 while (strchr (cp, '\n') == NULL)
429 {
430 cp = buf;
431 if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
432 /* Make sure the inner loop will be left. The outer
433 loop will exit at the `feof' test. */
434 *cp = '\n';
435 }
436 }
437
438 fclose (fp);
439 }
440
441 memset (linebuf, '-', sizeof (linebuf) - 1);
442 linebuf[sizeof (linebuf) - 1] = '\0';
443
444 /* Now we can look for all files in the directory. */
445 ndirents = scandir (LOCALEDIR, &dirents, select_dirs, alphasort);
446 for (cnt = 0; cnt < ndirents; ++cnt)
447 {
448 /* Test whether at least the LC_CTYPE data is there. Some
449 directories only contain translations. */
450 char buf[sizeof (LOCALEDIR) + strlen (dirents[cnt]->d_name)
451 + sizeof "/LC_IDENTIFICATION"];
452 char *enddir;
453 struct stat64 st;
454
455 stpcpy (enddir = stpcpy (stpcpy (stpcpy (buf, LOCALEDIR), "/"),
456 dirents[cnt]->d_name),
457 "/LC_IDENTIFICATION");
458
459 if (stat64 (buf, &st) == 0 && S_ISREG (st.st_mode))
460 {
461 if (verbose)
462 {
463 /* Provide some nice output of all kinds of
464 information. */
465 int fd;
466
467 if (! first_locale)
468 putchar_unlocked ('\n');
469 first_locale = 0;
470
471 printf ("locale: %-15.15s directory: %.*s\n%s\n",
472 dirents[cnt]->d_name, (int) (enddir - buf), buf,
473 linebuf);
474
475 fd = open64 (buf, O_RDONLY);
476 if (fd != -1)
477 {
478 void *mapped = mmap64 (NULL, st.st_size, PROT_READ,
479 MAP_SHARED, fd, 0);
480 if (mapped != MAP_FAILED)
481 {
482 /* Read the information from the file. */
483 struct
484 {
485 unsigned int magic;
486 unsigned int nstrings;
487 unsigned int strindex[0];
488 } *filedata = mapped;
489
490 if (filedata->magic == LIMAGIC (LC_IDENTIFICATION)
491 && (sizeof *filedata
492 + (filedata->nstrings
493 * sizeof (unsigned int))
494 <= (size_t) st.st_size))
495 {
496 const char *str;
497
498 #define HANDLE(idx, name) \
499 str = ((char *) mapped \
500 + filedata->strindex[_NL_ITEM_INDEX (_NL_IDENTIFICATION_##idx)]); \
501 if (*str != '\0') \
502 printf ("%9s | %s\n", name, str)
503 HANDLE (TITLE, "title");
504 HANDLE (SOURCE, "source");
505 HANDLE (ADDRESS, "address");
506 HANDLE (CONTACT, "contact");
507 HANDLE (EMAIL, "email");
508 HANDLE (TEL, "telephone");
509 HANDLE (FAX, "fax");
510 HANDLE (LANGUAGE, "language");
511 HANDLE (TERRITORY, "territory");
512 HANDLE (AUDIENCE, "audience");
513 HANDLE (APPLICATION, "application");
514 HANDLE (ABBREVIATION, "abbreviation");
515 HANDLE (REVISION, "revision");
516 HANDLE (DATE, "date");
517 }
518
519 munmap (mapped, st.st_size);
520 }
521
522 close (fd);
523
524 /* Now try to get the charset information. */
525 strcpy (enddir, "/LC_CTYPE");
526 fd = open64 (buf, O_RDONLY);
527 if (fd != -1 && fstat64 (fd, &st) >= 0
528 && ((mapped = mmap64 (NULL, st.st_size, PROT_READ,
529 MAP_SHARED, fd, 0))
530 != MAP_FAILED))
531 {
532 struct
533 {
534 unsigned int magic;
535 unsigned int nstrings;
536 unsigned int strindex[0];
537 } *filedata = mapped;
538
539 if (filedata->magic == LIMAGIC (LC_CTYPE)
540 && (sizeof *filedata
541 + (filedata->nstrings
542 * sizeof (unsigned int))
543 <= (size_t) st.st_size))
544 {
545 const char *str;
546
547 str = ((char *) mapped
548 + filedata->strindex[_NL_ITEM_INDEX (_NL_CTYPE_CODESET_NAME)]);
549 if (*str != '\0')
550 printf (" codeset | %s\n", str);
551 }
552
553 munmap (mapped, st.st_size);
554 }
555
556 if (fd != -1)
557 close (fd);
558 }
559 }
560 else
561 /* If the verbose format is not selected we simply
562 collect the names. */
563 PUT (xstrdup (dirents[cnt]->d_name));
564 }
565 }
566 if (ndirents > 0)
567 free (dirents);
568
569 if (! verbose)
570 {
571 /* `POSIX' locale is always available (POSIX.2 4.34.3). */
572 PUT ("POSIX");
573 /* And so is the "C" locale. */
574 PUT ("C");
575
576 twalk (all_data, print_names);
577 }
578 }
579
580
581 /* Write the names of all available character maps to stdout. */
582 static void
583 write_charmaps (void)
584 {
585 void *all_data = NULL;
586 CHARMAP_DIR *dir;
587 const char *dirent;
588
589 /* Look for all files in the charmap directory. */
590 dir = charmap_opendir (CHARMAP_PATH);
591 if (dir == NULL)
592 return;
593
594 while ((dirent = charmap_readdir (dir)) != NULL)
595 {
596 char **aliases;
597 char **p;
598
599 PUT (xstrdup (dirent));
600
601 aliases = charmap_aliases (CHARMAP_PATH, dirent);
602
603 #if 0
604 /* Add the code_set_name and the aliases. */
605 for (p = aliases; *p; p++)
606 PUT (xstrdup (*p));
607 #else
608 /* Add the code_set_name only. Most aliases are obsolete. */
609 p = aliases;
610 if (*p)
611 PUT (xstrdup (*p));
612 #endif
613
614 charmap_free_aliases (aliases);
615 }
616
617 charmap_closedir (dir);
618
619 twalk (all_data, print_names);
620 }
621
622
623 /* We have to show the contents of the environments determining the
624 locale. */
625 static void
626 show_locale_vars (void)
627 {
628 size_t cat_no;
629 const char *lcall = getenv ("LC_ALL");
630 const char *lang = getenv ("LANG") ? : "POSIX";
631
632 auto void get_source (const char *name);
633
634 void get_source (const char *name)
635 {
636 char *val = getenv (name);
637
638 if ((lcall ?: "")[0] != '\0' || val == NULL)
639 printf ("%s=\"%s\"\n", name, (lcall ?: "")[0] ? lcall : lang);
640 else
641 printf ("%s=%s\n", name, val);
642 }
643
644 /* LANG has to be the first value. */
645 printf ("LANG=%s\n", lang);
646
647 /* Now all categories in an unspecified order. */
648 for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
649 if (cat_no != LC_ALL)
650 get_source (category[cat_no].name);
651
652 /* The last is the LC_ALL value. */
653 printf ("LC_ALL=%s\n", lcall ? : "");
654 }
655
656
657 /* Show the information request for NAME. */
658 static void
659 show_info (const char *name)
660 {
661 size_t cat_no;
662
663 auto void print_item (struct cat_item *item);
664
665 void print_item (struct cat_item *item)
666 {
667 switch (item->value_type)
668 {
669 case string:
670 if (show_keyword_name)
671 printf ("%s=\"", item->name);
672 fputs (nl_langinfo (item->item_id) ? : "", stdout);
673 if (show_keyword_name)
674 putchar ('"');
675 putchar ('\n');
676 break;
677 case stringarray:
678 {
679 int cnt;
680 const char *val;
681
682 if (show_keyword_name)
683 printf ("%s=\"", item->name);
684
685 for (cnt = 0; cnt < item->max - 1; ++cnt)
686 {
687 val = nl_langinfo (item->item_id + cnt);
688 if (val != NULL)
689 fputs (val, stdout);
690 putchar (';');
691 }
692
693 val = nl_langinfo (item->item_id + cnt);
694 if (val != NULL)
695 fputs (val, stdout);
696
697 if (show_keyword_name)
698 putchar ('"');
699 putchar ('\n');
700 }
701 break;
702 case stringlist:
703 {
704 int first = 1;
705 const char *val = nl_langinfo (item->item_id) ? : "";
706 int cnt;
707
708 if (show_keyword_name)
709 printf ("%s=", item->name);
710
711 for (cnt = 0; cnt < item->max && *val != '\0'; ++cnt)
712 {
713 printf ("%s%s%s%s", first ? "" : ";",
714 show_keyword_name ? "\"" : "", val,
715 show_keyword_name ? "\"" : "");
716 val = strchr (val, '\0') + 1;
717 first = 0;
718 }
719 putchar ('\n');
720 }
721 break;
722 case byte:
723 {
724 const char *val = nl_langinfo (item->item_id);
725
726 if (show_keyword_name)
727 printf ("%s=", item->name);
728
729 if (val != NULL)
730 printf ("%d", *val == CHAR_MAX ? -1 : *val);
731 putchar ('\n');
732 }
733 break;
734 case bytearray:
735 {
736 const char *val = nl_langinfo (item->item_id);
737 int cnt = val ? strlen (val) : 0;
738
739 if (show_keyword_name)
740 printf ("%s=", item->name);
741
742 while (cnt > 1)
743 {
744 printf ("%d;", *val == CHAR_MAX ? -1 : *val);
745 --cnt;
746 ++val;
747 }
748
749 printf ("%d\n", cnt == 0 || *val == CHAR_MAX ? -1 : *val);
750 }
751 break;
752 case word:
753 {
754 unsigned int val =
755 (unsigned int) (unsigned long int) nl_langinfo (item->item_id);
756 if (show_keyword_name)
757 printf ("%s=", item->name);
758
759 printf ("%d\n", val);
760 }
761 break;
762 case wstring:
763 case wstringarray:
764 case wstringlist:
765 /* We don't print wide character information since the same
766 information is available in a multibyte string. */
767 default:
768 break;
769
770 }
771 }
772
773 for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
774 if (cat_no != LC_ALL)
775 {
776 size_t item_no;
777
778 if (strcmp (name, category[cat_no].name) == 0)
779 /* Print the whole category. */
780 {
781 if (show_category_name != 0)
782 puts (category[cat_no].name);
783
784 for (item_no = 0; item_no < category[cat_no].number; ++item_no)
785 print_item (&category[cat_no].item_desc[item_no]);
786
787 return;
788 }
789
790 for (item_no = 0; item_no < category[cat_no].number; ++item_no)
791 if (strcmp (name, category[cat_no].item_desc[item_no].name) == 0)
792 {
793 if (show_category_name != 0)
794 puts (category[cat_no].name);
795
796 print_item (&category[cat_no].item_desc[item_no]);
797 return;
798 }
799 }
800
801 /* The name is not a standard one.
802 For testing and perhaps advanced use allow some more symbols. */
803 locale_special (name, show_category_name, show_keyword_name);
804 }