1 /* Copyright (C) 1995-2021 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 <limits.h>
24 #include <stdlib.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 "linereader.h"
33 #include "localeinfo.h"
34 #include "locfile.h"
35 
36 
37 /* The real definition of the struct for the LC_MONETARY locale.  */
38 struct locale_monetary_t
39 {
40   const char *int_curr_symbol;
41   const char *currency_symbol;
42   const char *mon_decimal_point;
43   const char *mon_thousands_sep;
44   uint32_t mon_decimal_point_wc;
45   uint32_t mon_thousands_sep_wc;
46   char *mon_grouping;
47   size_t mon_grouping_len;
48   const char *positive_sign;
49   const char *negative_sign;
50   signed char int_frac_digits;
51   signed char frac_digits;
52   signed char p_cs_precedes;
53   signed char p_sep_by_space;
54   signed char n_cs_precedes;
55   signed char n_sep_by_space;
56   signed char p_sign_posn;
57   signed char n_sign_posn;
58   signed char int_p_cs_precedes;
59   signed char int_p_sep_by_space;
60   signed char int_n_cs_precedes;
61   signed char int_n_sep_by_space;
62   signed char int_p_sign_posn;
63   signed char int_n_sign_posn;
64   const char *duo_int_curr_symbol;
65   const char *duo_currency_symbol;
66   signed char duo_int_frac_digits;
67   signed char duo_frac_digits;
68   signed char duo_p_cs_precedes;
69   signed char duo_p_sep_by_space;
70   signed char duo_n_cs_precedes;
71   signed char duo_n_sep_by_space;
72   signed char duo_p_sign_posn;
73   signed char duo_n_sign_posn;
74   signed char duo_int_p_cs_precedes;
75   signed char duo_int_p_sep_by_space;
76   signed char duo_int_n_cs_precedes;
77   signed char duo_int_n_sep_by_space;
78   signed char duo_int_p_sign_posn;
79   signed char duo_int_n_sign_posn;
80   uint32_t uno_valid_from;
81   uint32_t uno_valid_to;
82   uint32_t duo_valid_from;
83   uint32_t duo_valid_to;
84   uint32_t conversion_rate[2];
85   char *crncystr;
86 };
87 
88 
89 /* The content iof the field int_curr_symbol has to be taken from
90    ISO-4217.  We test for correct values.  */
91 #define DEFINE_INT_CURR(str) str,
92 static const char *const valid_int_curr[] =
93   {
94 #   include "../iso-4217.def"
95   };
96 #define NR_VALID_INT_CURR ((sizeof (valid_int_curr) \
97 			    / sizeof (valid_int_curr[0])))
98 #undef DEFINE_INT_CURR
99 
100 
101 /* Prototypes for local functions.  */
102 static int curr_strcmp (const char *s1, const char **s2);
103 
104 
105 static void
monetary_startup(struct linereader * lr,struct localedef_t * locale,int ignore_content)106 monetary_startup (struct linereader *lr, struct localedef_t *locale,
107 		  int ignore_content)
108 {
109   if (!ignore_content)
110     {
111       struct locale_monetary_t *monetary;
112 
113       locale->categories[LC_MONETARY].monetary = monetary =
114 	(struct locale_monetary_t *) xmalloc (sizeof (*monetary));
115 
116       memset (monetary, '\0', sizeof (struct locale_monetary_t));
117 
118       monetary->mon_grouping = NULL;
119       monetary->mon_grouping_len = 0;
120 
121       monetary->int_frac_digits = -2;
122       monetary->frac_digits = -2;
123       monetary->p_cs_precedes = -2;
124       monetary->p_sep_by_space = -2;
125       monetary->n_cs_precedes = -2;
126       monetary->n_sep_by_space = -2;
127       monetary->p_sign_posn = -2;
128       monetary->n_sign_posn = -2;
129       monetary->int_p_cs_precedes = -2;
130       monetary->int_p_sep_by_space = -2;
131       monetary->int_n_cs_precedes = -2;
132       monetary->int_n_sep_by_space = -2;
133       monetary->int_p_sign_posn = -2;
134       monetary->int_n_sign_posn = -2;
135       monetary->duo_int_frac_digits = -2;
136       monetary->duo_frac_digits = -2;
137       monetary->duo_p_cs_precedes = -2;
138       monetary->duo_p_sep_by_space = -2;
139       monetary->duo_n_cs_precedes = -2;
140       monetary->duo_n_sep_by_space = -2;
141       monetary->duo_p_sign_posn = -2;
142       monetary->duo_n_sign_posn = -2;
143       monetary->duo_int_p_cs_precedes = -2;
144       monetary->duo_int_p_sep_by_space = -2;
145       monetary->duo_int_n_cs_precedes = -2;
146       monetary->duo_int_n_sep_by_space = -2;
147       monetary->duo_int_p_sign_posn = -2;
148       monetary->duo_int_n_sign_posn = -2;
149     }
150 
151   if (lr != NULL)
152     {
153       lr->translate_strings = 1;
154       lr->return_widestr = 0;
155     }
156 }
157 
158 
159 void
monetary_finish(struct localedef_t * locale,const struct charmap_t * charmap)160 monetary_finish (struct localedef_t *locale, const struct charmap_t *charmap)
161 {
162   struct locale_monetary_t *monetary
163     = locale->categories[LC_MONETARY].monetary;
164   int nothing = 0;
165 
166   /* Now resolve copying and also handle completely missing definitions.  */
167   if (monetary == NULL)
168     {
169       /* First see whether we were supposed to copy.  If yes, find the
170 	 actual definition.  */
171       if (locale->copy_name[LC_MONETARY] != NULL)
172 	{
173 	  /* Find the copying locale.  This has to happen transitively since
174 	     the locale we are copying from might also copying another one.  */
175 	  struct localedef_t *from = locale;
176 
177 	  do
178 	    from = find_locale (LC_MONETARY, from->copy_name[LC_MONETARY],
179 				from->repertoire_name, charmap);
180 	  while (from->categories[LC_MONETARY].monetary == NULL
181 		 && from->copy_name[LC_MONETARY] != NULL);
182 
183 	  monetary = locale->categories[LC_MONETARY].monetary
184 	    = from->categories[LC_MONETARY].monetary;
185 	}
186 
187       /* If there is still no definition issue a warning and create an
188 	 empty one.  */
189       if (monetary == NULL)
190 	{
191 	  record_warning (_("\
192 No definition for %s category found"), "LC_MONETARY");
193 	  monetary_startup (NULL, locale, 0);
194 	  monetary = locale->categories[LC_MONETARY].monetary;
195 	  nothing = 1;
196 	}
197     }
198 
199 #define TEST_ELEM(cat, initval) \
200   if (monetary->cat == NULL)						      \
201     {									      \
202       if (! nothing)							      \
203 	record_error (0, 0, _("%s: field `%s' not defined"),		      \
204 		      "LC_MONETARY", #cat);				      \
205       monetary->cat = initval;						      \
206     }
207 
208   TEST_ELEM (int_curr_symbol, "");
209   TEST_ELEM (currency_symbol, "");
210   TEST_ELEM (mon_decimal_point, ".");
211   TEST_ELEM (mon_thousands_sep, "");
212   TEST_ELEM (positive_sign, "");
213   TEST_ELEM (negative_sign, "");
214 
215   /* The international currency symbol must come from ISO 4217.  */
216   if (monetary->int_curr_symbol != NULL)
217     {
218       /* POSIX says this should be a 3-character symbol from ISO 4217
219 	 along with a 4th character that is a divider, but the POSIX
220 	 locale is documented as having a special case of "", and we
221 	 support that also, so allow other locales to be created with
222 	 a blank int_curr_symbol.  */
223       int ics_len = strlen (monetary->int_curr_symbol);
224       if (ics_len != 4 && ics_len != 0)
225 	{
226 	  if (! nothing)
227 	    record_error (0, 0, _("\
228 %s: value of field `int_curr_symbol' has wrong length"),
229 			  "LC_MONETARY");
230 	}
231       else if (ics_len == 4)
232 	{ /* Check the first three characters against ISO 4217 */
233 	  char symbol[4];
234 	  strncpy (symbol, monetary->int_curr_symbol, 3);
235 	  symbol[3] = '\0';
236 	  /* A user may disable this waning for testing purposes or
237 	     for building a locale with a 3 letter country code that
238 	     was not yet supported in our ISO 4217 list.
239 	     See the use of --no-warnings=intcurrsym.  */
240 	  if (bsearch (symbol, valid_int_curr, NR_VALID_INT_CURR,
241 		       sizeof (const char *),
242 		       (comparison_fn_t) curr_strcmp) == NULL
243 	      && warn_int_curr_symbol)
244 	    record_warning (_("\
245 %s: value of field `int_curr_symbol' does \
246 not correspond to a valid name in ISO 4217 [--no-warnings=intcurrsym]"),
247 			    "LC_MONETARY");
248 	}
249     }
250 
251   /* The decimal point must not be empty.  This is not said explicitly
252      in POSIX but ANSI C (ISO/IEC 9899) says in 4.4.2.1 it has to be
253      != "".  */
254   if (monetary->mon_decimal_point == NULL)
255     {
256       if (! nothing)
257 	record_error (0, 0, _("%s: field `%s' not defined"),
258 		      "LC_MONETARY", "mon_decimal_point");
259       monetary->mon_decimal_point = ".";
260     }
261   else if (monetary->mon_decimal_point[0] == '\0' && ! be_quiet && ! nothing)
262     {
263       record_error (0, 0, _("\
264 %s: value for field `%s' must not be an empty string"),
265 		    "LC_MONETARY", "mon_decimal_point");
266     }
267   if (monetary->mon_decimal_point_wc == L'\0')
268     monetary->mon_decimal_point_wc = L'.';
269 
270   if (monetary->mon_grouping_len == 0)
271     {
272       if (! nothing)
273 	record_error (0, 0, _("%s: field `%s' not defined"),
274 		      "LC_MONETARY", "mon_grouping");
275 
276       monetary->mon_grouping = (char *) "\177";
277       monetary->mon_grouping_len = 1;
278     }
279 
280 #undef TEST_ELEM
281 #define TEST_ELEM(cat, min, max, initval) \
282   if (monetary->cat == -2)						      \
283     {									      \
284        if (! nothing)							      \
285 	 record_error (0, 0, _("%s: field `%s' not defined"),		      \
286 		       "LC_MONETARY", #cat);				      \
287        monetary->cat = initval;						      \
288     }									      \
289   else if ((monetary->cat < min || monetary->cat > max)			      \
290 	   && min < max							      \
291 	   && !be_quiet && !nothing)					      \
292     record_error (0, 0, _("\
293 %s: value for field `%s' must be in range %d...%d"),			      \
294 		  "LC_MONETARY", #cat, min, max)
295 
296   TEST_ELEM (int_frac_digits, 1, 0, -1);
297   TEST_ELEM (frac_digits, 1, 0, -1);
298   TEST_ELEM (p_cs_precedes, -1, 1, -1);
299   TEST_ELEM (p_sep_by_space, -1, 2, -1);
300   TEST_ELEM (n_cs_precedes, -1, 1, -1);
301   TEST_ELEM (n_sep_by_space, -1, 2, -1);
302   TEST_ELEM (p_sign_posn, -1, 4, -1);
303   TEST_ELEM (n_sign_posn, -1, 4, -1);
304 
305   /* The non-POSIX.2 extensions are optional.  */
306   if (monetary->duo_int_curr_symbol == NULL)
307     monetary->duo_int_curr_symbol = monetary->int_curr_symbol;
308   if (monetary->duo_currency_symbol == NULL)
309     monetary->duo_currency_symbol = monetary->currency_symbol;
310 
311   if (monetary->duo_int_frac_digits == -2)
312     monetary->duo_int_frac_digits = monetary->int_frac_digits;
313   if (monetary->duo_frac_digits == -2)
314     monetary->duo_frac_digits = monetary->frac_digits;
315 
316 #undef TEST_ELEM
317 #define TEST_ELEM(cat, alt, min, max) \
318   if (monetary->cat == -2)						      \
319     monetary->cat = monetary->alt;					      \
320   else if ((monetary->cat < min || monetary->cat > max)	&& ! nothing)	      \
321     record_error (0, 0, _("\
322 %s: value for field `%s' must be in range %d...%d"),			      \
323 		  "LC_MONETARY", #cat, min, max)
324 
325   TEST_ELEM (int_p_cs_precedes, p_cs_precedes, -1, 1);
326   TEST_ELEM (int_p_sep_by_space, p_sep_by_space, -1, 2);
327   TEST_ELEM (int_n_cs_precedes, n_cs_precedes, -1, 1);
328   TEST_ELEM (int_n_sep_by_space, n_sep_by_space, -1, 2);
329   TEST_ELEM (int_p_sign_posn, p_sign_posn, -1, 4);
330   TEST_ELEM (int_n_sign_posn, n_sign_posn, -1, 4);
331 
332   TEST_ELEM (duo_p_cs_precedes, p_cs_precedes, -1, 1);
333   TEST_ELEM (duo_p_sep_by_space, p_sep_by_space, -1, 2);
334   TEST_ELEM (duo_n_cs_precedes, n_cs_precedes, -1, 1);
335   TEST_ELEM (duo_n_sep_by_space, n_sep_by_space, -1, 2);
336   TEST_ELEM (duo_int_p_cs_precedes, int_p_cs_precedes, -1, 1);
337   TEST_ELEM (duo_int_p_sep_by_space, int_p_sep_by_space, -1, 2);
338   TEST_ELEM (duo_int_n_cs_precedes, int_n_cs_precedes, -1, 1);
339   TEST_ELEM (duo_int_n_sep_by_space, int_n_sep_by_space, -1, 2);
340   TEST_ELEM (duo_p_sign_posn, p_sign_posn, -1, 4);
341   TEST_ELEM (duo_n_sign_posn, n_sign_posn, -1, 4);
342   TEST_ELEM (duo_int_p_sign_posn, int_p_sign_posn, -1, 4);
343   TEST_ELEM (duo_int_n_sign_posn, int_n_sign_posn, -1, 4);
344 
345   if (monetary->uno_valid_from == 0)
346     monetary->uno_valid_from = 10101;
347   if (monetary->uno_valid_to == 0)
348     monetary->uno_valid_to = 99991231;
349   if (monetary->duo_valid_from == 0)
350     monetary->duo_valid_from = 10101;
351   if (monetary->duo_valid_to == 0)
352     monetary->duo_valid_to = 99991231;
353 
354   if (monetary->conversion_rate[0] == 0)
355     {
356       monetary->conversion_rate[0] = 1;
357       monetary->conversion_rate[1] = 1;
358     }
359 
360   /* Create the crncystr entry.  */
361   monetary->crncystr = (char *) xmalloc (strlen (monetary->currency_symbol)
362 					 + 2);
363   monetary->crncystr[0] = monetary->p_cs_precedes ? '-' : '+';
364   strcpy (&monetary->crncystr[1], monetary->currency_symbol);
365 }
366 
367 
368 void
monetary_output(struct localedef_t * locale,const struct charmap_t * charmap,const char * output_path)369 monetary_output (struct localedef_t *locale, const struct charmap_t *charmap,
370 		 const char *output_path)
371 {
372   struct locale_monetary_t *monetary
373     = locale->categories[LC_MONETARY].monetary;
374   struct locale_file file;
375 
376   init_locale_data (&file, _NL_ITEM_INDEX (_NL_NUM_LC_MONETARY));
377   add_locale_string (&file, monetary->int_curr_symbol);
378   add_locale_string (&file, monetary->currency_symbol);
379   add_locale_string (&file, monetary->mon_decimal_point);
380   add_locale_string (&file, monetary->mon_thousands_sep);
381   add_locale_raw_data (&file, monetary->mon_grouping,
382 		       monetary->mon_grouping_len);
383   add_locale_string (&file, monetary->positive_sign);
384   add_locale_string (&file, monetary->negative_sign);
385   add_locale_char (&file, monetary->int_frac_digits);
386   add_locale_char (&file, monetary->frac_digits);
387   add_locale_char (&file, monetary->p_cs_precedes);
388   add_locale_char (&file, monetary->p_sep_by_space);
389   add_locale_char (&file, monetary->n_cs_precedes);
390   add_locale_char (&file, monetary->n_sep_by_space);
391   add_locale_char (&file, monetary->p_sign_posn);
392   add_locale_char (&file, monetary->n_sign_posn);
393   add_locale_string (&file, monetary->crncystr);
394   add_locale_char (&file, monetary->int_p_cs_precedes);
395   add_locale_char (&file, monetary->int_p_sep_by_space);
396   add_locale_char (&file, monetary->int_n_cs_precedes);
397   add_locale_char (&file, monetary->int_n_sep_by_space);
398   add_locale_char (&file, monetary->int_p_sign_posn);
399   add_locale_char (&file, monetary->int_n_sign_posn);
400   add_locale_string (&file, monetary->duo_int_curr_symbol);
401   add_locale_string (&file, monetary->duo_currency_symbol);
402   add_locale_char (&file, monetary->duo_int_frac_digits);
403   add_locale_char (&file, monetary->duo_frac_digits);
404   add_locale_char (&file, monetary->duo_p_cs_precedes);
405   add_locale_char (&file, monetary->duo_p_sep_by_space);
406   add_locale_char (&file, monetary->duo_n_cs_precedes);
407   add_locale_char (&file, monetary->duo_n_sep_by_space);
408   add_locale_char (&file, monetary->duo_int_p_cs_precedes);
409   add_locale_char (&file, monetary->duo_int_p_sep_by_space);
410   add_locale_char (&file, monetary->duo_int_n_cs_precedes);
411   add_locale_char (&file, monetary->duo_int_n_sep_by_space);
412   add_locale_char (&file, monetary->duo_p_sign_posn);
413   add_locale_char (&file, monetary->duo_n_sign_posn);
414   add_locale_char (&file, monetary->duo_int_p_sign_posn);
415   add_locale_char (&file, monetary->duo_int_n_sign_posn);
416   add_locale_uint32 (&file, monetary->uno_valid_from);
417   add_locale_uint32 (&file, monetary->uno_valid_to);
418   add_locale_uint32 (&file, monetary->duo_valid_from);
419   add_locale_uint32 (&file, monetary->duo_valid_to);
420   add_locale_uint32_array (&file, monetary->conversion_rate, 2);
421   add_locale_uint32 (&file, monetary->mon_decimal_point_wc);
422   add_locale_uint32 (&file, monetary->mon_thousands_sep_wc);
423   add_locale_string (&file, charmap->code_set_name);
424   write_locale_data (output_path, LC_MONETARY, "LC_MONETARY", &file);
425 }
426 
427 
428 static int
curr_strcmp(const char * s1,const char ** s2)429 curr_strcmp (const char *s1, const char **s2)
430 {
431   return strcmp (s1, *s2);
432 }
433 
434 
435 /* The parser for the LC_MONETARY section of the locale definition.  */
436 void
monetary_read(struct linereader * ldfile,struct localedef_t * result,const struct charmap_t * charmap,const char * repertoire_name,int ignore_content)437 monetary_read (struct linereader *ldfile, struct localedef_t *result,
438 	       const struct charmap_t *charmap, const char *repertoire_name,
439 	       int ignore_content)
440 {
441   struct repertoire_t *repertoire = NULL;
442   struct locale_monetary_t *monetary;
443   struct token *now;
444   enum token_t nowtok;
445 
446   /* Get the repertoire we have to use.  */
447   if (repertoire_name != NULL)
448     repertoire = repertoire_read (repertoire_name);
449 
450   /* The rest of the line containing `LC_MONETARY' must be free.  */
451   lr_ignore_rest (ldfile, 1);
452 
453   do
454     {
455       now = lr_token (ldfile, charmap, result, NULL, verbose);
456       nowtok = now->tok;
457     }
458   while (nowtok == tok_eol);
459 
460   /* If we see `copy' now we are almost done.  */
461   if (nowtok == tok_copy)
462     {
463       handle_copy (ldfile, charmap, repertoire_name, result, tok_lc_monetary,
464 		   LC_MONETARY, "LC_MONETARY", ignore_content);
465       return;
466     }
467 
468   /* Prepare the data structures.  */
469   monetary_startup (ldfile, result, ignore_content);
470   monetary = result->categories[LC_MONETARY].monetary;
471 
472   while (1)
473     {
474       /* Of course we don't proceed beyond the end of file.  */
475       if (nowtok == tok_eof)
476 	break;
477 
478       /* Ignore empty lines.  */
479       if (nowtok == tok_eol)
480 	{
481 	  now = lr_token (ldfile, charmap, result, NULL, verbose);
482 	  nowtok = now->tok;
483 	  continue;
484 	}
485 
486       switch (nowtok)
487 	{
488 #define STR_ELEM(cat) \
489 	case tok_##cat:							      \
490 	  /* Ignore the rest of the line if we don't need the input of	      \
491 	     this line.  */						      \
492 	  if (ignore_content)						      \
493 	    {								      \
494 	      lr_ignore_rest (ldfile, 0);				      \
495 	      break;							      \
496 	    }								      \
497 									      \
498 	  now = lr_token (ldfile, charmap, result, NULL, verbose);	      \
499 	  if (now->tok != tok_string)					      \
500 	    goto err_label;						      \
501 	  else if (monetary->cat != NULL)				      \
502 	    lr_error (ldfile, _("%s: field `%s' declared more than once"),    \
503 		      "LC_MONETARY", #cat);				      \
504 	  else if (!ignore_content && now->val.str.startmb == NULL)	      \
505 	    {								      \
506 	      lr_error (ldfile, _("\
507 %s: unknown character in field `%s'"), "LC_MONETARY", #cat);		      \
508 	      monetary->cat = "";					      \
509 	    }								      \
510 	  else if (!ignore_content)					      \
511 	    monetary->cat = now->val.str.startmb;			      \
512 	  lr_ignore_rest (ldfile, 1);					      \
513 	  break
514 
515 	  STR_ELEM (int_curr_symbol);
516 	  STR_ELEM (currency_symbol);
517 	  STR_ELEM (positive_sign);
518 	  STR_ELEM (negative_sign);
519 	  STR_ELEM (duo_int_curr_symbol);
520 	  STR_ELEM (duo_currency_symbol);
521 
522 #define STR_ELEM_WC(cat) \
523 	case tok_##cat:							      \
524 	  /* Ignore the rest of the line if we don't need the input of	      \
525 	     this line.  */						      \
526 	  if (ignore_content)						      \
527 	    {								      \
528 	      lr_ignore_rest (ldfile, 0);				      \
529 	      break;							      \
530 	    }								      \
531 									      \
532 	  ldfile->return_widestr = 1;					      \
533 	  now = lr_token (ldfile, charmap, result, repertoire, verbose);      \
534 	  if (now->tok != tok_string)					      \
535 	    goto err_label;						      \
536 	  if (monetary->cat != NULL)					      \
537 	    lr_error (ldfile, _("\
538 %s: field `%s' declared more than once"), "LC_MONETARY", #cat);		      \
539 	  else if (!ignore_content && now->val.str.startmb == NULL)	      \
540 	    {								      \
541 	      lr_error (ldfile, _("\
542 %s: unknown character in field `%s'"), "LC_MONETARY", #cat);		      \
543 	      monetary->cat = "";					      \
544 	      monetary->cat##_wc = L'\0';				      \
545 	    }								      \
546 	  else if (now->val.str.startwc != NULL && now->val.str.lenwc > 2)    \
547 	    {								      \
548 	      lr_error (ldfile, _("\
549 %s: value for field `%s' must be a single character"), "LC_MONETARY", #cat);  \
550 	    }								      \
551 	  else if (!ignore_content)					      \
552 	    {								      \
553 	      monetary->cat = now->val.str.startmb;			      \
554 									      \
555 	      if (now->val.str.startwc != NULL)				      \
556 		monetary->cat##_wc = *now->val.str.startwc;		      \
557 	    }								      \
558 	  ldfile->return_widestr = 0;					      \
559 	  break
560 
561 	  STR_ELEM_WC (mon_decimal_point);
562 	  STR_ELEM_WC (mon_thousands_sep);
563 
564 #define INT_ELEM(cat) \
565 	case tok_##cat:							      \
566 	  /* Ignore the rest of the line if we don't need the input of	      \
567 	     this line.  */						      \
568 	  if (ignore_content)						      \
569 	    {								      \
570 	      lr_ignore_rest (ldfile, 0);				      \
571 	      break;							      \
572 	    }								      \
573 									      \
574 	  now = lr_token (ldfile, charmap, result, NULL, verbose);	      \
575 	  if (now->tok != tok_minus1 && now->tok != tok_number)		      \
576 	    goto err_label;						      \
577 	  else if (monetary->cat != -2)					      \
578 	    lr_error (ldfile, _("%s: field `%s' declared more than once"),    \
579 		      "LC_MONETARY", #cat);				      \
580 	  else if (!ignore_content)					      \
581 	    monetary->cat = now->tok == tok_minus1 ? -1 : now->val.num;	      \
582 	  break
583 
584 	  INT_ELEM (int_frac_digits);
585 	  INT_ELEM (frac_digits);
586 	  INT_ELEM (p_cs_precedes);
587 	  INT_ELEM (p_sep_by_space);
588 	  INT_ELEM (n_cs_precedes);
589 	  INT_ELEM (n_sep_by_space);
590 	  INT_ELEM (p_sign_posn);
591 	  INT_ELEM (n_sign_posn);
592 	  INT_ELEM (int_p_cs_precedes);
593 	  INT_ELEM (int_p_sep_by_space);
594 	  INT_ELEM (int_n_cs_precedes);
595 	  INT_ELEM (int_n_sep_by_space);
596 	  INT_ELEM (int_p_sign_posn);
597 	  INT_ELEM (int_n_sign_posn);
598 	  INT_ELEM (duo_int_frac_digits);
599 	  INT_ELEM (duo_frac_digits);
600 	  INT_ELEM (duo_p_cs_precedes);
601 	  INT_ELEM (duo_p_sep_by_space);
602 	  INT_ELEM (duo_n_cs_precedes);
603 	  INT_ELEM (duo_n_sep_by_space);
604 	  INT_ELEM (duo_p_sign_posn);
605 	  INT_ELEM (duo_n_sign_posn);
606 	  INT_ELEM (duo_int_p_cs_precedes);
607 	  INT_ELEM (duo_int_p_sep_by_space);
608 	  INT_ELEM (duo_int_n_cs_precedes);
609 	  INT_ELEM (duo_int_n_sep_by_space);
610 	  INT_ELEM (duo_int_p_sign_posn);
611 	  INT_ELEM (duo_int_n_sign_posn);
612 	  INT_ELEM (uno_valid_from);
613 	  INT_ELEM (uno_valid_to);
614 	  INT_ELEM (duo_valid_from);
615 	  INT_ELEM (duo_valid_to);
616 
617 	case tok_mon_grouping:
618 	  /* Ignore the rest of the line if we don't need the input of
619 	     this line.  */
620 	  if (ignore_content)
621 	    {
622 	      lr_ignore_rest (ldfile, 0);
623 	      break;
624 	    }
625 
626 	  now = lr_token (ldfile, charmap, result, NULL, verbose);
627 	  if (now->tok != tok_minus1 && now->tok != tok_number)
628 	    goto err_label;
629 	  else
630 	    {
631 	      size_t act = 0;
632 	      size_t max = 10;
633 	      char *grouping = ignore_content ? NULL : xmalloc (max);
634 
635 	      do
636 		{
637 		  if (act + 1 >= max)
638 		    {
639 		      max *= 2;
640 		      grouping = xrealloc (grouping, max);
641 		    }
642 
643 		  if (act > 0 && grouping[act - 1] == '\177')
644 		    {
645 		      lr_error (ldfile, _("\
646 %s: `-1' must be last entry in `%s' field"),
647 				"LC_MONETARY", "mon_grouping");
648 		      lr_ignore_rest (ldfile, 0);
649 		      break;
650 		    }
651 
652 		  if (now->tok == tok_minus1)
653 		    {
654 		      if (!ignore_content)
655 			grouping[act++] = '\177';
656 		    }
657 		  else if (now->val.num == 0)
658 		    {
659 		      /* A value of 0 disables grouping from here on but
660 			 we must not store a NUL character since this
661 			 terminates the string.  Use something different
662 			 which must not be used otherwise.  */
663 		      if (!ignore_content)
664 			grouping[act++] = '\377';
665 		    }
666 		  else if (now->val.num > 126)
667 		    lr_error (ldfile, _("\
668 %s: values for field `%s' must be smaller than 127"),
669 			      "LC_MONETARY", "mon_grouping");
670 		  else if (!ignore_content)
671 		    grouping[act++] = now->val.num;
672 
673 		  /* Next must be semicolon.  */
674 		  now = lr_token (ldfile, charmap, result, NULL, verbose);
675 		  if (now->tok != tok_semicolon)
676 		    break;
677 
678 		  now = lr_token (ldfile, charmap, result, NULL, verbose);
679 		}
680 	      while (now->tok == tok_minus1 || now->tok == tok_number);
681 
682 	      if (now->tok != tok_eol)
683 		goto err_label;
684 
685 	      if (!ignore_content)
686 		{
687 		  /* A single -1 means no grouping.  */
688 		  if (act == 1 && grouping[0] == '\177')
689 		    act--;
690 		  grouping[act++] = '\0';
691 
692 		  monetary->mon_grouping = xrealloc (grouping, act);
693 		  monetary->mon_grouping_len = act;
694 		}
695 	    }
696 	  break;
697 
698 	case tok_conversion_rate:
699 	  /* Ignore the rest of the line if we don't need the input of
700 	     this line.  */
701 	  if (ignore_content)
702 	    {
703 	      lr_ignore_rest (ldfile, 0);
704 	      break;
705 	    }
706 
707 	  now = lr_token (ldfile, charmap, result, NULL, verbose);
708 	  if (now->tok != tok_number)
709 	    goto err_label;
710 	  if (now->val.num == 0)
711 	    {
712 	    invalid_conversion_rate:
713 	      lr_error (ldfile, _("conversion rate value cannot be zero"));
714 	      if (!ignore_content)
715 		{
716 		  monetary->conversion_rate[0] = 1;
717 		  monetary->conversion_rate[1] = 1;
718 		}
719 	      break;
720 	    }
721 	  if (!ignore_content)
722 	    monetary->conversion_rate[0] = now->val.num;
723 	  /* Next must be a semicolon.  */
724 	  now = lr_token (ldfile, charmap, result, NULL, verbose);
725 	  if (now->tok != tok_semicolon)
726 	    goto err_label;
727 	  /* And another number.  */
728 	  now = lr_token (ldfile, charmap, result, NULL, verbose);
729 	  if (now->tok != tok_number)
730 	    goto err_label;
731 	  if (now->val.num == 0)
732 	    goto invalid_conversion_rate;
733 	  if (!ignore_content)
734 	    monetary->conversion_rate[1] = now->val.num;
735 	  /* The rest of the line must be empty.  */
736 	  lr_ignore_rest (ldfile, 1);
737 	  break;
738 
739 	case tok_end:
740 	  /* Next we assume `LC_MONETARY'.  */
741 	  now = lr_token (ldfile, charmap, result, NULL, verbose);
742 	  if (now->tok == tok_eof)
743 	    break;
744 	  if (now->tok == tok_eol)
745 	    lr_error (ldfile, _("%s: incomplete `END' line"), "LC_MONETARY");
746 	  else if (now->tok != tok_lc_monetary)
747 	    lr_error (ldfile, _("\
748 %1$s: definition does not end with `END %1$s'"), "LC_MONETARY");
749 	  lr_ignore_rest (ldfile, now->tok == tok_lc_monetary);
750 	  return;
751 
752 	default:
753 	err_label:
754 	  SYNTAX_ERROR (_("%s: syntax error"), "LC_MONETARY");
755 	}
756 
757       /* Prepare for the next round.  */
758       now = lr_token (ldfile, charmap, result, NULL, verbose);
759       nowtok = now->tok;
760     }
761 
762   /* When we come here we reached the end of the file.  */
763   lr_error (ldfile, _("%s: premature end of file"), "LC_MONETARY");
764 }
765