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