]> git.ipfire.org Git - thirdparty/glibc.git/blame - locale/programs/locale.c
Update copyright dates with scripts/update-copyrights.
[thirdparty/glibc.git] / locale / programs / locale.c
CommitLineData
5107cf1d 1/* Implementation of the locale program according to POSIX 9945-2.
f7a9f785 2 Copyright (C) 1995-2016 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
90fe682d 48#define ARCHIVE_NAME COMPLOCALEDIR "/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\
9706dc5f 298"), "2015");
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;
90fe682d
CD
328 char buf[sizeof (COMPLOCALEDIR)
329 + strlen (dirent->d_name) + 1];
fdc6c28a 330
90fe682d
CD
331 stpcpy (stpcpy (stpcpy (buf, COMPLOCALEDIR), "/"),
332 dirent->d_name);
fdc6c28a
UD
333
334 if (stat64 (buf, &st) == 0)
335 mode = st.st_mode;
336 }
337
338 result = S_ISDIR (mode);
339 }
340
341 return result;
342}
343
344
e98b69a6
UD
345static void
346print_LC_IDENTIFICATION (void *mapped, size_t size)
347{
348 /* Read the information from the file. */
349 struct
350 {
351 unsigned int magic;
352 unsigned int nstrings;
353 unsigned int strindex[0];
354 } *filedata = mapped;
355
356 if (filedata->magic == LIMAGIC (LC_IDENTIFICATION)
357 && (sizeof *filedata
358 + (filedata->nstrings
359 * sizeof (unsigned int))
360 <= size))
361 {
362 const char *str;
363
364#define HANDLE(idx, name) \
365 str = ((char *) mapped \
366 + filedata->strindex[_NL_ITEM_INDEX (_NL_IDENTIFICATION_##idx)]); \
367 if (*str != '\0') \
368 printf ("%9s | %s\n", name, str)
369 HANDLE (TITLE, "title");
370 HANDLE (SOURCE, "source");
371 HANDLE (ADDRESS, "address");
372 HANDLE (CONTACT, "contact");
373 HANDLE (EMAIL, "email");
374 HANDLE (TEL, "telephone");
375 HANDLE (FAX, "fax");
376 HANDLE (LANGUAGE, "language");
377 HANDLE (TERRITORY, "territory");
378 HANDLE (AUDIENCE, "audience");
379 HANDLE (APPLICATION, "application");
380 HANDLE (ABBREVIATION, "abbreviation");
381 HANDLE (REVISION, "revision");
382 HANDLE (DATE, "date");
383 }
384}
385
386
387static void
388print_LC_CTYPE (void *mapped, size_t size)
389{
390 struct
391 {
392 unsigned int magic;
393 unsigned int nstrings;
394 unsigned int strindex[0];
395 } *filedata = mapped;
396
397 if (filedata->magic == LIMAGIC (LC_CTYPE)
398 && (sizeof *filedata
399 + (filedata->nstrings
400 * sizeof (unsigned int))
401 <= size))
402 {
403 const char *str;
404
405 str = ((char *) mapped
406 + filedata->strindex[_NL_ITEM_INDEX (_NL_CTYPE_CODESET_NAME)]);
407 if (*str != '\0')
408 printf (" codeset | %s\n", str);
409 }
410}
411
412
1fb05e3d
UD
413/* Write the names of all available locales to stdout. We have some
414 sources of the information: the contents of the locale directory
415 and the locale.alias file. To avoid duplicates and print the
416 result is a reasonable order we put all entries is a search tree
417 and print them afterwards. */
2b83a2a4
RM
418static void
419write_locales (void)
420{
91cd8340 421 char linebuf[80];
1fb05e3d 422 void *all_data = NULL;
fdc6c28a
UD
423 struct dirent **dirents;
424 int ndirents;
425 int cnt;
1fb05e3d
UD
426 char *alias_path;
427 size_t alias_path_len;
428 char *entry;
91cd8340 429 int first_locale = 1;
2b83a2a4 430
91cd8340 431#define PUT(name) tsearch (name, &all_data, \
1fb05e3d 432 (int (*) (const void *, const void *)) strcoll)
06a466e7
UD
433#define GET(name) tfind (name, &all_data, \
434 (int (*) (const void *, const void *)) strcoll)
2b83a2a4 435
06a466e7
UD
436 /* `POSIX' locale is always available (POSIX.2 4.34.3). */
437 PUT ("POSIX");
438 /* And so is the "C" locale. */
439 PUT ("C");
1fb05e3d 440
91cd8340
UD
441 memset (linebuf, '-', sizeof (linebuf) - 1);
442 linebuf[sizeof (linebuf) - 1] = '\0';
443
e98b69a6
UD
444 /* First scan the locale archive. */
445 if (write_archive_locales (&all_data, linebuf))
446 first_locale = 0;
447
91cd8340 448 /* Now we can look for all files in the directory. */
90fe682d
CD
449 ndirents = scandir (COMPLOCALEDIR, &dirents, select_dirs,
450 alphasort);
fdc6c28a
UD
451 for (cnt = 0; cnt < ndirents; ++cnt)
452 {
453 /* Test whether at least the LC_CTYPE data is there. Some
454 directories only contain translations. */
90fe682d
CD
455 char buf[sizeof (COMPLOCALEDIR)
456 + strlen (dirents[cnt]->d_name)
457 + sizeof "/LC_IDENTIFICATION"];
fdc6c28a
UD
458 char *enddir;
459 struct stat64 st;
460
90fe682d
CD
461 stpcpy (enddir = stpcpy (stpcpy (stpcpy (buf,
462 COMPLOCALEDIR),
463 "/"),
fdc6c28a
UD
464 dirents[cnt]->d_name),
465 "/LC_IDENTIFICATION");
466
467 if (stat64 (buf, &st) == 0 && S_ISREG (st.st_mode))
468 {
e98b69a6 469 if (verbose && GET (dirents[cnt]->d_name) == NULL)
fdc6c28a
UD
470 {
471 /* Provide some nice output of all kinds of
472 information. */
473 int fd;
91cd8340 474
fdc6c28a
UD
475 if (! first_locale)
476 putchar_unlocked ('\n');
477 first_locale = 0;
91cd8340 478
fdc6c28a
UD
479 printf ("locale: %-15.15s directory: %.*s\n%s\n",
480 dirents[cnt]->d_name, (int) (enddir - buf), buf,
481 linebuf);
482
483 fd = open64 (buf, O_RDONLY);
484 if (fd != -1)
485 {
486 void *mapped = mmap64 (NULL, st.st_size, PROT_READ,
487 MAP_SHARED, fd, 0);
488 if (mapped != MAP_FAILED)
489 {
e98b69a6 490 print_LC_IDENTIFICATION (mapped, st.st_size);
fdc6c28a
UD
491
492 munmap (mapped, st.st_size);
493 }
494
495 close (fd);
496
497 /* Now try to get the charset information. */
498 strcpy (enddir, "/LC_CTYPE");
499 fd = open64 (buf, O_RDONLY);
500 if (fd != -1 && fstat64 (fd, &st) >= 0
501 && ((mapped = mmap64 (NULL, st.st_size, PROT_READ,
502 MAP_SHARED, fd, 0))
503 != MAP_FAILED))
504 {
e98b69a6 505 print_LC_CTYPE (mapped, st.st_size);
fdc6c28a
UD
506
507 munmap (mapped, st.st_size);
508 }
509
510 if (fd != -1)
511 close (fd);
512 }
513 }
e98b69a6
UD
514
515 /* If the verbose format is not selected we simply
516 collect the names. */
517 PUT (xstrdup (dirents[cnt]->d_name));
fdc6c28a
UD
518 }
519 }
520 if (ndirents > 0)
521 free (dirents);
91cd8340 522
06a466e7
UD
523 /* Now read the locale.alias files. */
524 if (argz_create_sep (LOCALE_ALIAS_PATH, ':', &alias_path, &alias_path_len))
525 error (1, errno, gettext ("while preparing output"));
526
527 entry = NULL;
528 while ((entry = argz_next (alias_path, alias_path_len, entry)))
91cd8340 529 {
06a466e7
UD
530 static const char aliasfile[] = "/locale.alias";
531 FILE *fp;
532 char full_name[strlen (entry) + sizeof aliasfile];
533
534 stpcpy (stpcpy (full_name, entry), aliasfile);
2e2dc1a5 535 fp = fopen (full_name, "rm");
06a466e7
UD
536 if (fp == NULL)
537 /* Ignore non-existing files. */
538 continue;
539
540 /* No threads present. */
541 __fsetlocking (fp, FSETLOCKING_BYCALLER);
91cd8340 542
06a466e7
UD
543 while (! feof_unlocked (fp))
544 {
545 /* It is a reasonable approach to use a fix buffer here
546 because
547 a) we are only interested in the first two fields
548 b) these fields must be usable as file names and so must
561470e0 549 not be that long */
06a466e7
UD
550 char buf[BUFSIZ];
551 char *alias;
552 char *value;
553 char *cp;
554
555 if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
556 /* EOF reached. */
557 break;
558
559 cp = buf;
560 /* Ignore leading white space. */
561 while (isspace (cp[0]) && cp[0] != '\n')
562 ++cp;
563
564 /* A leading '#' signals a comment line. */
565 if (cp[0] != '\0' && cp[0] != '#' && cp[0] != '\n')
566 {
567 alias = cp++;
568 while (cp[0] != '\0' && !isspace (cp[0]))
569 ++cp;
570 /* Terminate alias name. */
571 if (cp[0] != '\0')
572 *cp++ = '\0';
573
574 /* Now look for the beginning of the value. */
575 while (isspace (cp[0]))
576 ++cp;
577
578 if (cp[0] != '\0')
579 {
580 value = cp++;
581 while (cp[0] != '\0' && !isspace (cp[0]))
582 ++cp;
583 /* Terminate value. */
584 if (cp[0] == '\n')
585 {
586 /* This has to be done to make the following
587 test for the end of line possible. We are
588 looking for the terminating '\n' which do not
589 overwrite here. */
590 *cp++ = '\0';
591 *cp = '\n';
592 }
593 else if (cp[0] != '\0')
594 *cp++ = '\0';
595
596 /* Add the alias. */
597 if (! verbose && GET (value) != NULL)
598 PUT (xstrdup (alias));
599 }
600 }
601
602 /* Possibly not the whole line fits into the buffer.
603 Ignore the rest of the line. */
604 while (strchr (cp, '\n') == NULL)
605 {
606 cp = buf;
607 if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
608 /* Make sure the inner loop will be left. The outer
609 loop will exit at the `feof' test. */
610 *cp = '\n';
611 }
612 }
613
614 fclose (fp);
615 }
616
617 if (! verbose)
618 {
91cd8340
UD
619 twalk (all_data, print_names);
620 }
2b83a2a4
RM
621}
622
623
e98b69a6
UD
624struct nameent
625{
626 char *name;
627 uint32_t locrec_offset;
628};
629
630
631static int
632nameentcmp (const void *a, const void *b)
633{
634 return strcoll (((const struct nameent *) a)->name,
635 ((const struct nameent *) b)->name);
636}
637
638
639static int
640write_archive_locales (void **all_datap, char *linebuf)
641{
642 struct stat64 st;
643 void *all_data = *all_datap;
644 size_t len = 0;
645 struct locarhead *head;
646 struct namehashent *namehashtab;
647 char *addr = MAP_FAILED;
648 int fd, ret = 0;
649 uint32_t cnt;
650
651 fd = open64 (ARCHIVE_NAME, O_RDONLY);
652 if (fd < 0)
653 return 0;
654
655 if (fstat64 (fd, &st) < 0 || st.st_size < sizeof (*head))
656 goto error_out;
657
658 len = st.st_size;
659 addr = mmap64 (NULL, len, PROT_READ, MAP_SHARED, fd, 0);
660 if (addr == MAP_FAILED)
661 goto error_out;
662
663 head = (struct locarhead *) addr;
664 if (head->namehash_offset + head->namehash_size > len
665 || head->string_offset + head->string_size > len
666 || head->locrectab_offset + head->locrectab_size > len
667 || head->sumhash_offset + head->sumhash_size > len)
668 goto error_out;
669
670 namehashtab = (struct namehashent *) (addr + head->namehash_offset);
671 if (! verbose)
672 {
673 for (cnt = 0; cnt < head->namehash_size; ++cnt)
674 if (namehashtab[cnt].locrec_offset != 0)
675 {
676 PUT (xstrdup (addr + namehashtab[cnt].name_offset));
677 ++ret;
678 }
679 }
680 else
681 {
682 struct nameent *names;
683 uint32_t used;
684
685 names = (struct nameent *) xmalloc (head->namehash_used
686 * sizeof (struct nameent));
687 for (cnt = used = 0; cnt < head->namehash_size; ++cnt)
688 if (namehashtab[cnt].locrec_offset != 0)
689 {
690 names[used].name = addr + namehashtab[cnt].name_offset;
691 names[used++].locrec_offset = namehashtab[cnt].locrec_offset;
692 }
693
694 /* Sort the names. */
695 qsort (names, used, sizeof (struct nameent), nameentcmp);
696
697 for (cnt = 0; cnt < used; ++cnt)
698 {
699 struct locrecent *locrec;
700
701 PUT (xstrdup (names[cnt].name));
702
703 if (cnt)
704 putchar_unlocked ('\n');
705
706 printf ("locale: %-15.15s archive: " ARCHIVE_NAME "\n%s\n",
707 names[cnt].name, linebuf);
708
709 locrec = (struct locrecent *) (addr + names[cnt].locrec_offset);
710
711 print_LC_IDENTIFICATION (addr
712 + locrec->record[LC_IDENTIFICATION].offset,
713 locrec->record[LC_IDENTIFICATION].len);
714
715 print_LC_CTYPE (addr + locrec->record[LC_CTYPE].offset,
716 locrec->record[LC_CTYPE].len);
717 }
718
719 ret = used;
720 }
721
722error_out:
723 if (addr != MAP_FAILED)
724 munmap (addr, len);
725 close (fd);
726 *all_datap = all_data;
727 return ret;
728}
729
730
2b83a2a4
RM
731/* Write the names of all available character maps to stdout. */
732static void
733write_charmaps (void)
734{
1fb05e3d 735 void *all_data = NULL;
3e076219
UD
736 CHARMAP_DIR *dir;
737 const char *dirent;
2b83a2a4 738
3e076219
UD
739 /* Look for all files in the charmap directory. */
740 dir = charmap_opendir (CHARMAP_PATH);
2b83a2a4 741 if (dir == NULL)
3e076219 742 return;
51702635 743
3e076219
UD
744 while ((dirent = charmap_readdir (dir)) != NULL)
745 {
746 char **aliases;
747 char **p;
748
749 PUT (xstrdup (dirent));
750
751 aliases = charmap_aliases (CHARMAP_PATH, dirent);
752
753#if 0
754 /* Add the code_set_name and the aliases. */
755 for (p = aliases; *p; p++)
756 PUT (xstrdup (*p));
757#else
758 /* Add the code_set_name only. Most aliases are obsolete. */
759 p = aliases;
760 if (*p)
761 PUT (xstrdup (*p));
1fb05e3d 762#endif
1fb05e3d 763
3e076219
UD
764 charmap_free_aliases (aliases);
765 }
2b83a2a4 766
3e076219 767 charmap_closedir (dir);
1fb05e3d
UD
768
769 twalk (all_data, print_names);
2b83a2a4
RM
770}
771
02637374
AS
772/* Print a properly quoted assignment of NAME with VAL, using double
773 quotes iff DQUOTE is true. */
774static void
775print_assignment (const char *name, const char *val, bool dquote)
776{
777 printf ("%s=", name);
778 if (dquote)
779 putchar ('"');
780 while (*val != '\0')
781 {
782 size_t segment
783 = strcspn (val, dquote ? "$`\"\\" : "~|&;<>()$`\\\"' \t\n");
784 printf ("%.*s", (int) segment, val);
785 val += segment;
786 if (*val == '\0')
787 break;
788 putchar ('\\');
789 putchar (*val++);
790 }
791 if (dquote)
792 putchar ('"');
793 putchar ('\n');
794}
2b83a2a4
RM
795
796/* We have to show the contents of the environments determining the
797 locale. */
798static void
799show_locale_vars (void)
800{
a62b3c15
RM
801 const char *lcall = getenv ("LC_ALL") ?: "";
802 const char *lang = getenv ("LANG") ?: "";
2b83a2a4
RM
803
804 /* LANG has to be the first value. */
02637374 805 print_assignment ("LANG", lang, false);
2b83a2a4
RM
806
807 /* Now all categories in an unspecified order. */
a62b3c15 808 for (size_t cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
643e9936 809 if (cat_no != LC_ALL)
a62b3c15
RM
810 {
811 const char *name = category[cat_no].name;
812 const char *val = getenv (name);
813
814 if (lcall[0] != '\0' || val == NULL)
815 print_assignment (name,
816 lcall[0] != '\0' ? lcall
817 : lang[0] != '\0' ? lang
818 : "POSIX",
819 true);
820 else
821 print_assignment (name, val, false);
822 }
2b83a2a4
RM
823
824 /* The last is the LC_ALL value. */
02637374 825 print_assignment ("LC_ALL", lcall, false);
2b83a2a4
RM
826}
827
828
a62b3c15 829/* Subroutine of show_info, below. */
2b83a2a4 830static void
a62b3c15 831print_item (struct cat_item *item)
2b83a2a4 832{
a62b3c15 833 switch (item->value_type)
2b83a2a4 834 {
a62b3c15
RM
835 case string:
836 if (show_keyword_name)
837 printf ("%s=\"", item->name);
838 fputs (nl_langinfo (item->item_id) ? : "", stdout);
839 if (show_keyword_name)
840 putchar ('"');
841 putchar ('\n');
842 break;
843 case stringarray:
844 {
845 const char *val;
846 int cnt;
2b83a2a4 847
a62b3c15
RM
848 if (show_keyword_name)
849 printf ("%s=\"", item->name);
2b83a2a4 850
a62b3c15
RM
851 for (cnt = 0; cnt < item->max - 1; ++cnt)
852 {
2b83a2a4 853 val = nl_langinfo (item->item_id + cnt);
8f2ece69 854 if (val != NULL)
7535b2d9 855 fputs (val, stdout);
a62b3c15 856 putchar (';');
2b83a2a4 857 }
a62b3c15
RM
858
859 val = nl_langinfo (item->item_id + cnt);
860 if (val != NULL)
861 fputs (val, stdout);
862
863 if (show_keyword_name)
864 putchar ('"');
865 putchar ('\n');
866 }
867 break;
868 case stringlist:
869 {
870 int first = 1;
871 const char *val = nl_langinfo (item->item_id) ? : "";
872
873 if (show_keyword_name)
874 printf ("%s=", item->name);
875
876 for (int cnt = 0; cnt < item->max && *val != '\0'; ++cnt)
51702635 877 {
a62b3c15
RM
878 printf ("%s%s%s%s", first ? "" : ";",
879 show_keyword_name ? "\"" : "", val,
880 show_keyword_name ? "\"" : "");
881 val = strchr (val, '\0') + 1;
882 first = 0;
51702635 883 }
a62b3c15
RM
884 putchar ('\n');
885 }
886 break;
887 case byte:
888 {
889 const char *val = nl_langinfo (item->item_id);
2b83a2a4 890
a62b3c15
RM
891 if (show_keyword_name)
892 printf ("%s=", item->name);
beaaf574 893
a62b3c15
RM
894 if (val != NULL)
895 printf ("%d", *val == '\377' ? -1 : *val);
896 putchar ('\n');
897 }
898 break;
899 case bytearray:
900 {
901 const char *val = nl_langinfo (item->item_id);
902 int cnt = val ? strlen (val) : 0;
903
904 if (show_keyword_name)
905 printf ("%s=", item->name);
906
907 while (cnt > 1)
2b83a2a4 908 {
a62b3c15
RM
909 printf ("%d;", *val == '\177' ? -1 : *val);
910 --cnt;
911 ++val;
912 }
2b83a2a4 913
a62b3c15
RM
914 printf ("%d\n", cnt == 0 || *val == '\177' ? -1 : *val);
915 }
916 break;
917 case word:
918 {
919 union { unsigned int word; char *string; } val;
920 val.string = nl_langinfo (item->item_id);
921 if (show_keyword_name)
922 printf ("%s=", item->name);
beaaf574 923
a62b3c15
RM
924 printf ("%d\n", val.word);
925 }
926 break;
927 case wordarray:
928 {
929 int first = 1;
930 union { unsigned int *wordarray; char *string; } val;
2b83a2a4 931
a62b3c15
RM
932 val.string = nl_langinfo (item->item_id);
933 if (show_keyword_name)
934 printf ("%s=", item->name);
beaaf574 935
a62b3c15 936 for (int cnt = 0; cnt < item->max; ++cnt)
b5449b12 937 {
a62b3c15
RM
938 printf ("%s%d", first ? "" : ";", val.wordarray[cnt]);
939 first = 0;
b5449b12 940 }
a62b3c15
RM
941 putchar ('\n');
942 }
943 break;
944 case wstring:
945 case wstringarray:
946 case wstringlist:
947 /* We don't print wide character information since the same
948 information is available in a multibyte string. */
949 default:
950 break;
2b83a2a4 951 }
a62b3c15 952}
2b83a2a4 953
a62b3c15
RM
954/* Show the information request for NAME. */
955static void
956show_info (const char *name)
957{
958 for (size_t cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
92d2e18f
UD
959 if (cat_no != LC_ALL)
960 {
92d2e18f
UD
961 if (strcmp (name, category[cat_no].name) == 0)
962 /* Print the whole category. */
2b83a2a4
RM
963 {
964 if (show_category_name != 0)
965 puts (category[cat_no].name);
966
a62b3c15
RM
967 for (size_t item_no = 0;
968 item_no < category[cat_no].number;
969 ++item_no)
92d2e18f
UD
970 print_item (&category[cat_no].item_desc[item_no]);
971
2b83a2a4
RM
972 return;
973 }
92d2e18f 974
a62b3c15 975 for (size_t item_no = 0; item_no < category[cat_no].number; ++item_no)
92d2e18f
UD
976 if (strcmp (name, category[cat_no].item_desc[item_no].name) == 0)
977 {
978 if (show_category_name != 0)
979 puts (category[cat_no].name);
980
981 print_item (&category[cat_no].item_desc[item_no]);
982 return;
983 }
984 }
036cc82f 985
6d52618b
UD
986 /* The name is not a standard one.
987 For testing and perhaps advanced use allow some more symbols. */
036cc82f 988 locale_special (name, show_category_name, show_keyword_name);
2b83a2a4 989}