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.
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
43 #include "localeinfo.h"
44 #include "charmap-dir.h"
46 extern void *xmalloc (size_t __n
);
47 extern char *xstrdup (const char *__str
);
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 write_locales (void);
171 static void write_charmaps (void);
172 static void show_locale_vars (void);
173 static void show_info (const char *name
);
177 main (int argc
, char *argv
[])
181 /* Set initial values for global variables. */
182 show_category_name
= 0;
183 show_keyword_name
= 0;
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
, "");
190 /* Initialize the message catalog. */
191 textdomain (PACKAGE
);
193 /* Parse and process arguments. */
194 argp_parse (&argp
, argc
, argv
, 0, &remaining
, NULL
);
196 /* `-a' requests the names of all available locales. */
199 setlocale (LC_COLLATE
, "");
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)
212 /* Specific information about the current locale are requested.
213 Change to this locale now. */
214 setlocale (LC_ALL
, "");
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)
224 /* Process all given names. */
225 while (remaining
< argc
)
226 show_info (argv
[remaining
++]);
232 /* Handle program arguments. */
234 parse_opt (int key
, char *arg
, struct argp_state
*state
)
242 show_category_name
= 1;
248 show_keyword_name
= 1;
254 return ARGP_ERR_UNKNOWN
;
261 more_help (int key
, const char *text
, void *input
)
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"));
272 return (char *) text
;
275 /* Print the version information. */
277 print_version (FILE *stream
, struct argp_state
*state
)
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\
285 fprintf (stream
, gettext ("Written by %s.\n"), "Ulrich Drepper");
289 /* Simple action function which prints arguments as strings. */
291 print_names (const void *nodep
, VISIT value
, int level
)
293 if (value
== postorder
|| value
== leaf
)
294 puts (*(char **) nodep
);
299 select_dirs (const struct dirent
*dirent
)
303 if (strcmp (dirent
->d_name
, ".") != 0 && strcmp (dirent
->d_name
, "..") != 0)
307 #ifdef _DIRENT_HAVE_D_TYPE
308 if (dirent
->d_type
!= DT_UNKNOWN
&& dirent
->d_type
!= DT_LNK
)
309 mode
= DTTOIF (dirent
->d_type
);
314 char buf
[sizeof (LOCALEDIR
) + strlen (dirent
->d_name
) + 1];
316 stpcpy (stpcpy (stpcpy (buf
, LOCALEDIR
), "/"), dirent
->d_name
);
318 if (stat64 (buf
, &st
) == 0)
322 result
= S_ISDIR (mode
);
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. */
338 void *all_data
= NULL
;
339 struct dirent
**dirents
;
343 size_t alias_path_len
;
345 int first_locale
= 1;
347 #define PUT(name) tsearch (name, &all_data, \
348 (int (*) (const void *, const void *)) strcoll)
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"));
355 while ((entry
= argz_next (alias_path
, alias_path_len
, entry
)))
357 static const char aliasfile
[] = "/locale.alias";
359 char full_name
[strlen (entry
) + sizeof aliasfile
];
361 stpcpy (stpcpy (full_name
, entry
), aliasfile
);
362 fp
= fopen (full_name
, "r");
364 /* Ignore non-existing files. */
369 /* It is a reasonable approach to use a fix buffer here
371 a) we are only interested in the first two fields
372 b) these fields must be usable as file names and so must
379 if (fgets_unlocked (buf
, BUFSIZ
, fp
) == NULL
)
384 /* Ignore leading white space. */
385 while (isspace (cp
[0]) && cp
[0] != '\n')
388 /* A leading '#' signals a comment line. */
389 if (cp
[0] != '\0' && cp
[0] != '#' && cp
[0] != '\n')
392 while (cp
[0] != '\0' && !isspace (cp
[0]))
394 /* Terminate alias name. */
398 /* Now look for the beginning of the value. */
399 while (isspace (cp
[0]))
405 while (cp
[0] != '\0' && !isspace (cp
[0]))
407 /* Terminate value. */
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
417 else if (cp
[0] != '\0')
422 PUT (xstrdup (alias
));
426 /* Possibly not the whole line fits into the buffer.
427 Ignore the rest of the line. */
428 while (strchr (cp
, '\n') == NULL
)
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. */
441 memset (linebuf
, '-', sizeof (linebuf
) - 1);
442 linebuf
[sizeof (linebuf
) - 1] = '\0';
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
)
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"];
455 stpcpy (enddir
= stpcpy (stpcpy (stpcpy (buf
, LOCALEDIR
), "/"),
456 dirents
[cnt
]->d_name
),
457 "/LC_IDENTIFICATION");
459 if (stat64 (buf
, &st
) == 0 && S_ISREG (st
.st_mode
))
463 /* Provide some nice output of all kinds of
468 putchar_unlocked ('\n');
471 printf ("locale: %-15.15s directory: %.*s\n%s\n",
472 dirents
[cnt
]->d_name
, (int) (enddir
- buf
), buf
,
475 fd
= open64 (buf
, O_RDONLY
);
478 void *mapped
= mmap64 (NULL
, st
.st_size
, PROT_READ
,
480 if (mapped
!= MAP_FAILED
)
482 /* Read the information from the file. */
486 unsigned int nstrings
;
487 unsigned int strindex
[0];
488 } *filedata
= mapped
;
490 if (filedata
->magic
== LIMAGIC (LC_IDENTIFICATION
)
492 + (filedata
->nstrings
493 * sizeof (unsigned int))
494 <= (size_t) st
.st_size
))
498 #define HANDLE(idx, name) \
499 str = ((char *) mapped \
500 + filedata->strindex[_NL_ITEM_INDEX (_NL_IDENTIFICATION_##idx)]); \
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");
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");
519 munmap (mapped
, st
.st_size
);
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
,
535 unsigned int nstrings
;
536 unsigned int strindex
[0];
537 } *filedata
= mapped
;
539 if (filedata
->magic
== LIMAGIC (LC_CTYPE
)
541 + (filedata
->nstrings
542 * sizeof (unsigned int))
543 <= (size_t) st
.st_size
))
547 str
= ((char *) mapped
548 + filedata
->strindex
[_NL_ITEM_INDEX (_NL_CTYPE_CODESET_NAME
)]);
550 printf (" codeset | %s\n", str
);
553 munmap (mapped
, st
.st_size
);
561 /* If the verbose format is not selected we simply
562 collect the names. */
563 PUT (xstrdup (dirents
[cnt
]->d_name
));
571 /* `POSIX' locale is always available (POSIX.2 4.34.3). */
573 /* And so is the "C" locale. */
576 twalk (all_data
, print_names
);
581 /* Write the names of all available character maps to stdout. */
583 write_charmaps (void)
585 void *all_data
= NULL
;
589 /* Look for all files in the charmap directory. */
590 dir
= charmap_opendir (CHARMAP_PATH
);
594 while ((dirent
= charmap_readdir (dir
)) != NULL
)
599 PUT (xstrdup (dirent
));
601 aliases
= charmap_aliases (CHARMAP_PATH
, dirent
);
604 /* Add the code_set_name and the aliases. */
605 for (p
= aliases
; *p
; p
++)
608 /* Add the code_set_name only. Most aliases are obsolete. */
614 charmap_free_aliases (aliases
);
617 charmap_closedir (dir
);
619 twalk (all_data
, print_names
);
623 /* We have to show the contents of the environments determining the
626 show_locale_vars (void)
629 const char *lcall
= getenv ("LC_ALL");
630 const char *lang
= getenv ("LANG") ? : "POSIX";
632 auto void get_source (const char *name
);
634 void get_source (const char *name
)
636 char *val
= getenv (name
);
638 if ((lcall
?: "")[0] != '\0' || val
== NULL
)
639 printf ("%s=\"%s\"\n", name
, (lcall
?: "")[0] ? lcall
: lang
);
641 printf ("%s=%s\n", name
, val
);
644 /* LANG has to be the first value. */
645 printf ("LANG=%s\n", lang
);
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
);
652 /* The last is the LC_ALL value. */
653 printf ("LC_ALL=%s\n", lcall
? : "");
657 /* Show the information request for NAME. */
659 show_info (const char *name
)
663 auto void print_item (struct cat_item
*item
);
665 void print_item (struct cat_item
*item
)
667 switch (item
->value_type
)
670 if (show_keyword_name
)
671 printf ("%s=\"", item
->name
);
672 fputs (nl_langinfo (item
->item_id
) ? : "", stdout
);
673 if (show_keyword_name
)
682 if (show_keyword_name
)
683 printf ("%s=\"", item
->name
);
685 for (cnt
= 0; cnt
< item
->max
- 1; ++cnt
)
687 val
= nl_langinfo (item
->item_id
+ cnt
);
693 val
= nl_langinfo (item
->item_id
+ cnt
);
697 if (show_keyword_name
)
705 const char *val
= nl_langinfo (item
->item_id
) ? : "";
708 if (show_keyword_name
)
709 printf ("%s=", item
->name
);
711 for (cnt
= 0; cnt
< item
->max
&& *val
!= '\0'; ++cnt
)
713 printf ("%s%s%s%s", first
? "" : ";",
714 show_keyword_name
? "\"" : "", val
,
715 show_keyword_name
? "\"" : "");
716 val
= strchr (val
, '\0') + 1;
724 const char *val
= nl_langinfo (item
->item_id
);
726 if (show_keyword_name
)
727 printf ("%s=", item
->name
);
730 printf ("%d", *val
== CHAR_MAX
? -1 : *val
);
736 const char *val
= nl_langinfo (item
->item_id
);
737 int cnt
= val
? strlen (val
) : 0;
739 if (show_keyword_name
)
740 printf ("%s=", item
->name
);
744 printf ("%d;", *val
== CHAR_MAX
? -1 : *val
);
749 printf ("%d\n", cnt
== 0 || *val
== CHAR_MAX
? -1 : *val
);
755 (unsigned int) (unsigned long int) nl_langinfo (item
->item_id
);
756 if (show_keyword_name
)
757 printf ("%s=", item
->name
);
759 printf ("%d\n", val
);
765 /* We don't print wide character information since the same
766 information is available in a multibyte string. */
773 for (cat_no
= 0; cat_no
< NCATEGORIES
; ++cat_no
)
774 if (cat_no
!= LC_ALL
)
778 if (strcmp (name
, category
[cat_no
].name
) == 0)
779 /* Print the whole category. */
781 if (show_category_name
!= 0)
782 puts (category
[cat_no
].name
);
784 for (item_no
= 0; item_no
< category
[cat_no
].number
; ++item_no
)
785 print_item (&category
[cat_no
].item_desc
[item_no
]);
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)
793 if (show_category_name
!= 0)
794 puts (category
[cat_no
].name
);
796 print_item (&category
[cat_no
].item_desc
[item_no
]);
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
);