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