1 /* Implementation of the locale program according to POSIX 9945-2.
2 Copyright (C) 1995-2014 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1995.
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published
8 by the Free Software Foundation; version 2 of the License, or
9 (at your option) any later version.
11 This program 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
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, see <http://www.gnu.org/licenses/>. */
35 #include <stdio_ext.h>
43 #include "localeinfo.h"
44 #include "charmap-dir.h"
45 #include "../locarchive.h"
46 #include <programs/xmalloc.h>
48 #define ARCHIVE_NAME LOCALEDIR "/locale-archive"
50 /* If set print the name of the category. */
51 static int show_category_name
;
53 /* If set print the name of the item. */
54 static int show_keyword_name
;
56 /* Print names of all available locales. */
59 /* Print names of all available character maps. */
60 static int do_charmaps
= 0;
62 /* Nonzero if verbose output is wanted. */
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
;
69 /* Definitions of arguments for argp functions. */
70 static const struct argp_option options
[] =
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
}
84 /* Short description of program. */
85 static const char doc
[] = N_("Get locale-specific information.");
87 /* Strings for arguments in help texts. */
88 static const char args_doc
[] = N_("NAME\n[-a|-m]");
90 /* Prototype for option handler. */
91 static error_t
parse_opt (int key
, char *arg
, struct argp_state
*state
);
93 /* Function to print some extra text in the help message. */
94 static char *more_help (int key
, const char *text
, void *input
);
96 /* Data structure to communicate with argp functions. */
97 static struct argp argp
=
99 options
, parse_opt
, args_doc
, doc
, NULL
, more_help
103 /* We don't have these constants defined because we don't use them. Give
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
115 /* Definition of the data structure which represents a category and its
126 enum { std
, opt
} status
;
127 enum value_type value_type
;
133 /* Simple helper macro. */
134 #define NELEMS(arr) ((sizeof (arr)) / (sizeof (arr[0])))
136 /* For some tricky stuff. */
137 #define NO_PAREN(Item, More...) Item, ## More
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[] = \
148 #include "categories.def"
149 #undef DEFINE_CATEGORY
151 static struct category category
[] =
153 #define DEFINE_CATEGORY(category, name, items, postload) \
154 [category] = { _NL_NUM_##category, name, NELEMS (category##_desc), \
156 #include "categories.def"
157 #undef DEFINE_CATEGORY
159 #define NCATEGORIES NELEMS (category)
162 /* Automatically set variable. */
163 extern const char *__progname
;
165 /* helper function for extended name handling. */
166 extern void locale_special (const char *name
, int show_category_name
,
167 int show_keyword_name
);
169 /* Prototypes for local functions. */
170 static void print_LC_IDENTIFICATION (void *mapped
, size_t size
);
171 static void print_LC_CTYPE (void *mapped
, size_t size
);
172 static void write_locales (void);
173 static int nameentcmp (const void *a
, const void *b
);
174 static int write_archive_locales (void **all_datap
, char *linebuf
);
175 static void write_charmaps (void);
176 static void show_locale_vars (void);
177 static void show_info (const char *name
);
181 main (int argc
, char *argv
[])
185 /* Set initial values for global variables. */
186 show_category_name
= 0;
187 show_keyword_name
= 0;
189 /* Set locale. Do not set LC_ALL because the other categories must
190 not be affected (according to POSIX.2). */
191 if (setlocale (LC_CTYPE
, "") == NULL
)
192 error (0, errno
, gettext ("Cannot set LC_CTYPE to default locale"));
193 if (setlocale (LC_MESSAGES
, "") == NULL
)
194 error (0, errno
, gettext ("Cannot set LC_MESSAGES to default locale"));
196 /* Initialize the message catalog. */
197 textdomain (PACKAGE
);
199 /* Parse and process arguments. */
200 argp_parse (&argp
, argc
, argv
, 0, &remaining
, NULL
);
202 /* `-a' requests the names of all available locales. */
205 if (setlocale (LC_COLLATE
, "") == NULL
)
207 gettext ("Cannot set LC_COLLATE to default locale"));
212 /* `m' requests the names of all available charmaps. The names can be
213 used for the -f argument to localedef(1). */
214 if (do_charmaps
!= 0)
220 /* Specific information about the current locale are requested.
221 Change to this locale now. */
222 if (setlocale (LC_ALL
, "") == NULL
)
223 error (0, errno
, gettext ("Cannot set LC_ALL to default locale"));
225 /* If no real argument is given we have to print the contents of the
226 current locale definition variables. These are LANG and the LC_*. */
227 if (remaining
== argc
&& show_keyword_name
== 0 && show_category_name
== 0)
233 /* Process all given names. */
234 while (remaining
< argc
)
235 show_info (argv
[remaining
++]);
241 /* Handle program arguments. */
243 parse_opt (int key
, char *arg
, struct argp_state
*state
)
251 show_category_name
= 1;
257 show_keyword_name
= 1;
263 return ARGP_ERR_UNKNOWN
;
270 more_help (int key
, const char *text
, void *input
)
275 case ARGP_KEY_HELP_EXTRA
:
276 /* We print some extra information. */
277 if (asprintf (&tp
, gettext ("\
278 For bug reporting instructions, please see:\n\
279 %s.\n"), REPORT_BUGS_TO
) < 0)
285 return (char *) text
;
289 /* Print the version information. */
291 print_version (FILE *stream
, struct argp_state
*state
)
293 fprintf (stream
, "locale %s%s\n", PKGVERSION
, VERSION
);
294 fprintf (stream
, gettext ("\
295 Copyright (C) %s Free Software Foundation, Inc.\n\
296 This is free software; see the source for copying conditions. There is NO\n\
297 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
299 fprintf (stream
, gettext ("Written by %s.\n"), "Ulrich Drepper");
303 /* Simple action function which prints arguments as strings. */
305 print_names (const void *nodep
, VISIT value
, int level
)
307 if (value
== postorder
|| value
== leaf
)
308 puts (*(char **) nodep
);
313 select_dirs (const struct dirent
*dirent
)
317 if (strcmp (dirent
->d_name
, ".") != 0 && strcmp (dirent
->d_name
, "..") != 0)
321 #ifdef _DIRENT_HAVE_D_TYPE
322 if (dirent
->d_type
!= DT_UNKNOWN
&& dirent
->d_type
!= DT_LNK
)
323 mode
= DTTOIF (dirent
->d_type
);
328 char buf
[sizeof (LOCALEDIR
) + strlen (dirent
->d_name
) + 1];
330 stpcpy (stpcpy (stpcpy (buf
, LOCALEDIR
), "/"), dirent
->d_name
);
332 if (stat64 (buf
, &st
) == 0)
336 result
= S_ISDIR (mode
);
344 print_LC_IDENTIFICATION (void *mapped
, size_t size
)
346 /* Read the information from the file. */
350 unsigned int nstrings
;
351 unsigned int strindex
[0];
352 } *filedata
= mapped
;
354 if (filedata
->magic
== LIMAGIC (LC_IDENTIFICATION
)
356 + (filedata
->nstrings
357 * sizeof (unsigned int))
362 #define HANDLE(idx, name) \
363 str = ((char *) mapped \
364 + filedata->strindex[_NL_ITEM_INDEX (_NL_IDENTIFICATION_##idx)]); \
366 printf ("%9s | %s\n", name, str)
367 HANDLE (TITLE
, "title");
368 HANDLE (SOURCE
, "source");
369 HANDLE (ADDRESS
, "address");
370 HANDLE (CONTACT
, "contact");
371 HANDLE (EMAIL
, "email");
372 HANDLE (TEL
, "telephone");
374 HANDLE (LANGUAGE
, "language");
375 HANDLE (TERRITORY
, "territory");
376 HANDLE (AUDIENCE
, "audience");
377 HANDLE (APPLICATION
, "application");
378 HANDLE (ABBREVIATION
, "abbreviation");
379 HANDLE (REVISION
, "revision");
380 HANDLE (DATE
, "date");
386 print_LC_CTYPE (void *mapped
, size_t size
)
391 unsigned int nstrings
;
392 unsigned int strindex
[0];
393 } *filedata
= mapped
;
395 if (filedata
->magic
== LIMAGIC (LC_CTYPE
)
397 + (filedata
->nstrings
398 * sizeof (unsigned int))
403 str
= ((char *) mapped
404 + filedata
->strindex
[_NL_ITEM_INDEX (_NL_CTYPE_CODESET_NAME
)]);
406 printf (" codeset | %s\n", str
);
411 /* Write the names of all available locales to stdout. We have some
412 sources of the information: the contents of the locale directory
413 and the locale.alias file. To avoid duplicates and print the
414 result is a reasonable order we put all entries is a search tree
415 and print them afterwards. */
420 void *all_data
= NULL
;
421 struct dirent
**dirents
;
425 size_t alias_path_len
;
427 int first_locale
= 1;
429 #define PUT(name) tsearch (name, &all_data, \
430 (int (*) (const void *, const void *)) strcoll)
431 #define GET(name) tfind (name, &all_data, \
432 (int (*) (const void *, const void *)) strcoll)
434 /* `POSIX' locale is always available (POSIX.2 4.34.3). */
436 /* And so is the "C" locale. */
439 memset (linebuf
, '-', sizeof (linebuf
) - 1);
440 linebuf
[sizeof (linebuf
) - 1] = '\0';
442 /* First scan the locale archive. */
443 if (write_archive_locales (&all_data
, linebuf
))
446 /* Now we can look for all files in the directory. */
447 ndirents
= scandir (LOCALEDIR
, &dirents
, select_dirs
, alphasort
);
448 for (cnt
= 0; cnt
< ndirents
; ++cnt
)
450 /* Test whether at least the LC_CTYPE data is there. Some
451 directories only contain translations. */
452 char buf
[sizeof (LOCALEDIR
) + strlen (dirents
[cnt
]->d_name
)
453 + sizeof "/LC_IDENTIFICATION"];
457 stpcpy (enddir
= stpcpy (stpcpy (stpcpy (buf
, LOCALEDIR
), "/"),
458 dirents
[cnt
]->d_name
),
459 "/LC_IDENTIFICATION");
461 if (stat64 (buf
, &st
) == 0 && S_ISREG (st
.st_mode
))
463 if (verbose
&& GET (dirents
[cnt
]->d_name
) == NULL
)
465 /* Provide some nice output of all kinds of
470 putchar_unlocked ('\n');
473 printf ("locale: %-15.15s directory: %.*s\n%s\n",
474 dirents
[cnt
]->d_name
, (int) (enddir
- buf
), buf
,
477 fd
= open64 (buf
, O_RDONLY
);
480 void *mapped
= mmap64 (NULL
, st
.st_size
, PROT_READ
,
482 if (mapped
!= MAP_FAILED
)
484 print_LC_IDENTIFICATION (mapped
, st
.st_size
);
486 munmap (mapped
, st
.st_size
);
491 /* Now try to get the charset information. */
492 strcpy (enddir
, "/LC_CTYPE");
493 fd
= open64 (buf
, O_RDONLY
);
494 if (fd
!= -1 && fstat64 (fd
, &st
) >= 0
495 && ((mapped
= mmap64 (NULL
, st
.st_size
, PROT_READ
,
499 print_LC_CTYPE (mapped
, st
.st_size
);
501 munmap (mapped
, st
.st_size
);
509 /* If the verbose format is not selected we simply
510 collect the names. */
511 PUT (xstrdup (dirents
[cnt
]->d_name
));
517 /* Now read the locale.alias files. */
518 if (argz_create_sep (LOCALE_ALIAS_PATH
, ':', &alias_path
, &alias_path_len
))
519 error (1, errno
, gettext ("while preparing output"));
522 while ((entry
= argz_next (alias_path
, alias_path_len
, entry
)))
524 static const char aliasfile
[] = "/locale.alias";
526 char full_name
[strlen (entry
) + sizeof aliasfile
];
528 stpcpy (stpcpy (full_name
, entry
), aliasfile
);
529 fp
= fopen (full_name
, "rm");
531 /* Ignore non-existing files. */
534 /* No threads present. */
535 __fsetlocking (fp
, FSETLOCKING_BYCALLER
);
537 while (! feof_unlocked (fp
))
539 /* It is a reasonable approach to use a fix buffer here
541 a) we are only interested in the first two fields
542 b) these fields must be usable as file names and so must
549 if (fgets_unlocked (buf
, BUFSIZ
, fp
) == NULL
)
554 /* Ignore leading white space. */
555 while (isspace (cp
[0]) && cp
[0] != '\n')
558 /* A leading '#' signals a comment line. */
559 if (cp
[0] != '\0' && cp
[0] != '#' && cp
[0] != '\n')
562 while (cp
[0] != '\0' && !isspace (cp
[0]))
564 /* Terminate alias name. */
568 /* Now look for the beginning of the value. */
569 while (isspace (cp
[0]))
575 while (cp
[0] != '\0' && !isspace (cp
[0]))
577 /* Terminate value. */
580 /* This has to be done to make the following
581 test for the end of line possible. We are
582 looking for the terminating '\n' which do not
587 else if (cp
[0] != '\0')
591 if (! verbose
&& GET (value
) != NULL
)
592 PUT (xstrdup (alias
));
596 /* Possibly not the whole line fits into the buffer.
597 Ignore the rest of the line. */
598 while (strchr (cp
, '\n') == NULL
)
601 if (fgets_unlocked (buf
, BUFSIZ
, fp
) == NULL
)
602 /* Make sure the inner loop will be left. The outer
603 loop will exit at the `feof' test. */
613 twalk (all_data
, print_names
);
621 uint32_t locrec_offset
;
626 nameentcmp (const void *a
, const void *b
)
628 return strcoll (((const struct nameent
*) a
)->name
,
629 ((const struct nameent
*) b
)->name
);
634 write_archive_locales (void **all_datap
, char *linebuf
)
637 void *all_data
= *all_datap
;
639 struct locarhead
*head
;
640 struct namehashent
*namehashtab
;
641 char *addr
= MAP_FAILED
;
645 fd
= open64 (ARCHIVE_NAME
, O_RDONLY
);
649 if (fstat64 (fd
, &st
) < 0 || st
.st_size
< sizeof (*head
))
653 addr
= mmap64 (NULL
, len
, PROT_READ
, MAP_SHARED
, fd
, 0);
654 if (addr
== MAP_FAILED
)
657 head
= (struct locarhead
*) addr
;
658 if (head
->namehash_offset
+ head
->namehash_size
> len
659 || head
->string_offset
+ head
->string_size
> len
660 || head
->locrectab_offset
+ head
->locrectab_size
> len
661 || head
->sumhash_offset
+ head
->sumhash_size
> len
)
664 namehashtab
= (struct namehashent
*) (addr
+ head
->namehash_offset
);
667 for (cnt
= 0; cnt
< head
->namehash_size
; ++cnt
)
668 if (namehashtab
[cnt
].locrec_offset
!= 0)
670 PUT (xstrdup (addr
+ namehashtab
[cnt
].name_offset
));
676 struct nameent
*names
;
679 names
= (struct nameent
*) xmalloc (head
->namehash_used
680 * sizeof (struct nameent
));
681 for (cnt
= used
= 0; cnt
< head
->namehash_size
; ++cnt
)
682 if (namehashtab
[cnt
].locrec_offset
!= 0)
684 names
[used
].name
= addr
+ namehashtab
[cnt
].name_offset
;
685 names
[used
++].locrec_offset
= namehashtab
[cnt
].locrec_offset
;
688 /* Sort the names. */
689 qsort (names
, used
, sizeof (struct nameent
), nameentcmp
);
691 for (cnt
= 0; cnt
< used
; ++cnt
)
693 struct locrecent
*locrec
;
695 PUT (xstrdup (names
[cnt
].name
));
698 putchar_unlocked ('\n');
700 printf ("locale: %-15.15s archive: " ARCHIVE_NAME
"\n%s\n",
701 names
[cnt
].name
, linebuf
);
703 locrec
= (struct locrecent
*) (addr
+ names
[cnt
].locrec_offset
);
705 print_LC_IDENTIFICATION (addr
706 + locrec
->record
[LC_IDENTIFICATION
].offset
,
707 locrec
->record
[LC_IDENTIFICATION
].len
);
709 print_LC_CTYPE (addr
+ locrec
->record
[LC_CTYPE
].offset
,
710 locrec
->record
[LC_CTYPE
].len
);
717 if (addr
!= MAP_FAILED
)
720 *all_datap
= all_data
;
725 /* Write the names of all available character maps to stdout. */
727 write_charmaps (void)
729 void *all_data
= NULL
;
733 /* Look for all files in the charmap directory. */
734 dir
= charmap_opendir (CHARMAP_PATH
);
738 while ((dirent
= charmap_readdir (dir
)) != NULL
)
743 PUT (xstrdup (dirent
));
745 aliases
= charmap_aliases (CHARMAP_PATH
, dirent
);
748 /* Add the code_set_name and the aliases. */
749 for (p
= aliases
; *p
; p
++)
752 /* Add the code_set_name only. Most aliases are obsolete. */
758 charmap_free_aliases (aliases
);
761 charmap_closedir (dir
);
763 twalk (all_data
, print_names
);
766 /* Print a properly quoted assignment of NAME with VAL, using double
767 quotes iff DQUOTE is true. */
769 print_assignment (const char *name
, const char *val
, bool dquote
)
771 printf ("%s=", name
);
777 = strcspn (val
, dquote
? "$`\"\\" : "~|&;<>()$`\\\"' \t\n");
778 printf ("%.*s", (int) segment
, val
);
790 /* We have to show the contents of the environments determining the
793 show_locale_vars (void)
796 const char *lcall
= getenv ("LC_ALL") ? : "";
797 const char *lang
= getenv ("LANG") ? : "";
799 auto void get_source (const char *name
);
801 void get_source (const char *name
)
803 char *val
= getenv (name
);
805 if (lcall
[0] != '\0' || val
== NULL
)
806 print_assignment (name
, lcall
[0] ? lcall
: lang
[0] ? lang
: "POSIX",
809 print_assignment (name
, val
, false);
812 /* LANG has to be the first value. */
813 print_assignment ("LANG", lang
, false);
815 /* Now all categories in an unspecified order. */
816 for (cat_no
= 0; cat_no
< NCATEGORIES
; ++cat_no
)
817 if (cat_no
!= LC_ALL
)
818 get_source (category
[cat_no
].name
);
820 /* The last is the LC_ALL value. */
821 print_assignment ("LC_ALL", lcall
, false);
825 /* Show the information request for NAME. */
827 show_info (const char *name
)
831 auto void print_item (struct cat_item
*item
);
833 void print_item (struct cat_item
*item
)
835 switch (item
->value_type
)
838 if (show_keyword_name
)
839 printf ("%s=\"", item
->name
);
840 fputs (nl_langinfo (item
->item_id
) ? : "", stdout
);
841 if (show_keyword_name
)
850 if (show_keyword_name
)
851 printf ("%s=\"", item
->name
);
853 for (cnt
= 0; cnt
< item
->max
- 1; ++cnt
)
855 val
= nl_langinfo (item
->item_id
+ cnt
);
861 val
= nl_langinfo (item
->item_id
+ cnt
);
865 if (show_keyword_name
)
873 const char *val
= nl_langinfo (item
->item_id
) ? : "";
876 if (show_keyword_name
)
877 printf ("%s=", item
->name
);
879 for (cnt
= 0; cnt
< item
->max
&& *val
!= '\0'; ++cnt
)
881 printf ("%s%s%s%s", first
? "" : ";",
882 show_keyword_name
? "\"" : "", val
,
883 show_keyword_name
? "\"" : "");
884 val
= strchr (val
, '\0') + 1;
892 const char *val
= nl_langinfo (item
->item_id
);
894 if (show_keyword_name
)
895 printf ("%s=", item
->name
);
898 printf ("%d", *val
== '\377' ? -1 : *val
);
904 const char *val
= nl_langinfo (item
->item_id
);
905 int cnt
= val
? strlen (val
) : 0;
907 if (show_keyword_name
)
908 printf ("%s=", item
->name
);
912 printf ("%d;", *val
== '\177' ? -1 : *val
);
917 printf ("%d\n", cnt
== 0 || *val
== '\177' ? -1 : *val
);
922 union { unsigned int word
; char *string
; } val
;
923 val
.string
= nl_langinfo (item
->item_id
);
924 if (show_keyword_name
)
925 printf ("%s=", item
->name
);
927 printf ("%d\n", val
.word
);
933 union { unsigned int *wordarray
; char *string
; } val
;
936 val
.string
= nl_langinfo (item
->item_id
);
937 if (show_keyword_name
)
938 printf ("%s=", item
->name
);
940 for (cnt
= 0; cnt
< item
->max
; ++cnt
)
942 printf ("%s%d", first
? "" : ";", val
.wordarray
[cnt
]);
951 /* We don't print wide character information since the same
952 information is available in a multibyte string. */
959 for (cat_no
= 0; cat_no
< NCATEGORIES
; ++cat_no
)
960 if (cat_no
!= LC_ALL
)
964 if (strcmp (name
, category
[cat_no
].name
) == 0)
965 /* Print the whole category. */
967 if (show_category_name
!= 0)
968 puts (category
[cat_no
].name
);
970 for (item_no
= 0; item_no
< category
[cat_no
].number
; ++item_no
)
971 print_item (&category
[cat_no
].item_desc
[item_no
]);
976 for (item_no
= 0; item_no
< category
[cat_no
].number
; ++item_no
)
977 if (strcmp (name
, category
[cat_no
].item_desc
[item_no
].name
) == 0)
979 if (show_category_name
!= 0)
980 puts (category
[cat_no
].name
);
982 print_item (&category
[cat_no
].item_desc
[item_no
]);
987 /* The name is not a standard one.
988 For testing and perhaps advanced use allow some more symbols. */
989 locale_special (name
, show_category_name
, show_keyword_name
);