1 /* Implementation of the locale program according to POSIX 9945-2.
2 Copyright (C) 1995-1997, 1999-2003, 2004 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1995.
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.
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.
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
37 #include <stdio_ext.h>
44 #include "localeinfo.h"
45 #include "charmap-dir.h"
46 #include "../locarchive.h"
48 extern void *xmalloc (size_t __n
);
49 extern char *xstrdup (const char *__str
);
51 #define ARCHIVE_NAME LOCALEDIR "/locale-archive"
53 /* If set print the name of the category. */
54 static int show_category_name
;
56 /* If set print the name of the item. */
57 static int show_keyword_name
;
59 /* Print names of all available locales. */
62 /* Print names of all available character maps. */
63 static int do_charmaps
= 0;
65 /* Nonzero if verbose output is wanted. */
68 /* Name and version of program. */
69 static void print_version (FILE *stream
, struct argp_state
*state
);
70 void (*argp_program_version_hook
) (FILE *, struct argp_state
*) = print_version
;
72 /* Definitions of arguments for argp functions. */
73 static const struct argp_option options
[] =
75 { NULL
, 0, NULL
, 0, N_("System information:") },
76 { "all-locales", 'a', NULL
, OPTION_NO_USAGE
,
77 N_("Write names of available locales") },
78 { "charmaps", 'm', NULL
, OPTION_NO_USAGE
,
79 N_("Write names of available charmaps") },
80 { NULL
, 0, NULL
, 0, N_("Modify output format:") },
81 { "category-name", 'c', NULL
, 0, N_("Write names of selected categories") },
82 { "keyword-name", 'k', NULL
, 0, N_("Write names of selected keywords") },
83 { "verbose", 'v', NULL
, 0, N_("Print more information") },
84 { NULL
, 0, NULL
, 0, NULL
}
87 /* Short description of program. */
88 static const char doc
[] = N_("Get locale-specific information.\v\
89 For bug reporting instructions, please see:\n\
90 <http://www.gnu.org/software/libc/bugs.html>.\n");
92 /* Strings for arguments in help texts. */
93 static const char args_doc
[] = N_("NAME\n[-a|-m]");
95 /* Prototype for option handler. */
96 static error_t
parse_opt (int key
, char *arg
, struct argp_state
*state
);
98 /* Data structure to communicate with argp functions. */
99 static struct argp argp
=
101 options
, parse_opt
, args_doc
, doc
105 /* We don't have these constants defined because we don't use them. Give
107 #define CTYPE_MB_CUR_MIN 0
108 #define CTYPE_MB_CUR_MAX 0
109 #define CTYPE_HASH_SIZE 0
110 #define CTYPE_HASH_LAYERS 0
111 #define CTYPE_CLASS 0
112 #define CTYPE_TOUPPER_EB 0
113 #define CTYPE_TOLOWER_EB 0
114 #define CTYPE_TOUPPER_EL 0
115 #define CTYPE_TOLOWER_EL 0
117 /* Definition of the data structure which represents a category and its
128 enum { std
, opt
} status
;
129 enum value_type value_type
;
135 /* Simple helper macro. */
136 #define NELEMS(arr) ((sizeof (arr)) / (sizeof (arr[0])))
138 /* For some tricky stuff. */
139 #define NO_PAREN(Item, More...) Item, ## More
141 /* We have all categories defined in `categories.def'. Now construct
142 the description and data structure used for all categories. */
143 #define DEFINE_ELEMENT(Item, More...) { Item, ## More },
144 #define DEFINE_CATEGORY(category, name, items, postload) \
145 static struct cat_item category##_desc[] = \
150 #include "categories.def"
151 #undef DEFINE_CATEGORY
153 static struct category category
[] =
155 #define DEFINE_CATEGORY(category, name, items, postload) \
156 [category] = { _NL_NUM_##category, name, NELEMS (category##_desc), \
158 #include "categories.def"
159 #undef DEFINE_CATEGORY
161 #define NCATEGORIES NELEMS (category)
164 /* Automatically set variable. */
165 extern const char *__progname
;
167 /* helper function for extended name handling. */
168 extern void locale_special (const char *name
, int show_category_name
,
169 int show_keyword_name
);
171 /* Prototypes for local functions. */
172 static void print_LC_IDENTIFICATION (void *mapped
, size_t size
);
173 static void print_LC_CTYPE (void *mapped
, size_t size
);
174 static void write_locales (void);
175 static int nameentcmp (const void *a
, const void *b
);
176 static int write_archive_locales (void **all_datap
, char *linebuf
);
177 static void write_charmaps (void);
178 static void show_locale_vars (void);
179 static void show_info (const char *name
);
183 main (int argc
, char *argv
[])
187 /* Set initial values for global variables. */
188 show_category_name
= 0;
189 show_keyword_name
= 0;
191 /* Set locale. Do not set LC_ALL because the other categories must
192 not be affected (according to POSIX.2). */
193 if (setlocale (LC_CTYPE
, "") == NULL
)
194 error (0, errno
, gettext ("Cannot set LC_CTYPE to default locale"));
195 if (setlocale (LC_MESSAGES
, "") == NULL
)
196 error (0, errno
, gettext ("Cannot set LC_MESSAGES to default locale"));
198 /* Initialize the message catalog. */
199 textdomain (PACKAGE
);
201 /* Parse and process arguments. */
202 argp_parse (&argp
, argc
, argv
, 0, &remaining
, NULL
);
204 /* `-a' requests the names of all available locales. */
207 if (setlocale (LC_COLLATE
, "") == NULL
)
209 gettext ("Cannot set LC_COLLATE to default locale"));
214 /* `m' requests the names of all available charmaps. The names can be
215 used for the -f argument to localedef(1). */
216 if (do_charmaps
!= 0)
222 /* Specific information about the current locale are requested.
223 Change to this locale now. */
224 if (setlocale (LC_ALL
, "") == NULL
)
225 error (0, errno
, gettext ("Cannot set LC_ALL to default locale"));
227 /* If no real argument is given we have to print the contents of the
228 current locale definition variables. These are LANG and the LC_*. */
229 if (remaining
== argc
&& show_keyword_name
== 0 && show_category_name
== 0)
235 /* Process all given names. */
236 while (remaining
< argc
)
237 show_info (argv
[remaining
++]);
243 /* Handle program arguments. */
245 parse_opt (int key
, char *arg
, struct argp_state
*state
)
253 show_category_name
= 1;
259 show_keyword_name
= 1;
265 return ARGP_ERR_UNKNOWN
;
271 /* Print the version information. */
273 print_version (FILE *stream
, struct argp_state
*state
)
275 fprintf (stream
, "locale (GNU %s) %s\n", PACKAGE
, VERSION
);
276 fprintf (stream
, gettext ("\
277 Copyright (C) %s Free Software Foundation, Inc.\n\
278 This is free software; see the source for copying conditions. There is NO\n\
279 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
281 fprintf (stream
, gettext ("Written by %s.\n"), "Ulrich Drepper");
285 /* Simple action function which prints arguments as strings. */
287 print_names (const void *nodep
, VISIT value
, int level
)
289 if (value
== postorder
|| value
== leaf
)
290 puts (*(char **) nodep
);
295 select_dirs (const struct dirent
*dirent
)
299 if (strcmp (dirent
->d_name
, ".") != 0 && strcmp (dirent
->d_name
, "..") != 0)
303 #ifdef _DIRENT_HAVE_D_TYPE
304 if (dirent
->d_type
!= DT_UNKNOWN
&& dirent
->d_type
!= DT_LNK
)
305 mode
= DTTOIF (dirent
->d_type
);
310 char buf
[sizeof (LOCALEDIR
) + strlen (dirent
->d_name
) + 1];
312 stpcpy (stpcpy (stpcpy (buf
, LOCALEDIR
), "/"), dirent
->d_name
);
314 if (stat64 (buf
, &st
) == 0)
318 result
= S_ISDIR (mode
);
326 print_LC_IDENTIFICATION (void *mapped
, size_t size
)
328 /* Read the information from the file. */
332 unsigned int nstrings
;
333 unsigned int strindex
[0];
334 } *filedata
= mapped
;
336 if (filedata
->magic
== LIMAGIC (LC_IDENTIFICATION
)
338 + (filedata
->nstrings
339 * sizeof (unsigned int))
344 #define HANDLE(idx, name) \
345 str = ((char *) mapped \
346 + filedata->strindex[_NL_ITEM_INDEX (_NL_IDENTIFICATION_##idx)]); \
348 printf ("%9s | %s\n", name, str)
349 HANDLE (TITLE
, "title");
350 HANDLE (SOURCE
, "source");
351 HANDLE (ADDRESS
, "address");
352 HANDLE (CONTACT
, "contact");
353 HANDLE (EMAIL
, "email");
354 HANDLE (TEL
, "telephone");
356 HANDLE (LANGUAGE
, "language");
357 HANDLE (TERRITORY
, "territory");
358 HANDLE (AUDIENCE
, "audience");
359 HANDLE (APPLICATION
, "application");
360 HANDLE (ABBREVIATION
, "abbreviation");
361 HANDLE (REVISION
, "revision");
362 HANDLE (DATE
, "date");
368 print_LC_CTYPE (void *mapped
, size_t size
)
373 unsigned int nstrings
;
374 unsigned int strindex
[0];
375 } *filedata
= mapped
;
377 if (filedata
->magic
== LIMAGIC (LC_CTYPE
)
379 + (filedata
->nstrings
380 * sizeof (unsigned int))
385 str
= ((char *) mapped
386 + filedata
->strindex
[_NL_ITEM_INDEX (_NL_CTYPE_CODESET_NAME
)]);
388 printf (" codeset | %s\n", str
);
393 /* Write the names of all available locales to stdout. We have some
394 sources of the information: the contents of the locale directory
395 and the locale.alias file. To avoid duplicates and print the
396 result is a reasonable order we put all entries is a search tree
397 and print them afterwards. */
402 void *all_data
= NULL
;
403 struct dirent
**dirents
;
407 size_t alias_path_len
;
409 int first_locale
= 1;
411 #define PUT(name) tsearch (name, &all_data, \
412 (int (*) (const void *, const void *)) strcoll)
413 #define GET(name) tfind (name, &all_data, \
414 (int (*) (const void *, const void *)) strcoll)
416 /* `POSIX' locale is always available (POSIX.2 4.34.3). */
418 /* And so is the "C" locale. */
421 memset (linebuf
, '-', sizeof (linebuf
) - 1);
422 linebuf
[sizeof (linebuf
) - 1] = '\0';
424 /* First scan the locale archive. */
425 if (write_archive_locales (&all_data
, linebuf
))
428 /* Now we can look for all files in the directory. */
429 ndirents
= scandir (LOCALEDIR
, &dirents
, select_dirs
, alphasort
);
430 for (cnt
= 0; cnt
< ndirents
; ++cnt
)
432 /* Test whether at least the LC_CTYPE data is there. Some
433 directories only contain translations. */
434 char buf
[sizeof (LOCALEDIR
) + strlen (dirents
[cnt
]->d_name
)
435 + sizeof "/LC_IDENTIFICATION"];
439 stpcpy (enddir
= stpcpy (stpcpy (stpcpy (buf
, LOCALEDIR
), "/"),
440 dirents
[cnt
]->d_name
),
441 "/LC_IDENTIFICATION");
443 if (stat64 (buf
, &st
) == 0 && S_ISREG (st
.st_mode
))
445 if (verbose
&& GET (dirents
[cnt
]->d_name
) == NULL
)
447 /* Provide some nice output of all kinds of
452 putchar_unlocked ('\n');
455 printf ("locale: %-15.15s directory: %.*s\n%s\n",
456 dirents
[cnt
]->d_name
, (int) (enddir
- buf
), buf
,
459 fd
= open64 (buf
, O_RDONLY
);
462 void *mapped
= mmap64 (NULL
, st
.st_size
, PROT_READ
,
464 if (mapped
!= MAP_FAILED
)
466 print_LC_IDENTIFICATION (mapped
, st
.st_size
);
468 munmap (mapped
, st
.st_size
);
473 /* Now try to get the charset information. */
474 strcpy (enddir
, "/LC_CTYPE");
475 fd
= open64 (buf
, O_RDONLY
);
476 if (fd
!= -1 && fstat64 (fd
, &st
) >= 0
477 && ((mapped
= mmap64 (NULL
, st
.st_size
, PROT_READ
,
481 print_LC_CTYPE (mapped
, st
.st_size
);
483 munmap (mapped
, st
.st_size
);
491 /* If the verbose format is not selected we simply
492 collect the names. */
493 PUT (xstrdup (dirents
[cnt
]->d_name
));
499 /* Now read the locale.alias files. */
500 if (argz_create_sep (LOCALE_ALIAS_PATH
, ':', &alias_path
, &alias_path_len
))
501 error (1, errno
, gettext ("while preparing output"));
504 while ((entry
= argz_next (alias_path
, alias_path_len
, entry
)))
506 static const char aliasfile
[] = "/locale.alias";
508 char full_name
[strlen (entry
) + sizeof aliasfile
];
510 stpcpy (stpcpy (full_name
, entry
), aliasfile
);
511 fp
= fopen (full_name
, "rm");
513 /* Ignore non-existing files. */
516 /* No threads present. */
517 __fsetlocking (fp
, FSETLOCKING_BYCALLER
);
519 while (! feof_unlocked (fp
))
521 /* It is a reasonable approach to use a fix buffer here
523 a) we are only interested in the first two fields
524 b) these fields must be usable as file names and so must
531 if (fgets_unlocked (buf
, BUFSIZ
, fp
) == NULL
)
536 /* Ignore leading white space. */
537 while (isspace (cp
[0]) && cp
[0] != '\n')
540 /* A leading '#' signals a comment line. */
541 if (cp
[0] != '\0' && cp
[0] != '#' && cp
[0] != '\n')
544 while (cp
[0] != '\0' && !isspace (cp
[0]))
546 /* Terminate alias name. */
550 /* Now look for the beginning of the value. */
551 while (isspace (cp
[0]))
557 while (cp
[0] != '\0' && !isspace (cp
[0]))
559 /* Terminate value. */
562 /* This has to be done to make the following
563 test for the end of line possible. We are
564 looking for the terminating '\n' which do not
569 else if (cp
[0] != '\0')
573 if (! verbose
&& GET (value
) != NULL
)
574 PUT (xstrdup (alias
));
578 /* Possibly not the whole line fits into the buffer.
579 Ignore the rest of the line. */
580 while (strchr (cp
, '\n') == NULL
)
583 if (fgets_unlocked (buf
, BUFSIZ
, fp
) == NULL
)
584 /* Make sure the inner loop will be left. The outer
585 loop will exit at the `feof' test. */
595 twalk (all_data
, print_names
);
603 uint32_t locrec_offset
;
608 nameentcmp (const void *a
, const void *b
)
610 return strcoll (((const struct nameent
*) a
)->name
,
611 ((const struct nameent
*) b
)->name
);
616 write_archive_locales (void **all_datap
, char *linebuf
)
619 void *all_data
= *all_datap
;
621 struct locarhead
*head
;
622 struct namehashent
*namehashtab
;
623 char *addr
= MAP_FAILED
;
627 fd
= open64 (ARCHIVE_NAME
, O_RDONLY
);
631 if (fstat64 (fd
, &st
) < 0 || st
.st_size
< sizeof (*head
))
635 addr
= mmap64 (NULL
, len
, PROT_READ
, MAP_SHARED
, fd
, 0);
636 if (addr
== MAP_FAILED
)
639 head
= (struct locarhead
*) addr
;
640 if (head
->namehash_offset
+ head
->namehash_size
> len
641 || head
->string_offset
+ head
->string_size
> len
642 || head
->locrectab_offset
+ head
->locrectab_size
> len
643 || head
->sumhash_offset
+ head
->sumhash_size
> len
)
646 namehashtab
= (struct namehashent
*) (addr
+ head
->namehash_offset
);
649 for (cnt
= 0; cnt
< head
->namehash_size
; ++cnt
)
650 if (namehashtab
[cnt
].locrec_offset
!= 0)
652 PUT (xstrdup (addr
+ namehashtab
[cnt
].name_offset
));
658 struct nameent
*names
;
661 names
= (struct nameent
*) xmalloc (head
->namehash_used
662 * sizeof (struct nameent
));
663 for (cnt
= used
= 0; cnt
< head
->namehash_size
; ++cnt
)
664 if (namehashtab
[cnt
].locrec_offset
!= 0)
666 names
[used
].name
= addr
+ namehashtab
[cnt
].name_offset
;
667 names
[used
++].locrec_offset
= namehashtab
[cnt
].locrec_offset
;
670 /* Sort the names. */
671 qsort (names
, used
, sizeof (struct nameent
), nameentcmp
);
673 for (cnt
= 0; cnt
< used
; ++cnt
)
675 struct locrecent
*locrec
;
677 PUT (xstrdup (names
[cnt
].name
));
680 putchar_unlocked ('\n');
682 printf ("locale: %-15.15s archive: " ARCHIVE_NAME
"\n%s\n",
683 names
[cnt
].name
, linebuf
);
685 locrec
= (struct locrecent
*) (addr
+ names
[cnt
].locrec_offset
);
687 print_LC_IDENTIFICATION (addr
688 + locrec
->record
[LC_IDENTIFICATION
].offset
,
689 locrec
->record
[LC_IDENTIFICATION
].len
);
691 print_LC_CTYPE (addr
+ locrec
->record
[LC_CTYPE
].offset
,
692 locrec
->record
[LC_CTYPE
].len
);
699 if (addr
!= MAP_FAILED
)
702 *all_datap
= all_data
;
707 /* Write the names of all available character maps to stdout. */
709 write_charmaps (void)
711 void *all_data
= NULL
;
715 /* Look for all files in the charmap directory. */
716 dir
= charmap_opendir (CHARMAP_PATH
);
720 while ((dirent
= charmap_readdir (dir
)) != NULL
)
725 PUT (xstrdup (dirent
));
727 aliases
= charmap_aliases (CHARMAP_PATH
, dirent
);
730 /* Add the code_set_name and the aliases. */
731 for (p
= aliases
; *p
; p
++)
734 /* Add the code_set_name only. Most aliases are obsolete. */
740 charmap_free_aliases (aliases
);
743 charmap_closedir (dir
);
745 twalk (all_data
, print_names
);
749 /* We have to show the contents of the environments determining the
752 show_locale_vars (void)
755 const char *lcall
= getenv ("LC_ALL");
756 const char *lang
= getenv ("LANG") ? : "";
758 auto void get_source (const char *name
);
760 void get_source (const char *name
)
762 char *val
= getenv (name
);
764 if ((lcall
?: "")[0] != '\0' || val
== NULL
)
765 printf ("%s=\"%s\"\n", name
,
766 (lcall
?: "")[0] ? lcall
: (lang
?: "")[0] ? lang
: "POSIX");
768 printf ("%s=%s\n", name
, val
);
771 /* LANG has to be the first value. */
772 printf ("LANG=%s\n", lang
);
774 /* Now all categories in an unspecified order. */
775 for (cat_no
= 0; cat_no
< NCATEGORIES
; ++cat_no
)
776 if (cat_no
!= LC_ALL
)
777 get_source (category
[cat_no
].name
);
779 /* The last is the LC_ALL value. */
780 printf ("LC_ALL=%s\n", lcall
? : "");
784 /* Show the information request for NAME. */
786 show_info (const char *name
)
790 auto void print_item (struct cat_item
*item
);
792 void print_item (struct cat_item
*item
)
794 switch (item
->value_type
)
797 if (show_keyword_name
)
798 printf ("%s=\"", item
->name
);
799 fputs (nl_langinfo (item
->item_id
) ? : "", stdout
);
800 if (show_keyword_name
)
809 if (show_keyword_name
)
810 printf ("%s=\"", item
->name
);
812 for (cnt
= 0; cnt
< item
->max
- 1; ++cnt
)
814 val
= nl_langinfo (item
->item_id
+ cnt
);
820 val
= nl_langinfo (item
->item_id
+ cnt
);
824 if (show_keyword_name
)
832 const char *val
= nl_langinfo (item
->item_id
) ? : "";
835 if (show_keyword_name
)
836 printf ("%s=", item
->name
);
838 for (cnt
= 0; cnt
< item
->max
&& *val
!= '\0'; ++cnt
)
840 printf ("%s%s%s%s", first
? "" : ";",
841 show_keyword_name
? "\"" : "", val
,
842 show_keyword_name
? "\"" : "");
843 val
= strchr (val
, '\0') + 1;
851 const char *val
= nl_langinfo (item
->item_id
);
853 if (show_keyword_name
)
854 printf ("%s=", item
->name
);
857 printf ("%d", *val
== '\177' ? -1 : *val
);
863 const char *val
= nl_langinfo (item
->item_id
);
864 int cnt
= val
? strlen (val
) : 0;
866 if (show_keyword_name
)
867 printf ("%s=", item
->name
);
871 printf ("%d;", *val
== '\177' ? -1 : *val
);
876 printf ("%d\n", cnt
== 0 || *val
== '\177' ? -1 : *val
);
881 union { unsigned int word
; char *string
; } val
;
882 val
.string
= nl_langinfo (item
->item_id
);
883 if (show_keyword_name
)
884 printf ("%s=", item
->name
);
886 printf ("%d\n", val
.word
);
892 /* We don't print wide character information since the same
893 information is available in a multibyte string. */
900 for (cat_no
= 0; cat_no
< NCATEGORIES
; ++cat_no
)
901 if (cat_no
!= LC_ALL
)
905 if (strcmp (name
, category
[cat_no
].name
) == 0)
906 /* Print the whole category. */
908 if (show_category_name
!= 0)
909 puts (category
[cat_no
].name
);
911 for (item_no
= 0; item_no
< category
[cat_no
].number
; ++item_no
)
912 print_item (&category
[cat_no
].item_desc
[item_no
]);
917 for (item_no
= 0; item_no
< category
[cat_no
].number
; ++item_no
)
918 if (strcmp (name
, category
[cat_no
].item_desc
[item_no
].name
) == 0)
920 if (show_category_name
!= 0)
921 puts (category
[cat_no
].name
);
923 print_item (&category
[cat_no
].item_desc
[item_no
]);
928 /* The name is not a standard one.
929 For testing and perhaps advanced use allow some more symbols. */
930 locale_special (name
, show_category_name
, show_keyword_name
);