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