1 /* Copyright (C) 1998-2022 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
address_startup(struct linereader * lr,struct localedef_t * locale,int ignore_content)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
address_finish(struct localedef_t * locale,const struct charmap_t * charmap)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
address_output(struct localedef_t * locale,const struct charmap_t * charmap,const char * output_path)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
address_read(struct linereader * ldfile,struct localedef_t * result,const struct charmap_t * charmap,const char * repertoire_name,int ignore_content)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