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