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/>. */
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 /* 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
;
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
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_("\
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
;
190 /* Set initial values for global variables. */
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. */
209 show_archive_content (remaining
> 1 ? argv
[remaining
] : NULL
, verbose
);
211 return add_locales_to_archive (argc
- remaining
, &argv
[remaining
],
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
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
);
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
;
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. */
263 struct localedef_t
*runp
= locales
;
265 while (runp
!= NULL
&& (runp
->needed
& runp
->avail
) == runp
->needed
)
269 /* Everything read. */
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
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
]);
300 write_all_categories (locales
, charmap
, argv
[remaining
], output_path
);
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. */
313 set_warnings (char *warnings
, bool enabled
)
315 char *tok
= warnings
;
316 char *copy
= (char *) malloc (strlen (warnings
) + 1);
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)
327 while ((*save
++ = *tok
++) != '\0');
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
)
339 if (strcmp (tok
, "ascii") == 0)
340 warn_ascii
= enabled
;
341 else if (strcmp (tok
, "intcurrsym") == 0)
342 warn_int_curr_symbol
= enabled
;
348 /* Handle program arguments. */
350 parse_opt (int key
, char *arg
, struct argp_state
*state
)
358 posix_conformance
= 1;
366 case OPT_ADD_TO_ARCHIVE
:
367 add_to_archive
= true;
370 replace_archive
= true;
372 case OPT_DELETE_FROM_ARCHIVE
:
373 delete_from_archive
= true;
375 case OPT_LIST_ARCHIVE
:
378 case OPT_LITTLE_ENDIAN
:
379 set_big_endian (false);
382 set_big_endian (true);
385 /* Disable the warnings. */
386 set_warnings (arg
, false);
389 /* Enable the warnings. */
390 set_warnings (arg
, true);
405 repertoire_global
= arg
;
411 return ARGP_ERR_UNKNOWN
;
418 more_help (int key
, const char *text
, void *input
)
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)
431 if (asprintf (&cp
, gettext ("\
432 System's directory for character maps : %s\n\
433 repertoire maps: %s\n\
436 CHARMAP_PATH
, REPERTOIREMAP_PATH
, LOCALE_PATH
, tp
) < 0)
445 return (char *) text
;
448 /* Print the version information. */
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\
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
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. */
474 construct_output_path (char *path
)
476 const char *normal
= NULL
;
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
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
!= '.')
494 /* We found a codeset specification. Now find the end. */
496 while (*endp
!= '\0' && *endp
!= '@')
500 normal
= normalize_codeset (startp
, endp
- startp
);
503 /* This is to keep gcc quiet. */
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
511 n
= asprintf (&result
, "%s%s/%s%c", output_prefix
?: "",
512 COMPLOCALEDIR
, path
, '\0');
514 n
= asprintf (&result
, "%s%s/%.*s%s%s%c",
515 output_prefix
?: "", COMPLOCALEDIR
,
516 (int) (startp
- path
), path
, normal
, endp
, '\0');
521 endp
= result
+ n
- 1;
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
538 if (no_archive
&& euidaccess (result
, W_OK
) == -1)
539 /* Perhaps the directory does not exist now. Try to create it. */
543 if (mkdir (result
, 0777) < 0)
554 /* Normalize codeset name. There is no standard for the codeset
555 names. Normalization allows the user to use any of the common
558 normalize_codeset (const char *codeset
, size_t name_len
)
566 for (cnt
= 0; cnt
< name_len
; ++cnt
)
567 if (isalnum (codeset
[cnt
]))
571 if (isalpha (codeset
[cnt
]))
575 retval
= (char *) malloc ((only_digit
? 3 : 0) + len
+ 1);
580 wp
= stpcpy (retval
, "iso");
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
];
593 return (const char *) retval
;
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)
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
));
615 newp
->repertoire_name
= repertoire_name
;
618 runp
= locales
= newp
;
622 while (runp
->next
!= NULL
)
624 runp
= runp
->next
= newp
;
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
);
640 runp
->categories
[category
].generic
=
641 copy_locale
->categories
[category
].generic
;
644 runp
->needed
|= 1 << category
;
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
);
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
);