]> sourceware.org Git - glibc.git/blame - locale/programs/locale.c
io: Fix destructive nature of tst-fchmod-errors
[glibc.git] / locale / programs / locale.c
CommitLineData
5107cf1d 1/* Implementation of the locale program according to POSIX 9945-2.
dff8da6b 2 Copyright (C) 1995-2024 Free Software Foundation, Inc.
6d52618b 3 This file is part of the GNU C Library.
2b83a2a4 4
43bc8ac6 5 This program is free software; you can redistribute it and/or modify
2e2efe65
RM
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; version 2 of the License, or
8 (at your option) any later version.
2b83a2a4 9
43bc8ac6 10 This program is distributed in the hope that it will be useful,
6d52618b 11 but WITHOUT ANY WARRANTY; without even the implied warranty of
43bc8ac6
UD
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
2b83a2a4 14
43bc8ac6 15 You should have received a copy of the GNU General Public License
5a82c748 16 along with this program; if not, see <https://www.gnu.org/licenses/>. */
2b83a2a4 17
299a95b9
RM
18#ifdef HAVE_CONFIG_H
19# include <config.h>
20#endif
21
1fb05e3d
UD
22#include <argp.h>
23#include <argz.h>
2b83a2a4 24#include <dirent.h>
1fb05e3d 25#include <errno.h>
299a95b9 26#include <error.h>
91cd8340 27#include <fcntl.h>
2b83a2a4
RM
28#include <langinfo.h>
29#include <libintl.h>
30#include <limits.h>
31#include <locale.h>
1fb05e3d 32#include <search.h>
2b83a2a4 33#include <stdio.h>
06b5289f 34#include <stdio_ext.h>
2b83a2a4 35#include <stdlib.h>
a1470b6f 36#include <string.h>
91cd8340 37#include <unistd.h>
e054f494 38#include <stdint.h>
91cd8340 39#include <sys/mman.h>
1fb05e3d 40#include <sys/stat.h>
2b83a2a4 41
02eec681 42#include "record-status.h"
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
62/* Name and version of program. */
63static void print_version (FILE *stream, struct argp_state *state);
64void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
65
66/* Definitions of arguments for argp functions. */
5a97622d 67static const struct argp_option options[] =
19bc17a9 68{
5a97622d
UD
69 { NULL, 0, NULL, 0, N_("System information:") },
70 { "all-locales", 'a', NULL, OPTION_NO_USAGE,
71 N_("Write names of available locales") },
72 { "charmaps", 'm', NULL, OPTION_NO_USAGE,
73 N_("Write names of available charmaps") },
74 { NULL, 0, NULL, 0, N_("Modify output format:") },
75 { "category-name", 'c', NULL, 0, N_("Write names of selected categories") },
76 { "keyword-name", 'k', NULL, 0, N_("Write names of selected keywords") },
91cd8340 77 { "verbose", 'v', NULL, 0, N_("Print more information") },
1fb05e3d
UD
78 { NULL, 0, NULL, 0, NULL }
79};
80
81/* Short description of program. */
cbbcaf23 82static const char doc[] = N_("Get locale-specific information.");
1fb05e3d
UD
83
84/* Strings for arguments in help texts. */
5a97622d 85static const char args_doc[] = N_("NAME\n[-a|-m]");
1fb05e3d
UD
86
87/* Prototype for option handler. */
88static error_t parse_opt (int key, char *arg, struct argp_state *state);
89
cbbcaf23
UD
90/* Function to print some extra text in the help message. */
91static char *more_help (int key, const char *text, void *input);
92
1fb05e3d
UD
93/* Data structure to communicate with argp functions. */
94static struct argp argp =
95{
cbbcaf23 96 options, parse_opt, args_doc, doc, NULL, more_help
19bc17a9 97};
2b83a2a4
RM
98
99
100/* We don't have these constants defined because we don't use them. Give
101 default values. */
102#define CTYPE_MB_CUR_MIN 0
103#define CTYPE_MB_CUR_MAX 0
104#define CTYPE_HASH_SIZE 0
105#define CTYPE_HASH_LAYERS 0
106#define CTYPE_CLASS 0
107#define CTYPE_TOUPPER_EB 0
108#define CTYPE_TOLOWER_EB 0
109#define CTYPE_TOUPPER_EL 0
110#define CTYPE_TOLOWER_EL 0
19bc17a9 111
299a95b9
RM
112/* Definition of the data structure which represents a category and its
113 items. */
114struct category
19bc17a9 115{
299a95b9 116 int cat_id;
19bc17a9 117 const char *name;
299a95b9
RM
118 size_t number;
119 struct cat_item
120 {
121 int item_id;
122 const char *name;
123 enum { std, opt } status;
124 enum value_type value_type;
125 int min;
126 int max;
127 } *item_desc;
19bc17a9
RM
128};
129
299a95b9
RM
130/* Simple helper macro. */
131#define NELEMS(arr) ((sizeof (arr)) / (sizeof (arr[0])))
132
133/* For some tricky stuff. */
134#define NO_PAREN(Item, More...) Item, ## More
2b83a2a4
RM
135
136/* We have all categories defined in `categories.def'. Now construct
137 the description and data structure used for all categories. */
299a95b9 138#define DEFINE_ELEMENT(Item, More...) { Item, ## More },
4b10dd6c 139#define DEFINE_CATEGORY(category, name, items, postload) \
2b83a2a4
RM
140 static struct cat_item category##_desc[] = \
141 { \
561470e0 142 NO_PAREN items \
2b83a2a4
RM
143 };
144
299a95b9 145#include "categories.def"
2b83a2a4
RM
146#undef DEFINE_CATEGORY
147
148static struct category category[] =
149 {
4b10dd6c 150#define DEFINE_CATEGORY(category, name, items, postload) \
036cc82f
RM
151 [category] = { _NL_NUM_##category, name, NELEMS (category##_desc), \
152 category##_desc },
299a95b9 153#include "categories.def"
2b83a2a4
RM
154#undef DEFINE_CATEGORY
155 };
156#define NCATEGORIES NELEMS (category)
157
158
036cc82f
RM
159/* Automatically set variable. */
160extern const char *__progname;
161
162/* helper function for extended name handling. */
163extern void locale_special (const char *name, int show_category_name,
164 int show_keyword_name);
165
2b83a2a4 166/* Prototypes for local functions. */
e98b69a6
UD
167static void print_LC_IDENTIFICATION (void *mapped, size_t size);
168static void print_LC_CTYPE (void *mapped, size_t size);
2b83a2a4 169static void write_locales (void);
e98b69a6
UD
170static int nameentcmp (const void *a, const void *b);
171static int write_archive_locales (void **all_datap, char *linebuf);
2b83a2a4
RM
172static void write_charmaps (void);
173static void show_locale_vars (void);
174static void show_info (const char *name);
e485b2b6
FW
175static void try_setlocale (int category, const char *category_name);
176static char *quote_string (const char *input);
177static void setlocale_diagnostics (void);
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). */
e485b2b6
FW
191 try_setlocale (LC_CTYPE, "LC_CTYPE");
192 try_setlocale (LC_MESSAGES, "LC_MESSAGES");
2b83a2a4
RM
193
194 /* Initialize the message catalog. */
195 textdomain (PACKAGE);
196
1fb05e3d 197 /* Parse and process arguments. */
2f6d1f1b 198 argp_parse (&argp, argc, argv, 0, &remaining, NULL);
2b83a2a4 199
2b83a2a4
RM
200 /* `-a' requests the names of all available locales. */
201 if (do_all != 0)
202 {
e485b2b6
FW
203 setlocale_diagnostics ();
204 try_setlocale (LC_COLLATE, "LC_COLLATE");
2b83a2a4
RM
205 write_locales ();
206 exit (EXIT_SUCCESS);
207 }
208
209 /* `m' requests the names of all available charmaps. The names can be
8f2ece69 210 used for the -f argument to localedef(1). */
2b83a2a4
RM
211 if (do_charmaps != 0)
212 {
e485b2b6 213 setlocale_diagnostics ();
2b83a2a4
RM
214 write_charmaps ();
215 exit (EXIT_SUCCESS);
216 }
217
76060ec0
RM
218 /* Specific information about the current locale are requested.
219 Change to this locale now. */
e485b2b6
FW
220 try_setlocale (LC_ALL, "LC_ALL");
221 setlocale_diagnostics ();
76060ec0 222
2b83a2a4
RM
223 /* If no real argument is given we have to print the contents of the
224 current locale definition variables. These are LANG and the LC_*. */
2f6d1f1b 225 if (remaining == argc && show_keyword_name == 0 && show_category_name == 0)
2b83a2a4
RM
226 {
227 show_locale_vars ();
228 exit (EXIT_SUCCESS);
229 }
230
231 /* Process all given names. */
2f6d1f1b
UD
232 while (remaining < argc)
233 show_info (argv[remaining++]);
2b83a2a4
RM
234
235 exit (EXIT_SUCCESS);
236}
237
238
1fb05e3d
UD
239/* Handle program arguments. */
240static error_t
241parse_opt (int key, char *arg, struct argp_state *state)
2b83a2a4 242{
1fb05e3d 243 switch (key)
fafaa44e 244 {
1fb05e3d
UD
245 case 'a':
246 do_all = 1;
247 break;
248 case 'c':
249 show_category_name = 1;
250 break;
251 case 'm':
252 do_charmaps = 1;
253 break;
254 case 'k':
255 show_keyword_name = 1;
256 break;
91cd8340
UD
257 case 'v':
258 verbose = 1;
259 break;
1fb05e3d
UD
260 default:
261 return ARGP_ERR_UNKNOWN;
fafaa44e 262 }
1fb05e3d
UD
263 return 0;
264}
265
2b83a2a4 266
cbbcaf23
UD
267static char *
268more_help (int key, const char *text, void *input)
269{
8b748aed 270 char *tp = NULL;
cbbcaf23
UD
271 switch (key)
272 {
273 case ARGP_KEY_HELP_EXTRA:
274 /* We print some extra information. */
8b748aed 275 if (asprintf (&tp, gettext ("\
cbbcaf23 276For bug reporting instructions, please see:\n\
8b748aed
JM
277%s.\n"), REPORT_BUGS_TO) < 0)
278 return NULL;
279 return tp;
cbbcaf23
UD
280 default:
281 break;
282 }
283 return (char *) text;
284}
285
286
1fb05e3d
UD
287/* Print the version information. */
288static void
289print_version (FILE *stream, struct argp_state *state)
290{
8b748aed 291 fprintf (stream, "locale %s%s\n", PKGVERSION, VERSION);
1fb05e3d
UD
292 fprintf (stream, gettext ("\
293Copyright (C) %s Free Software Foundation, Inc.\n\
294This is free software; see the source for copying conditions. There is NO\n\
295warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
1059defe 296"), "2024");
1fb05e3d 297 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
2b83a2a4
RM
298}
299
300
1fb05e3d
UD
301/* Simple action function which prints arguments as strings. */
302static void
303print_names (const void *nodep, VISIT value, int level)
304{
305 if (value == postorder || value == leaf)
306 puts (*(char **) nodep);
307}
308
309
fdc6c28a
UD
310static int
311select_dirs (const struct dirent *dirent)
312{
313 int result = 0;
314
315 if (strcmp (dirent->d_name, ".") != 0 && strcmp (dirent->d_name, "..") != 0)
316 {
317 mode_t mode = 0;
318
fdc6c28a
UD
319 if (dirent->d_type != DT_UNKNOWN && dirent->d_type != DT_LNK)
320 mode = DTTOIF (dirent->d_type);
321 else
fdc6c28a
UD
322 {
323 struct stat64 st;
90fe682d
CD
324 char buf[sizeof (COMPLOCALEDIR)
325 + strlen (dirent->d_name) + 1];
fdc6c28a 326
90fe682d
CD
327 stpcpy (stpcpy (stpcpy (buf, COMPLOCALEDIR), "/"),
328 dirent->d_name);
fdc6c28a
UD
329
330 if (stat64 (buf, &st) == 0)
331 mode = st.st_mode;
332 }
333
334 result = S_ISDIR (mode);
335 }
336
337 return result;
338}
339
340
e98b69a6
UD
341static void
342print_LC_IDENTIFICATION (void *mapped, size_t size)
343{
344 /* Read the information from the file. */
345 struct
346 {
347 unsigned int magic;
348 unsigned int nstrings;
349 unsigned int strindex[0];
350 } *filedata = mapped;
351
352 if (filedata->magic == LIMAGIC (LC_IDENTIFICATION)
353 && (sizeof *filedata
354 + (filedata->nstrings
355 * sizeof (unsigned int))
356 <= size))
357 {
358 const char *str;
359
360#define HANDLE(idx, name) \
361 str = ((char *) mapped \
362 + filedata->strindex[_NL_ITEM_INDEX (_NL_IDENTIFICATION_##idx)]); \
363 if (*str != '\0') \
364 printf ("%9s | %s\n", name, str)
365 HANDLE (TITLE, "title");
366 HANDLE (SOURCE, "source");
367 HANDLE (ADDRESS, "address");
368 HANDLE (CONTACT, "contact");
369 HANDLE (EMAIL, "email");
370 HANDLE (TEL, "telephone");
371 HANDLE (FAX, "fax");
372 HANDLE (LANGUAGE, "language");
373 HANDLE (TERRITORY, "territory");
374 HANDLE (AUDIENCE, "audience");
375 HANDLE (APPLICATION, "application");
376 HANDLE (ABBREVIATION, "abbreviation");
377 HANDLE (REVISION, "revision");
378 HANDLE (DATE, "date");
379 }
380}
381
382
383static void
384print_LC_CTYPE (void *mapped, size_t size)
385{
386 struct
387 {
388 unsigned int magic;
389 unsigned int nstrings;
390 unsigned int strindex[0];
391 } *filedata = mapped;
392
393 if (filedata->magic == LIMAGIC (LC_CTYPE)
394 && (sizeof *filedata
395 + (filedata->nstrings
396 * sizeof (unsigned int))
397 <= size))
398 {
399 const char *str;
400
401 str = ((char *) mapped
402 + filedata->strindex[_NL_ITEM_INDEX (_NL_CTYPE_CODESET_NAME)]);
403 if (*str != '\0')
404 printf (" codeset | %s\n", str);
405 }
406}
407
408
1fb05e3d
UD
409/* Write the names of all available locales to stdout. We have some
410 sources of the information: the contents of the locale directory
411 and the locale.alias file. To avoid duplicates and print the
412 result is a reasonable order we put all entries is a search tree
413 and print them afterwards. */
2b83a2a4
RM
414static void
415write_locales (void)
416{
91cd8340 417 char linebuf[80];
1fb05e3d 418 void *all_data = NULL;
fdc6c28a
UD
419 struct dirent **dirents;
420 int ndirents;
421 int cnt;
1fb05e3d
UD
422 char *alias_path;
423 size_t alias_path_len;
424 char *entry;
91cd8340 425 int first_locale = 1;
2b83a2a4 426
91cd8340 427#define PUT(name) tsearch (name, &all_data, \
1fb05e3d 428 (int (*) (const void *, const void *)) strcoll)
06a466e7
UD
429#define GET(name) tfind (name, &all_data, \
430 (int (*) (const void *, const void *)) strcoll)
2b83a2a4 431
06a466e7
UD
432 /* `POSIX' locale is always available (POSIX.2 4.34.3). */
433 PUT ("POSIX");
434 /* And so is the "C" locale. */
435 PUT ("C");
1fb05e3d 436
91cd8340
UD
437 memset (linebuf, '-', sizeof (linebuf) - 1);
438 linebuf[sizeof (linebuf) - 1] = '\0';
439
e98b69a6
UD
440 /* First scan the locale archive. */
441 if (write_archive_locales (&all_data, linebuf))
442 first_locale = 0;
443
91cd8340 444 /* Now we can look for all files in the directory. */
90fe682d
CD
445 ndirents = scandir (COMPLOCALEDIR, &dirents, select_dirs,
446 alphasort);
fdc6c28a
UD
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. */
90fe682d
CD
451 char buf[sizeof (COMPLOCALEDIR)
452 + strlen (dirents[cnt]->d_name)
453 + sizeof "/LC_IDENTIFICATION"];
fdc6c28a
UD
454 char *enddir;
455 struct stat64 st;
456
90fe682d
CD
457 stpcpy (enddir = stpcpy (stpcpy (stpcpy (buf,
458 COMPLOCALEDIR),
459 "/"),
fdc6c28a
UD
460 dirents[cnt]->d_name),
461 "/LC_IDENTIFICATION");
462
463 if (stat64 (buf, &st) == 0 && S_ISREG (st.st_mode))
464 {
e98b69a6 465 if (verbose && GET (dirents[cnt]->d_name) == NULL)
fdc6c28a
UD
466 {
467 /* Provide some nice output of all kinds of
468 information. */
469 int fd;
91cd8340 470
fdc6c28a
UD
471 if (! first_locale)
472 putchar_unlocked ('\n');
473 first_locale = 0;
91cd8340 474
fdc6c28a
UD
475 printf ("locale: %-15.15s directory: %.*s\n%s\n",
476 dirents[cnt]->d_name, (int) (enddir - buf), buf,
477 linebuf);
478
479 fd = open64 (buf, O_RDONLY);
480 if (fd != -1)
481 {
482 void *mapped = mmap64 (NULL, st.st_size, PROT_READ,
483 MAP_SHARED, fd, 0);
484 if (mapped != MAP_FAILED)
485 {
e98b69a6 486 print_LC_IDENTIFICATION (mapped, st.st_size);
fdc6c28a
UD
487
488 munmap (mapped, st.st_size);
489 }
490
491 close (fd);
492
493 /* Now try to get the charset information. */
494 strcpy (enddir, "/LC_CTYPE");
495 fd = open64 (buf, O_RDONLY);
496 if (fd != -1 && fstat64 (fd, &st) >= 0
497 && ((mapped = mmap64 (NULL, st.st_size, PROT_READ,
498 MAP_SHARED, fd, 0))
499 != MAP_FAILED))
500 {
e98b69a6 501 print_LC_CTYPE (mapped, st.st_size);
fdc6c28a
UD
502
503 munmap (mapped, st.st_size);
504 }
505
506 if (fd != -1)
507 close (fd);
508 }
509 }
e98b69a6
UD
510
511 /* If the verbose format is not selected we simply
512 collect the names. */
513 PUT (xstrdup (dirents[cnt]->d_name));
fdc6c28a
UD
514 }
515 }
516 if (ndirents > 0)
517 free (dirents);
91cd8340 518
06a466e7
UD
519 /* Now read the locale.alias files. */
520 if (argz_create_sep (LOCALE_ALIAS_PATH, ':', &alias_path, &alias_path_len))
521 error (1, errno, gettext ("while preparing output"));
522
523 entry = NULL;
524 while ((entry = argz_next (alias_path, alias_path_len, entry)))
91cd8340 525 {
06a466e7
UD
526 static const char aliasfile[] = "/locale.alias";
527 FILE *fp;
528 char full_name[strlen (entry) + sizeof aliasfile];
529
530 stpcpy (stpcpy (full_name, entry), aliasfile);
2e2dc1a5 531 fp = fopen (full_name, "rm");
06a466e7
UD
532 if (fp == NULL)
533 /* Ignore non-existing files. */
534 continue;
535
536 /* No threads present. */
537 __fsetlocking (fp, FSETLOCKING_BYCALLER);
91cd8340 538
06a466e7
UD
539 while (! feof_unlocked (fp))
540 {
541 /* It is a reasonable approach to use a fix buffer here
542 because
543 a) we are only interested in the first two fields
544 b) these fields must be usable as file names and so must
561470e0 545 not be that long */
06a466e7
UD
546 char buf[BUFSIZ];
547 char *alias;
548 char *value;
549 char *cp;
550
551 if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
552 /* EOF reached. */
553 break;
554
555 cp = buf;
556 /* Ignore leading white space. */
557 while (isspace (cp[0]) && cp[0] != '\n')
558 ++cp;
559
560 /* A leading '#' signals a comment line. */
561 if (cp[0] != '\0' && cp[0] != '#' && cp[0] != '\n')
562 {
563 alias = cp++;
564 while (cp[0] != '\0' && !isspace (cp[0]))
565 ++cp;
566 /* Terminate alias name. */
567 if (cp[0] != '\0')
568 *cp++ = '\0';
569
570 /* Now look for the beginning of the value. */
571 while (isspace (cp[0]))
572 ++cp;
573
574 if (cp[0] != '\0')
575 {
576 value = cp++;
577 while (cp[0] != '\0' && !isspace (cp[0]))
578 ++cp;
579 /* Terminate value. */
580 if (cp[0] == '\n')
581 {
582 /* This has to be done to make the following
583 test for the end of line possible. We are
584 looking for the terminating '\n' which do not
585 overwrite here. */
586 *cp++ = '\0';
587 *cp = '\n';
588 }
589 else if (cp[0] != '\0')
590 *cp++ = '\0';
591
592 /* Add the alias. */
593 if (! verbose && GET (value) != NULL)
594 PUT (xstrdup (alias));
595 }
596 }
597
598 /* Possibly not the whole line fits into the buffer.
599 Ignore the rest of the line. */
600 while (strchr (cp, '\n') == NULL)
601 {
602 cp = buf;
603 if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
604 /* Make sure the inner loop will be left. The outer
605 loop will exit at the `feof' test. */
606 *cp = '\n';
607 }
608 }
609
610 fclose (fp);
611 }
612
613 if (! verbose)
614 {
91cd8340
UD
615 twalk (all_data, print_names);
616 }
2b83a2a4
RM
617}
618
619
e98b69a6
UD
620struct nameent
621{
622 char *name;
623 uint32_t locrec_offset;
624};
625
626
627static int
628nameentcmp (const void *a, const void *b)
629{
630 return strcoll (((const struct nameent *) a)->name,
631 ((const struct nameent *) b)->name);
632}
633
634
635static int
636write_archive_locales (void **all_datap, char *linebuf)
637{
638 struct stat64 st;
639 void *all_data = *all_datap;
640 size_t len = 0;
641 struct locarhead *head;
642 struct namehashent *namehashtab;
643 char *addr = MAP_FAILED;
644 int fd, ret = 0;
645 uint32_t cnt;
646
647 fd = open64 (ARCHIVE_NAME, O_RDONLY);
648 if (fd < 0)
649 return 0;
650
651 if (fstat64 (fd, &st) < 0 || st.st_size < sizeof (*head))
652 goto error_out;
653
654 len = st.st_size;
655 addr = mmap64 (NULL, len, PROT_READ, MAP_SHARED, fd, 0);
656 if (addr == MAP_FAILED)
657 goto error_out;
658
659 head = (struct locarhead *) addr;
660 if (head->namehash_offset + head->namehash_size > len
661 || head->string_offset + head->string_size > len
662 || head->locrectab_offset + head->locrectab_size > len
663 || head->sumhash_offset + head->sumhash_size > len)
664 goto error_out;
665
666 namehashtab = (struct namehashent *) (addr + head->namehash_offset);
667 if (! verbose)
668 {
669 for (cnt = 0; cnt < head->namehash_size; ++cnt)
670 if (namehashtab[cnt].locrec_offset != 0)
671 {
672 PUT (xstrdup (addr + namehashtab[cnt].name_offset));
673 ++ret;
674 }
675 }
676 else
677 {
678 struct nameent *names;
679 uint32_t used;
680
681 names = (struct nameent *) xmalloc (head->namehash_used
682 * sizeof (struct nameent));
683 for (cnt = used = 0; cnt < head->namehash_size; ++cnt)
684 if (namehashtab[cnt].locrec_offset != 0)
685 {
686 names[used].name = addr + namehashtab[cnt].name_offset;
687 names[used++].locrec_offset = namehashtab[cnt].locrec_offset;
688 }
689
690 /* Sort the names. */
691 qsort (names, used, sizeof (struct nameent), nameentcmp);
692
693 for (cnt = 0; cnt < used; ++cnt)
694 {
695 struct locrecent *locrec;
696
697 PUT (xstrdup (names[cnt].name));
698
699 if (cnt)
700 putchar_unlocked ('\n');
701
702 printf ("locale: %-15.15s archive: " ARCHIVE_NAME "\n%s\n",
703 names[cnt].name, linebuf);
704
705 locrec = (struct locrecent *) (addr + names[cnt].locrec_offset);
706
707 print_LC_IDENTIFICATION (addr
708 + locrec->record[LC_IDENTIFICATION].offset,
709 locrec->record[LC_IDENTIFICATION].len);
710
711 print_LC_CTYPE (addr + locrec->record[LC_CTYPE].offset,
712 locrec->record[LC_CTYPE].len);
713 }
714
213573f8
SP
715 free (names);
716
e98b69a6
UD
717 ret = used;
718 }
719
720error_out:
721 if (addr != MAP_FAILED)
722 munmap (addr, len);
723 close (fd);
724 *all_datap = all_data;
725 return ret;
726}
727
728
2b83a2a4
RM
729/* Write the names of all available character maps to stdout. */
730static void
731write_charmaps (void)
732{
1fb05e3d 733 void *all_data = NULL;
3e076219
UD
734 CHARMAP_DIR *dir;
735 const char *dirent;
2b83a2a4 736
3e076219
UD
737 /* Look for all files in the charmap directory. */
738 dir = charmap_opendir (CHARMAP_PATH);
2b83a2a4 739 if (dir == NULL)
3e076219 740 return;
51702635 741
3e076219
UD
742 while ((dirent = charmap_readdir (dir)) != NULL)
743 {
744 char **aliases;
745 char **p;
746
747 PUT (xstrdup (dirent));
748
749 aliases = charmap_aliases (CHARMAP_PATH, dirent);
750
751#if 0
752 /* Add the code_set_name and the aliases. */
753 for (p = aliases; *p; p++)
754 PUT (xstrdup (*p));
755#else
756 /* Add the code_set_name only. Most aliases are obsolete. */
757 p = aliases;
758 if (*p)
759 PUT (xstrdup (*p));
1fb05e3d 760#endif
1fb05e3d 761
3e076219
UD
762 charmap_free_aliases (aliases);
763 }
2b83a2a4 764
3e076219 765 charmap_closedir (dir);
1fb05e3d
UD
766
767 twalk (all_data, print_names);
2b83a2a4
RM
768}
769
02637374
AS
770/* Print a properly quoted assignment of NAME with VAL, using double
771 quotes iff DQUOTE is true. */
772static void
773print_assignment (const char *name, const char *val, bool dquote)
774{
775 printf ("%s=", name);
776 if (dquote)
777 putchar ('"');
778 while (*val != '\0')
779 {
780 size_t segment
781 = strcspn (val, dquote ? "$`\"\\" : "~|&;<>()$`\\\"' \t\n");
782 printf ("%.*s", (int) segment, val);
783 val += segment;
784 if (*val == '\0')
785 break;
786 putchar ('\\');
787 putchar (*val++);
788 }
789 if (dquote)
790 putchar ('"');
791 putchar ('\n');
792}
2b83a2a4
RM
793
794/* We have to show the contents of the environments determining the
795 locale. */
796static void
797show_locale_vars (void)
798{
a62b3c15
RM
799 const char *lcall = getenv ("LC_ALL") ?: "";
800 const char *lang = getenv ("LANG") ?: "";
2b83a2a4
RM
801
802 /* LANG has to be the first value. */
02637374 803 print_assignment ("LANG", lang, false);
2b83a2a4
RM
804
805 /* Now all categories in an unspecified order. */
a62b3c15 806 for (size_t cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
643e9936 807 if (cat_no != LC_ALL)
a62b3c15
RM
808 {
809 const char *name = category[cat_no].name;
810 const char *val = getenv (name);
811
812 if (lcall[0] != '\0' || val == NULL)
813 print_assignment (name,
814 lcall[0] != '\0' ? lcall
815 : lang[0] != '\0' ? lang
816 : "POSIX",
817 true);
818 else
819 print_assignment (name, val, false);
820 }
2b83a2a4
RM
821
822 /* The last is the LC_ALL value. */
02637374 823 print_assignment ("LC_ALL", lcall, false);
2b83a2a4
RM
824}
825
826
a62b3c15 827/* Subroutine of show_info, below. */
2b83a2a4 828static void
a62b3c15 829print_item (struct cat_item *item)
2b83a2a4 830{
a62b3c15 831 switch (item->value_type)
2b83a2a4 832 {
a62b3c15
RM
833 case string:
834 if (show_keyword_name)
835 printf ("%s=\"", item->name);
836 fputs (nl_langinfo (item->item_id) ? : "", stdout);
837 if (show_keyword_name)
838 putchar ('"');
839 putchar ('\n');
840 break;
841 case stringarray:
842 {
843 const char *val;
844 int cnt;
2b83a2a4 845
a62b3c15
RM
846 if (show_keyword_name)
847 printf ("%s=\"", item->name);
2b83a2a4 848
a62b3c15
RM
849 for (cnt = 0; cnt < item->max - 1; ++cnt)
850 {
2b83a2a4 851 val = nl_langinfo (item->item_id + cnt);
8f2ece69 852 if (val != NULL)
7535b2d9 853 fputs (val, stdout);
a62b3c15 854 putchar (';');
2b83a2a4 855 }
a62b3c15
RM
856
857 val = nl_langinfo (item->item_id + cnt);
858 if (val != NULL)
859 fputs (val, stdout);
860
861 if (show_keyword_name)
862 putchar ('"');
863 putchar ('\n');
864 }
865 break;
866 case stringlist:
867 {
868 int first = 1;
869 const char *val = nl_langinfo (item->item_id) ? : "";
870
871 if (show_keyword_name)
872 printf ("%s=", item->name);
873
874 for (int cnt = 0; cnt < item->max && *val != '\0'; ++cnt)
51702635 875 {
a62b3c15
RM
876 printf ("%s%s%s%s", first ? "" : ";",
877 show_keyword_name ? "\"" : "", val,
878 show_keyword_name ? "\"" : "");
879 val = strchr (val, '\0') + 1;
880 first = 0;
51702635 881 }
a62b3c15
RM
882 putchar ('\n');
883 }
884 break;
885 case byte:
886 {
887 const char *val = nl_langinfo (item->item_id);
2b83a2a4 888
a62b3c15
RM
889 if (show_keyword_name)
890 printf ("%s=", item->name);
beaaf574 891
a62b3c15
RM
892 if (val != NULL)
893 printf ("%d", *val == '\377' ? -1 : *val);
894 putchar ('\n');
895 }
896 break;
897 case bytearray:
898 {
899 const char *val = nl_langinfo (item->item_id);
900 int cnt = val ? strlen (val) : 0;
901
902 if (show_keyword_name)
903 printf ("%s=", item->name);
904
905 while (cnt > 1)
2b83a2a4 906 {
a62b3c15
RM
907 printf ("%d;", *val == '\177' ? -1 : *val);
908 --cnt;
909 ++val;
910 }
2b83a2a4 911
a62b3c15
RM
912 printf ("%d\n", cnt == 0 || *val == '\177' ? -1 : *val);
913 }
914 break;
915 case word:
916 {
917 union { unsigned int word; char *string; } val;
918 val.string = nl_langinfo (item->item_id);
919 if (show_keyword_name)
920 printf ("%s=", item->name);
beaaf574 921
a62b3c15
RM
922 printf ("%d\n", val.word);
923 }
924 break;
925 case wordarray:
926 {
927 int first = 1;
928 union { unsigned int *wordarray; char *string; } val;
2b83a2a4 929
a62b3c15
RM
930 val.string = nl_langinfo (item->item_id);
931 if (show_keyword_name)
932 printf ("%s=", item->name);
beaaf574 933
a62b3c15 934 for (int cnt = 0; cnt < item->max; ++cnt)
b5449b12 935 {
a62b3c15
RM
936 printf ("%s%d", first ? "" : ";", val.wordarray[cnt]);
937 first = 0;
b5449b12 938 }
a62b3c15
RM
939 putchar ('\n');
940 }
941 break;
942 case wstring:
943 case wstringarray:
944 case wstringlist:
945 /* We don't print wide character information since the same
946 information is available in a multibyte string. */
947 default:
948 break;
2b83a2a4 949 }
a62b3c15 950}
2b83a2a4 951
a62b3c15
RM
952/* Show the information request for NAME. */
953static void
954show_info (const char *name)
955{
956 for (size_t cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
92d2e18f
UD
957 if (cat_no != LC_ALL)
958 {
92d2e18f
UD
959 if (strcmp (name, category[cat_no].name) == 0)
960 /* Print the whole category. */
2b83a2a4
RM
961 {
962 if (show_category_name != 0)
963 puts (category[cat_no].name);
964
a62b3c15
RM
965 for (size_t item_no = 0;
966 item_no < category[cat_no].number;
967 ++item_no)
92d2e18f
UD
968 print_item (&category[cat_no].item_desc[item_no]);
969
2b83a2a4
RM
970 return;
971 }
92d2e18f 972
a62b3c15 973 for (size_t item_no = 0; item_no < category[cat_no].number; ++item_no)
92d2e18f
UD
974 if (strcmp (name, category[cat_no].item_desc[item_no].name) == 0)
975 {
976 if (show_category_name != 0)
977 puts (category[cat_no].name);
978
979 print_item (&category[cat_no].item_desc[item_no]);
980 return;
981 }
982 }
036cc82f 983
6d52618b
UD
984 /* The name is not a standard one.
985 For testing and perhaps advanced use allow some more symbols. */
036cc82f 986 locale_special (name, show_category_name, show_keyword_name);
2b83a2a4 987}
e485b2b6
FW
988
989/* Set to true by try_setlocale if setlocale fails. Used by
990 setlocale_diagnostics. */
991static bool setlocale_failed;
992
993/* Call setlocale, with non-fatal error reporting. */
994static void
995try_setlocale (int category, const char *category_name)
996{
997 if (setlocale (category, "") == NULL)
998 {
999 error (0, errno, gettext ("Cannot set %s to default locale"),
1000 category_name);
1001 setlocale_failed = true;
1002 }
1003}
1004
1005/* Return a quoted version of the passed string, or NULL on error. */
1006static char *
1007quote_string (const char *input)
1008{
1009 char *buffer;
1010 size_t length;
1011 FILE *stream = open_memstream (&buffer, &length);
1012 if (stream == NULL)
1013 return NULL;
1014
1015 while (true)
1016 {
1017 unsigned char ch = *input++;
1018 if (ch == '\0')
1019 break;
1020
1021 /* Use C backslash escapes for those control characters for
1022 which they are defined. */
1023 switch (ch)
1024 {
1025 case '\a':
1026 putc_unlocked ('\\', stream);
1027 putc_unlocked ('a', stream);
1028 break;
1029 case '\b':
1030 putc_unlocked ('\\', stream);
1031 putc_unlocked ('b', stream);
1032 break;
1033 case '\f':
1034 putc_unlocked ('\\', stream);
1035 putc_unlocked ('f', stream);
1036 break;
1037 case '\n':
1038 putc_unlocked ('\\', stream);
1039 putc_unlocked ('n', stream);
1040 break;
1041 case '\r':
1042 putc_unlocked ('\\', stream);
1043 putc_unlocked ('r', stream);
1044 break;
1045 case '\t':
1046 putc_unlocked ('\\', stream);
1047 putc_unlocked ('t', stream);
1048 break;
1049 case '\v':
1050 putc_unlocked ('\\', stream);
1051 putc_unlocked ('v', stream);
1052 break;
1053 case '\\':
1054 case '\'':
1055 case '\"':
1056 putc_unlocked ('\\', stream);
1057 putc_unlocked (ch, stream);
1058 break;
1059 default:
1060 if (ch < ' ' || ch > '~')
1061 /* Use octal sequences because they are fixed width,
1062 unlike hexadecimal sequences. */
1063 fprintf (stream, "\\%03o", ch);
1064 else
1065 putc_unlocked (ch, stream);
1066 }
1067 }
1068
1069 if (ferror (stream))
1070 {
1071 fclose (stream);
1072 free (buffer);
1073 return NULL;
1074 }
1075 if (fclose (stream) != 0)
1076 {
1077 free (buffer);
1078 return NULL;
1079 }
1080
1081 return buffer;
1082}
1083
1084/* Print additional information if there was a setlocale error (during
1085 try_setlocale). */
1086static void
1087setlocale_diagnostics (void)
1088{
1089 if (setlocale_failed)
1090 {
1091 const char *locpath = getenv ("LOCPATH");
1092 if (locpath != NULL)
1093 {
1094 char *quoted = quote_string (locpath);
1095 if (quoted != NULL)
1096 fprintf (stderr,
1097 gettext ("\
1098warning: The LOCPATH variable is set to \"%s\"\n"),
1099 quoted);
1100 else
1101 fputs ("warning: The LOCPATH variable is set\n", stderr);
1102 free (quoted);
1103 }
1104 }
1105}
This page took 0.768917 seconds and 6 git commands to generate.