]> 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
d614a753 1/* Copyright (C) 1995-2020 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);
7a12c6bb 183static const char *construct_output_path (char *path);
900bec85 184static const char *normalize_codeset (const char *codeset, size_t name_len);
19bc17a9
RM
185
186
187int
188main (int argc, char *argv[])
189{
19bc17a9
RM
190 const char *output_path;
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
RM
234 /* The parameter describes the output path of the constructed files.
235 If the described files cannot be written return a NULL pointer. */
b459976e
UD
236 output_path = construct_output_path (argv[remaining]);
237 if (output_path == NULL && ! no_archive)
238 error (4, errno, _("cannot create directory for output files"));
239 cannot_write_why = errno;
19bc17a9
RM
240
241 /* Now that the parameters are processed we have to reset the local
242 ctype locale. (P1003.2 4.35.5.2) */
243 setlocale (LC_CTYPE, "POSIX");
244
245 /* Look whether the system really allows locale definitions. POSIX
246 defines error code 3 for this situation so I think it must be
247 a fatal error (see P1003.2 4.35.8). */
248 if (sysconf (_SC_2_LOCALEDEF) < 0)
f16491eb
CD
249 record_error (3, 0, _("\
250FATAL: system does not define `_POSIX2_LOCALEDEF'"));
19bc17a9
RM
251
252 /* Process charmap file. */
8a6537b0 253 charmap = charmap_read (charmap_file, verbose, 1, be_quiet, 1);
4b10dd6c
UD
254
255 /* Add the first entry in the locale list. */
256 memset (&global, '\0', sizeof (struct localedef_t));
dcf56f42 257 global.name = input_file ?: "/dev/stdin";
4b10dd6c
UD
258 global.needed = ALL_LOCALES;
259 locales = &global;
19bc17a9
RM
260
261 /* Now read the locale file. */
4b10dd6c 262 if (locfile_read (&global, charmap) != 0)
f16491eb
CD
263 record_error (4, errno, _("\
264cannot open locale definition file `%s'"), input_file);
19bc17a9 265
4b10dd6c
UD
266 /* Perhaps we saw some `copy' instructions. */
267 while (1)
19bc17a9 268 {
4b10dd6c 269 struct localedef_t *runp = locales;
19bc17a9 270
b9eb05d6 271 while (runp != NULL && (runp->needed & runp->avail) == runp->needed)
4b10dd6c 272 runp = runp->next;
19bc17a9 273
4b10dd6c
UD
274 if (runp == NULL)
275 /* Everything read. */
276 break;
277
278 if (locfile_read (runp, charmap) != 0)
f16491eb
CD
279 record_error (4, errno, _("\
280cannot open locale definition file `%s'"), runp->name);
19bc17a9 281 }
19bc17a9
RM
282
283 /* Check the categories we processed in source form. */
4b10dd6c 284 check_all_categories (locales, charmap);
19bc17a9 285
f16491eb
CD
286 /* What we do next depends on the number of errors and warnings we
287 have generated in processing the input files.
288
289 * No errors: Write the output file.
290
291 * Some warnings: Write the output file and exit with status 1 to
292 indicate there may be problems using the output file e.g. missing
293 data that makes it difficult to use
294
295 * Errors: We don't write the output file and we exit with status 4
296 to indicate no output files were written.
297
298 The use of -c|--force writes the output file even if errors were
299 seen. */
300 if (recorded_error_count == 0 || force_output != 0)
19bc17a9
RM
301 {
302 if (cannot_write_why != 0)
f16491eb
CD
303 record_error (4, cannot_write_why, _("\
304cannot write output files to `%s'"), output_path ? : argv[remaining]);
19bc17a9 305 else
a7b65cdc 306 write_all_categories (locales, charmap, argv[remaining], output_path);
19bc17a9
RM
307 }
308 else
f16491eb
CD
309 record_error (4, 0, _("\
310no output file produced because errors were issued"));
19bc17a9
RM
311
312 /* This exit status is prescribed by POSIX.2 4.35.7. */
f16491eb 313 exit (recorded_warning_count != 0);
19bc17a9
RM
314}
315
02eec681
CD
316/* Search warnings for matching warnings and if found enable those
317 warnings if ENABLED is true, otherwise disable the warnings. */
318static void
319set_warnings (char *warnings, bool enabled)
320{
321 char *tok = warnings;
322 char *copy = (char *) malloc (strlen (warnings) + 1);
323 char *save = copy;
324
325 /* As we make a copy of the warnings list we remove all spaces from
326 the warnings list to make the processing a more robust. We don't
327 support spaces in a warning name. */
328 do
329 {
330 while (isspace (*tok) != 0)
331 tok++;
332 }
333 while ((*save++ = *tok++) != '\0');
334
335 warnings = copy;
336
337 /* Tokenize the input list of warnings to set, compare them to
338 known warnings, and set the warning. We purposely ignore unknown
339 warnings, and are thus forward compatible, users can attempt to
340 disable whaterver new warnings they know about, but we will only
341 disable those *we* known about. */
342 while ((tok = strtok_r (warnings, ",", &save)) != NULL)
343 {
344 warnings = NULL;
345 if (strcmp (tok, "ascii") == 0)
346 warn_ascii = enabled;
347 else if (strcmp (tok, "intcurrsym") == 0)
348 warn_int_curr_symbol = enabled;
349 }
350
351 free (copy);
352}
19bc17a9 353
5a97622d
UD
354/* Handle program arguments. */
355static error_t
356parse_opt (int key, char *arg, struct argp_state *state)
357{
358 switch (key)
359 {
360 case OPT_QUIET:
361 be_quiet = 1;
362 break;
363 case OPT_POSIX:
364 posix_conformance = 1;
365 break;
8a82cc30
UD
366 case OPT_PREFIX:
367 output_prefix = arg;
368 break;
a7b65cdc
UD
369 case OPT_NO_ARCHIVE:
370 no_archive = true;
371 break;
372 case OPT_ADD_TO_ARCHIVE:
373 add_to_archive = true;
374 break;
375 case OPT_REPLACE:
376 replace_archive = true;
377 break;
378 case OPT_DELETE_FROM_ARCHIVE:
379 delete_from_archive = true;
380 break;
381 case OPT_LIST_ARCHIVE:
382 list_archive = true;
383 break;
6055173a
JM
384 case OPT_LITTLE_ENDIAN:
385 set_big_endian (false);
386 break;
387 case OPT_BIG_ENDIAN:
388 set_big_endian (true);
389 break;
02eec681
CD
390 case OPT_NO_WARN:
391 /* Disable the warnings. */
392 set_warnings (arg, false);
393 break;
394 case OPT_WARN:
395 /* Enable the warnings. */
396 set_warnings (arg, true);
397 break;
8cebd4ff
CD
398 case OPT_NO_HARD_LINKS:
399 /* Do not hard link to other locales. */
400 hard_links = false;
401 break;
5a97622d
UD
402 case 'c':
403 force_output = 1;
404 break;
405 case 'f':
406 charmap_file = arg;
407 break;
cb09a2cd
RM
408 case 'A':
409 alias_file = arg;
410 break;
5a97622d
UD
411 case 'i':
412 input_file = arg;
413 break;
414 case 'u':
4b10dd6c 415 repertoire_global = arg;
5a97622d
UD
416 break;
417 case 'v':
418 verbose = 1;
419 break;
420 default:
421 return ARGP_ERR_UNKNOWN;
422 }
423 return 0;
424}
425
426
427static char *
428more_help (int key, const char *text, void *input)
429{
430 char *cp;
8b748aed 431 char *tp;
5a97622d
UD
432
433 switch (key)
434 {
435 case ARGP_KEY_HELP_EXTRA:
436 /* We print some extra information. */
8b748aed
JM
437 if (asprintf (&tp, gettext ("\
438For bug reporting instructions, please see:\n\
439%s.\n"), REPORT_BUGS_TO) < 0)
440 return NULL;
322861e8 441 if (asprintf (&cp, 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"),
8b748aed
JM
446 CHARMAP_PATH, REPERTOIREMAP_PATH, LOCALE_PATH, tp) < 0)
447 {
448 free (tp);
449 return NULL;
450 }
5a97622d
UD
451 return cp;
452 default:
453 break;
454 }
455 return (char *) text;
456}
457
458/* Print the version information. */
459static void
460print_version (FILE *stream, struct argp_state *state)
461{
8b748aed 462 fprintf (stream, "localedef %s%s\n", PKGVERSION, VERSION);
5a97622d
UD
463 fprintf (stream, gettext ("\
464Copyright (C) %s Free Software Foundation, Inc.\n\
465This is free software; see the source for copying conditions. There is NO\n\
466warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
c9123888 467"), "2019");
5a97622d
UD
468 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
469}
470
471
19bc17a9
RM
472/* The address of this function will be assigned to the hook in the error
473 functions. */
474static void
4b10dd6c 475error_print (void)
19bc17a9 476{
19bc17a9
RM
477}
478
479
480/* The parameter to localedef describes the output path. If it does
6d52618b 481 contain a '/' character it is a relative path. Otherwise it names the
19bc17a9
RM
482 locale this definition is for. */
483static const char *
7a12c6bb 484construct_output_path (char *path)
19bc17a9 485{
c4029823 486 const char *normal = NULL;
19bc17a9 487 char *result;
4b10dd6c 488 char *endp;
19bc17a9
RM
489
490 if (strchr (path, '/') == NULL)
491 {
7a12c6bb
RM
492 /* This is a system path. First examine whether the locale name
493 contains a reference to the codeset. This should be
494 normalized. */
4b10dd6c 495 char *startp;
7a12c6bb
RM
496
497 startp = path;
498 /* We must be prepared for finding a CEN name or a location of
499 the introducing `.' where it is not possible anymore. */
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;
506 while (*endp != '\0' && *endp != '@')
507 ++endp;
508
509 if (endp > startp)
900bec85 510 normal = normalize_codeset (startp, endp - startp);
7a12c6bb 511 }
c4029823
UD
512 else
513 /* This is to keep gcc quiet. */
514 endp = NULL;
19bc17a9 515
7a12c6bb
RM
516 /* We put an additional '\0' at the end of the string because at
517 the end of the function we need another byte for the trailing
518 '/'. */
2952b117 519 ssize_t n;
7a12c6bb 520 if (normal == NULL)
90fe682d
CD
521 n = asprintf (&result, "%s%s/%s%c", output_prefix ?: "",
522 COMPLOCALEDIR, path, '\0');
7a12c6bb 523 else
8a82cc30 524 n = asprintf (&result, "%s%s/%.*s%s%s%c",
90fe682d 525 output_prefix ?: "", COMPLOCALEDIR,
dbbbaf53 526 (int) (startp - path), path, normal, endp, '\0');
4b10dd6c 527
383bd1c5
UD
528 if (n < 0)
529 return NULL;
530
16fe0a88 531 endp = result + n - 1;
19bc17a9
RM
532 }
533 else
534 {
7a12c6bb
RM
535 /* This is a user path. Please note the additional byte in the
536 memory allocation. */
4b10dd6c
UD
537 size_t len = strlen (path) + 1;
538 result = xmalloc (len + 1);
30e0f9c3 539 endp = mempcpy (result, path, len) - 1;
a7b65cdc
UD
540
541 /* If the user specified an output path we cannot add the output
542 to the archive. */
543 no_archive = true;
19bc17a9
RM
544 }
545
546 errno = 0;
547
9813858f 548 if (no_archive && euidaccess (result, W_OK) == -1)
19bc17a9
RM
549 /* Perhaps the directory does not exist now. Try to create it. */
550 if (errno == ENOENT)
551 {
552 errno = 0;
383bd1c5
UD
553 if (mkdir (result, 0777) < 0)
554 return NULL;
19bc17a9
RM
555 }
556
4b10dd6c
UD
557 *endp++ = '/';
558 *endp = '\0';
19bc17a9
RM
559
560 return result;
561}
900bec85 562
4b10dd6c 563
900bec85
UD
564/* Normalize codeset name. There is no standard for the codeset
565 names. Normalization allows the user to use any of the common
566 names. */
567static const char *
9dd346ff 568normalize_codeset (const char *codeset, size_t name_len)
900bec85
UD
569{
570 int len = 0;
571 int only_digit = 1;
572 char *retval;
573 char *wp;
574 size_t cnt;
575
576 for (cnt = 0; cnt < name_len; ++cnt)
577 if (isalnum (codeset[cnt]))
578 {
579 ++len;
580
581 if (isalpha (codeset[cnt]))
582 only_digit = 0;
583 }
584
585 retval = (char *) malloc ((only_digit ? 3 : 0) + len + 1);
586
587 if (retval != NULL)
588 {
589 if (only_digit)
590 wp = stpcpy (retval, "iso");
591 else
592 wp = retval;
593
594 for (cnt = 0; cnt < name_len; ++cnt)
595 if (isalpha (codeset[cnt]))
4b10dd6c 596 *wp++ = tolower (codeset[cnt]);
900bec85
UD
597 else if (isdigit (codeset[cnt]))
598 *wp++ = codeset[cnt];
599
600 *wp = '\0';
601 }
602
603 return (const char *) retval;
604}
4b10dd6c
UD
605
606
607struct localedef_t *
c10d32c8 608add_to_readlist (int category, const char *name, const char *repertoire_name,
07dab0c3 609 int generate, struct localedef_t *copy_locale)
4b10dd6c
UD
610{
611 struct localedef_t *runp = locales;
612
613 while (runp != NULL && strcmp (name, runp->name) != 0)
614 runp = runp->next;
615
616 if (runp == NULL)
617 {
618 /* Add a new entry at the end. */
b9eb05d6
UD
619 struct localedef_t *newp;
620
621 assert (generate == 1);
622
623 newp = xcalloc (1, sizeof (struct localedef_t));
4b10dd6c
UD
624 newp->name = name;
625 newp->repertoire_name = repertoire_name;
626
627 if (locales == NULL)
628 runp = locales = newp;
629 else
630 {
631 runp = locales;
632 while (runp->next != NULL)
633 runp = runp->next;
634 runp = runp->next = newp;
635 }
636 }
637
69f6a804 638 if (generate
c10d32c8
UD
639 && (runp->needed & (1 << category)) != 0
640 && (runp->avail & (1 << category)) == 0)
f16491eb
CD
641 record_error (5, 0, _("\
642circular dependencies between locale definitions"));
4b10dd6c 643
07dab0c3
UD
644 if (copy_locale != NULL)
645 {
c10d32c8 646 if (runp->categories[category].generic != NULL)
f16491eb
CD
647 record_error (5, 0, _("\
648cannot add already read locale `%s' a second time"), name);
07dab0c3 649 else
c10d32c8
UD
650 runp->categories[category].generic =
651 copy_locale->categories[category].generic;
07dab0c3
UD
652 }
653
c10d32c8 654 runp->needed |= 1 << category;
4b10dd6c
UD
655
656 return runp;
657}
658
659
660struct localedef_t *
c10d32c8 661find_locale (int category, const char *name, const char *repertoire_name,
47e8b443 662 const struct charmap_t *charmap)
4b10dd6c 663{
b9eb05d6
UD
664 struct localedef_t *result;
665
666 /* Find the locale, but do not generate it since this would be a bug. */
c10d32c8 667 result = add_to_readlist (category, name, repertoire_name, 0, NULL);
b9eb05d6
UD
668
669 assert (result != NULL);
4b10dd6c 670
c10d32c8 671 if ((result->avail & (1 << category)) == 0
b9eb05d6 672 && locfile_read (result, charmap) != 0)
f16491eb
CD
673 record_error (4, errno, _("\
674cannot open locale definition file `%s'"), result->name);
4b10dd6c
UD
675
676 return result;
677}
70e51ab9
UD
678
679
680struct localedef_t *
c10d32c8 681load_locale (int category, const char *name, const char *repertoire_name,
47e8b443 682 const struct charmap_t *charmap, struct localedef_t *copy_locale)
70e51ab9
UD
683{
684 struct localedef_t *result;
685
686 /* Generate the locale if it does not exist. */
c10d32c8 687 result = add_to_readlist (category, name, repertoire_name, 1, copy_locale);
70e51ab9
UD
688
689 assert (result != NULL);
690
c10d32c8 691 if ((result->avail & (1 << category)) == 0
70e51ab9 692 && locfile_read (result, charmap) != 0)
f16491eb
CD
693 record_error (4, errno, _("\
694cannot open locale definition file `%s'"), result->name);
70e51ab9
UD
695
696 return result;
697}