]> 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.
d614a753 2 Copyright (C) 1995-2020 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
5a82c748 17 along with this program; if not, see <https://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
02eec681 43#include "record-status.h"
19bc17a9 44#include "localeinfo.h"
3e076219 45#include "charmap-dir.h"
e98b69a6 46#include "../locarchive.h"
6ff444c4 47#include <programs/xmalloc.h>
2b83a2a4 48
90fe682d 49#define ARCHIVE_NAME COMPLOCALEDIR "/locale-archive"
2b83a2a4 50
2b83a2a4
RM
51/* If set print the name of the category. */
52static int show_category_name;
53
54/* If set print the name of the item. */
55static int show_keyword_name;
56
1fb05e3d
UD
57/* Print names of all available locales. */
58static int do_all;
59
60/* Print names of all available character maps. */
61static int do_charmaps = 0;
62
63/* Name and version of program. */
64static void print_version (FILE *stream, struct argp_state *state);
65void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
66
67/* Definitions of arguments for argp functions. */
5a97622d 68static const struct argp_option options[] =
19bc17a9 69{
5a97622d
UD
70 { NULL, 0, NULL, 0, N_("System information:") },
71 { "all-locales", 'a', NULL, OPTION_NO_USAGE,
72 N_("Write names of available locales") },
73 { "charmaps", 'm', NULL, OPTION_NO_USAGE,
74 N_("Write names of available charmaps") },
75 { NULL, 0, NULL, 0, N_("Modify output format:") },
76 { "category-name", 'c', NULL, 0, N_("Write names of selected categories") },
77 { "keyword-name", 'k', NULL, 0, N_("Write names of selected keywords") },
91cd8340 78 { "verbose", 'v', NULL, 0, N_("Print more information") },
1fb05e3d
UD
79 { NULL, 0, NULL, 0, NULL }
80};
81
82/* Short description of program. */
cbbcaf23 83static const char doc[] = N_("Get locale-specific information.");
1fb05e3d
UD
84
85/* Strings for arguments in help texts. */
5a97622d 86static const char args_doc[] = N_("NAME\n[-a|-m]");
1fb05e3d
UD
87
88/* Prototype for option handler. */
89static error_t parse_opt (int key, char *arg, struct argp_state *state);
90
cbbcaf23
UD
91/* Function to print some extra text in the help message. */
92static char *more_help (int key, const char *text, void *input);
93
1fb05e3d
UD
94/* Data structure to communicate with argp functions. */
95static struct argp argp =
96{
cbbcaf23 97 options, parse_opt, args_doc, doc, NULL, more_help
19bc17a9 98};
2b83a2a4
RM
99
100
101/* We don't have these constants defined because we don't use them. Give
102 default values. */
103#define CTYPE_MB_CUR_MIN 0
104#define CTYPE_MB_CUR_MAX 0
105#define CTYPE_HASH_SIZE 0
106#define CTYPE_HASH_LAYERS 0
107#define CTYPE_CLASS 0
108#define CTYPE_TOUPPER_EB 0
109#define CTYPE_TOLOWER_EB 0
110#define CTYPE_TOUPPER_EL 0
111#define CTYPE_TOLOWER_EL 0
19bc17a9 112
299a95b9
RM
113/* Definition of the data structure which represents a category and its
114 items. */
115struct category
19bc17a9 116{
299a95b9 117 int cat_id;
19bc17a9 118 const char *name;
299a95b9
RM
119 size_t number;
120 struct cat_item
121 {
122 int item_id;
123 const char *name;
124 enum { std, opt } status;
125 enum value_type value_type;
126 int min;
127 int max;
128 } *item_desc;
19bc17a9
RM
129};
130
299a95b9
RM
131/* Simple helper macro. */
132#define NELEMS(arr) ((sizeof (arr)) / (sizeof (arr[0])))
133
134/* For some tricky stuff. */
135#define NO_PAREN(Item, More...) Item, ## More
2b83a2a4
RM
136
137/* We have all categories defined in `categories.def'. Now construct
138 the description and data structure used for all categories. */
299a95b9 139#define DEFINE_ELEMENT(Item, More...) { Item, ## More },
4b10dd6c 140#define DEFINE_CATEGORY(category, name, items, postload) \
2b83a2a4
RM
141 static struct cat_item category##_desc[] = \
142 { \
561470e0 143 NO_PAREN items \
2b83a2a4
RM
144 };
145
299a95b9 146#include "categories.def"
2b83a2a4
RM
147#undef DEFINE_CATEGORY
148
149static struct category category[] =
150 {
4b10dd6c 151#define DEFINE_CATEGORY(category, name, items, postload) \
036cc82f
RM
152 [category] = { _NL_NUM_##category, name, NELEMS (category##_desc), \
153 category##_desc },
299a95b9 154#include "categories.def"
2b83a2a4
RM
155#undef DEFINE_CATEGORY
156 };
157#define NCATEGORIES NELEMS (category)
158
159
036cc82f
RM
160/* Automatically set variable. */
161extern const char *__progname;
162
163/* helper function for extended name handling. */
164extern void locale_special (const char *name, int show_category_name,
165 int show_keyword_name);
166
2b83a2a4 167/* Prototypes for local functions. */
e98b69a6
UD
168static void print_LC_IDENTIFICATION (void *mapped, size_t size);
169static void print_LC_CTYPE (void *mapped, size_t size);
2b83a2a4 170static void write_locales (void);
e98b69a6
UD
171static int nameentcmp (const void *a, const void *b);
172static int write_archive_locales (void **all_datap, char *linebuf);
2b83a2a4
RM
173static void write_charmaps (void);
174static void show_locale_vars (void);
175static void show_info (const char *name);
e485b2b6
FW
176static void try_setlocale (int category, const char *category_name);
177static char *quote_string (const char *input);
178static void setlocale_diagnostics (void);
2b83a2a4
RM
179
180
181int
182main (int argc, char *argv[])
183{
2f6d1f1b
UD
184 int remaining;
185
bba7bb78 186 /* Set initial values for global variables. */
2b83a2a4
RM
187 show_category_name = 0;
188 show_keyword_name = 0;
189
190 /* Set locale. Do not set LC_ALL because the other categories must
6d52618b 191 not be affected (according to POSIX.2). */
e485b2b6
FW
192 try_setlocale (LC_CTYPE, "LC_CTYPE");
193 try_setlocale (LC_MESSAGES, "LC_MESSAGES");
2b83a2a4
RM
194
195 /* Initialize the message catalog. */
196 textdomain (PACKAGE);
197
1fb05e3d 198 /* Parse and process arguments. */
2f6d1f1b 199 argp_parse (&argp, argc, argv, 0, &remaining, NULL);
2b83a2a4 200
2b83a2a4
RM
201 /* `-a' requests the names of all available locales. */
202 if (do_all != 0)
203 {
e485b2b6
FW
204 setlocale_diagnostics ();
205 try_setlocale (LC_COLLATE, "LC_COLLATE");
2b83a2a4
RM
206 write_locales ();
207 exit (EXIT_SUCCESS);
208 }
209
210 /* `m' requests the names of all available charmaps. The names can be
8f2ece69 211 used for the -f argument to localedef(1). */
2b83a2a4
RM
212 if (do_charmaps != 0)
213 {
e485b2b6 214 setlocale_diagnostics ();
2b83a2a4
RM
215 write_charmaps ();
216 exit (EXIT_SUCCESS);
217 }
218
76060ec0
RM
219 /* Specific information about the current locale are requested.
220 Change to this locale now. */
e485b2b6
FW
221 try_setlocale (LC_ALL, "LC_ALL");
222 setlocale_diagnostics ();
76060ec0 223
2b83a2a4
RM
224 /* If no real argument is given we have to print the contents of the
225 current locale definition variables. These are LANG and the LC_*. */
2f6d1f1b 226 if (remaining == argc && show_keyword_name == 0 && show_category_name == 0)
2b83a2a4
RM
227 {
228 show_locale_vars ();
229 exit (EXIT_SUCCESS);
230 }
231
232 /* Process all given names. */
2f6d1f1b
UD
233 while (remaining < argc)
234 show_info (argv[remaining++]);
2b83a2a4
RM
235
236 exit (EXIT_SUCCESS);
237}
238
239
1fb05e3d
UD
240/* Handle program arguments. */
241static error_t
242parse_opt (int key, char *arg, struct argp_state *state)
2b83a2a4 243{
1fb05e3d 244 switch (key)
fafaa44e 245 {
1fb05e3d
UD
246 case 'a':
247 do_all = 1;
248 break;
249 case 'c':
250 show_category_name = 1;
251 break;
252 case 'm':
253 do_charmaps = 1;
254 break;
255 case 'k':
256 show_keyword_name = 1;
257 break;
91cd8340
UD
258 case 'v':
259 verbose = 1;
260 break;
1fb05e3d
UD
261 default:
262 return ARGP_ERR_UNKNOWN;
fafaa44e 263 }
1fb05e3d
UD
264 return 0;
265}
266
2b83a2a4 267
cbbcaf23
UD
268static char *
269more_help (int key, const char *text, void *input)
270{
8b748aed 271 char *tp = NULL;
cbbcaf23
UD
272 switch (key)
273 {
274 case ARGP_KEY_HELP_EXTRA:
275 /* We print some extra information. */
8b748aed 276 if (asprintf (&tp, gettext ("\
cbbcaf23 277For bug reporting instructions, please see:\n\
8b748aed
JM
278%s.\n"), REPORT_BUGS_TO) < 0)
279 return NULL;
280 return tp;
cbbcaf23
UD
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{
8b748aed 292 fprintf (stream, "locale %s%s\n", PKGVERSION, VERSION);
1fb05e3d
UD
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\
c9123888 297"), "2019");
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
fdc6c28a
UD
320 if (dirent->d_type != DT_UNKNOWN && dirent->d_type != DT_LNK)
321 mode = DTTOIF (dirent->d_type);
322 else
fdc6c28a
UD
323 {
324 struct stat64 st;
90fe682d
CD
325 char buf[sizeof (COMPLOCALEDIR)
326 + strlen (dirent->d_name) + 1];
fdc6c28a 327
90fe682d
CD
328 stpcpy (stpcpy (stpcpy (buf, COMPLOCALEDIR), "/"),
329 dirent->d_name);
fdc6c28a
UD
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. */
90fe682d
CD
446 ndirents = scandir (COMPLOCALEDIR, &dirents, select_dirs,
447 alphasort);
fdc6c28a
UD
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. */
90fe682d
CD
452 char buf[sizeof (COMPLOCALEDIR)
453 + strlen (dirents[cnt]->d_name)
454 + sizeof "/LC_IDENTIFICATION"];
fdc6c28a
UD
455 char *enddir;
456 struct stat64 st;
457
90fe682d
CD
458 stpcpy (enddir = stpcpy (stpcpy (stpcpy (buf,
459 COMPLOCALEDIR),
460 "/"),
fdc6c28a
UD
461 dirents[cnt]->d_name),
462 "/LC_IDENTIFICATION");
463
464 if (stat64 (buf, &st) == 0 && S_ISREG (st.st_mode))
465 {
e98b69a6 466 if (verbose && GET (dirents[cnt]->d_name) == NULL)
fdc6c28a
UD
467 {
468 /* Provide some nice output of all kinds of
469 information. */
470 int fd;
91cd8340 471
fdc6c28a
UD
472 if (! first_locale)
473 putchar_unlocked ('\n');
474 first_locale = 0;
91cd8340 475
fdc6c28a
UD
476 printf ("locale: %-15.15s directory: %.*s\n%s\n",
477 dirents[cnt]->d_name, (int) (enddir - buf), buf,
478 linebuf);
479
480 fd = open64 (buf, O_RDONLY);
481 if (fd != -1)
482 {
483 void *mapped = mmap64 (NULL, st.st_size, PROT_READ,
484 MAP_SHARED, fd, 0);
485 if (mapped != MAP_FAILED)
486 {
e98b69a6 487 print_LC_IDENTIFICATION (mapped, st.st_size);
fdc6c28a
UD
488
489 munmap (mapped, st.st_size);
490 }
491
492 close (fd);
493
494 /* Now try to get the charset information. */
495 strcpy (enddir, "/LC_CTYPE");
496 fd = open64 (buf, O_RDONLY);
497 if (fd != -1 && fstat64 (fd, &st) >= 0
498 && ((mapped = mmap64 (NULL, st.st_size, PROT_READ,
499 MAP_SHARED, fd, 0))
500 != MAP_FAILED))
501 {
e98b69a6 502 print_LC_CTYPE (mapped, st.st_size);
fdc6c28a
UD
503
504 munmap (mapped, st.st_size);
505 }
506
507 if (fd != -1)
508 close (fd);
509 }
510 }
e98b69a6
UD
511
512 /* If the verbose format is not selected we simply
513 collect the names. */
514 PUT (xstrdup (dirents[cnt]->d_name));
fdc6c28a
UD
515 }
516 }
517 if (ndirents > 0)
518 free (dirents);
91cd8340 519
06a466e7
UD
520 /* Now read the locale.alias files. */
521 if (argz_create_sep (LOCALE_ALIAS_PATH, ':', &alias_path, &alias_path_len))
522 error (1, errno, gettext ("while preparing output"));
523
524 entry = NULL;
525 while ((entry = argz_next (alias_path, alias_path_len, entry)))
91cd8340 526 {
06a466e7
UD
527 static const char aliasfile[] = "/locale.alias";
528 FILE *fp;
529 char full_name[strlen (entry) + sizeof aliasfile];
530
531 stpcpy (stpcpy (full_name, entry), aliasfile);
2e2dc1a5 532 fp = fopen (full_name, "rm");
06a466e7
UD
533 if (fp == NULL)
534 /* Ignore non-existing files. */
535 continue;
536
537 /* No threads present. */
538 __fsetlocking (fp, FSETLOCKING_BYCALLER);
91cd8340 539
06a466e7
UD
540 while (! feof_unlocked (fp))
541 {
542 /* It is a reasonable approach to use a fix buffer here
543 because
544 a) we are only interested in the first two fields
545 b) these fields must be usable as file names and so must
561470e0 546 not be that long */
06a466e7
UD
547 char buf[BUFSIZ];
548 char *alias;
549 char *value;
550 char *cp;
551
552 if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
553 /* EOF reached. */
554 break;
555
556 cp = buf;
557 /* Ignore leading white space. */
558 while (isspace (cp[0]) && cp[0] != '\n')
559 ++cp;
560
561 /* A leading '#' signals a comment line. */
562 if (cp[0] != '\0' && cp[0] != '#' && cp[0] != '\n')
563 {
564 alias = cp++;
565 while (cp[0] != '\0' && !isspace (cp[0]))
566 ++cp;
567 /* Terminate alias name. */
568 if (cp[0] != '\0')
569 *cp++ = '\0';
570
571 /* Now look for the beginning of the value. */
572 while (isspace (cp[0]))
573 ++cp;
574
575 if (cp[0] != '\0')
576 {
577 value = cp++;
578 while (cp[0] != '\0' && !isspace (cp[0]))
579 ++cp;
580 /* Terminate value. */
581 if (cp[0] == '\n')
582 {
583 /* This has to be done to make the following
584 test for the end of line possible. We are
585 looking for the terminating '\n' which do not
586 overwrite here. */
587 *cp++ = '\0';
588 *cp = '\n';
589 }
590 else if (cp[0] != '\0')
591 *cp++ = '\0';
592
593 /* Add the alias. */
594 if (! verbose && GET (value) != NULL)
595 PUT (xstrdup (alias));
596 }
597 }
598
599 /* Possibly not the whole line fits into the buffer.
600 Ignore the rest of the line. */
601 while (strchr (cp, '\n') == NULL)
602 {
603 cp = buf;
604 if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
605 /* Make sure the inner loop will be left. The outer
606 loop will exit at the `feof' test. */
607 *cp = '\n';
608 }
609 }
610
611 fclose (fp);
612 }
613
614 if (! verbose)
615 {
91cd8340
UD
616 twalk (all_data, print_names);
617 }
2b83a2a4
RM
618}
619
620
e98b69a6
UD
621struct nameent
622{
623 char *name;
624 uint32_t locrec_offset;
625};
626
627
628static int
629nameentcmp (const void *a, const void *b)
630{
631 return strcoll (((const struct nameent *) a)->name,
632 ((const struct nameent *) b)->name);
633}
634
635
636static int
637write_archive_locales (void **all_datap, char *linebuf)
638{
639 struct stat64 st;
640 void *all_data = *all_datap;
641 size_t len = 0;
642 struct locarhead *head;
643 struct namehashent *namehashtab;
644 char *addr = MAP_FAILED;
645 int fd, ret = 0;
646 uint32_t cnt;
647
648 fd = open64 (ARCHIVE_NAME, O_RDONLY);
649 if (fd < 0)
650 return 0;
651
652 if (fstat64 (fd, &st) < 0 || st.st_size < sizeof (*head))
653 goto error_out;
654
655 len = st.st_size;
656 addr = mmap64 (NULL, len, PROT_READ, MAP_SHARED, fd, 0);
657 if (addr == MAP_FAILED)
658 goto error_out;
659
660 head = (struct locarhead *) addr;
661 if (head->namehash_offset + head->namehash_size > len
662 || head->string_offset + head->string_size > len
663 || head->locrectab_offset + head->locrectab_size > len
664 || head->sumhash_offset + head->sumhash_size > len)
665 goto error_out;
666
667 namehashtab = (struct namehashent *) (addr + head->namehash_offset);
668 if (! verbose)
669 {
670 for (cnt = 0; cnt < head->namehash_size; ++cnt)
671 if (namehashtab[cnt].locrec_offset != 0)
672 {
673 PUT (xstrdup (addr + namehashtab[cnt].name_offset));
674 ++ret;
675 }
676 }
677 else
678 {
679 struct nameent *names;
680 uint32_t used;
681
682 names = (struct nameent *) xmalloc (head->namehash_used
683 * sizeof (struct nameent));
684 for (cnt = used = 0; cnt < head->namehash_size; ++cnt)
685 if (namehashtab[cnt].locrec_offset != 0)
686 {
687 names[used].name = addr + namehashtab[cnt].name_offset;
688 names[used++].locrec_offset = namehashtab[cnt].locrec_offset;
689 }
690
691 /* Sort the names. */
692 qsort (names, used, sizeof (struct nameent), nameentcmp);
693
694 for (cnt = 0; cnt < used; ++cnt)
695 {
696 struct locrecent *locrec;
697
698 PUT (xstrdup (names[cnt].name));
699
700 if (cnt)
701 putchar_unlocked ('\n');
702
703 printf ("locale: %-15.15s archive: " ARCHIVE_NAME "\n%s\n",
704 names[cnt].name, linebuf);
705
706 locrec = (struct locrecent *) (addr + names[cnt].locrec_offset);
707
708 print_LC_IDENTIFICATION (addr
709 + locrec->record[LC_IDENTIFICATION].offset,
710 locrec->record[LC_IDENTIFICATION].len);
711
712 print_LC_CTYPE (addr + locrec->record[LC_CTYPE].offset,
713 locrec->record[LC_CTYPE].len);
714 }
715
716 ret = used;
717 }
718
719error_out:
720 if (addr != MAP_FAILED)
721 munmap (addr, len);
722 close (fd);
723 *all_datap = all_data;
724 return ret;
725}
726
727
2b83a2a4
RM
728/* Write the names of all available character maps to stdout. */
729static void
730write_charmaps (void)
731{
1fb05e3d 732 void *all_data = NULL;
3e076219
UD
733 CHARMAP_DIR *dir;
734 const char *dirent;
2b83a2a4 735
3e076219
UD
736 /* Look for all files in the charmap directory. */
737 dir = charmap_opendir (CHARMAP_PATH);
2b83a2a4 738 if (dir == NULL)
3e076219 739 return;
51702635 740
3e076219
UD
741 while ((dirent = charmap_readdir (dir)) != NULL)
742 {
743 char **aliases;
744 char **p;
745
746 PUT (xstrdup (dirent));
747
748 aliases = charmap_aliases (CHARMAP_PATH, dirent);
749
750#if 0
751 /* Add the code_set_name and the aliases. */
752 for (p = aliases; *p; p++)
753 PUT (xstrdup (*p));
754#else
755 /* Add the code_set_name only. Most aliases are obsolete. */
756 p = aliases;
757 if (*p)
758 PUT (xstrdup (*p));
1fb05e3d 759#endif
1fb05e3d 760
3e076219
UD
761 charmap_free_aliases (aliases);
762 }
2b83a2a4 763
3e076219 764 charmap_closedir (dir);
1fb05e3d
UD
765
766 twalk (all_data, print_names);
2b83a2a4
RM
767}
768
02637374
AS
769/* Print a properly quoted assignment of NAME with VAL, using double
770 quotes iff DQUOTE is true. */
771static void
772print_assignment (const char *name, const char *val, bool dquote)
773{
774 printf ("%s=", name);
775 if (dquote)
776 putchar ('"');
777 while (*val != '\0')
778 {
779 size_t segment
780 = strcspn (val, dquote ? "$`\"\\" : "~|&;<>()$`\\\"' \t\n");
781 printf ("%.*s", (int) segment, val);
782 val += segment;
783 if (*val == '\0')
784 break;
785 putchar ('\\');
786 putchar (*val++);
787 }
788 if (dquote)
789 putchar ('"');
790 putchar ('\n');
791}
2b83a2a4
RM
792
793/* We have to show the contents of the environments determining the
794 locale. */
795static void
796show_locale_vars (void)
797{
a62b3c15
RM
798 const char *lcall = getenv ("LC_ALL") ?: "";
799 const char *lang = getenv ("LANG") ?: "";
2b83a2a4
RM
800
801 /* LANG has to be the first value. */
02637374 802 print_assignment ("LANG", lang, false);
2b83a2a4
RM
803
804 /* Now all categories in an unspecified order. */
a62b3c15 805 for (size_t cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
643e9936 806 if (cat_no != LC_ALL)
a62b3c15
RM
807 {
808 const char *name = category[cat_no].name;
809 const char *val = getenv (name);
810
811 if (lcall[0] != '\0' || val == NULL)
812 print_assignment (name,
813 lcall[0] != '\0' ? lcall
814 : lang[0] != '\0' ? lang
815 : "POSIX",
816 true);
817 else
818 print_assignment (name, val, false);
819 }
2b83a2a4
RM
820
821 /* The last is the LC_ALL value. */
02637374 822 print_assignment ("LC_ALL", lcall, false);
2b83a2a4
RM
823}
824
825
a62b3c15 826/* Subroutine of show_info, below. */
2b83a2a4 827static void
a62b3c15 828print_item (struct cat_item *item)
2b83a2a4 829{
a62b3c15 830 switch (item->value_type)
2b83a2a4 831 {
a62b3c15
RM
832 case string:
833 if (show_keyword_name)
834 printf ("%s=\"", item->name);
835 fputs (nl_langinfo (item->item_id) ? : "", stdout);
836 if (show_keyword_name)
837 putchar ('"');
838 putchar ('\n');
839 break;
840 case stringarray:
841 {
842 const char *val;
843 int cnt;
2b83a2a4 844
a62b3c15
RM
845 if (show_keyword_name)
846 printf ("%s=\"", item->name);
2b83a2a4 847
a62b3c15
RM
848 for (cnt = 0; cnt < item->max - 1; ++cnt)
849 {
2b83a2a4 850 val = nl_langinfo (item->item_id + cnt);
8f2ece69 851 if (val != NULL)
7535b2d9 852 fputs (val, stdout);
a62b3c15 853 putchar (';');
2b83a2a4 854 }
a62b3c15
RM
855
856 val = nl_langinfo (item->item_id + cnt);
857 if (val != NULL)
858 fputs (val, stdout);
859
860 if (show_keyword_name)
861 putchar ('"');
862 putchar ('\n');
863 }
864 break;
865 case stringlist:
866 {
867 int first = 1;
868 const char *val = nl_langinfo (item->item_id) ? : "";
869
870 if (show_keyword_name)
871 printf ("%s=", item->name);
872
873 for (int cnt = 0; cnt < item->max && *val != '\0'; ++cnt)
51702635 874 {
a62b3c15
RM
875 printf ("%s%s%s%s", first ? "" : ";",
876 show_keyword_name ? "\"" : "", val,
877 show_keyword_name ? "\"" : "");
878 val = strchr (val, '\0') + 1;
879 first = 0;
51702635 880 }
a62b3c15
RM
881 putchar ('\n');
882 }
883 break;
884 case byte:
885 {
886 const char *val = nl_langinfo (item->item_id);
2b83a2a4 887
a62b3c15
RM
888 if (show_keyword_name)
889 printf ("%s=", item->name);
beaaf574 890
a62b3c15
RM
891 if (val != NULL)
892 printf ("%d", *val == '\377' ? -1 : *val);
893 putchar ('\n');
894 }
895 break;
896 case bytearray:
897 {
898 const char *val = nl_langinfo (item->item_id);
899 int cnt = val ? strlen (val) : 0;
900
901 if (show_keyword_name)
902 printf ("%s=", item->name);
903
904 while (cnt > 1)
2b83a2a4 905 {
a62b3c15
RM
906 printf ("%d;", *val == '\177' ? -1 : *val);
907 --cnt;
908 ++val;
909 }
2b83a2a4 910
a62b3c15
RM
911 printf ("%d\n", cnt == 0 || *val == '\177' ? -1 : *val);
912 }
913 break;
914 case word:
915 {
916 union { unsigned int word; char *string; } val;
917 val.string = nl_langinfo (item->item_id);
918 if (show_keyword_name)
919 printf ("%s=", item->name);
beaaf574 920
a62b3c15
RM
921 printf ("%d\n", val.word);
922 }
923 break;
924 case wordarray:
925 {
926 int first = 1;
927 union { unsigned int *wordarray; char *string; } val;
2b83a2a4 928
a62b3c15
RM
929 val.string = nl_langinfo (item->item_id);
930 if (show_keyword_name)
931 printf ("%s=", item->name);
beaaf574 932
a62b3c15 933 for (int cnt = 0; cnt < item->max; ++cnt)
b5449b12 934 {
a62b3c15
RM
935 printf ("%s%d", first ? "" : ";", val.wordarray[cnt]);
936 first = 0;
b5449b12 937 }
a62b3c15
RM
938 putchar ('\n');
939 }
940 break;
941 case wstring:
942 case wstringarray:
943 case wstringlist:
944 /* We don't print wide character information since the same
945 information is available in a multibyte string. */
946 default:
947 break;
2b83a2a4 948 }
a62b3c15 949}
2b83a2a4 950
a62b3c15
RM
951/* Show the information request for NAME. */
952static void
953show_info (const char *name)
954{
955 for (size_t cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
92d2e18f
UD
956 if (cat_no != LC_ALL)
957 {
92d2e18f
UD
958 if (strcmp (name, category[cat_no].name) == 0)
959 /* Print the whole category. */
2b83a2a4
RM
960 {
961 if (show_category_name != 0)
962 puts (category[cat_no].name);
963
a62b3c15
RM
964 for (size_t item_no = 0;
965 item_no < category[cat_no].number;
966 ++item_no)
92d2e18f
UD
967 print_item (&category[cat_no].item_desc[item_no]);
968
2b83a2a4
RM
969 return;
970 }
92d2e18f 971
a62b3c15 972 for (size_t item_no = 0; item_no < category[cat_no].number; ++item_no)
92d2e18f
UD
973 if (strcmp (name, category[cat_no].item_desc[item_no].name) == 0)
974 {
975 if (show_category_name != 0)
976 puts (category[cat_no].name);
977
978 print_item (&category[cat_no].item_desc[item_no]);
979 return;
980 }
981 }
036cc82f 982
6d52618b
UD
983 /* The name is not a standard one.
984 For testing and perhaps advanced use allow some more symbols. */
036cc82f 985 locale_special (name, show_category_name, show_keyword_name);
2b83a2a4 986}
e485b2b6
FW
987
988/* Set to true by try_setlocale if setlocale fails. Used by
989 setlocale_diagnostics. */
990static bool setlocale_failed;
991
992/* Call setlocale, with non-fatal error reporting. */
993static void
994try_setlocale (int category, const char *category_name)
995{
996 if (setlocale (category, "") == NULL)
997 {
998 error (0, errno, gettext ("Cannot set %s to default locale"),
999 category_name);
1000 setlocale_failed = true;
1001 }
1002}
1003
1004/* Return a quoted version of the passed string, or NULL on error. */
1005static char *
1006quote_string (const char *input)
1007{
1008 char *buffer;
1009 size_t length;
1010 FILE *stream = open_memstream (&buffer, &length);
1011 if (stream == NULL)
1012 return NULL;
1013
1014 while (true)
1015 {
1016 unsigned char ch = *input++;
1017 if (ch == '\0')
1018 break;
1019
1020 /* Use C backslash escapes for those control characters for
1021 which they are defined. */
1022 switch (ch)
1023 {
1024 case '\a':
1025 putc_unlocked ('\\', stream);
1026 putc_unlocked ('a', stream);
1027 break;
1028 case '\b':
1029 putc_unlocked ('\\', stream);
1030 putc_unlocked ('b', stream);
1031 break;
1032 case '\f':
1033 putc_unlocked ('\\', stream);
1034 putc_unlocked ('f', stream);
1035 break;
1036 case '\n':
1037 putc_unlocked ('\\', stream);
1038 putc_unlocked ('n', stream);
1039 break;
1040 case '\r':
1041 putc_unlocked ('\\', stream);
1042 putc_unlocked ('r', stream);
1043 break;
1044 case '\t':
1045 putc_unlocked ('\\', stream);
1046 putc_unlocked ('t', stream);
1047 break;
1048 case '\v':
1049 putc_unlocked ('\\', stream);
1050 putc_unlocked ('v', stream);
1051 break;
1052 case '\\':
1053 case '\'':
1054 case '\"':
1055 putc_unlocked ('\\', stream);
1056 putc_unlocked (ch, stream);
1057 break;
1058 default:
1059 if (ch < ' ' || ch > '~')
1060 /* Use octal sequences because they are fixed width,
1061 unlike hexadecimal sequences. */
1062 fprintf (stream, "\\%03o", ch);
1063 else
1064 putc_unlocked (ch, stream);
1065 }
1066 }
1067
1068 if (ferror (stream))
1069 {
1070 fclose (stream);
1071 free (buffer);
1072 return NULL;
1073 }
1074 if (fclose (stream) != 0)
1075 {
1076 free (buffer);
1077 return NULL;
1078 }
1079
1080 return buffer;
1081}
1082
1083/* Print additional information if there was a setlocale error (during
1084 try_setlocale). */
1085static void
1086setlocale_diagnostics (void)
1087{
1088 if (setlocale_failed)
1089 {
1090 const char *locpath = getenv ("LOCPATH");
1091 if (locpath != NULL)
1092 {
1093 char *quoted = quote_string (locpath);
1094 if (quoted != NULL)
1095 fprintf (stderr,
1096 gettext ("\
1097warning: The LOCPATH variable is set to \"%s\"\n"),
1098 quoted);
1099 else
1100 fputs ("warning: The LOCPATH variable is set\n", stderr);
1101 free (quoted);
1102 }
1103 }
1104}