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