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