1 /* Copyright (C) 1995-2019 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/>. */
37 #include "localedef.h"
41 /* Undefine the following line in the production version. */
42 /* #define NDEBUG 1 */
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. */
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 /* If true create hard links to other locales (default). */
89 bool hard_links
= true;
91 /* Maximum number of retries when opening the locale archive. */
92 int max_locarchive_open_retry
= 10;
95 /* Name and version of program. */
96 static void print_version (FILE *stream
, struct argp_state
*state
);
97 void (*argp_program_version_hook
) (FILE *, struct argp_state
*) = print_version
;
100 #define OPT_QUIET 302
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
107 #define OPT_LITTLE_ENDIAN 400
108 #define OPT_BIG_ENDIAN 401
109 #define OPT_NO_WARN 402
111 #define OPT_NO_HARD_LINKS 404
113 /* Definitions of arguments for argp functions. */
114 static const struct argp_option options
[] =
116 { NULL
, 0, NULL
, 0, N_("Input Files:") },
117 { "charmap", 'f', N_("FILE"), 0,
118 N_("Symbolic character names defined in FILE") },
119 { "inputfile", 'i', N_("FILE"), 0,
120 N_("Source definitions are found in FILE") },
121 { "repertoire-map", 'u', N_("FILE"), 0,
122 N_("FILE contains mapping from symbolic names to UCS4 values") },
124 { NULL
, 0, NULL
, 0, N_("Output control:") },
125 { "force", 'c', NULL
, 0,
126 N_("Create output even if warning messages were issued") },
127 { "no-hard-links", OPT_NO_HARD_LINKS
, NULL
, 0,
128 N_("Do not create hard links between installed locales") },
129 { "prefix", OPT_PREFIX
, N_("PATH"), 0, N_("Optional output file prefix") },
130 { "posix", OPT_POSIX
, NULL
, 0, N_("Strictly conform to POSIX") },
131 { "quiet", OPT_QUIET
, NULL
, 0,
132 N_("Suppress warnings and information messages") },
133 { "verbose", 'v', NULL
, 0, N_("Print more messages") },
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") },
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") },
150 { "alias-file", 'A', N_("FILE"), 0,
151 N_("locale.alias file to consult when making archive")},
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") },
156 { NULL
, 0, NULL
, 0, NULL
}
159 /* Short description of program. */
160 static const char doc
[] = N_("Compile locale specification");
162 /* Strings for arguments in help texts. */
163 static const char args_doc
[] = N_("\
165 [--add-to-archive|--delete-from-archive] FILE...\n\
166 --list-archive [FILE]");
168 /* Prototype for option handler. */
169 static error_t
parse_opt (int key
, char *arg
, struct argp_state
*state
);
171 /* Function to print some extra text in the help message. */
172 static char *more_help (int key
, const char *text
, void *input
);
174 /* Data structure to communicate with argp functions. */
175 static struct argp argp
=
177 options
, parse_opt
, args_doc
, doc
, NULL
, more_help
181 /* Prototypes for local functions. */
182 static void error_print (void);
183 static const char *construct_output_path (char *path
);
184 static const char *normalize_codeset (const char *codeset
, size_t name_len
);
188 main (int argc
, char *argv
[])
190 const char *output_path
;
191 int cannot_write_why
;
192 struct charmap_t
*charmap
;
193 struct localedef_t global
;
196 /* Set initial values for global variables. */
198 posix_conformance
= getenv ("POSIXLY_CORRECT") != NULL
;
199 error_print_progname
= error_print
;
201 /* Set locale. Do not set LC_ALL because the other categories must
202 not be affected (according to POSIX.2). */
203 setlocale (LC_MESSAGES
, "");
204 setlocale (LC_CTYPE
, "");
206 /* Initialize the message catalog. */
207 textdomain (_libc_intl_domainname
);
209 /* Parse and process arguments. */
210 argp_err_exit_status
= 4;
211 argp_parse (&argp
, argc
, argv
, 0, &remaining
, NULL
);
213 /* Handle a few special cases. */
215 show_archive_content (remaining
> 1 ? argv
[remaining
] : NULL
, verbose
);
217 return add_locales_to_archive (argc
- remaining
, &argv
[remaining
],
219 if (delete_from_archive
)
220 return delete_locales_from_archive (argc
- remaining
, &argv
[remaining
]);
222 /* POSIX.2 requires to be verbose about missing characters in the
224 verbose
|= posix_conformance
;
226 if (argc
- remaining
!= 1)
228 /* We need exactly one non-option parameter. */
229 argp_help (&argp
, stdout
, ARGP_HELP_SEE
| ARGP_HELP_EXIT_ERR
,
230 program_invocation_short_name
);
234 /* The parameter describes the output path of the constructed files.
235 If the described files cannot be written return a NULL pointer. */
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
;
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");
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)
249 record_error (3, 0, _("\
250 FATAL: system does not define `_POSIX2_LOCALEDEF'"));
252 /* Process charmap file. */
253 charmap
= charmap_read (charmap_file
, verbose
, 1, be_quiet
, 1);
255 /* Add the first entry in the locale list. */
256 memset (&global
, '\0', sizeof (struct localedef_t
));
257 global
.name
= input_file
?: "/dev/stdin";
258 global
.needed
= ALL_LOCALES
;
261 /* Now read the locale file. */
262 if (locfile_read (&global
, charmap
) != 0)
263 record_error (4, errno
, _("\
264 cannot open locale definition file `%s'"), input_file
);
266 /* Perhaps we saw some `copy' instructions. */
269 struct localedef_t
*runp
= locales
;
271 while (runp
!= NULL
&& (runp
->needed
& runp
->avail
) == runp
->needed
)
275 /* Everything read. */
278 if (locfile_read (runp
, charmap
) != 0)
279 record_error (4, errno
, _("\
280 cannot open locale definition file `%s'"), runp
->name
);
283 /* Check the categories we processed in source form. */
284 check_all_categories (locales
, charmap
);
286 /* What we do next depends on the number of errors and warnings we
287 have generated in processing the input files.
289 * No errors: Write the output file.
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
295 * Errors: We don't write the output file and we exit with status 4
296 to indicate no output files were written.
298 The use of -c|--force writes the output file even if errors were
300 if (recorded_error_count
== 0 || force_output
!= 0)
302 if (cannot_write_why
!= 0)
303 record_error (4, cannot_write_why
, _("\
304 cannot write output files to `%s'"), output_path
? : argv
[remaining
]);
306 write_all_categories (locales
, charmap
, argv
[remaining
], output_path
);
309 record_error (4, 0, _("\
310 no output file produced because errors were issued"));
312 /* This exit status is prescribed by POSIX.2 4.35.7. */
313 exit (recorded_warning_count
!= 0);
316 /* Search warnings for matching warnings and if found enable those
317 warnings if ENABLED is true, otherwise disable the warnings. */
319 set_warnings (char *warnings
, bool enabled
)
321 char *tok
= warnings
;
322 char *copy
= (char *) malloc (strlen (warnings
) + 1);
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. */
330 while (isspace (*tok
) != 0)
333 while ((*save
++ = *tok
++) != '\0');
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
)
345 if (strcmp (tok
, "ascii") == 0)
346 warn_ascii
= enabled
;
347 else if (strcmp (tok
, "intcurrsym") == 0)
348 warn_int_curr_symbol
= enabled
;
354 /* Handle program arguments. */
356 parse_opt (int key
, char *arg
, struct argp_state
*state
)
364 posix_conformance
= 1;
372 case OPT_ADD_TO_ARCHIVE
:
373 add_to_archive
= true;
376 replace_archive
= true;
378 case OPT_DELETE_FROM_ARCHIVE
:
379 delete_from_archive
= true;
381 case OPT_LIST_ARCHIVE
:
384 case OPT_LITTLE_ENDIAN
:
385 set_big_endian (false);
388 set_big_endian (true);
391 /* Disable the warnings. */
392 set_warnings (arg
, false);
395 /* Enable the warnings. */
396 set_warnings (arg
, true);
398 case OPT_NO_HARD_LINKS
:
399 /* Do not hard link to other locales. */
415 repertoire_global
= arg
;
421 return ARGP_ERR_UNKNOWN
;
428 more_help (int key
, const char *text
, void *input
)
435 case ARGP_KEY_HELP_EXTRA
:
436 /* We print some extra information. */
437 if (asprintf (&tp
, gettext ("\
438 For bug reporting instructions, please see:\n\
439 %s.\n"), REPORT_BUGS_TO
) < 0)
441 if (asprintf (&cp
, gettext ("\
442 System's directory for character maps : %s\n\
443 repertoire maps: %s\n\
446 CHARMAP_PATH
, REPERTOIREMAP_PATH
, LOCALE_PATH
, tp
) < 0)
455 return (char *) text
;
458 /* Print the version information. */
460 print_version (FILE *stream
, struct argp_state
*state
)
462 fprintf (stream
, "localedef %s%s\n", PKGVERSION
, VERSION
);
463 fprintf (stream
, gettext ("\
464 Copyright (C) %s Free Software Foundation, Inc.\n\
465 This is free software; see the source for copying conditions. There is NO\n\
466 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
468 fprintf (stream
, gettext ("Written by %s.\n"), "Ulrich Drepper");
472 /* The address of this function will be assigned to the hook in the error
480 /* The parameter to localedef describes the output path. If it does
481 contain a '/' character it is a relative path. Otherwise it names the
482 locale this definition is for. */
484 construct_output_path (char *path
)
486 const char *normal
= NULL
;
490 if (strchr (path
, '/') == NULL
)
492 /* This is a system path. First examine whether the locale name
493 contains a reference to the codeset. This should be
498 /* We must be prepared for finding a CEN name or a location of
499 the introducing `.' where it is not possible anymore. */
500 while (*startp
!= '\0' && *startp
!= '@' && *startp
!= '.')
504 /* We found a codeset specification. Now find the end. */
506 while (*endp
!= '\0' && *endp
!= '@')
510 normal
= normalize_codeset (startp
, endp
- startp
);
513 /* This is to keep gcc quiet. */
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
521 n
= asprintf (&result
, "%s%s/%s%c", output_prefix
?: "",
522 COMPLOCALEDIR
, path
, '\0');
524 n
= asprintf (&result
, "%s%s/%.*s%s%s%c",
525 output_prefix
?: "", COMPLOCALEDIR
,
526 (int) (startp
- path
), path
, normal
, endp
, '\0');
531 endp
= result
+ n
- 1;
535 /* This is a user path. Please note the additional byte in the
536 memory allocation. */
537 size_t len
= strlen (path
) + 1;
538 result
= xmalloc (len
+ 1);
539 endp
= mempcpy (result
, path
, len
) - 1;
541 /* If the user specified an output path we cannot add the output
548 if (no_archive
&& euidaccess (result
, W_OK
) == -1)
549 /* Perhaps the directory does not exist now. Try to create it. */
553 if (mkdir (result
, 0777) < 0)
564 /* Normalize codeset name. There is no standard for the codeset
565 names. Normalization allows the user to use any of the common
568 normalize_codeset (const char *codeset
, size_t name_len
)
576 for (cnt
= 0; cnt
< name_len
; ++cnt
)
577 if (isalnum (codeset
[cnt
]))
581 if (isalpha (codeset
[cnt
]))
585 retval
= (char *) malloc ((only_digit
? 3 : 0) + len
+ 1);
590 wp
= stpcpy (retval
, "iso");
594 for (cnt
= 0; cnt
< name_len
; ++cnt
)
595 if (isalpha (codeset
[cnt
]))
596 *wp
++ = tolower (codeset
[cnt
]);
597 else if (isdigit (codeset
[cnt
]))
598 *wp
++ = codeset
[cnt
];
603 return (const char *) retval
;
608 add_to_readlist (int category
, const char *name
, const char *repertoire_name
,
609 int generate
, struct localedef_t
*copy_locale
)
611 struct localedef_t
*runp
= locales
;
613 while (runp
!= NULL
&& strcmp (name
, runp
->name
) != 0)
618 /* Add a new entry at the end. */
619 struct localedef_t
*newp
;
621 assert (generate
== 1);
623 newp
= xcalloc (1, sizeof (struct localedef_t
));
625 newp
->repertoire_name
= repertoire_name
;
628 runp
= locales
= newp
;
632 while (runp
->next
!= NULL
)
634 runp
= runp
->next
= newp
;
639 && (runp
->needed
& (1 << category
)) != 0
640 && (runp
->avail
& (1 << category
)) == 0)
641 record_error (5, 0, _("\
642 circular dependencies between locale definitions"));
644 if (copy_locale
!= NULL
)
646 if (runp
->categories
[category
].generic
!= NULL
)
647 record_error (5, 0, _("\
648 cannot add already read locale `%s' a second time"), name
);
650 runp
->categories
[category
].generic
=
651 copy_locale
->categories
[category
].generic
;
654 runp
->needed
|= 1 << category
;
661 find_locale (int category
, const char *name
, const char *repertoire_name
,
662 const struct charmap_t
*charmap
)
664 struct localedef_t
*result
;
666 /* Find the locale, but do not generate it since this would be a bug. */
667 result
= add_to_readlist (category
, name
, repertoire_name
, 0, NULL
);
669 assert (result
!= NULL
);
671 if ((result
->avail
& (1 << category
)) == 0
672 && locfile_read (result
, charmap
) != 0)
673 record_error (4, errno
, _("\
674 cannot open locale definition file `%s'"), result
->name
);
681 load_locale (int category
, const char *name
, const char *repertoire_name
,
682 const struct charmap_t
*charmap
, struct localedef_t
*copy_locale
)
684 struct localedef_t
*result
;
686 /* Generate the locale if it does not exist. */
687 result
= add_to_readlist (category
, name
, repertoire_name
, 1, copy_locale
);
689 assert (result
!= NULL
);
691 if ((result
->avail
& (1 << category
)) == 0
692 && locfile_read (result
, charmap
) != 0)
693 record_error (4, errno
, _("\
694 cannot open locale definition file `%s'"), result
->name
);