]> git.ipfire.org Git - thirdparty/glibc.git/blame - locale/programs/localedef.c
Update copyright dates with scripts/update-copyrights
[thirdparty/glibc.git] / locale / programs / localedef.c
CommitLineData
2b778ceb 1/* Copyright (C) 1995-2021 Free Software Foundation, Inc.
e4cf5070 2 This file is part of the GNU C Library.
4b10dd6c 3 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1995.
19bc17a9 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.
19bc17a9 9
43bc8ac6 10 This program is distributed in the hope that it will be useful,
e4cf5070 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.
19bc17a9 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/>. */
19bc17a9
RM
17
18#ifdef HAVE_CONFIG_H
19# include <config.h>
20#endif
21
5a97622d 22#include <argp.h>
19bc17a9
RM
23#include <errno.h>
24#include <fcntl.h>
19bc17a9
RM
25#include <libintl.h>
26#include <locale.h>
a7b65cdc 27#include <stdbool.h>
19bc17a9
RM
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <unistd.h>
f2b98f97 32#include <error.h>
4b10dd6c 33#include <sys/mman.h>
19bc17a9 34#include <sys/stat.h>
02eec681 35#include <ctype.h>
19bc17a9 36
f2b98f97 37#include "localedef.h"
4b10dd6c 38#include "charmap.h"
19bc17a9 39#include "locfile.h"
19bc17a9 40
4b10dd6c
UD
41/* Undefine the following line in the production version. */
42/* #define NDEBUG 1 */
43#include <assert.h>
19bc17a9
RM
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
5a97622d
UD
52/* If not zero force output even if warning were issued. */
53static int force_output;
19bc17a9 54
8a82cc30
UD
55/* Prefix for output files. */
56const char *output_prefix;
57
5a97622d
UD
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
69f155d4 64/* Name of the repertoire map file. */
4b10dd6c
UD
65const char *repertoire_global;
66
cb09a2cd
RM
67/* Name of the locale.alias file. */
68const char *alias_file;
69
4b10dd6c
UD
70/* List of all locales. */
71static struct localedef_t *locales;
5a97622d 72
a7b65cdc
UD
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
8cebd4ff
CD
88/* If true create hard links to other locales (default). */
89bool hard_links = true;
90
a7b65cdc
UD
91/* Maximum number of retries when opening the locale archive. */
92int max_locarchive_open_retry = 10;
93
5a97622d
UD
94
95/* Name and version of program. */
96static void print_version (FILE *stream, struct argp_state *state);
97void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
98
a7b65cdc
UD
99#define OPT_POSIX 301
100#define OPT_QUIET 302
a7b65cdc
UD
101#define OPT_PREFIX 304
102#define OPT_NO_ARCHIVE 305
103#define OPT_ADD_TO_ARCHIVE 306
104#define OPT_REPLACE 307
105#define OPT_DELETE_FROM_ARCHIVE 308
106#define OPT_LIST_ARCHIVE 309
6055173a
JM
107#define OPT_LITTLE_ENDIAN 400
108#define OPT_BIG_ENDIAN 401
02eec681
CD
109#define OPT_NO_WARN 402
110#define OPT_WARN 403
8cebd4ff 111#define OPT_NO_HARD_LINKS 404
5a97622d
UD
112
113/* Definitions of arguments for argp functions. */
114static const struct argp_option options[] =
115{
116 { NULL, 0, NULL, 0, N_("Input Files:") },
912cc4b3 117 { "charmap", 'f', N_("FILE"), 0,
5a97622d 118 N_("Symbolic character names defined in FILE") },
912cc4b3
RM
119 { "inputfile", 'i', N_("FILE"), 0,
120 N_("Source definitions are found in FILE") },
121 { "repertoire-map", 'u', N_("FILE"), 0,
7c38dd46 122 N_("FILE contains mapping from symbolic names to UCS4 values") },
5a97622d
UD
123
124 { NULL, 0, NULL, 0, N_("Output control:") },
125 { "force", 'c', NULL, 0,
126 N_("Create output even if warning messages were issued") },
8cebd4ff
CD
127 { "no-hard-links", OPT_NO_HARD_LINKS, NULL, 0,
128 N_("Do not create hard links between installed locales") },
912cc4b3 129 { "prefix", OPT_PREFIX, N_("PATH"), 0, N_("Optional output file prefix") },
fd80f0b7 130 { "posix", OPT_POSIX, NULL, 0, N_("Strictly conform to POSIX") },
5a97622d
UD
131 { "quiet", OPT_QUIET, NULL, 0,
132 N_("Suppress warnings and information messages") },
a5a0310d 133 { "verbose", 'v', NULL, 0, N_("Print more messages") },
02eec681
CD
134 { "no-warnings", OPT_NO_WARN, N_("<warnings>"), 0,
135 N_("Comma-separated list of warnings to disable; "
136 "supported warnings are: ascii, intcurrsym") },
137 { "warnings", OPT_WARN, N_("<warnings>"), 0,
138 N_("Comma-separated list of warnings to enable; "
139 "supported warnings are: ascii, intcurrsym") },
140
a7b65cdc
UD
141 { NULL, 0, NULL, 0, N_("Archive control:") },
142 { "no-archive", OPT_NO_ARCHIVE, NULL, 0,
143 N_("Don't add new data to archive") },
144 { "add-to-archive", OPT_ADD_TO_ARCHIVE, NULL, 0,
145 N_("Add locales named by parameters to archive") },
146 { "replace", OPT_REPLACE, NULL, 0, N_("Replace existing archive content") },
147 { "delete-from-archive", OPT_DELETE_FROM_ARCHIVE, NULL, 0,
148 N_("Remove locales named by parameters from archive") },
149 { "list-archive", OPT_LIST_ARCHIVE, NULL, 0, N_("List content of archive") },
912cc4b3 150 { "alias-file", 'A', N_("FILE"), 0,
cb09a2cd 151 N_("locale.alias file to consult when making archive")},
6055173a
JM
152 { "little-endian", OPT_LITTLE_ENDIAN, NULL, 0,
153 N_("Generate little-endian output") },
154 { "big-endian", OPT_BIG_ENDIAN, NULL, 0,
155 N_("Generate big-endian output") },
5a97622d
UD
156 { NULL, 0, NULL, 0, NULL }
157};
158
159/* Short description of program. */
160static const char doc[] = N_("Compile locale specification");
161
162/* Strings for arguments in help texts. */
a7b65cdc
UD
163static const char args_doc[] = N_("\
164NAME\n\
165[--add-to-archive|--delete-from-archive] FILE...\n\
166--list-archive [FILE]");
5a97622d
UD
167
168/* Prototype for option handler. */
169static error_t parse_opt (int key, char *arg, struct argp_state *state);
170
171/* Function to print some extra text in the help message. */
172static char *more_help (int key, const char *text, void *input);
173
174/* Data structure to communicate with argp functions. */
175static struct argp argp =
19bc17a9 176{
5a97622d 177 options, parse_opt, args_doc, doc, NULL, more_help
19bc17a9
RM
178};
179
180
19bc17a9 181/* Prototypes for local functions. */
19bc17a9 182static void error_print (void);
92954ffa
CD
183static char *construct_output_path (char *path);
184static char *normalize_codeset (const char *codeset, size_t name_len);
19bc17a9
RM
185
186
187int
188main (int argc, char *argv[])
189{
92954ffa 190 char *output_path;
19bc17a9 191 int cannot_write_why;
4b10dd6c
UD
192 struct charmap_t *charmap;
193 struct localedef_t global;
2f6d1f1b 194 int remaining;
19bc17a9 195
6d52618b 196 /* Set initial values for global variables. */
4b10dd6c 197 copy_list = NULL;
19bc17a9 198 posix_conformance = getenv ("POSIXLY_CORRECT") != NULL;
19bc17a9 199 error_print_progname = error_print;
19bc17a9
RM
200
201 /* Set locale. Do not set LC_ALL because the other categories must
6d52618b 202 not be affected (according to POSIX.2). */
19bc17a9
RM
203 setlocale (LC_MESSAGES, "");
204 setlocale (LC_CTYPE, "");
205
206 /* Initialize the message catalog. */
19bc17a9 207 textdomain (_libc_intl_domainname);
19bc17a9 208
5a97622d 209 /* Parse and process arguments. */
4cca6b86 210 argp_err_exit_status = 4;
2f6d1f1b 211 argp_parse (&argp, argc, argv, 0, &remaining, NULL);
c84142e8 212
a7b65cdc
UD
213 /* Handle a few special cases. */
214 if (list_archive)
484c12fb 215 show_archive_content (remaining > 1 ? argv[remaining] : NULL, verbose);
a7b65cdc
UD
216 if (add_to_archive)
217 return add_locales_to_archive (argc - remaining, &argv[remaining],
218 replace_archive);
219 if (delete_from_archive)
220 return delete_locales_from_archive (argc - remaining, &argv[remaining]);
221
19bc17a9
RM
222 /* POSIX.2 requires to be verbose about missing characters in the
223 character map. */
224 verbose |= posix_conformance;
225
2f6d1f1b 226 if (argc - remaining != 1)
19bc17a9 227 {
5a97622d 228 /* We need exactly one non-option parameter. */
4cca6b86 229 argp_help (&argp, stdout, ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR,
5a97622d 230 program_invocation_short_name);
5a97622d 231 exit (4);
19bc17a9
RM
232 }
233
19bc17a9 234 /* The parameter describes the output path of the constructed files.
92954ffa
CD
235 If the described files cannot be written return a NULL pointer.
236 We don't free output_path because we will exit. */
b459976e
UD
237 output_path = construct_output_path (argv[remaining]);
238 if (output_path == NULL && ! no_archive)
239 error (4, errno, _("cannot create directory for output files"));
240 cannot_write_why = errno;
19bc17a9
RM
241
242 /* Now that the parameters are processed we have to reset the local
243 ctype locale. (P1003.2 4.35.5.2) */
244 setlocale (LC_CTYPE, "POSIX");
245
246 /* Look whether the system really allows locale definitions. POSIX
247 defines error code 3 for this situation so I think it must be
248 a fatal error (see P1003.2 4.35.8). */
249 if (sysconf (_SC_2_LOCALEDEF) < 0)
f16491eb
CD
250 record_error (3, 0, _("\
251FATAL: system does not define `_POSIX2_LOCALEDEF'"));
19bc17a9
RM
252
253 /* Process charmap file. */
8a6537b0 254 charmap = charmap_read (charmap_file, verbose, 1, be_quiet, 1);
4b10dd6c
UD
255
256 /* Add the first entry in the locale list. */
257 memset (&global, '\0', sizeof (struct localedef_t));
dcf56f42 258 global.name = input_file ?: "/dev/stdin";
4b10dd6c
UD
259 global.needed = ALL_LOCALES;
260 locales = &global;
19bc17a9
RM
261
262 /* Now read the locale file. */
4b10dd6c 263 if (locfile_read (&global, charmap) != 0)
f16491eb
CD
264 record_error (4, errno, _("\
265cannot open locale definition file `%s'"), input_file);
19bc17a9 266
4b10dd6c
UD
267 /* Perhaps we saw some `copy' instructions. */
268 while (1)
19bc17a9 269 {
4b10dd6c 270 struct localedef_t *runp = locales;
19bc17a9 271
b9eb05d6 272 while (runp != NULL && (runp->needed & runp->avail) == runp->needed)
4b10dd6c 273 runp = runp->next;
19bc17a9 274
4b10dd6c
UD
275 if (runp == NULL)
276 /* Everything read. */
277 break;
278
279 if (locfile_read (runp, charmap) != 0)
f16491eb
CD
280 record_error (4, errno, _("\
281cannot open locale definition file `%s'"), runp->name);
19bc17a9 282 }
19bc17a9
RM
283
284 /* Check the categories we processed in source form. */
4b10dd6c 285 check_all_categories (locales, charmap);
19bc17a9 286
f16491eb
CD
287 /* What we do next depends on the number of errors and warnings we
288 have generated in processing the input files.
289
290 * No errors: Write the output file.
291
292 * Some warnings: Write the output file and exit with status 1 to
293 indicate there may be problems using the output file e.g. missing
294 data that makes it difficult to use
295
296 * Errors: We don't write the output file and we exit with status 4
297 to indicate no output files were written.
298
299 The use of -c|--force writes the output file even if errors were
300 seen. */
301 if (recorded_error_count == 0 || force_output != 0)
19bc17a9
RM
302 {
303 if (cannot_write_why != 0)
f16491eb
CD
304 record_error (4, cannot_write_why, _("\
305cannot write output files to `%s'"), output_path ? : argv[remaining]);
19bc17a9 306 else
a7b65cdc 307 write_all_categories (locales, charmap, argv[remaining], output_path);
19bc17a9
RM
308 }
309 else
f16491eb
CD
310 record_error (4, 0, _("\
311no output file produced because errors were issued"));
19bc17a9
RM
312
313 /* This exit status is prescribed by POSIX.2 4.35.7. */
f16491eb 314 exit (recorded_warning_count != 0);
19bc17a9
RM
315}
316
02eec681
CD
317/* Search warnings for matching warnings and if found enable those
318 warnings if ENABLED is true, otherwise disable the warnings. */
319static void
320set_warnings (char *warnings, bool enabled)
321{
322 char *tok = warnings;
323 char *copy = (char *) malloc (strlen (warnings) + 1);
324 char *save = copy;
325
326 /* As we make a copy of the warnings list we remove all spaces from
327 the warnings list to make the processing a more robust. We don't
328 support spaces in a warning name. */
329 do
330 {
331 while (isspace (*tok) != 0)
332 tok++;
333 }
334 while ((*save++ = *tok++) != '\0');
335
336 warnings = copy;
337
338 /* Tokenize the input list of warnings to set, compare them to
339 known warnings, and set the warning. We purposely ignore unknown
340 warnings, and are thus forward compatible, users can attempt to
341 disable whaterver new warnings they know about, but we will only
342 disable those *we* known about. */
343 while ((tok = strtok_r (warnings, ",", &save)) != NULL)
344 {
345 warnings = NULL;
346 if (strcmp (tok, "ascii") == 0)
347 warn_ascii = enabled;
348 else if (strcmp (tok, "intcurrsym") == 0)
349 warn_int_curr_symbol = enabled;
350 }
351
352 free (copy);
353}
19bc17a9 354
5a97622d
UD
355/* Handle program arguments. */
356static error_t
357parse_opt (int key, char *arg, struct argp_state *state)
358{
359 switch (key)
360 {
361 case OPT_QUIET:
362 be_quiet = 1;
363 break;
364 case OPT_POSIX:
365 posix_conformance = 1;
366 break;
8a82cc30
UD
367 case OPT_PREFIX:
368 output_prefix = arg;
369 break;
a7b65cdc
UD
370 case OPT_NO_ARCHIVE:
371 no_archive = true;
372 break;
373 case OPT_ADD_TO_ARCHIVE:
374 add_to_archive = true;
375 break;
376 case OPT_REPLACE:
377 replace_archive = true;
378 break;
379 case OPT_DELETE_FROM_ARCHIVE:
380 delete_from_archive = true;
381 break;
382 case OPT_LIST_ARCHIVE:
383 list_archive = true;
384 break;
6055173a
JM
385 case OPT_LITTLE_ENDIAN:
386 set_big_endian (false);
387 break;
388 case OPT_BIG_ENDIAN:
389 set_big_endian (true);
390 break;
02eec681
CD
391 case OPT_NO_WARN:
392 /* Disable the warnings. */
393 set_warnings (arg, false);
394 break;
395 case OPT_WARN:
396 /* Enable the warnings. */
397 set_warnings (arg, true);
398 break;
8cebd4ff
CD
399 case OPT_NO_HARD_LINKS:
400 /* Do not hard link to other locales. */
401 hard_links = false;
402 break;
5a97622d
UD
403 case 'c':
404 force_output = 1;
405 break;
406 case 'f':
407 charmap_file = arg;
408 break;
cb09a2cd
RM
409 case 'A':
410 alias_file = arg;
411 break;
5a97622d
UD
412 case 'i':
413 input_file = arg;
414 break;
415 case 'u':
4b10dd6c 416 repertoire_global = arg;
5a97622d
UD
417 break;
418 case 'v':
419 verbose = 1;
420 break;
421 default:
422 return ARGP_ERR_UNKNOWN;
423 }
424 return 0;
425}
426
427
428static char *
429more_help (int key, const char *text, void *input)
430{
431 char *cp;
8b748aed 432 char *tp;
5a97622d
UD
433
434 switch (key)
435 {
436 case ARGP_KEY_HELP_EXTRA:
437 /* We print some extra information. */
92954ffa 438 tp = xasprintf (gettext ("\
8b748aed 439For bug reporting instructions, please see:\n\
92954ffa
CD
440%s.\n"), REPORT_BUGS_TO);
441 cp = xasprintf (gettext ("\
69f155d4 442System's directory for character maps : %s\n\
561470e0
UD
443 repertoire maps: %s\n\
444 locale path : %s\n\
5a97622d 445%s"),
92954ffa
CD
446 CHARMAP_PATH, REPERTOIREMAP_PATH, LOCALE_PATH, tp);
447 free (tp);
5a97622d
UD
448 return cp;
449 default:
450 break;
451 }
452 return (char *) text;
453}
454
455/* Print the version information. */
456static void
457print_version (FILE *stream, struct argp_state *state)
458{
8b748aed 459 fprintf (stream, "localedef %s%s\n", PKGVERSION, VERSION);
5a97622d
UD
460 fprintf (stream, gettext ("\
461Copyright (C) %s Free Software Foundation, Inc.\n\
462This is free software; see the source for copying conditions. There is NO\n\
463warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
5f72f980 464"), "2020");
5a97622d
UD
465 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
466}
467
468
19bc17a9
RM
469/* The address of this function will be assigned to the hook in the error
470 functions. */
471static void
4b10dd6c 472error_print (void)
19bc17a9 473{
19bc17a9
RM
474}
475
476
92954ffa
CD
477/* The parameter to localedef describes the output path. If it does contain a
478 '/' character it is a relative path. Otherwise it names the locale this
479 definition is for. The returned path must be freed by the caller. */
480static char *
7a12c6bb 481construct_output_path (char *path)
19bc17a9
RM
482{
483 char *result;
484
485 if (strchr (path, '/') == NULL)
486 {
7a12c6bb
RM
487 /* This is a system path. First examine whether the locale name
488 contains a reference to the codeset. This should be
489 normalized. */
4b10dd6c 490 char *startp;
92954ffa
CD
491 char *endp = NULL;
492 char *normal = NULL;
7a12c6bb
RM
493
494 startp = path;
92954ffa
CD
495 /* Either we have a '@' which starts a CEN name or '.' which starts the
496 codeset specification. The CEN name starts with '@' and may also have
497 a codeset specification, but we do not normalize the string after '@'.
498 If we only find the codeset specification then we normalize only the codeset
499 specification (but not anything after a subsequent '@'). */
c3d08707 500 while (*startp != '\0' && *startp != '@' && *startp != '.')
7a12c6bb
RM
501 ++startp;
502 if (*startp == '.')
503 {
504 /* We found a codeset specification. Now find the end. */
505 endp = ++startp;
92954ffa
CD
506
507 /* Stop at the first '@', and don't normalize anything past that. */
7a12c6bb
RM
508 while (*endp != '\0' && *endp != '@')
509 ++endp;
510
511 if (endp > startp)
900bec85 512 normal = normalize_codeset (startp, endp - startp);
7a12c6bb 513 }
19bc17a9 514
7a12c6bb 515 if (normal == NULL)
92954ffa
CD
516 result = xasprintf ("%s%s/%s/", output_prefix ?: "",
517 COMPLOCALEDIR, path);
7a12c6bb 518 else
92954ffa
CD
519 result = xasprintf ("%s%s/%.*s%s%s/",
520 output_prefix ?: "", COMPLOCALEDIR,
521 (int) (startp - path), path, normal, endp ?: "");
522 /* Free the allocated normalized codeset name. */
523 free (normal);
19bc17a9
RM
524 }
525 else
526 {
92954ffa
CD
527 /* This is a user path. */
528 result = xasprintf ("%s/", path);
a7b65cdc
UD
529
530 /* If the user specified an output path we cannot add the output
531 to the archive. */
532 no_archive = true;
19bc17a9
RM
533 }
534
535 errno = 0;
536
9813858f 537 if (no_archive && euidaccess (result, W_OK) == -1)
92954ffa
CD
538 {
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 {
545 record_verbose (stderr,
546 _("cannot create output path \'%s\': %s"),
547 result, strerror (errno));
548 free (result);
549 return NULL;
550 }
551 }
552 else
553 record_verbose (stderr,
554 _("no write permission to output path \'%s\': %s"),
555 result, strerror (errno));
556 }
19bc17a9
RM
557
558 return result;
559}
900bec85 560
4b10dd6c 561
92954ffa
CD
562/* Normalize codeset name. There is no standard for the codeset names.
563 Normalization allows the user to use any of the common names e.g. UTF-8,
564 utf-8, utf8, UTF8 etc.
565
566 We normalize using the following rules:
567 - Remove all non-alpha-numeric characters
568 - Lowercase all characters.
569 - If there are only digits assume it's an ISO standard and prefix with 'iso'
570
571 We return the normalized string which needs to be freed by free. */
572static char *
9dd346ff 573normalize_codeset (const char *codeset, size_t name_len)
900bec85
UD
574{
575 int len = 0;
576 int only_digit = 1;
577 char *retval;
578 char *wp;
579 size_t cnt;
580
92954ffa 581 /* Compute the length of only the alpha-numeric characters. */
900bec85
UD
582 for (cnt = 0; cnt < name_len; ++cnt)
583 if (isalnum (codeset[cnt]))
584 {
585 ++len;
586
587 if (isalpha (codeset[cnt]))
588 only_digit = 0;
589 }
590
92954ffa
CD
591 /* If there were only digits we assume it's an ISO standard and we will
592 prefix with 'iso' so include space for that. We fill in the required
593 space from codeset up to the converted length. */
594 wp = retval = xasprintf ("%s%.*s", only_digit ? "iso" : "", len, codeset);
900bec85 595
92954ffa
CD
596 /* Skip "iso". */
597 if (only_digit)
598 wp += 3;
900bec85 599
92954ffa
CD
600 /* Lowercase all characters. */
601 for (cnt = 0; cnt < name_len; ++cnt)
602 if (isalpha (codeset[cnt]))
603 *wp++ = tolower (codeset[cnt]);
604 else if (isdigit (codeset[cnt]))
605 *wp++ = codeset[cnt];
900bec85 606
92954ffa
CD
607 /* Return allocated and converted name for caller to free. */
608 return retval;
900bec85 609}
4b10dd6c
UD
610
611
612struct localedef_t *
c10d32c8 613add_to_readlist (int category, const char *name, const char *repertoire_name,
07dab0c3 614 int generate, struct localedef_t *copy_locale)
4b10dd6c
UD
615{
616 struct localedef_t *runp = locales;
617
618 while (runp != NULL && strcmp (name, runp->name) != 0)
619 runp = runp->next;
620
621 if (runp == NULL)
622 {
623 /* Add a new entry at the end. */
b9eb05d6
UD
624 struct localedef_t *newp;
625
626 assert (generate == 1);
627
628 newp = xcalloc (1, sizeof (struct localedef_t));
4b10dd6c
UD
629 newp->name = name;
630 newp->repertoire_name = repertoire_name;
631
632 if (locales == NULL)
633 runp = locales = newp;
634 else
635 {
636 runp = locales;
637 while (runp->next != NULL)
638 runp = runp->next;
639 runp = runp->next = newp;
640 }
641 }
642
69f6a804 643 if (generate
c10d32c8
UD
644 && (runp->needed & (1 << category)) != 0
645 && (runp->avail & (1 << category)) == 0)
f16491eb
CD
646 record_error (5, 0, _("\
647circular dependencies between locale definitions"));
4b10dd6c 648
07dab0c3
UD
649 if (copy_locale != NULL)
650 {
c10d32c8 651 if (runp->categories[category].generic != NULL)
f16491eb
CD
652 record_error (5, 0, _("\
653cannot add already read locale `%s' a second time"), name);
07dab0c3 654 else
c10d32c8
UD
655 runp->categories[category].generic =
656 copy_locale->categories[category].generic;
07dab0c3
UD
657 }
658
c10d32c8 659 runp->needed |= 1 << category;
4b10dd6c
UD
660
661 return runp;
662}
663
664
665struct localedef_t *
c10d32c8 666find_locale (int category, const char *name, const char *repertoire_name,
47e8b443 667 const struct charmap_t *charmap)
4b10dd6c 668{
b9eb05d6
UD
669 struct localedef_t *result;
670
671 /* Find the locale, but do not generate it since this would be a bug. */
c10d32c8 672 result = add_to_readlist (category, name, repertoire_name, 0, NULL);
b9eb05d6
UD
673
674 assert (result != NULL);
4b10dd6c 675
c10d32c8 676 if ((result->avail & (1 << category)) == 0
b9eb05d6 677 && locfile_read (result, charmap) != 0)
f16491eb
CD
678 record_error (4, errno, _("\
679cannot open locale definition file `%s'"), result->name);
4b10dd6c
UD
680
681 return result;
682}
70e51ab9
UD
683
684
685struct localedef_t *
c10d32c8 686load_locale (int category, const char *name, const char *repertoire_name,
47e8b443 687 const struct charmap_t *charmap, struct localedef_t *copy_locale)
70e51ab9
UD
688{
689 struct localedef_t *result;
690
691 /* Generate the locale if it does not exist. */
c10d32c8 692 result = add_to_readlist (category, name, repertoire_name, 1, copy_locale);
70e51ab9
UD
693
694 assert (result != NULL);
695
c10d32c8 696 if ((result->avail & (1 << category)) == 0
70e51ab9 697 && locfile_read (result, charmap) != 0)
f16491eb
CD
698 record_error (4, errno, _("\
699cannot open locale definition file `%s'"), result->name);
70e51ab9
UD
700
701 return result;
702}