hurd: Fix build
[glibc.git] / locale / programs / localedef.c
blobd718d2e9f47382bc0743da62bd5b0134042ac7ef
1 /* Copyright (C) 1995-2018 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1995.
5 This program is free software; you can redistribute it and/or modify
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.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, see <http://www.gnu.org/licenses/>. */
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
22 #include <argp.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <libintl.h>
26 #include <locale.h>
27 #include <stdbool.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <error.h>
33 #include <sys/mman.h>
34 #include <sys/stat.h>
35 #include <ctype.h>
37 #include "localedef.h"
38 #include "charmap.h"
39 #include "locfile.h"
41 /* Undefine the following line in the production version. */
42 /* #define NDEBUG 1 */
43 #include <assert.h>
46 /* List of copied locales. */
47 struct copy_def_list_t *copy_list;
49 /* If this is defined be POSIX conform. */
50 int posix_conformance;
52 /* If not zero force output even if warning were issued. */
53 static int force_output;
55 /* Prefix for output files. */
56 const char *output_prefix;
58 /* Name of the character map file. */
59 static const char *charmap_file;
61 /* Name of the locale definition file. */
62 static const char *input_file;
64 /* Name of the repertoire map file. */
65 const char *repertoire_global;
67 /* Name of the locale.alias file. */
68 const char *alias_file;
70 /* List of all locales. */
71 static struct localedef_t *locales;
73 /* If true don't add locale data to archive. */
74 bool no_archive;
76 /* If true add named locales to archive. */
77 static bool add_to_archive;
79 /* If true delete named locales from archive. */
80 static bool delete_from_archive;
82 /* If true replace archive content when adding. */
83 static bool replace_archive;
85 /* If true list archive content. */
86 static bool list_archive;
88 /* Maximum number of retries when opening the locale archive. */
89 int max_locarchive_open_retry = 10;
92 /* Name and version of program. */
93 static void print_version (FILE *stream, struct argp_state *state);
94 void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
96 #define OPT_POSIX 301
97 #define OPT_QUIET 302
98 #define OPT_PREFIX 304
99 #define OPT_NO_ARCHIVE 305
100 #define OPT_ADD_TO_ARCHIVE 306
101 #define OPT_REPLACE 307
102 #define OPT_DELETE_FROM_ARCHIVE 308
103 #define OPT_LIST_ARCHIVE 309
104 #define OPT_LITTLE_ENDIAN 400
105 #define OPT_BIG_ENDIAN 401
106 #define OPT_NO_WARN 402
107 #define OPT_WARN 403
109 /* Definitions of arguments for argp functions. */
110 static const struct argp_option options[] =
112 { NULL, 0, NULL, 0, N_("Input Files:") },
113 { "charmap", 'f', N_("FILE"), 0,
114 N_("Symbolic character names defined in FILE") },
115 { "inputfile", 'i', N_("FILE"), 0,
116 N_("Source definitions are found in FILE") },
117 { "repertoire-map", 'u', N_("FILE"), 0,
118 N_("FILE contains mapping from symbolic names to UCS4 values") },
120 { NULL, 0, NULL, 0, N_("Output control:") },
121 { "force", 'c', NULL, 0,
122 N_("Create output even if warning messages were issued") },
123 { "prefix", OPT_PREFIX, N_("PATH"), 0, N_("Optional output file prefix") },
124 { "posix", OPT_POSIX, NULL, 0, N_("Strictly conform to POSIX") },
125 { "quiet", OPT_QUIET, NULL, 0,
126 N_("Suppress warnings and information messages") },
127 { "verbose", 'v', NULL, 0, N_("Print more messages") },
128 { "no-warnings", OPT_NO_WARN, N_("<warnings>"), 0,
129 N_("Comma-separated list of warnings to disable; "
130 "supported warnings are: ascii, intcurrsym") },
131 { "warnings", OPT_WARN, N_("<warnings>"), 0,
132 N_("Comma-separated list of warnings to enable; "
133 "supported warnings are: ascii, intcurrsym") },
135 { NULL, 0, NULL, 0, N_("Archive control:") },
136 { "no-archive", OPT_NO_ARCHIVE, NULL, 0,
137 N_("Don't add new data to archive") },
138 { "add-to-archive", OPT_ADD_TO_ARCHIVE, NULL, 0,
139 N_("Add locales named by parameters to archive") },
140 { "replace", OPT_REPLACE, NULL, 0, N_("Replace existing archive content") },
141 { "delete-from-archive", OPT_DELETE_FROM_ARCHIVE, NULL, 0,
142 N_("Remove locales named by parameters from archive") },
143 { "list-archive", OPT_LIST_ARCHIVE, NULL, 0, N_("List content of archive") },
144 { "alias-file", 'A', N_("FILE"), 0,
145 N_("locale.alias file to consult when making archive")},
146 { "little-endian", OPT_LITTLE_ENDIAN, NULL, 0,
147 N_("Generate little-endian output") },
148 { "big-endian", OPT_BIG_ENDIAN, NULL, 0,
149 N_("Generate big-endian output") },
150 { NULL, 0, NULL, 0, NULL }
153 /* Short description of program. */
154 static const char doc[] = N_("Compile locale specification");
156 /* Strings for arguments in help texts. */
157 static const char args_doc[] = N_("\
158 NAME\n\
159 [--add-to-archive|--delete-from-archive] FILE...\n\
160 --list-archive [FILE]");
162 /* Prototype for option handler. */
163 static error_t parse_opt (int key, char *arg, struct argp_state *state);
165 /* Function to print some extra text in the help message. */
166 static char *more_help (int key, const char *text, void *input);
168 /* Data structure to communicate with argp functions. */
169 static struct argp argp =
171 options, parse_opt, args_doc, doc, NULL, more_help
175 /* Prototypes for local functions. */
176 static void error_print (void);
177 static const char *construct_output_path (char *path);
178 static const char *normalize_codeset (const char *codeset, size_t name_len);
182 main (int argc, char *argv[])
184 const char *output_path;
185 int cannot_write_why;
186 struct charmap_t *charmap;
187 struct localedef_t global;
188 int remaining;
190 /* Set initial values for global variables. */
191 copy_list = NULL;
192 posix_conformance = getenv ("POSIXLY_CORRECT") != NULL;
193 error_print_progname = error_print;
195 /* Set locale. Do not set LC_ALL because the other categories must
196 not be affected (according to POSIX.2). */
197 setlocale (LC_MESSAGES, "");
198 setlocale (LC_CTYPE, "");
200 /* Initialize the message catalog. */
201 textdomain (_libc_intl_domainname);
203 /* Parse and process arguments. */
204 argp_err_exit_status = 4;
205 argp_parse (&argp, argc, argv, 0, &remaining, NULL);
207 /* Handle a few special cases. */
208 if (list_archive)
209 show_archive_content (remaining > 1 ? argv[remaining] : NULL, verbose);
210 if (add_to_archive)
211 return add_locales_to_archive (argc - remaining, &argv[remaining],
212 replace_archive);
213 if (delete_from_archive)
214 return delete_locales_from_archive (argc - remaining, &argv[remaining]);
216 /* POSIX.2 requires to be verbose about missing characters in the
217 character map. */
218 verbose |= posix_conformance;
220 if (argc - remaining != 1)
222 /* We need exactly one non-option parameter. */
223 argp_help (&argp, stdout, ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR,
224 program_invocation_short_name);
225 exit (4);
228 /* The parameter describes the output path of the constructed files.
229 If the described files cannot be written return a NULL pointer. */
230 output_path = construct_output_path (argv[remaining]);
231 if (output_path == NULL && ! no_archive)
232 error (4, errno, _("cannot create directory for output files"));
233 cannot_write_why = errno;
235 /* Now that the parameters are processed we have to reset the local
236 ctype locale. (P1003.2 4.35.5.2) */
237 setlocale (LC_CTYPE, "POSIX");
239 /* Look whether the system really allows locale definitions. POSIX
240 defines error code 3 for this situation so I think it must be
241 a fatal error (see P1003.2 4.35.8). */
242 if (sysconf (_SC_2_LOCALEDEF) < 0)
243 record_error (3, 0, _("\
244 FATAL: system does not define `_POSIX2_LOCALEDEF'"));
246 /* Process charmap file. */
247 charmap = charmap_read (charmap_file, verbose, 1, be_quiet, 1);
249 /* Add the first entry in the locale list. */
250 memset (&global, '\0', sizeof (struct localedef_t));
251 global.name = input_file ?: "/dev/stdin";
252 global.needed = ALL_LOCALES;
253 locales = &global;
255 /* Now read the locale file. */
256 if (locfile_read (&global, charmap) != 0)
257 record_error (4, errno, _("\
258 cannot open locale definition file `%s'"), input_file);
260 /* Perhaps we saw some `copy' instructions. */
261 while (1)
263 struct localedef_t *runp = locales;
265 while (runp != NULL && (runp->needed & runp->avail) == runp->needed)
266 runp = runp->next;
268 if (runp == NULL)
269 /* Everything read. */
270 break;
272 if (locfile_read (runp, charmap) != 0)
273 record_error (4, errno, _("\
274 cannot open locale definition file `%s'"), runp->name);
277 /* Check the categories we processed in source form. */
278 check_all_categories (locales, charmap);
280 /* What we do next depends on the number of errors and warnings we
281 have generated in processing the input files.
283 * No errors: Write the output file.
285 * Some warnings: Write the output file and exit with status 1 to
286 indicate there may be problems using the output file e.g. missing
287 data that makes it difficult to use
289 * Errors: We don't write the output file and we exit with status 4
290 to indicate no output files were written.
292 The use of -c|--force writes the output file even if errors were
293 seen. */
294 if (recorded_error_count == 0 || force_output != 0)
296 if (cannot_write_why != 0)
297 record_error (4, cannot_write_why, _("\
298 cannot write output files to `%s'"), output_path ? : argv[remaining]);
299 else
300 write_all_categories (locales, charmap, argv[remaining], output_path);
302 else
303 record_error (4, 0, _("\
304 no output file produced because errors were issued"));
306 /* This exit status is prescribed by POSIX.2 4.35.7. */
307 exit (recorded_warning_count != 0);
310 /* Search warnings for matching warnings and if found enable those
311 warnings if ENABLED is true, otherwise disable the warnings. */
312 static void
313 set_warnings (char *warnings, bool enabled)
315 char *tok = warnings;
316 char *copy = (char *) malloc (strlen (warnings) + 1);
317 char *save = copy;
319 /* As we make a copy of the warnings list we remove all spaces from
320 the warnings list to make the processing a more robust. We don't
321 support spaces in a warning name. */
324 while (isspace (*tok) != 0)
325 tok++;
327 while ((*save++ = *tok++) != '\0');
329 warnings = copy;
331 /* Tokenize the input list of warnings to set, compare them to
332 known warnings, and set the warning. We purposely ignore unknown
333 warnings, and are thus forward compatible, users can attempt to
334 disable whaterver new warnings they know about, but we will only
335 disable those *we* known about. */
336 while ((tok = strtok_r (warnings, ",", &save)) != NULL)
338 warnings = NULL;
339 if (strcmp (tok, "ascii") == 0)
340 warn_ascii = enabled;
341 else if (strcmp (tok, "intcurrsym") == 0)
342 warn_int_curr_symbol = enabled;
345 free (copy);
348 /* Handle program arguments. */
349 static error_t
350 parse_opt (int key, char *arg, struct argp_state *state)
352 switch (key)
354 case OPT_QUIET:
355 be_quiet = 1;
356 break;
357 case OPT_POSIX:
358 posix_conformance = 1;
359 break;
360 case OPT_PREFIX:
361 output_prefix = arg;
362 break;
363 case OPT_NO_ARCHIVE:
364 no_archive = true;
365 break;
366 case OPT_ADD_TO_ARCHIVE:
367 add_to_archive = true;
368 break;
369 case OPT_REPLACE:
370 replace_archive = true;
371 break;
372 case OPT_DELETE_FROM_ARCHIVE:
373 delete_from_archive = true;
374 break;
375 case OPT_LIST_ARCHIVE:
376 list_archive = true;
377 break;
378 case OPT_LITTLE_ENDIAN:
379 set_big_endian (false);
380 break;
381 case OPT_BIG_ENDIAN:
382 set_big_endian (true);
383 break;
384 case OPT_NO_WARN:
385 /* Disable the warnings. */
386 set_warnings (arg, false);
387 break;
388 case OPT_WARN:
389 /* Enable the warnings. */
390 set_warnings (arg, true);
391 break;
392 case 'c':
393 force_output = 1;
394 break;
395 case 'f':
396 charmap_file = arg;
397 break;
398 case 'A':
399 alias_file = arg;
400 break;
401 case 'i':
402 input_file = arg;
403 break;
404 case 'u':
405 repertoire_global = arg;
406 break;
407 case 'v':
408 verbose = 1;
409 break;
410 default:
411 return ARGP_ERR_UNKNOWN;
413 return 0;
417 static char *
418 more_help (int key, const char *text, void *input)
420 char *cp;
421 char *tp;
423 switch (key)
425 case ARGP_KEY_HELP_EXTRA:
426 /* We print some extra information. */
427 if (asprintf (&tp, gettext ("\
428 For bug reporting instructions, please see:\n\
429 %s.\n"), REPORT_BUGS_TO) < 0)
430 return NULL;
431 if (asprintf (&cp, gettext ("\
432 System's directory for character maps : %s\n\
433 repertoire maps: %s\n\
434 locale path : %s\n\
435 %s"),
436 CHARMAP_PATH, REPERTOIREMAP_PATH, LOCALE_PATH, tp) < 0)
438 free (tp);
439 return NULL;
441 return cp;
442 default:
443 break;
445 return (char *) text;
448 /* Print the version information. */
449 static void
450 print_version (FILE *stream, struct argp_state *state)
452 fprintf (stream, "localedef %s%s\n", PKGVERSION, VERSION);
453 fprintf (stream, gettext ("\
454 Copyright (C) %s Free Software Foundation, Inc.\n\
455 This is free software; see the source for copying conditions. There is NO\n\
456 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
457 "), "2018");
458 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
462 /* The address of this function will be assigned to the hook in the error
463 functions. */
464 static void
465 error_print (void)
470 /* The parameter to localedef describes the output path. If it does
471 contain a '/' character it is a relative path. Otherwise it names the
472 locale this definition is for. */
473 static const char *
474 construct_output_path (char *path)
476 const char *normal = NULL;
477 char *result;
478 char *endp;
480 if (strchr (path, '/') == NULL)
482 /* This is a system path. First examine whether the locale name
483 contains a reference to the codeset. This should be
484 normalized. */
485 char *startp;
487 startp = path;
488 /* We must be prepared for finding a CEN name or a location of
489 the introducing `.' where it is not possible anymore. */
490 while (*startp != '\0' && *startp != '@' && *startp != '.')
491 ++startp;
492 if (*startp == '.')
494 /* We found a codeset specification. Now find the end. */
495 endp = ++startp;
496 while (*endp != '\0' && *endp != '@')
497 ++endp;
499 if (endp > startp)
500 normal = normalize_codeset (startp, endp - startp);
502 else
503 /* This is to keep gcc quiet. */
504 endp = NULL;
506 /* We put an additional '\0' at the end of the string because at
507 the end of the function we need another byte for the trailing
508 '/'. */
509 ssize_t n;
510 if (normal == NULL)
511 n = asprintf (&result, "%s%s/%s%c", output_prefix ?: "",
512 COMPLOCALEDIR, path, '\0');
513 else
514 n = asprintf (&result, "%s%s/%.*s%s%s%c",
515 output_prefix ?: "", COMPLOCALEDIR,
516 (int) (startp - path), path, normal, endp, '\0');
518 if (n < 0)
519 return NULL;
521 endp = result + n - 1;
523 else
525 /* This is a user path. Please note the additional byte in the
526 memory allocation. */
527 size_t len = strlen (path) + 1;
528 result = xmalloc (len + 1);
529 endp = mempcpy (result, path, len) - 1;
531 /* If the user specified an output path we cannot add the output
532 to the archive. */
533 no_archive = true;
536 errno = 0;
538 if (no_archive && euidaccess (result, W_OK) == -1)
539 /* Perhaps the directory does not exist now. Try to create it. */
540 if (errno == ENOENT)
542 errno = 0;
543 if (mkdir (result, 0777) < 0)
544 return NULL;
547 *endp++ = '/';
548 *endp = '\0';
550 return result;
554 /* Normalize codeset name. There is no standard for the codeset
555 names. Normalization allows the user to use any of the common
556 names. */
557 static const char *
558 normalize_codeset (const char *codeset, size_t name_len)
560 int len = 0;
561 int only_digit = 1;
562 char *retval;
563 char *wp;
564 size_t cnt;
566 for (cnt = 0; cnt < name_len; ++cnt)
567 if (isalnum (codeset[cnt]))
569 ++len;
571 if (isalpha (codeset[cnt]))
572 only_digit = 0;
575 retval = (char *) malloc ((only_digit ? 3 : 0) + len + 1);
577 if (retval != NULL)
579 if (only_digit)
580 wp = stpcpy (retval, "iso");
581 else
582 wp = retval;
584 for (cnt = 0; cnt < name_len; ++cnt)
585 if (isalpha (codeset[cnt]))
586 *wp++ = tolower (codeset[cnt]);
587 else if (isdigit (codeset[cnt]))
588 *wp++ = codeset[cnt];
590 *wp = '\0';
593 return (const char *) retval;
597 struct localedef_t *
598 add_to_readlist (int category, const char *name, const char *repertoire_name,
599 int generate, struct localedef_t *copy_locale)
601 struct localedef_t *runp = locales;
603 while (runp != NULL && strcmp (name, runp->name) != 0)
604 runp = runp->next;
606 if (runp == NULL)
608 /* Add a new entry at the end. */
609 struct localedef_t *newp;
611 assert (generate == 1);
613 newp = xcalloc (1, sizeof (struct localedef_t));
614 newp->name = name;
615 newp->repertoire_name = repertoire_name;
617 if (locales == NULL)
618 runp = locales = newp;
619 else
621 runp = locales;
622 while (runp->next != NULL)
623 runp = runp->next;
624 runp = runp->next = newp;
628 if (generate
629 && (runp->needed & (1 << category)) != 0
630 && (runp->avail & (1 << category)) == 0)
631 record_error (5, 0, _("\
632 circular dependencies between locale definitions"));
634 if (copy_locale != NULL)
636 if (runp->categories[category].generic != NULL)
637 record_error (5, 0, _("\
638 cannot add already read locale `%s' a second time"), name);
639 else
640 runp->categories[category].generic =
641 copy_locale->categories[category].generic;
644 runp->needed |= 1 << category;
646 return runp;
650 struct localedef_t *
651 find_locale (int category, const char *name, const char *repertoire_name,
652 const struct charmap_t *charmap)
654 struct localedef_t *result;
656 /* Find the locale, but do not generate it since this would be a bug. */
657 result = add_to_readlist (category, name, repertoire_name, 0, NULL);
659 assert (result != NULL);
661 if ((result->avail & (1 << category)) == 0
662 && locfile_read (result, charmap) != 0)
663 record_error (4, errno, _("\
664 cannot open locale definition file `%s'"), result->name);
666 return result;
670 struct localedef_t *
671 load_locale (int category, const char *name, const char *repertoire_name,
672 const struct charmap_t *charmap, struct localedef_t *copy_locale)
674 struct localedef_t *result;
676 /* Generate the locale if it does not exist. */
677 result = add_to_readlist (category, name, repertoire_name, 1, copy_locale);
679 assert (result != NULL);
681 if ((result->avail & (1 << category)) == 0
682 && locfile_read (result, charmap) != 0)
683 record_error (4, errno, _("\
684 cannot open locale definition file `%s'"), result->name);
686 return result;