1 | /* Copyright (C) 1998-2021 Free Software Foundation, Inc. |
2 | This file is part of the GNU C Library. |
3 | Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998. |
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 <https://www.gnu.org/licenses/>. */ |
17 | |
18 | #ifdef HAVE_CONFIG_H |
19 | # include <config.h> |
20 | #endif |
21 | |
22 | #include <byteswap.h> |
23 | #include <langinfo.h> |
24 | #include <string.h> |
25 | #include <stdint.h> |
26 | #include <sys/uio.h> |
27 | |
28 | #include <assert.h> |
29 | |
30 | #include "localedef.h" |
31 | #include "localeinfo.h" |
32 | #include "locfile.h" |
33 | |
34 | |
35 | static struct |
36 | { |
37 | const char ab2[3]; |
38 | const char ab3[4]; |
39 | uint32_t num; |
40 | } iso3166[] = |
41 | { |
42 | #define DEFINE_COUNTRY_CODE(Name, Ab2, Ab3, Num) \ |
43 | { #Ab2, #Ab3, Num }, |
44 | #include "iso-3166.def" |
45 | }; |
46 | |
47 | |
48 | static struct |
49 | { |
50 | const char ab[3]; |
51 | const char term[4]; |
52 | const char lib[4]; |
53 | } iso639[] = |
54 | { |
55 | #define DEFINE_LANGUAGE_CODE(Name, Ab, Term, Lib) \ |
56 | { #Ab, #Term, #Lib }, |
57 | #define DEFINE_LANGUAGE_CODE3(Name, Term, Lib) \ |
58 | { "", #Term, #Lib }, |
59 | #define DEFINE_LANGUAGE_CODE2(Name, Term) \ |
60 | { "", #Term, "" }, |
61 | #include "iso-639.def" |
62 | }; |
63 | |
64 | |
65 | /* The real definition of the struct for the LC_ADDRESS locale. */ |
66 | struct locale_address_t |
67 | { |
68 | const char *postal_fmt; |
69 | const char *country_name; |
70 | const char *country_post; |
71 | const char *country_ab2; |
72 | const char *country_ab3; |
73 | uint32_t country_num; |
74 | const char *country_car; |
75 | const char *country_isbn; |
76 | const char *lang_name; |
77 | const char *lang_ab; |
78 | const char *lang_term; |
79 | const char *lang_lib; |
80 | }; |
81 | |
82 | |
83 | static void |
84 | address_startup (struct linereader *lr, struct localedef_t *locale, |
85 | int ignore_content) |
86 | { |
87 | if (!ignore_content) |
88 | locale->categories[LC_ADDRESS].address = |
89 | (struct locale_address_t *) xcalloc (1, |
90 | sizeof (struct locale_address_t)); |
91 | |
92 | if (lr != NULL) |
93 | { |
94 | lr->translate_strings = 1; |
95 | lr->return_widestr = 0; |
96 | } |
97 | } |
98 | |
99 | |
100 | void |
101 | address_finish (struct localedef_t *locale, const struct charmap_t *charmap) |
102 | { |
103 | struct locale_address_t *address = locale->categories[LC_ADDRESS].address; |
104 | size_t cnt; |
105 | int helper; |
106 | int nothing = 0; |
107 | |
108 | /* Now resolve copying and also handle completely missing definitions. */ |
109 | if (address == NULL) |
110 | { |
111 | /* First see whether we were supposed to copy. If yes, find the |
112 | actual definition. */ |
113 | if (locale->copy_name[LC_ADDRESS] != NULL) |
114 | { |
115 | /* Find the copying locale. This has to happen transitively since |
116 | the locale we are copying from might also copying another one. */ |
117 | struct localedef_t *from = locale; |
118 | |
119 | do |
120 | from = find_locale (LC_ADDRESS, from->copy_name[LC_ADDRESS], |
121 | from->repertoire_name, charmap); |
122 | while (from->categories[LC_ADDRESS].address == NULL |
123 | && from->copy_name[LC_ADDRESS] != NULL); |
124 | |
125 | address = locale->categories[LC_ADDRESS].address |
126 | = from->categories[LC_ADDRESS].address; |
127 | } |
128 | |
129 | /* If there is still no definition issue an warning and create an |
130 | empty one. */ |
131 | if (address == NULL) |
132 | { |
133 | record_warning (_("\ |
134 | No definition for %s category found" ), "LC_ADDRESS" ); |
135 | address_startup (NULL, locale, 0); |
136 | address = locale->categories[LC_ADDRESS].address; |
137 | nothing = 1; |
138 | } |
139 | } |
140 | |
141 | if (address->postal_fmt == NULL) |
142 | { |
143 | if (! nothing) |
144 | record_error (0, 0, _("%s: field `%s' not defined" ), |
145 | "LC_ADDRESS" , "postal_fmt" ); |
146 | /* Use as the default value the value of the i18n locale. */ |
147 | address->postal_fmt = "%a%N%f%N%d%N%b%N%s %h %e %r%N%C-%z %T%N%c%N" ; |
148 | } |
149 | else |
150 | { |
151 | /* We must check whether the format string contains only the allowed |
152 | escape sequences. Last checked against ISO 30112 WD10 [2014]. */ |
153 | const char *cp = address->postal_fmt; |
154 | |
155 | if (*cp == '\0') |
156 | record_error (0, 0, _("%s: field `%s' must not be empty" ), |
157 | "LC_ADDRESS" , "postal_fmt" ); |
158 | else |
159 | while (*cp != '\0') |
160 | { |
161 | if (*cp == '%') |
162 | { |
163 | if (*++cp == 'R') |
164 | /* Romanize-flag. */ |
165 | ++cp; |
166 | if (strchr ("nafdbshNtreClzTSc%" , *cp) == NULL) |
167 | { |
168 | record_error (0, 0, _("\ |
169 | %s: invalid escape `%%%c' sequence in field `%s'" ), |
170 | "LC_ADDRESS" , *cp, "postal_fmt" ); |
171 | break; |
172 | } |
173 | } |
174 | ++cp; |
175 | } |
176 | } |
177 | |
178 | #define TEST_ELEM(cat) \ |
179 | if (address->cat == NULL) \ |
180 | { \ |
181 | if (verbose && ! nothing) \ |
182 | record_warning (_("%s: field `%s' not defined"), "LC_ADDRESS", #cat); \ |
183 | address->cat = ""; \ |
184 | } |
185 | |
186 | TEST_ELEM (country_name); |
187 | /* XXX Test against list of defined codes. */ |
188 | TEST_ELEM (country_post); |
189 | /* XXX Test against list of defined codes. */ |
190 | TEST_ELEM (country_car); |
191 | /* XXX Test against list of defined codes. */ |
192 | TEST_ELEM (country_isbn); |
193 | TEST_ELEM (lang_name); |
194 | |
195 | helper = 1; |
196 | if (address->lang_term == NULL) |
197 | { |
198 | if (verbose && ! nothing) |
199 | record_warning (_("%s: field `%s' not defined" ), "LC_ADDRESS" , |
200 | "lang_term" ); |
201 | address->lang_term = "" ; |
202 | cnt = sizeof (iso639) / sizeof (iso639[0]); |
203 | } |
204 | else if (address->lang_term[0] == '\0') |
205 | { |
206 | if (verbose) |
207 | record_warning (_("%s: field `%s' must not be empty" ), "LC_ADDRESS" , |
208 | "lang_term" ); |
209 | cnt = sizeof (iso639) / sizeof (iso639[0]); |
210 | } |
211 | else |
212 | { |
213 | /* Look for this language in the table. */ |
214 | for (cnt = 0; cnt < sizeof (iso639) / sizeof (iso639[0]); ++cnt) |
215 | if (strcmp (address->lang_term, iso639[cnt].term) == 0) |
216 | break; |
217 | if (cnt == sizeof (iso639) / sizeof (iso639[0])) |
218 | record_error (0, 0, _("\ |
219 | %s: terminology language code `%s' not defined" ), |
220 | "LC_ADDRESS" , address->lang_term); |
221 | } |
222 | |
223 | if (address->lang_ab == NULL) |
224 | { |
225 | if ((cnt == sizeof (iso639) / sizeof (iso639[0]) |
226 | || iso639[cnt].ab[0] != '\0') |
227 | && verbose && ! nothing) |
228 | record_warning (_("%s: field `%s' not defined" ), "LC_ADDRESS" , |
229 | "lang_ab" ); |
230 | address->lang_ab = "" ; |
231 | } |
232 | else if (address->lang_ab[0] == '\0') |
233 | { |
234 | if ((cnt == sizeof (iso639) / sizeof (iso639[0]) |
235 | || iso639[cnt].ab[0] != '\0') |
236 | && verbose) |
237 | record_warning (_("%s: field `%s' must not be empty" ), |
238 | "LC_ADDRESS" , "lang_ab" ); |
239 | } |
240 | else if (cnt < sizeof (iso639) / sizeof (iso639[0]) |
241 | && iso639[cnt].ab[0] == '\0') |
242 | { |
243 | record_error (0, 0, _("%s: field `%s' must not be defined" ), |
244 | "LC_ADDRESS" , "lang_ab" ); |
245 | |
246 | address->lang_ab = "" ; |
247 | } |
248 | else |
249 | { |
250 | if (cnt == sizeof (iso639) / sizeof (iso639[0])) |
251 | { |
252 | helper = 2; |
253 | for (cnt = 0; cnt < sizeof (iso639) / sizeof (iso639[0]); ++cnt) |
254 | if (strcmp (address->lang_ab, iso639[cnt].ab) == 0) |
255 | break; |
256 | if (cnt == sizeof (iso639) / sizeof (iso639[0])) |
257 | record_error (0, 0, _("\ |
258 | %s: language abbreviation `%s' not defined" ), |
259 | "LC_ADDRESS" , address->lang_ab); |
260 | } |
261 | else |
262 | if (strcmp (iso639[cnt].ab, address->lang_ab) != 0 |
263 | && iso639[cnt].ab[0] != '\0') |
264 | record_error (0, 0, _("\ |
265 | %s: `%s' value does not match `%s' value" ), |
266 | "LC_ADDRESS" , "lang_ab" , "lang_term" ); |
267 | } |
268 | |
269 | if (address->lang_lib == NULL) |
270 | /* This is no error. */ |
271 | address->lang_lib = address->lang_term; |
272 | else if (address->lang_lib[0] == '\0') |
273 | { |
274 | if (verbose) |
275 | record_warning (_("%s: field `%s' must not be empty" ), |
276 | "LC_ADDRESS" , "lang_lib" ); |
277 | } |
278 | else |
279 | { |
280 | if (cnt == sizeof (iso639) / sizeof (iso639[0])) |
281 | { |
282 | for (cnt = 0; cnt < sizeof (iso639) / sizeof (iso639[0]); ++cnt) |
283 | if (strcmp (address->lang_lib, iso639[cnt].lib) == 0) |
284 | break; |
285 | if (cnt == sizeof (iso639) / sizeof (iso639[0])) |
286 | record_error (0, 0, _("\ |
287 | %s: language abbreviation `%s' not defined" ), |
288 | "LC_ADDRESS" , address->lang_lib); |
289 | } |
290 | else |
291 | if (strcmp (iso639[cnt].ab, address->lang_ab) != 0) |
292 | record_error (0, 0, _("\ |
293 | %s: `%s' value does not match `%s' value" ), "LC_ADDRESS" , "lang_lib" , |
294 | helper == 1 ? "lang_term" : "lang_ab" ); |
295 | } |
296 | |
297 | if (address->country_num == 0) |
298 | { |
299 | if (verbose && ! nothing) |
300 | record_warning (_("%s: field `%s' not defined" ), "LC_ADDRESS" , |
301 | "country_num" ); |
302 | cnt = sizeof (iso3166) / sizeof (iso3166[0]); |
303 | } |
304 | else |
305 | { |
306 | for (cnt = 0; cnt < sizeof (iso3166) / sizeof (iso3166[0]); ++cnt) |
307 | if (address->country_num == iso3166[cnt].num) |
308 | break; |
309 | |
310 | if (cnt == sizeof (iso3166) / sizeof (iso3166[0])) |
311 | record_error (0, 0, _("\ |
312 | %s: numeric country code `%d' not valid" ), |
313 | "LC_ADDRESS" , address->country_num); |
314 | } |
315 | |
316 | if (address->country_ab2 == NULL) |
317 | { |
318 | if (verbose && ! nothing) |
319 | record_warning (_("%s: field `%s' not defined" ), "LC_ADDRESS" , |
320 | "country_ab2" ); |
321 | address->country_ab2 = " " ; |
322 | } |
323 | else if (cnt != sizeof (iso3166) / sizeof (iso3166[0]) |
324 | && strcmp (address->country_ab2, iso3166[cnt].ab2) != 0) |
325 | record_error (0, 0, _("%s: `%s' value does not match `%s' value" ), |
326 | "LC_ADDRESS" , "country_ab2" , "country_num" ); |
327 | |
328 | if (address->country_ab3 == NULL) |
329 | { |
330 | if (verbose && ! nothing) |
331 | record_warning (_("%s: field `%s' not defined" ), "LC_ADDRESS" , |
332 | "country_ab3" ); |
333 | address->country_ab3 = " " ; |
334 | } |
335 | else if (cnt != sizeof (iso3166) / sizeof (iso3166[0]) |
336 | && strcmp (address->country_ab3, iso3166[cnt].ab3) != 0) |
337 | record_error (0, 0, _("\ |
338 | %s: `%s' value does not match `%s' value" ), |
339 | "LC_ADDRESS" , "country_ab3" , "country_num" ); |
340 | } |
341 | |
342 | |
343 | void |
344 | address_output (struct localedef_t *locale, const struct charmap_t *charmap, |
345 | const char *output_path) |
346 | { |
347 | struct locale_address_t *address = locale->categories[LC_ADDRESS].address; |
348 | struct locale_file file; |
349 | |
350 | init_locale_data (&file, _NL_ITEM_INDEX (_NL_NUM_LC_ADDRESS)); |
351 | add_locale_string (&file, address->postal_fmt); |
352 | add_locale_string (&file, address->country_name); |
353 | add_locale_string (&file, address->country_post); |
354 | add_locale_string (&file, address->country_ab2); |
355 | add_locale_string (&file, address->country_ab3); |
356 | add_locale_string (&file, address->country_car); |
357 | add_locale_uint32 (&file, address->country_num); |
358 | add_locale_string (&file, address->country_isbn); |
359 | add_locale_string (&file, address->lang_name); |
360 | add_locale_string (&file, address->lang_ab); |
361 | add_locale_string (&file, address->lang_term); |
362 | add_locale_string (&file, address->lang_lib); |
363 | add_locale_string (&file, charmap->code_set_name); |
364 | write_locale_data (output_path, LC_ADDRESS, "LC_ADDRESS" , &file); |
365 | } |
366 | |
367 | |
368 | /* The parser for the LC_ADDRESS section of the locale definition. */ |
369 | void |
370 | address_read (struct linereader *ldfile, struct localedef_t *result, |
371 | const struct charmap_t *charmap, const char *repertoire_name, |
372 | int ignore_content) |
373 | { |
374 | struct locale_address_t *address; |
375 | struct token *now; |
376 | struct token *arg; |
377 | enum token_t nowtok; |
378 | |
379 | /* The rest of the line containing `LC_ADDRESS' must be free. */ |
380 | lr_ignore_rest (ldfile, 1); |
381 | |
382 | |
383 | do |
384 | { |
385 | now = lr_token (ldfile, charmap, result, NULL, verbose); |
386 | nowtok = now->tok; |
387 | } |
388 | while (nowtok == tok_eol); |
389 | |
390 | /* If we see `copy' now we are almost done. */ |
391 | if (nowtok == tok_copy) |
392 | { |
393 | handle_copy (ldfile, charmap, repertoire_name, result, tok_lc_address, |
394 | LC_ADDRESS, "LC_ADDRESS" , ignore_content); |
395 | return; |
396 | } |
397 | |
398 | /* Prepare the data structures. */ |
399 | address_startup (ldfile, result, ignore_content); |
400 | address = result->categories[LC_ADDRESS].address; |
401 | |
402 | while (1) |
403 | { |
404 | /* Of course we don't proceed beyond the end of file. */ |
405 | if (nowtok == tok_eof) |
406 | break; |
407 | |
408 | /* Ignore empty lines. */ |
409 | if (nowtok == tok_eol) |
410 | { |
411 | now = lr_token (ldfile, charmap, result, NULL, verbose); |
412 | nowtok = now->tok; |
413 | continue; |
414 | } |
415 | |
416 | switch (nowtok) |
417 | { |
418 | #define STR_ELEM(cat) \ |
419 | case tok_##cat: \ |
420 | /* Ignore the rest of the line if we don't need the input of \ |
421 | this line. */ \ |
422 | if (ignore_content) \ |
423 | { \ |
424 | lr_ignore_rest (ldfile, 0); \ |
425 | break; \ |
426 | } \ |
427 | \ |
428 | arg = lr_token (ldfile, charmap, result, NULL, verbose); \ |
429 | if (arg->tok != tok_string) \ |
430 | goto err_label; \ |
431 | if (address->cat != NULL) \ |
432 | lr_error (ldfile, _("\ |
433 | %s: field `%s' declared more than once"), "LC_ADDRESS", #cat); \ |
434 | else if (!ignore_content && arg->val.str.startmb == NULL) \ |
435 | { \ |
436 | lr_error (ldfile, _("\ |
437 | %s: unknown character in field `%s'"), "LC_ADDRESS", #cat); \ |
438 | address->cat = ""; \ |
439 | } \ |
440 | else if (!ignore_content) \ |
441 | address->cat = arg->val.str.startmb; \ |
442 | break |
443 | |
444 | STR_ELEM (postal_fmt); |
445 | STR_ELEM (country_name); |
446 | STR_ELEM (country_post); |
447 | STR_ELEM (country_ab2); |
448 | STR_ELEM (country_ab3); |
449 | STR_ELEM (country_car); |
450 | STR_ELEM (lang_name); |
451 | STR_ELEM (lang_ab); |
452 | STR_ELEM (lang_term); |
453 | STR_ELEM (lang_lib); |
454 | |
455 | #define INT_STR_ELEM(cat) \ |
456 | case tok_##cat: \ |
457 | /* Ignore the rest of the line if we don't need the input of \ |
458 | this line. */ \ |
459 | if (ignore_content) \ |
460 | { \ |
461 | lr_ignore_rest (ldfile, 0); \ |
462 | break; \ |
463 | } \ |
464 | \ |
465 | arg = lr_token (ldfile, charmap, result, NULL, verbose); \ |
466 | if (arg->tok != tok_string && arg->tok != tok_number) \ |
467 | goto err_label; \ |
468 | if (address->cat != NULL) \ |
469 | lr_error (ldfile, _("\ |
470 | %s: field `%s' declared more than once"), "LC_ADDRESS", #cat); \ |
471 | else if (!ignore_content && arg->tok == tok_string \ |
472 | && arg->val.str.startmb == NULL) \ |
473 | { \ |
474 | lr_error (ldfile, _("\ |
475 | %s: unknown character in field `%s'"), "LC_ADDRESS", #cat); \ |
476 | address->cat = ""; \ |
477 | } \ |
478 | else if (!ignore_content) \ |
479 | { \ |
480 | if (arg->tok == tok_string) \ |
481 | address->cat = arg->val.str.startmb; \ |
482 | else \ |
483 | { \ |
484 | char *numbuf = (char *) xmalloc (21); \ |
485 | snprintf (numbuf, 21, "%ld", arg->val.num); \ |
486 | address->cat = numbuf; \ |
487 | } \ |
488 | } \ |
489 | break |
490 | |
491 | INT_STR_ELEM (country_isbn); |
492 | |
493 | #define INT_ELEM(cat) \ |
494 | case tok_##cat: \ |
495 | /* Ignore the rest of the line if we don't need the input of \ |
496 | this line. */ \ |
497 | if (ignore_content) \ |
498 | { \ |
499 | lr_ignore_rest (ldfile, 0); \ |
500 | break; \ |
501 | } \ |
502 | \ |
503 | arg = lr_token (ldfile, charmap, result, NULL, verbose); \ |
504 | if (arg->tok != tok_number) \ |
505 | goto err_label; \ |
506 | else if (address->cat != 0) \ |
507 | lr_error (ldfile, _("\ |
508 | %s: field `%s' declared more than once"), "LC_ADDRESS", #cat); \ |
509 | else if (!ignore_content) \ |
510 | address->cat = arg->val.num; \ |
511 | break |
512 | |
513 | INT_ELEM (country_num); |
514 | |
515 | case tok_end: |
516 | /* Next we assume `LC_ADDRESS'. */ |
517 | arg = lr_token (ldfile, charmap, result, NULL, verbose); |
518 | if (arg->tok == tok_eof) |
519 | break; |
520 | if (arg->tok == tok_eol) |
521 | lr_error (ldfile, _("%s: incomplete `END' line" ), |
522 | "LC_ADDRESS" ); |
523 | else if (arg->tok != tok_lc_address) |
524 | lr_error (ldfile, _("\ |
525 | %1$s: definition does not end with `END %1$s'" ), "LC_ADDRESS" ); |
526 | lr_ignore_rest (ldfile, arg->tok == tok_lc_address); |
527 | return; |
528 | |
529 | default: |
530 | err_label: |
531 | SYNTAX_ERROR (_("%s: syntax error" ), "LC_ADDRESS" ); |
532 | } |
533 | |
534 | /* Prepare for the next round. */ |
535 | now = lr_token (ldfile, charmap, result, NULL, verbose); |
536 | nowtok = now->tok; |
537 | } |
538 | |
539 | /* When we come here we reached the end of the file. */ |
540 | lr_error (ldfile, _("%s: premature end of file" ), "LC_ADDRESS" ); |
541 | } |
542 | |