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.
4
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.
9
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.
14
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/>. */
17
18#ifdef HAVE_CONFIG_H
19# include <config.h>
20#endif
21
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>
36
37#include "localedef.h"
38#include "charmap.h"
39#include "locfile.h"
40
41/* Undefine the following line in the production version. */
42/* #define NDEBUG 1 */
43#include <assert.h>
44
45
46/* List of copied locales. */
47struct copy_def_list_t *copy_list;
48
49/* If this is defined be POSIX conform. */
50int posix_conformance;
51
52/* If not zero force output even if warning were issued. */
53static int force_output;
54
55/* Prefix for output files. */
56const char *output_prefix;
57
58/* Name of the character map file. */
59static const char *charmap_file;
60
61/* Name of the locale definition file. */
62static const char *input_file;
63
64/* Name of the repertoire map file. */
65const char *repertoire_global;
66
67/* Name of the locale.alias file. */
68const char *alias_file;
69
70/* List of all locales. */
71static struct localedef_t *locales;
72
73/* If true don't add locale data to archive. */
74bool no_archive;
75
76/* If true add named locales to archive. */
77static bool add_to_archive;
78
79/* If true delete named locales from archive. */
80static bool delete_from_archive;
81
82/* If true replace archive content when adding. */
83static bool replace_archive;
84
85/* If true list archive content. */
86static bool list_archive;
87
88/* Maximum number of retries when opening the locale archive. */
89int max_locarchive_open_retry = 10;
90
91
92/* Name and version of program. */
93static void print_version (FILE *stream, struct argp_state *state);
94void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
95
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
108
109/* Definitions of arguments for argp functions. */
110static const struct argp_option options[] =
111{
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") },
119
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") },
134
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 }
151};
152
153/* Short description of program. */
154static const char doc[] = N_("Compile locale specification");
155
156/* Strings for arguments in help texts. */
157static const char args_doc[] = N_("\
158NAME\n\
159[--add-to-archive|--delete-from-archive] FILE...\n\
160--list-archive [FILE]");
161
162/* Prototype for option handler. */
163static error_t parse_opt (int key, char *arg, struct argp_state *state);
164
165/* Function to print some extra text in the help message. */
166static char *more_help (int key, const char *text, void *input);
167
168/* Data structure to communicate with argp functions. */
169static struct argp argp =
170{
171 options, parse_opt, args_doc, doc, NULL, more_help
172};
173
174
175/* Prototypes for local functions. */
176static void error_print (void);
177static const char *construct_output_path (char *path);
178static const char *normalize_codeset (const char *codeset, size_t name_len);
179
180
181int
182main (int argc, char *argv[])
183{
184 const char *output_path;
185 int cannot_write_why;
186 struct charmap_t *charmap;
187 struct localedef_t global;
188 int remaining;
189
190 /* Set initial values for global variables. */
191 copy_list = NULL;
192 posix_conformance = getenv ("POSIXLY_CORRECT") != NULL;
193 error_print_progname = error_print;
194
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, "");
199
200 /* Initialize the message catalog. */
201 textdomain (_libc_intl_domainname);
202
203 /* Parse and process arguments. */
204 argp_err_exit_status = 4;
205 argp_parse (&argp, argc, argv, 0, &remaining, NULL);
206
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]);
215
216 /* POSIX.2 requires to be verbose about missing characters in the
217 character map. */
218 verbose |= posix_conformance;
219
220 if (argc - remaining != 1)
221 {
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);
226 }
227
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;
234
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");
238
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, _("\
244FATAL: system does not define `_POSIX2_LOCALEDEF'"));
245
246 /* Process charmap file. */
247 charmap = charmap_read (charmap_file, verbose, 1, be_quiet, 1);
248
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;
254
255 /* Now read the locale file. */
256 if (locfile_read (&global, charmap) != 0)
257 record_error (4, errno, _("\
258cannot open locale definition file `%s'"), input_file);
259
260 /* Perhaps we saw some `copy' instructions. */
261 while (1)
262 {
263 struct localedef_t *runp = locales;
264
265 while (runp != NULL && (runp->needed & runp->avail) == runp->needed)
266 runp = runp->next;
267
268 if (runp == NULL)
269 /* Everything read. */
270 break;
271
272 if (locfile_read (runp, charmap) != 0)
273 record_error (4, errno, _("\
274cannot open locale definition file `%s'"), runp->name);
275 }
276
277 /* Check the categories we processed in source form. */
278 check_all_categories (locales, charmap);
279
280 /* What we do next depends on the number of errors and warnings we
281 have generated in processing the input files.
282
283 * No errors: Write the output file.
284
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
288
289 * Errors: We don't write the output file and we exit with status 4
290 to indicate no output files were written.
291
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)
295 {
296 if (cannot_write_why != 0)
297 record_error (4, cannot_write_why, _("\
298cannot write output files to `%s'"), output_path ? : argv[remaining]);
299 else
300 write_all_categories (locales, charmap, argv[remaining], output_path);
301 }
302 else
303 record_error (4, 0, _("\
304no output file produced because errors were issued"));
305
306 /* This exit status is prescribed by POSIX.2 4.35.7. */
307 exit (recorded_warning_count != 0);
308}
309
310/* Search warnings for matching warnings and if found enable those
311 warnings if ENABLED is true, otherwise disable the warnings. */
312static void
313set_warnings (char *warnings, bool enabled)
314{
315 char *tok = warnings;
316 char *copy = (char *) malloc (strlen (warnings) + 1);
317 char *save = copy;
318
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. */
322 do
323 {
324 while (isspace (*tok) != 0)
325 tok++;
326 }
327 while ((*save++ = *tok++) != '\0');
328
329 warnings = copy;
330
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)
337 {
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;
343 }
344
345 free (copy);
346}
347
348/* Handle program arguments. */
349static error_t
350parse_opt (int key, char *arg, struct argp_state *state)
351{
352 switch (key)
353 {
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;
412 }
413 return 0;
414}
415
416
417static char *
418more_help (int key, const char *text, void *input)
419{
420 char *cp;
421 char *tp;
422
423 switch (key)
424 {
425 case ARGP_KEY_HELP_EXTRA:
426 /* We print some extra information. */
427 if (asprintf (&tp, gettext ("\
428For bug reporting instructions, please see:\n\
429%s.\n"), REPORT_BUGS_TO) < 0)
430 return NULL;
431 if (asprintf (&cp, gettext ("\
432System'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)
437 {
438 free (tp);
439 return NULL;
440 }
441 return cp;
442 default:
443 break;
444 }
445 return (char *) text;
446}
447
448/* Print the version information. */
449static void
450print_version (FILE *stream, struct argp_state *state)
451{
452 fprintf (stream, "localedef %s%s\n", PKGVERSION, VERSION);
453 fprintf (stream, gettext ("\
454Copyright (C) %s Free Software Foundation, Inc.\n\
455This is free software; see the source for copying conditions. There is NO\n\
456warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
457"), "2018");
458 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
459}
460
461
462/* The address of this function will be assigned to the hook in the error
463 functions. */
464static void
465error_print (void)
466{
467}
468
469
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. */
473static const char *
474construct_output_path (char *path)
475{
476 const char *normal = NULL;
477 char *result;
478 char *endp;
479
480 if (strchr (path, '/') == NULL)
481 {
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;
486
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 == '.')
493 {
494 /* We found a codeset specification. Now find the end. */
495 endp = ++startp;
496 while (*endp != '\0' && *endp != '@')
497 ++endp;
498
499 if (endp > startp)
500 normal = normalize_codeset (startp, endp - startp);
501 }
502 else
503 /* This is to keep gcc quiet. */
504 endp = NULL;
505
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');
517
518 if (n < 0)
519 return NULL;
520
521 endp = result + n - 1;
522 }
523 else
524 {
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;
530
531 /* If the user specified an output path we cannot add the output
532 to the archive. */
533 no_archive = true;
534 }
535
536 errno = 0;
537
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)
541 {
542 errno = 0;
543 if (mkdir (result, 0777) < 0)
544 return NULL;
545 }
546
547 *endp++ = '/';
548 *endp = '\0';
549
550 return result;
551}
552
553
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. */
557static const char *
558normalize_codeset (const char *codeset, size_t name_len)
559{
560 int len = 0;
561 int only_digit = 1;
562 char *retval;
563 char *wp;
564 size_t cnt;
565
566 for (cnt = 0; cnt < name_len; ++cnt)
567 if (isalnum (codeset[cnt]))
568 {
569 ++len;
570
571 if (isalpha (codeset[cnt]))
572 only_digit = 0;
573 }
574
575 retval = (char *) malloc ((only_digit ? 3 : 0) + len + 1);
576
577 if (retval != NULL)
578 {
579 if (only_digit)
580 wp = stpcpy (retval, "iso");
581 else
582 wp = retval;
583
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];
589
590 *wp = '\0';
591 }
592
593 return (const char *) retval;
594}
595
596
597struct localedef_t *
598add_to_readlist (int category, const char *name, const char *repertoire_name,
599 int generate, struct localedef_t *copy_locale)
600{
601 struct localedef_t *runp = locales;
602
603 while (runp != NULL && strcmp (name, runp->name) != 0)
604 runp = runp->next;
605
606 if (runp == NULL)
607 {
608 /* Add a new entry at the end. */
609 struct localedef_t *newp;
610
611 assert (generate == 1);
612
613 newp = xcalloc (1, sizeof (struct localedef_t));
614 newp->name = name;
615 newp->repertoire_name = repertoire_name;
616
617 if (locales == NULL)
618 runp = locales = newp;
619 else
620 {
621 runp = locales;
622 while (runp->next != NULL)
623 runp = runp->next;
624 runp = runp->next = newp;
625 }
626 }
627
628 if (generate
629 && (runp->needed & (1 << category)) != 0
630 && (runp->avail & (1 << category)) == 0)
631 record_error (5, 0, _("\
632circular dependencies between locale definitions"));
633
634 if (copy_locale != NULL)
635 {
636 if (runp->categories[category].generic != NULL)
637 record_error (5, 0, _("\
638cannot add already read locale `%s' a second time"), name);
639 else
640 runp->categories[category].generic =
641 copy_locale->categories[category].generic;
642 }
643
644 runp->needed |= 1 << category;
645
646 return runp;
647}
648
649
650struct localedef_t *
651find_locale (int category, const char *name, const char *repertoire_name,
652 const struct charmap_t *charmap)
653{
654 struct localedef_t *result;
655
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);
658
659 assert (result != NULL);
660
661 if ((result->avail & (1 << category)) == 0
662 && locfile_read (result, charmap) != 0)
663 record_error (4, errno, _("\
664cannot open locale definition file `%s'"), result->name);
665
666 return result;
667}
668
669
670struct localedef_t *
671load_locale (int category, const char *name, const char *repertoire_name,
672 const struct charmap_t *charmap, struct localedef_t *copy_locale)
673{
674 struct localedef_t *result;
675
676 /* Generate the locale if it does not exist. */
677 result = add_to_readlist (category, name, repertoire_name, 1, copy_locale);
678
679 assert (result != NULL);
680
681 if ((result->avail & (1 << category)) == 0
682 && locfile_read (result, charmap) != 0)
683 record_error (4, errno, _("\
684cannot open locale definition file `%s'"), result->name);
685
686 return result;
687}
688