]> git.ipfire.org Git - thirdparty/glibc.git/blame - locale/programs/locale.c
Update copyright year.
[thirdparty/glibc.git] / locale / programs / locale.c
CommitLineData
5107cf1d 1/* Implementation of the locale program according to POSIX 9945-2.
561470e0 2 Copyright (C) 1995-1997, 1999-2008, 2009, 2010 Free Software Foundation, Inc.
6d52618b 3 This file is part of the GNU C Library.
4b10dd6c 4 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1995.
2b83a2a4 5
43bc8ac6 6 This program is free software; you can redistribute it and/or modify
2e2efe65
RM
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.
2b83a2a4 10
43bc8ac6 11 This program is distributed in the hope that it will be useful,
6d52618b 12 but WITHOUT ANY WARRANTY; without even the implied warranty of
43bc8ac6
UD
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
2b83a2a4 15
43bc8ac6
UD
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software Foundation,
18 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
2b83a2a4 19
299a95b9
RM
20#ifdef HAVE_CONFIG_H
21# include <config.h>
22#endif
23
1fb05e3d
UD
24#include <argp.h>
25#include <argz.h>
2b83a2a4 26#include <dirent.h>
1fb05e3d 27#include <errno.h>
299a95b9 28#include <error.h>
91cd8340 29#include <fcntl.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 35#include <stdio.h>
06b5289f 36#include <stdio_ext.h>
2b83a2a4 37#include <stdlib.h>
a1470b6f 38#include <string.h>
91cd8340
UD
39#include <unistd.h>
40#include <sys/mman.h>
1fb05e3d 41#include <sys/stat.h>
2b83a2a4 42
19bc17a9 43#include "localeinfo.h"
3e076219 44#include "charmap-dir.h"
e98b69a6 45#include "../locarchive.h"
3e076219 46
91cd8340 47extern void *xmalloc (size_t __n);
3e076219 48extern char *xstrdup (const char *__str);
2b83a2a4 49
e98b69a6 50#define ARCHIVE_NAME LOCALEDIR "/locale-archive"
2b83a2a4 51
2b83a2a4
RM
52/* If set print the name of the category. */
53static int show_category_name;
54
55/* If set print the name of the item. */
56static int show_keyword_name;
57
1fb05e3d
UD
58/* Print names of all available locales. */
59static int do_all;
60
61/* Print names of all available character maps. */
62static int do_charmaps = 0;
63
91cd8340
UD
64/* Nonzero if verbose output is wanted. */
65static int verbose;
66
1fb05e3d
UD
67/* Name and version of program. */
68static void print_version (FILE *stream, struct argp_state *state);
69void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
70
71/* Definitions of arguments for argp functions. */
5a97622d 72static const struct argp_option options[] =
19bc17a9 73{
5a97622d
UD
74 { NULL, 0, NULL, 0, N_("System information:") },
75 { "all-locales", 'a', NULL, OPTION_NO_USAGE,
76 N_("Write names of available locales") },
77 { "charmaps", 'm', NULL, OPTION_NO_USAGE,
78 N_("Write names of available charmaps") },
79 { NULL, 0, NULL, 0, N_("Modify output format:") },
80 { "category-name", 'c', NULL, 0, N_("Write names of selected categories") },
81 { "keyword-name", 'k', NULL, 0, N_("Write names of selected keywords") },
91cd8340 82 { "verbose", 'v', NULL, 0, N_("Print more information") },
1fb05e3d
UD
83 { NULL, 0, NULL, 0, NULL }
84};
85
86/* Short description of program. */
cbbcaf23 87static const char doc[] = N_("Get locale-specific information.");
1fb05e3d
UD
88
89/* Strings for arguments in help texts. */
5a97622d 90static const char args_doc[] = N_("NAME\n[-a|-m]");
1fb05e3d
UD
91
92/* Prototype for option handler. */
93static error_t parse_opt (int key, char *arg, struct argp_state *state);
94
cbbcaf23
UD
95/* Function to print some extra text in the help message. */
96static char *more_help (int key, const char *text, void *input);
97
1fb05e3d
UD
98/* Data structure to communicate with argp functions. */
99static struct argp argp =
100{
cbbcaf23 101 options, parse_opt, args_doc, doc, NULL, more_help
19bc17a9 102};
2b83a2a4
RM
103
104
105/* We don't have these constants defined because we don't use them. Give
106 default values. */
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
19bc17a9 116
299a95b9
RM
117/* Definition of the data structure which represents a category and its
118 items. */
119struct category
19bc17a9 120{
299a95b9 121 int cat_id;
19bc17a9 122 const char *name;
299a95b9
RM
123 size_t number;
124 struct cat_item
125 {
126 int item_id;
127 const char *name;
128 enum { std, opt } status;
129 enum value_type value_type;
130 int min;
131 int max;
132 } *item_desc;
19bc17a9
RM
133};
134
299a95b9
RM
135/* Simple helper macro. */
136#define NELEMS(arr) ((sizeof (arr)) / (sizeof (arr[0])))
137
138/* For some tricky stuff. */
139#define NO_PAREN(Item, More...) Item, ## More
2b83a2a4
RM
140
141/* We have all categories defined in `categories.def'. Now construct
142 the description and data structure used for all categories. */
299a95b9 143#define DEFINE_ELEMENT(Item, More...) { Item, ## More },
4b10dd6c 144#define DEFINE_CATEGORY(category, name, items, postload) \
2b83a2a4
RM
145 static struct cat_item category##_desc[] = \
146 { \
561470e0 147 NO_PAREN items \
2b83a2a4
RM
148 };
149
299a95b9 150#include "categories.def"
2b83a2a4
RM
151#undef DEFINE_CATEGORY
152
153static struct category category[] =
154 {
4b10dd6c 155#define DEFINE_CATEGORY(category, name, items, postload) \
036cc82f
RM
156 [category] = { _NL_NUM_##category, name, NELEMS (category##_desc), \
157 category##_desc },
299a95b9 158#include "categories.def"
2b83a2a4
RM
159#undef DEFINE_CATEGORY
160 };
161#define NCATEGORIES NELEMS (category)
162
163
036cc82f
RM
164/* Automatically set variable. */
165extern const char *__progname;
166
167/* helper function for extended name handling. */
168extern void locale_special (const char *name, int show_category_name,
169 int show_keyword_name);
170
2b83a2a4 171/* Prototypes for local functions. */
e98b69a6
UD
172static void print_LC_IDENTIFICATION (void *mapped, size_t size);
173static void print_LC_CTYPE (void *mapped, size_t size);
2b83a2a4 174static void write_locales (void);
e98b69a6
UD
175static int nameentcmp (const void *a, const void *b);
176static int write_archive_locales (void **all_datap, char *linebuf);
2b83a2a4
RM
177static void write_charmaps (void);
178static void show_locale_vars (void);
179static void show_info (const char *name);
2b83a2a4
RM
180
181
182int
183main (int argc, char *argv[])
184{
2f6d1f1b
UD
185 int remaining;
186
bba7bb78 187 /* Set initial values for global variables. */
2b83a2a4
RM
188 show_category_name = 0;
189 show_keyword_name = 0;
190
191 /* Set locale. Do not set LC_ALL because the other categories must
6d52618b 192 not be affected (according to POSIX.2). */
2e119864
UD
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"));
2b83a2a4
RM
197
198 /* Initialize the message catalog. */
199 textdomain (PACKAGE);
200
1fb05e3d 201 /* Parse and process arguments. */
2f6d1f1b 202 argp_parse (&argp, argc, argv, 0, &remaining, NULL);
2b83a2a4 203
2b83a2a4
RM
204 /* `-a' requests the names of all available locales. */
205 if (do_all != 0)
206 {
2e119864
UD
207 if (setlocale (LC_COLLATE, "") == NULL)
208 error (0, errno,
209 gettext ("Cannot set LC_COLLATE to default locale"));
2b83a2a4
RM
210 write_locales ();
211 exit (EXIT_SUCCESS);
212 }
213
214 /* `m' requests the names of all available charmaps. The names can be
8f2ece69 215 used for the -f argument to localedef(1). */
2b83a2a4
RM
216 if (do_charmaps != 0)
217 {
218 write_charmaps ();
219 exit (EXIT_SUCCESS);
220 }
221
76060ec0
RM
222 /* Specific information about the current locale are requested.
223 Change to this locale now. */
84c9409e 224 if (setlocale (LC_ALL, "") == NULL)
2e119864 225 error (0, errno, gettext ("Cannot set LC_ALL to default locale"));
76060ec0 226
2b83a2a4
RM
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_*. */
2f6d1f1b 229 if (remaining == argc && show_keyword_name == 0 && show_category_name == 0)
2b83a2a4
RM
230 {
231 show_locale_vars ();
232 exit (EXIT_SUCCESS);
233 }
234
235 /* Process all given names. */
2f6d1f1b
UD
236 while (remaining < argc)
237 show_info (argv[remaining++]);
2b83a2a4
RM
238
239 exit (EXIT_SUCCESS);
240}
241
242
1fb05e3d
UD
243/* Handle program arguments. */
244static error_t
245parse_opt (int key, char *arg, struct argp_state *state)
2b83a2a4 246{
1fb05e3d 247 switch (key)
fafaa44e 248 {
1fb05e3d
UD
249 case 'a':
250 do_all = 1;
251 break;
252 case 'c':
253 show_category_name = 1;
254 break;
255 case 'm':
256 do_charmaps = 1;
257 break;
258 case 'k':
259 show_keyword_name = 1;
260 break;
91cd8340
UD
261 case 'v':
262 verbose = 1;
263 break;
1fb05e3d
UD
264 default:
265 return ARGP_ERR_UNKNOWN;
fafaa44e 266 }
1fb05e3d
UD
267 return 0;
268}
269
2b83a2a4 270
cbbcaf23
UD
271static char *
272more_help (int key, const char *text, void *input)
273{
274 switch (key)
275 {
276 case ARGP_KEY_HELP_EXTRA:
277 /* We print some extra information. */
278 return strdup (gettext ("\
279For bug reporting instructions, please see:\n\
280<http://www.gnu.org/software/libc/bugs.html>.\n"));
281 default:
282 break;
283 }
284 return (char *) text;
285}
286
287
1fb05e3d
UD
288/* Print the version information. */
289static void
290print_version (FILE *stream, struct argp_state *state)
291{
292 fprintf (stream, "locale (GNU %s) %s\n", PACKAGE, VERSION);
293 fprintf (stream, gettext ("\
294Copyright (C) %s Free Software Foundation, Inc.\n\
295This is free software; see the source for copying conditions. There is NO\n\
296warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
561470e0 297"), "2010");
1fb05e3d 298 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
2b83a2a4
RM
299}
300
301
1fb05e3d
UD
302/* Simple action function which prints arguments as strings. */
303static void
304print_names (const void *nodep, VISIT value, int level)
305{
306 if (value == postorder || value == leaf)
307 puts (*(char **) nodep);
308}
309
310
fdc6c28a
UD
311static int
312select_dirs (const struct dirent *dirent)
313{
314 int result = 0;
315
316 if (strcmp (dirent->d_name, ".") != 0 && strcmp (dirent->d_name, "..") != 0)
317 {
318 mode_t mode = 0;
319
320#ifdef _DIRENT_HAVE_D_TYPE
321 if (dirent->d_type != DT_UNKNOWN && dirent->d_type != DT_LNK)
322 mode = DTTOIF (dirent->d_type);
323 else
324#endif
325 {
326 struct stat64 st;
327 char buf[sizeof (LOCALEDIR) + strlen (dirent->d_name) + 1];
328
329 stpcpy (stpcpy (stpcpy (buf, LOCALEDIR), "/"), dirent->d_name);
330
331 if (stat64 (buf, &st) == 0)
332 mode = st.st_mode;
333 }
334
335 result = S_ISDIR (mode);
336 }
337
338 return result;
339}
340
341
e98b69a6
UD
342static void
343print_LC_IDENTIFICATION (void *mapped, size_t size)
344{
345 /* Read the information from the file. */
346 struct
347 {
348 unsigned int magic;
349 unsigned int nstrings;
350 unsigned int strindex[0];
351 } *filedata = mapped;
352
353 if (filedata->magic == LIMAGIC (LC_IDENTIFICATION)
354 && (sizeof *filedata
355 + (filedata->nstrings
356 * sizeof (unsigned int))
357 <= size))
358 {
359 const char *str;
360
361#define HANDLE(idx, name) \
362 str = ((char *) mapped \
363 + filedata->strindex[_NL_ITEM_INDEX (_NL_IDENTIFICATION_##idx)]); \
364 if (*str != '\0') \
365 printf ("%9s | %s\n", name, str)
366 HANDLE (TITLE, "title");
367 HANDLE (SOURCE, "source");
368 HANDLE (ADDRESS, "address");
369 HANDLE (CONTACT, "contact");
370 HANDLE (EMAIL, "email");
371 HANDLE (TEL, "telephone");
372 HANDLE (FAX, "fax");
373 HANDLE (LANGUAGE, "language");
374 HANDLE (TERRITORY, "territory");
375 HANDLE (AUDIENCE, "audience");
376 HANDLE (APPLICATION, "application");
377 HANDLE (ABBREVIATION, "abbreviation");
378 HANDLE (REVISION, "revision");
379 HANDLE (DATE, "date");
380 }
381}
382
383
384static void
385print_LC_CTYPE (void *mapped, size_t size)
386{
387 struct
388 {
389 unsigned int magic;
390 unsigned int nstrings;
391 unsigned int strindex[0];
392 } *filedata = mapped;
393
394 if (filedata->magic == LIMAGIC (LC_CTYPE)
395 && (sizeof *filedata
396 + (filedata->nstrings
397 * sizeof (unsigned int))
398 <= size))
399 {
400 const char *str;
401
402 str = ((char *) mapped
403 + filedata->strindex[_NL_ITEM_INDEX (_NL_CTYPE_CODESET_NAME)]);
404 if (*str != '\0')
405 printf (" codeset | %s\n", str);
406 }
407}
408
409
1fb05e3d
UD
410/* Write the names of all available locales to stdout. We have some
411 sources of the information: the contents of the locale directory
412 and the locale.alias file. To avoid duplicates and print the
413 result is a reasonable order we put all entries is a search tree
414 and print them afterwards. */
2b83a2a4
RM
415static void
416write_locales (void)
417{
91cd8340 418 char linebuf[80];
1fb05e3d 419 void *all_data = NULL;
fdc6c28a
UD
420 struct dirent **dirents;
421 int ndirents;
422 int cnt;
1fb05e3d
UD
423 char *alias_path;
424 size_t alias_path_len;
425 char *entry;
91cd8340 426 int first_locale = 1;
2b83a2a4 427
91cd8340 428#define PUT(name) tsearch (name, &all_data, \
1fb05e3d 429 (int (*) (const void *, const void *)) strcoll)
06a466e7
UD
430#define GET(name) tfind (name, &all_data, \
431 (int (*) (const void *, const void *)) strcoll)
2b83a2a4 432
06a466e7
UD
433 /* `POSIX' locale is always available (POSIX.2 4.34.3). */
434 PUT ("POSIX");
435 /* And so is the "C" locale. */
436 PUT ("C");
1fb05e3d 437
91cd8340
UD
438 memset (linebuf, '-', sizeof (linebuf) - 1);
439 linebuf[sizeof (linebuf) - 1] = '\0';
440
e98b69a6
UD
441 /* First scan the locale archive. */
442 if (write_archive_locales (&all_data, linebuf))
443 first_locale = 0;
444
91cd8340 445 /* Now we can look for all files in the directory. */
fdc6c28a
UD
446 ndirents = scandir (LOCALEDIR, &dirents, select_dirs, alphasort);
447 for (cnt = 0; cnt < ndirents; ++cnt)
448 {
449 /* Test whether at least the LC_CTYPE data is there. Some
450 directories only contain translations. */
451 char buf[sizeof (LOCALEDIR) + strlen (dirents[cnt]->d_name)
452 + sizeof "/LC_IDENTIFICATION"];
453 char *enddir;
454 struct stat64 st;
455
456 stpcpy (enddir = stpcpy (stpcpy (stpcpy (buf, LOCALEDIR), "/"),
457 dirents[cnt]->d_name),
458 "/LC_IDENTIFICATION");
459
460 if (stat64 (buf, &st) == 0 && S_ISREG (st.st_mode))
461 {
e98b69a6 462 if (verbose && GET (dirents[cnt]->d_name) == NULL)
fdc6c28a
UD
463 {
464 /* Provide some nice output of all kinds of
465 information. */
466 int fd;
91cd8340 467
fdc6c28a
UD
468 if (! first_locale)
469 putchar_unlocked ('\n');
470 first_locale = 0;
91cd8340 471
fdc6c28a
UD
472 printf ("locale: %-15.15s directory: %.*s\n%s\n",
473 dirents[cnt]->d_name, (int) (enddir - buf), buf,
474 linebuf);
475
476 fd = open64 (buf, O_RDONLY);
477 if (fd != -1)
478 {
479 void *mapped = mmap64 (NULL, st.st_size, PROT_READ,
480 MAP_SHARED, fd, 0);
481 if (mapped != MAP_FAILED)
482 {
e98b69a6 483 print_LC_IDENTIFICATION (mapped, st.st_size);
fdc6c28a
UD
484
485 munmap (mapped, st.st_size);
486 }
487
488 close (fd);
489
490 /* Now try to get the charset information. */
491 strcpy (enddir, "/LC_CTYPE");
492 fd = open64 (buf, O_RDONLY);
493 if (fd != -1 && fstat64 (fd, &st) >= 0
494 && ((mapped = mmap64 (NULL, st.st_size, PROT_READ,
495 MAP_SHARED, fd, 0))
496 != MAP_FAILED))
497 {
e98b69a6 498 print_LC_CTYPE (mapped, st.st_size);
fdc6c28a
UD
499
500 munmap (mapped, st.st_size);
501 }
502
503 if (fd != -1)
504 close (fd);
505 }
506 }
e98b69a6
UD
507
508 /* If the verbose format is not selected we simply
509 collect the names. */
510 PUT (xstrdup (dirents[cnt]->d_name));
fdc6c28a
UD
511 }
512 }
513 if (ndirents > 0)
514 free (dirents);
91cd8340 515
06a466e7
UD
516 /* Now read the locale.alias files. */
517 if (argz_create_sep (LOCALE_ALIAS_PATH, ':', &alias_path, &alias_path_len))
518 error (1, errno, gettext ("while preparing output"));
519
520 entry = NULL;
521 while ((entry = argz_next (alias_path, alias_path_len, entry)))
91cd8340 522 {
06a466e7
UD
523 static const char aliasfile[] = "/locale.alias";
524 FILE *fp;
525 char full_name[strlen (entry) + sizeof aliasfile];
526
527 stpcpy (stpcpy (full_name, entry), aliasfile);
2e2dc1a5 528 fp = fopen (full_name, "rm");
06a466e7
UD
529 if (fp == NULL)
530 /* Ignore non-existing files. */
531 continue;
532
533 /* No threads present. */
534 __fsetlocking (fp, FSETLOCKING_BYCALLER);
91cd8340 535
06a466e7
UD
536 while (! feof_unlocked (fp))
537 {
538 /* It is a reasonable approach to use a fix buffer here
539 because
540 a) we are only interested in the first two fields
541 b) these fields must be usable as file names and so must
561470e0 542 not be that long */
06a466e7
UD
543 char buf[BUFSIZ];
544 char *alias;
545 char *value;
546 char *cp;
547
548 if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
549 /* EOF reached. */
550 break;
551
552 cp = buf;
553 /* Ignore leading white space. */
554 while (isspace (cp[0]) && cp[0] != '\n')
555 ++cp;
556
557 /* A leading '#' signals a comment line. */
558 if (cp[0] != '\0' && cp[0] != '#' && cp[0] != '\n')
559 {
560 alias = cp++;
561 while (cp[0] != '\0' && !isspace (cp[0]))
562 ++cp;
563 /* Terminate alias name. */
564 if (cp[0] != '\0')
565 *cp++ = '\0';
566
567 /* Now look for the beginning of the value. */
568 while (isspace (cp[0]))
569 ++cp;
570
571 if (cp[0] != '\0')
572 {
573 value = cp++;
574 while (cp[0] != '\0' && !isspace (cp[0]))
575 ++cp;
576 /* Terminate value. */
577 if (cp[0] == '\n')
578 {
579 /* This has to be done to make the following
580 test for the end of line possible. We are
581 looking for the terminating '\n' which do not
582 overwrite here. */
583 *cp++ = '\0';
584 *cp = '\n';
585 }
586 else if (cp[0] != '\0')
587 *cp++ = '\0';
588
589 /* Add the alias. */
590 if (! verbose && GET (value) != NULL)
591 PUT (xstrdup (alias));
592 }
593 }
594
595 /* Possibly not the whole line fits into the buffer.
596 Ignore the rest of the line. */
597 while (strchr (cp, '\n') == NULL)
598 {
599 cp = buf;
600 if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
601 /* Make sure the inner loop will be left. The outer
602 loop will exit at the `feof' test. */
603 *cp = '\n';
604 }
605 }
606
607 fclose (fp);
608 }
609
610 if (! verbose)
611 {
91cd8340
UD
612 twalk (all_data, print_names);
613 }
2b83a2a4
RM
614}
615
616
e98b69a6
UD
617struct nameent
618{
619 char *name;
620 uint32_t locrec_offset;
621};
622
623
624static int
625nameentcmp (const void *a, const void *b)
626{
627 return strcoll (((const struct nameent *) a)->name,
628 ((const struct nameent *) b)->name);
629}
630
631
632static int
633write_archive_locales (void **all_datap, char *linebuf)
634{
635 struct stat64 st;
636 void *all_data = *all_datap;
637 size_t len = 0;
638 struct locarhead *head;
639 struct namehashent *namehashtab;
640 char *addr = MAP_FAILED;
641 int fd, ret = 0;
642 uint32_t cnt;
643
644 fd = open64 (ARCHIVE_NAME, O_RDONLY);
645 if (fd < 0)
646 return 0;
647
648 if (fstat64 (fd, &st) < 0 || st.st_size < sizeof (*head))
649 goto error_out;
650
651 len = st.st_size;
652 addr = mmap64 (NULL, len, PROT_READ, MAP_SHARED, fd, 0);
653 if (addr == MAP_FAILED)
654 goto error_out;
655
656 head = (struct locarhead *) addr;
657 if (head->namehash_offset + head->namehash_size > len
658 || head->string_offset + head->string_size > len
659 || head->locrectab_offset + head->locrectab_size > len
660 || head->sumhash_offset + head->sumhash_size > len)
661 goto error_out;
662
663 namehashtab = (struct namehashent *) (addr + head->namehash_offset);
664 if (! verbose)
665 {
666 for (cnt = 0; cnt < head->namehash_size; ++cnt)
667 if (namehashtab[cnt].locrec_offset != 0)
668 {
669 PUT (xstrdup (addr + namehashtab[cnt].name_offset));
670 ++ret;
671 }
672 }
673 else
674 {
675 struct nameent *names;
676 uint32_t used;
677
678 names = (struct nameent *) xmalloc (head->namehash_used
679 * sizeof (struct nameent));
680 for (cnt = used = 0; cnt < head->namehash_size; ++cnt)
681 if (namehashtab[cnt].locrec_offset != 0)
682 {
683 names[used].name = addr + namehashtab[cnt].name_offset;
684 names[used++].locrec_offset = namehashtab[cnt].locrec_offset;
685 }
686
687 /* Sort the names. */
688 qsort (names, used, sizeof (struct nameent), nameentcmp);
689
690 for (cnt = 0; cnt < used; ++cnt)
691 {
692 struct locrecent *locrec;
693
694 PUT (xstrdup (names[cnt].name));
695
696 if (cnt)
697 putchar_unlocked ('\n');
698
699 printf ("locale: %-15.15s archive: " ARCHIVE_NAME "\n%s\n",
700 names[cnt].name, linebuf);
701
702 locrec = (struct locrecent *) (addr + names[cnt].locrec_offset);
703
704 print_LC_IDENTIFICATION (addr
705 + locrec->record[LC_IDENTIFICATION].offset,
706 locrec->record[LC_IDENTIFICATION].len);
707
708 print_LC_CTYPE (addr + locrec->record[LC_CTYPE].offset,
709 locrec->record[LC_CTYPE].len);
710 }
711
712 ret = used;
713 }
714
715error_out:
716 if (addr != MAP_FAILED)
717 munmap (addr, len);
718 close (fd);
719 *all_datap = all_data;
720 return ret;
721}
722
723
2b83a2a4
RM
724/* Write the names of all available character maps to stdout. */
725static void
726write_charmaps (void)
727{
1fb05e3d 728 void *all_data = NULL;
3e076219
UD
729 CHARMAP_DIR *dir;
730 const char *dirent;
2b83a2a4 731
3e076219
UD
732 /* Look for all files in the charmap directory. */
733 dir = charmap_opendir (CHARMAP_PATH);
2b83a2a4 734 if (dir == NULL)
3e076219 735 return;
51702635 736
3e076219
UD
737 while ((dirent = charmap_readdir (dir)) != NULL)
738 {
739 char **aliases;
740 char **p;
741
742 PUT (xstrdup (dirent));
743
744 aliases = charmap_aliases (CHARMAP_PATH, dirent);
745
746#if 0
747 /* Add the code_set_name and the aliases. */
748 for (p = aliases; *p; p++)
749 PUT (xstrdup (*p));
750#else
751 /* Add the code_set_name only. Most aliases are obsolete. */
752 p = aliases;
753 if (*p)
754 PUT (xstrdup (*p));
1fb05e3d 755#endif
1fb05e3d 756
3e076219
UD
757 charmap_free_aliases (aliases);
758 }
2b83a2a4 759
3e076219 760 charmap_closedir (dir);
1fb05e3d
UD
761
762 twalk (all_data, print_names);
2b83a2a4
RM
763}
764
765
766/* We have to show the contents of the environments determining the
767 locale. */
768static void
769show_locale_vars (void)
770{
771 size_t cat_no;
772 const char *lcall = getenv ("LC_ALL");
02860e47 773 const char *lang = getenv ("LANG") ? : "";
2b83a2a4 774
938c669e
AJ
775 auto void get_source (const char *name);
776
2b83a2a4
RM
777 void get_source (const char *name)
778 {
779 char *val = getenv (name);
780
51702635 781 if ((lcall ?: "")[0] != '\0' || val == NULL)
02860e47
UD
782 printf ("%s=\"%s\"\n", name,
783 (lcall ?: "")[0] ? lcall : (lang ?: "")[0] ? lang : "POSIX");
2b83a2a4
RM
784 else
785 printf ("%s=%s\n", name, val);
786 }
787
788 /* LANG has to be the first value. */
789 printf ("LANG=%s\n", lang);
790
791 /* Now all categories in an unspecified order. */
792 for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
643e9936
UD
793 if (cat_no != LC_ALL)
794 get_source (category[cat_no].name);
2b83a2a4
RM
795
796 /* The last is the LC_ALL value. */
797 printf ("LC_ALL=%s\n", lcall ? : "");
798}
799
800
801/* Show the information request for NAME. */
802static void
803show_info (const char *name)
804{
805 size_t cat_no;
806
938c669e
AJ
807 auto void print_item (struct cat_item *item);
808
2b83a2a4
RM
809 void print_item (struct cat_item *item)
810 {
2b83a2a4
RM
811 switch (item->value_type)
812 {
813 case string:
8f2ece69 814 if (show_keyword_name)
beaaf574 815 printf ("%s=\"", item->name);
7535b2d9 816 fputs (nl_langinfo (item->item_id) ? : "", stdout);
8f2ece69
UD
817 if (show_keyword_name)
818 putchar ('"');
beaaf574 819 putchar ('\n');
2b83a2a4
RM
820 break;
821 case stringarray:
822 {
823 int cnt;
824 const char *val;
825
826 if (show_keyword_name)
beaaf574 827 printf ("%s=\"", item->name);
2b83a2a4
RM
828
829 for (cnt = 0; cnt < item->max - 1; ++cnt)
830 {
831 val = nl_langinfo (item->item_id + cnt);
8f2ece69 832 if (val != NULL)
7535b2d9 833 fputs (val, stdout);
8f2ece69 834 putchar (';');
2b83a2a4
RM
835 }
836
837 val = nl_langinfo (item->item_id + cnt);
8f2ece69 838 if (val != NULL)
7535b2d9 839 fputs (val, stdout);
2b83a2a4
RM
840
841 if (show_keyword_name)
842 putchar ('"');
beaaf574 843 putchar ('\n');
2b83a2a4
RM
844 }
845 break;
51702635
UD
846 case stringlist:
847 {
848 int first = 1;
849 const char *val = nl_langinfo (item->item_id) ? : "";
7535b2d9 850 int cnt;
51702635 851
beaaf574
UD
852 if (show_keyword_name)
853 printf ("%s=", item->name);
854
7535b2d9 855 for (cnt = 0; cnt < item->max && *val != '\0'; ++cnt)
51702635
UD
856 {
857 printf ("%s%s%s%s", first ? "" : ";",
858 show_keyword_name ? "\"" : "", val,
859 show_keyword_name ? "\"" : "");
860 val = strchr (val, '\0') + 1;
861 first = 0;
862 }
beaaf574 863 putchar ('\n');
51702635
UD
864 }
865 break;
2b83a2a4
RM
866 case byte:
867 {
868 const char *val = nl_langinfo (item->item_id);
869
beaaf574
UD
870 if (show_keyword_name)
871 printf ("%s=", item->name);
872
2b83a2a4 873 if (val != NULL)
76352f64 874 printf ("%d", *val == '\177' ? -1 : *val);
beaaf574 875 putchar ('\n');
2b83a2a4
RM
876 }
877 break;
878 case bytearray:
879 {
880 const char *val = nl_langinfo (item->item_id);
881 int cnt = val ? strlen (val) : 0;
882
beaaf574
UD
883 if (show_keyword_name)
884 printf ("%s=", item->name);
885
2b83a2a4
RM
886 while (cnt > 1)
887 {
76352f64 888 printf ("%d;", *val == '\177' ? -1 : *val);
561470e0 889 --cnt;
2b83a2a4
RM
890 ++val;
891 }
892
76352f64 893 printf ("%d\n", cnt == 0 || *val == '\177' ? -1 : *val);
2b83a2a4
RM
894 }
895 break;
299a95b9
RM
896 case word:
897 {
4d54786a
UD
898 union { unsigned int word; char *string; } val;
899 val.string = nl_langinfo (item->item_id);
beaaf574
UD
900 if (show_keyword_name)
901 printf ("%s=", item->name);
902
4d54786a 903 printf ("%d\n", val.word);
299a95b9
RM
904 }
905 break;
beaaf574
UD
906 case wstring:
907 case wstringarray:
908 case wstringlist:
909 /* We don't print wide character information since the same
910 information is available in a multibyte string. */
2b83a2a4 911 default:
9fea9ed6 912 break;
938c669e 913
2b83a2a4 914 }
2b83a2a4
RM
915 }
916
917 for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
92d2e18f
UD
918 if (cat_no != LC_ALL)
919 {
920 size_t item_no;
19bc17a9 921
92d2e18f
UD
922 if (strcmp (name, category[cat_no].name) == 0)
923 /* Print the whole category. */
2b83a2a4
RM
924 {
925 if (show_category_name != 0)
926 puts (category[cat_no].name);
927
92d2e18f
UD
928 for (item_no = 0; item_no < category[cat_no].number; ++item_no)
929 print_item (&category[cat_no].item_desc[item_no]);
930
2b83a2a4
RM
931 return;
932 }
92d2e18f
UD
933
934 for (item_no = 0; item_no < category[cat_no].number; ++item_no)
935 if (strcmp (name, category[cat_no].item_desc[item_no].name) == 0)
936 {
937 if (show_category_name != 0)
938 puts (category[cat_no].name);
939
940 print_item (&category[cat_no].item_desc[item_no]);
941 return;
942 }
943 }
036cc82f 944
6d52618b
UD
945 /* The name is not a standard one.
946 For testing and perhaps advanced use allow some more symbols. */
036cc82f 947 locale_special (name, show_category_name, show_keyword_name);
2b83a2a4 948}