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. */
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 /* Generally speaking there are 3 standards the define the default,
200 warning, and error behaviour of LC_MONETARY. They are ISO/IEC TR 30112,
201 ISO/IEC 9899:2018 (ISO C17), and POSIX.1-2017. Within 30112 we have the
202 definition of a standard i18n FDCC-set, which for LC_MONETARY has the
203 following default values:
204 int_curr_symbol ""
205 currency_symbol ""
206 mon_decimal_point "<U002C>" i.e. ","
207 mon_thousand_sep ""
208 mon_grouping "\177" i.e. CHAR_MAX
209 positive_sign ""
210 negative_sign "<U002E>" i.e. "."
211 int_frac_digits -1
212 frac_digits -1
213 p_cs_precedes -1
214 p_sep_by_space -1
215 n_cs_precedes -1
216 n_sep_by_space -1
217 p_sign_posn -1
218 n_sign_posn -1
219 Under 30112 a keyword that is not provided implies an empty string ""
220 for string values or a -1 for integer values, and indicates the value
221 is unspecified with no default implied. No errors are considered.
222 The exception is mon_grouping which is a string with a terminating
223 CHAR_MAX.
224 For POSIX Issue 7 we have:
225 https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap07.html
226 and again values not provided default to "" or -1, and indicate the value
227 is not available to the locale. The exception is mon_grouping which is
228 a string with a terminating CHAR_MAX. For the POSIX locale the values of
229 LC_MONETARY should be:
230 int_curr_symbol ""
231 currency_symbol ""
232 mon_decimal_point ""
233 mon_thousands_sep ""
234 mon_grouping "\177" i.e. CHAR_MAX
235 positive_sign ""
236 negative_sign ""
237 int_frac_digits -1
238 frac_digits -1
239 p_cs_precedes -1
240 p_sep_by_space -1
241 n_cs_precedes -1
242 n_sep_by_space -1
243 p_sign_posn -1
244 n_sign_posn -1
245 int_p_cs_precedes -1
246 int_p_sep_by_space -1
247 int_n_cs_precedes -1
248 int_n_sep_by_space -1
249 int_p_sign_posn -1
250 int_n_sign_posn -1
251 Like with 30112, POSIX also considers no error if the keywords are
252 missing, only that if the cateory as a whole is missing the referencing
253 of the category results in unspecified behaviour.
254 For ISO C17 there is no default value provided, but the localeconv
255 specification in 7.11.2.1 admits that members of char * type may point
256 to "" to indicate a value is not available or is of length zero.
257 The exception is decimal_point (not mon_decimal_point) which must be a
258 defined non-empty string. The values of char, which are generally
259 mapped to integer values in 30112 and POSIX, must be non-negative
260 numbers that map to CHAR_MAX when a value is not available in the
261 locale.
262 In ISO C17 for the "C" locale all values are empty strings "", or
263 CHAR_MAX, with the exception of decimal_point which is "." (defined
264 in LC_NUMERIC). ISO C17 makes no exception for mon_grouping like
265 30112 and POSIX, but a value of "" is functionally equivalent to
266 "\177" since neither defines a grouping (though the latter terminates
267 the grouping).
268
269 Lastly, we must consider the legacy C/POSIX locale that implemented
270 as a builtin in glibc and wether a default value mapping to the
271 C/POSIX locale may benefit the user from a compatibility perspective.
272
273 Thus given 30112, POSIX, ISO C, and the builtin C/POSIX locale we
274 need to pick appropriate defaults below. */
275
276 /* The members of LC_MONETARY are handled in the order of their definition
277 in locale/categories.def. Please keep them in that order. */
278
279 /* The purpose of TEST_ELEM is to define a default value for the fields
280 in the category if the field was not defined in the cateory. If the
281 category was present but we didn't see a definition for the field then
282 we also issue a warning, otherwise the only warning you get is the one
283 earlier when a default category is created (completely missing category).
284 This missing field warning is glibc-specific since no standard requires
285 this warning, but we consider it valuable to print a warning for all
286 missing fields in the category. */
287 #define TEST_ELEM(cat, initval) \
288 if (monetary->cat == NULL) \
289 { \
290 if (! nothing) \
291 record_warning (_("%s: field `%s' not defined"), \
292 "LC_MONETARY", #cat); \
293 monetary->cat = initval; \
294 }
295
296 /* Keyword: int_curr_symbol. */
297 TEST_ELEM (int_curr_symbol, "");
298 /* The international currency symbol must come from ISO 4217. */
299 if (monetary->int_curr_symbol != NULL)
300 {
301 /* POSIX says this should be a 3-character symbol from ISO 4217
302 along with a 4th character that is a divider, but the POSIX
303 locale is documented as having a special case of "", and we
304 support that also, so allow other locales to be created with
305 a blank int_curr_symbol. */
306 int ics_len = strlen (monetary->int_curr_symbol);
307 if (ics_len != 4 && ics_len != 0)
308 {
309 if (! nothing)
310 record_error (0, 0, _("\
311 %s: value of field `int_curr_symbol' has wrong length"),
312 "LC_MONETARY");
313 }
314 else if (ics_len == 4)
315 { /* Check the first three characters against ISO 4217 */
316 char symbol[4];
317 strncpy (symbol, monetary->int_curr_symbol, 3);
318 symbol[3] = '\0';
319 /* A user may disable this waning for testing purposes or
320 for building a locale with a 3 letter country code that
321 was not yet supported in our ISO 4217 list.
322 See the use of --no-warnings=intcurrsym. */
323 if (bsearch (symbol, valid_int_curr, NR_VALID_INT_CURR,
324 sizeof (const char *),
325 (comparison_fn_t) curr_strcmp) == NULL
326 && warn_int_curr_symbol)
327 record_warning (_("\
328 %s: value of field `int_curr_symbol' does \
329 not correspond to a valid name in ISO 4217 [--no-warnings=intcurrsym]"),
330 "LC_MONETARY");
331 }
332 }
333
334 /* Keyword: currency_symbol */
335 TEST_ELEM (currency_symbol, "");
336
337 /* Keyword: mon_decimal_point */
338 /* ISO C17 7.11.2.1.3 explicitly allows mon_decimal_point to be the
339 empty string e.g. "". This indicates the value is not available in the
340 current locale or is of zero length. However, if the value was never
341 defined then we issue a warning and use a glibc-specific default. ISO
342 30112 in the i18n FDCC-Set uses <U002C> ",", and POSIX Issue 7 in the
343 POSIX locale uses "". It is specific to glibc that the default is <U002E>
344 "."; we retain this existing behaviour for backwards compatibility. */
345 if (monetary->mon_decimal_point == NULL)
346 {
347 if (! nothing)
348 record_warning (_("%s: field `%s' not defined, using defaults"),
349 "LC_MONETARY", "mon_decimal_point");
350 monetary->mon_decimal_point = ".";
351 monetary->mon_decimal_point_wc = L'.';
352 }
353
354 /* Keyword: mon_thousands_sep */
355 if (monetary->mon_thousands_sep == NULL)
356 {
357 if (! nothing)
358 record_warning (_("%s: field `%s' not defined, using defaults"),
359 "LC_MONETARY", "mon_thousands_sep");
360 monetary->mon_thousands_sep = "";
361 monetary->mon_thousands_sep_wc = L'\0';
362 }
363
364 /* Keyword: mon_grouping */
365 if (monetary->mon_grouping_len == 0)
366 {
367 if (! nothing)
368 record_warning (_("%s: field `%s' not defined"),
369 "LC_MONETARY", "mon_grouping");
370 /* Missing entries are given 1 element in their bytearray with
371 a value of CHAR_MAX which indicates that "No further grouping
372 is to be performed" (functionally equivalent to ISO C's "C"
373 locale default of ""). */
374 monetary->mon_grouping = (char *) "\177";
375 monetary->mon_grouping_len = 1;
376 }
377
378 /* Keyword: positive_sign */
379 TEST_ELEM (positive_sign, "");
380
381 /* Keyword: negative_sign */
382 TEST_ELEM (negative_sign, "");
383
384 #undef TEST_ELEM
385 #define TEST_ELEM(cat, min, max, initval) \
386 if (monetary->cat == -2) \
387 { \
388 if (! nothing) \
389 record_warning (_("%s: field `%s' not defined"), \
390 "LC_MONETARY", #cat); \
391 monetary->cat = initval; \
392 } \
393 else if ((monetary->cat < min || monetary->cat > max) \
394 && min < max \
395 && !be_quiet && !nothing) \
396 record_error (0, 0, _("\
397 %s: value for field `%s' must be in range %d...%d"), \
398 "LC_MONETARY", #cat, min, max)
399
400 TEST_ELEM (int_frac_digits, 1, 0, -1);
401 TEST_ELEM (frac_digits, 1, 0, -1);
402 TEST_ELEM (p_cs_precedes, -1, 1, -1);
403 TEST_ELEM (p_sep_by_space, -1, 2, -1);
404 TEST_ELEM (n_cs_precedes, -1, 1, -1);
405 TEST_ELEM (n_sep_by_space, -1, 2, -1);
406 TEST_ELEM (p_sign_posn, -1, 4, -1);
407 TEST_ELEM (n_sign_posn, -1, 4, -1);
408
409 /* Keyword: crncystr */
410 monetary->crncystr = (char *) xmalloc (strlen (monetary->currency_symbol)
411 + 2);
412 monetary->crncystr[0] = monetary->p_cs_precedes ? '-' : '+';
413 strcpy (&monetary->crncystr[1], monetary->currency_symbol);
414
415 #undef TEST_ELEM
416 #define TEST_ELEM(cat, alt, min, max) \
417 if (monetary->cat == -2) \
418 monetary->cat = monetary->alt; \
419 else if ((monetary->cat < min || monetary->cat > max) && ! nothing) \
420 record_error (0, 0, _("\
421 %s: value for field `%s' must be in range %d...%d"), \
422 "LC_MONETARY", #cat, min, max)
423
424 TEST_ELEM (int_p_cs_precedes, p_cs_precedes, -1, 1);
425 TEST_ELEM (int_p_sep_by_space, p_sep_by_space, -1, 2);
426 TEST_ELEM (int_n_cs_precedes, n_cs_precedes, -1, 1);
427 TEST_ELEM (int_n_sep_by_space, n_sep_by_space, -1, 2);
428 TEST_ELEM (int_p_sign_posn, p_sign_posn, -1, 4);
429 TEST_ELEM (int_n_sign_posn, n_sign_posn, -1, 4);
430
431 /* The non-POSIX.2 extensions are optional. */
432 if (monetary->duo_int_curr_symbol == NULL)
433 monetary->duo_int_curr_symbol = monetary->int_curr_symbol;
434 if (monetary->duo_currency_symbol == NULL)
435 monetary->duo_currency_symbol = monetary->currency_symbol;
436
437 if (monetary->duo_int_frac_digits == -2)
438 monetary->duo_int_frac_digits = monetary->int_frac_digits;
439 if (monetary->duo_frac_digits == -2)
440 monetary->duo_frac_digits = monetary->frac_digits;
441
442 TEST_ELEM (duo_p_cs_precedes, p_cs_precedes, -1, 1);
443 TEST_ELEM (duo_p_sep_by_space, p_sep_by_space, -1, 2);
444 TEST_ELEM (duo_n_cs_precedes, n_cs_precedes, -1, 1);
445 TEST_ELEM (duo_n_sep_by_space, n_sep_by_space, -1, 2);
446 TEST_ELEM (duo_int_p_cs_precedes, int_p_cs_precedes, -1, 1);
447 TEST_ELEM (duo_int_p_sep_by_space, int_p_sep_by_space, -1, 2);
448 TEST_ELEM (duo_int_n_cs_precedes, int_n_cs_precedes, -1, 1);
449 TEST_ELEM (duo_int_n_sep_by_space, int_n_sep_by_space, -1, 2);
450 TEST_ELEM (duo_p_sign_posn, p_sign_posn, -1, 4);
451 TEST_ELEM (duo_n_sign_posn, n_sign_posn, -1, 4);
452 TEST_ELEM (duo_int_p_sign_posn, int_p_sign_posn, -1, 4);
453 TEST_ELEM (duo_int_n_sign_posn, int_n_sign_posn, -1, 4);
454
455 if (monetary->uno_valid_from == 0)
456 monetary->uno_valid_from = 10101;
457 if (monetary->uno_valid_to == 0)
458 monetary->uno_valid_to = 99991231;
459 if (monetary->duo_valid_from == 0)
460 monetary->duo_valid_from = 10101;
461 if (monetary->duo_valid_to == 0)
462 monetary->duo_valid_to = 99991231;
463
464 /* Keyword: conversion_rate */
465 if (monetary->conversion_rate[0] == 0)
466 {
467 monetary->conversion_rate[0] = 1;
468 monetary->conversion_rate[1] = 1;
469 }
470
471 /* A value for monetary-decimal-point-wc was set when
472 monetary_decimal_point was set, likewise for monetary-thousands-sep-wc. */
473 }
474
475
476 void
monetary_output(struct localedef_t * locale,const struct charmap_t * charmap,const char * output_path)477 monetary_output (struct localedef_t *locale, const struct charmap_t *charmap,
478 const char *output_path)
479 {
480 struct locale_monetary_t *monetary
481 = locale->categories[LC_MONETARY].monetary;
482 struct locale_file file;
483
484 init_locale_data (&file, _NL_ITEM_INDEX (_NL_NUM_LC_MONETARY));
485 add_locale_string (&file, monetary->int_curr_symbol);
486 add_locale_string (&file, monetary->currency_symbol);
487 add_locale_string (&file, monetary->mon_decimal_point);
488 add_locale_string (&file, monetary->mon_thousands_sep);
489 add_locale_raw_data (&file, monetary->mon_grouping,
490 monetary->mon_grouping_len);
491 add_locale_string (&file, monetary->positive_sign);
492 add_locale_string (&file, monetary->negative_sign);
493 add_locale_char (&file, monetary->int_frac_digits);
494 add_locale_char (&file, monetary->frac_digits);
495 add_locale_char (&file, monetary->p_cs_precedes);
496 add_locale_char (&file, monetary->p_sep_by_space);
497 add_locale_char (&file, monetary->n_cs_precedes);
498 add_locale_char (&file, monetary->n_sep_by_space);
499 add_locale_char (&file, monetary->p_sign_posn);
500 add_locale_char (&file, monetary->n_sign_posn);
501 add_locale_string (&file, monetary->crncystr);
502 add_locale_char (&file, monetary->int_p_cs_precedes);
503 add_locale_char (&file, monetary->int_p_sep_by_space);
504 add_locale_char (&file, monetary->int_n_cs_precedes);
505 add_locale_char (&file, monetary->int_n_sep_by_space);
506 add_locale_char (&file, monetary->int_p_sign_posn);
507 add_locale_char (&file, monetary->int_n_sign_posn);
508 add_locale_string (&file, monetary->duo_int_curr_symbol);
509 add_locale_string (&file, monetary->duo_currency_symbol);
510 add_locale_char (&file, monetary->duo_int_frac_digits);
511 add_locale_char (&file, monetary->duo_frac_digits);
512 add_locale_char (&file, monetary->duo_p_cs_precedes);
513 add_locale_char (&file, monetary->duo_p_sep_by_space);
514 add_locale_char (&file, monetary->duo_n_cs_precedes);
515 add_locale_char (&file, monetary->duo_n_sep_by_space);
516 add_locale_char (&file, monetary->duo_int_p_cs_precedes);
517 add_locale_char (&file, monetary->duo_int_p_sep_by_space);
518 add_locale_char (&file, monetary->duo_int_n_cs_precedes);
519 add_locale_char (&file, monetary->duo_int_n_sep_by_space);
520 add_locale_char (&file, monetary->duo_p_sign_posn);
521 add_locale_char (&file, monetary->duo_n_sign_posn);
522 add_locale_char (&file, monetary->duo_int_p_sign_posn);
523 add_locale_char (&file, monetary->duo_int_n_sign_posn);
524 add_locale_uint32 (&file, monetary->uno_valid_from);
525 add_locale_uint32 (&file, monetary->uno_valid_to);
526 add_locale_uint32 (&file, monetary->duo_valid_from);
527 add_locale_uint32 (&file, monetary->duo_valid_to);
528 add_locale_uint32_array (&file, monetary->conversion_rate, 2);
529 add_locale_uint32 (&file, monetary->mon_decimal_point_wc);
530 add_locale_uint32 (&file, monetary->mon_thousands_sep_wc);
531 add_locale_string (&file, charmap->code_set_name);
532 write_locale_data (output_path, LC_MONETARY, "LC_MONETARY", &file);
533 }
534
535
536 static int
curr_strcmp(const char * s1,const char ** s2)537 curr_strcmp (const char *s1, const char **s2)
538 {
539 return strcmp (s1, *s2);
540 }
541
542
543 /* The parser for the LC_MONETARY section of the locale definition. */
544 void
monetary_read(struct linereader * ldfile,struct localedef_t * result,const struct charmap_t * charmap,const char * repertoire_name,int ignore_content)545 monetary_read (struct linereader *ldfile, struct localedef_t *result,
546 const struct charmap_t *charmap, const char *repertoire_name,
547 int ignore_content)
548 {
549 struct repertoire_t *repertoire = NULL;
550 struct locale_monetary_t *monetary;
551 struct token *now;
552 enum token_t nowtok;
553
554 /* Get the repertoire we have to use. */
555 if (repertoire_name != NULL)
556 repertoire = repertoire_read (repertoire_name);
557
558 /* The rest of the line containing `LC_MONETARY' must be free. */
559 lr_ignore_rest (ldfile, 1);
560
561 do
562 {
563 now = lr_token (ldfile, charmap, result, NULL, verbose);
564 nowtok = now->tok;
565 }
566 while (nowtok == tok_eol);
567
568 /* If we see `copy' now we are almost done. */
569 if (nowtok == tok_copy)
570 {
571 handle_copy (ldfile, charmap, repertoire_name, result, tok_lc_monetary,
572 LC_MONETARY, "LC_MONETARY", ignore_content);
573 return;
574 }
575
576 /* Prepare the data structures. */
577 monetary_startup (ldfile, result, ignore_content);
578 monetary = result->categories[LC_MONETARY].monetary;
579
580 while (1)
581 {
582 /* Of course we don't proceed beyond the end of file. */
583 if (nowtok == tok_eof)
584 break;
585
586 /* Ignore empty lines. */
587 if (nowtok == tok_eol)
588 {
589 now = lr_token (ldfile, charmap, result, NULL, verbose);
590 nowtok = now->tok;
591 continue;
592 }
593
594 switch (nowtok)
595 {
596 #define STR_ELEM(cat) \
597 case tok_##cat: \
598 /* Ignore the rest of the line if we don't need the input of \
599 this line. */ \
600 if (ignore_content) \
601 { \
602 lr_ignore_rest (ldfile, 0); \
603 break; \
604 } \
605 \
606 now = lr_token (ldfile, charmap, result, NULL, verbose); \
607 if (now->tok != tok_string) \
608 goto err_label; \
609 else if (monetary->cat != NULL) \
610 lr_error (ldfile, _("%s: field `%s' declared more than once"), \
611 "LC_MONETARY", #cat); \
612 else if (!ignore_content && now->val.str.startmb == NULL) \
613 { \
614 lr_error (ldfile, _("\
615 %s: unknown character in field `%s'"), "LC_MONETARY", #cat); \
616 monetary->cat = ""; \
617 } \
618 else if (!ignore_content) \
619 monetary->cat = now->val.str.startmb; \
620 lr_ignore_rest (ldfile, 1); \
621 break
622
623 STR_ELEM (int_curr_symbol);
624 STR_ELEM (currency_symbol);
625 STR_ELEM (positive_sign);
626 STR_ELEM (negative_sign);
627 STR_ELEM (duo_int_curr_symbol);
628 STR_ELEM (duo_currency_symbol);
629
630 #define STR_ELEM_WC(cat) \
631 case tok_##cat: \
632 /* Ignore the rest of the line if we don't need the input of \
633 this line. */ \
634 if (ignore_content) \
635 { \
636 lr_ignore_rest (ldfile, 0); \
637 break; \
638 } \
639 \
640 ldfile->return_widestr = 1; \
641 now = lr_token (ldfile, charmap, result, repertoire, verbose); \
642 if (now->tok != tok_string) \
643 goto err_label; \
644 if (monetary->cat != NULL) \
645 lr_error (ldfile, _("\
646 %s: field `%s' declared more than once"), "LC_MONETARY", #cat); \
647 else if (!ignore_content && now->val.str.startmb == NULL) \
648 { \
649 lr_error (ldfile, _("\
650 %s: unknown character in field `%s'"), "LC_MONETARY", #cat); \
651 monetary->cat = ""; \
652 monetary->cat##_wc = L'\0'; \
653 } \
654 else if (now->val.str.startwc != NULL && now->val.str.lenwc > 2) \
655 { \
656 lr_error (ldfile, _("\
657 %s: value for field `%s' must be a single character"), "LC_MONETARY", #cat); \
658 } \
659 else if (!ignore_content) \
660 { \
661 monetary->cat = now->val.str.startmb; \
662 \
663 if (now->val.str.startwc != NULL) \
664 monetary->cat##_wc = *now->val.str.startwc; \
665 } \
666 ldfile->return_widestr = 0; \
667 break
668
669 STR_ELEM_WC (mon_decimal_point);
670 STR_ELEM_WC (mon_thousands_sep);
671
672 #define INT_ELEM(cat) \
673 case tok_##cat: \
674 /* Ignore the rest of the line if we don't need the input of \
675 this line. */ \
676 if (ignore_content) \
677 { \
678 lr_ignore_rest (ldfile, 0); \
679 break; \
680 } \
681 \
682 now = lr_token (ldfile, charmap, result, NULL, verbose); \
683 if (now->tok != tok_minus1 && now->tok != tok_number) \
684 goto err_label; \
685 else if (monetary->cat != -2) \
686 lr_error (ldfile, _("%s: field `%s' declared more than once"), \
687 "LC_MONETARY", #cat); \
688 else if (!ignore_content) \
689 monetary->cat = now->tok == tok_minus1 ? -1 : now->val.num; \
690 break
691
692 INT_ELEM (int_frac_digits);
693 INT_ELEM (frac_digits);
694 INT_ELEM (p_cs_precedes);
695 INT_ELEM (p_sep_by_space);
696 INT_ELEM (n_cs_precedes);
697 INT_ELEM (n_sep_by_space);
698 INT_ELEM (p_sign_posn);
699 INT_ELEM (n_sign_posn);
700 INT_ELEM (int_p_cs_precedes);
701 INT_ELEM (int_p_sep_by_space);
702 INT_ELEM (int_n_cs_precedes);
703 INT_ELEM (int_n_sep_by_space);
704 INT_ELEM (int_p_sign_posn);
705 INT_ELEM (int_n_sign_posn);
706 INT_ELEM (duo_int_frac_digits);
707 INT_ELEM (duo_frac_digits);
708 INT_ELEM (duo_p_cs_precedes);
709 INT_ELEM (duo_p_sep_by_space);
710 INT_ELEM (duo_n_cs_precedes);
711 INT_ELEM (duo_n_sep_by_space);
712 INT_ELEM (duo_p_sign_posn);
713 INT_ELEM (duo_n_sign_posn);
714 INT_ELEM (duo_int_p_cs_precedes);
715 INT_ELEM (duo_int_p_sep_by_space);
716 INT_ELEM (duo_int_n_cs_precedes);
717 INT_ELEM (duo_int_n_sep_by_space);
718 INT_ELEM (duo_int_p_sign_posn);
719 INT_ELEM (duo_int_n_sign_posn);
720 INT_ELEM (uno_valid_from);
721 INT_ELEM (uno_valid_to);
722 INT_ELEM (duo_valid_from);
723 INT_ELEM (duo_valid_to);
724
725 case tok_mon_grouping:
726 /* Ignore the rest of the line if we don't need the input of
727 this line. */
728 if (ignore_content)
729 {
730 lr_ignore_rest (ldfile, 0);
731 break;
732 }
733
734 now = lr_token (ldfile, charmap, result, NULL, verbose);
735 if (now->tok != tok_minus1 && now->tok != tok_number)
736 goto err_label;
737 else
738 {
739 size_t act = 0;
740 size_t max = 10;
741 char *grouping = ignore_content ? NULL : xmalloc (max);
742
743 do
744 {
745 if (act + 1 >= max)
746 {
747 max *= 2;
748 grouping = xrealloc (grouping, max);
749 }
750
751 if (act > 0 && grouping[act - 1] == '\177')
752 {
753 lr_error (ldfile, _("\
754 %s: `-1' must be last entry in `%s' field"),
755 "LC_MONETARY", "mon_grouping");
756 lr_ignore_rest (ldfile, 0);
757 break;
758 }
759
760 if (now->tok == tok_minus1)
761 {
762 if (!ignore_content)
763 grouping[act++] = '\177';
764 }
765 else if (now->val.num == 0)
766 {
767 /* A value of 0 disables grouping from here on but
768 we must not store a NUL character since this
769 terminates the string. Use something different
770 which must not be used otherwise. */
771 if (!ignore_content)
772 grouping[act++] = '\377';
773 }
774 else if (now->val.num > 126)
775 lr_error (ldfile, _("\
776 %s: values for field `%s' must be smaller than 127"),
777 "LC_MONETARY", "mon_grouping");
778 else if (!ignore_content)
779 grouping[act++] = now->val.num;
780
781 /* Next must be semicolon. */
782 now = lr_token (ldfile, charmap, result, NULL, verbose);
783 if (now->tok != tok_semicolon)
784 break;
785
786 now = lr_token (ldfile, charmap, result, NULL, verbose);
787 }
788 while (now->tok == tok_minus1 || now->tok == tok_number);
789
790 if (now->tok != tok_eol)
791 goto err_label;
792
793 if (!ignore_content)
794 {
795 /* A single -1 means no grouping. */
796 if (act == 1 && grouping[0] == '\177')
797 act--;
798 grouping[act++] = '\0';
799
800 monetary->mon_grouping = xrealloc (grouping, act);
801 monetary->mon_grouping_len = act;
802 }
803 }
804 break;
805
806 case tok_conversion_rate:
807 /* Ignore the rest of the line if we don't need the input of
808 this line. */
809 if (ignore_content)
810 {
811 lr_ignore_rest (ldfile, 0);
812 break;
813 }
814
815 now = lr_token (ldfile, charmap, result, NULL, verbose);
816 if (now->tok != tok_number)
817 goto err_label;
818 if (now->val.num == 0)
819 {
820 invalid_conversion_rate:
821 lr_error (ldfile, _("conversion rate value cannot be zero"));
822 if (!ignore_content)
823 {
824 monetary->conversion_rate[0] = 1;
825 monetary->conversion_rate[1] = 1;
826 }
827 break;
828 }
829 if (!ignore_content)
830 monetary->conversion_rate[0] = now->val.num;
831 /* Next must be a semicolon. */
832 now = lr_token (ldfile, charmap, result, NULL, verbose);
833 if (now->tok != tok_semicolon)
834 goto err_label;
835 /* And another number. */
836 now = lr_token (ldfile, charmap, result, NULL, verbose);
837 if (now->tok != tok_number)
838 goto err_label;
839 if (now->val.num == 0)
840 goto invalid_conversion_rate;
841 if (!ignore_content)
842 monetary->conversion_rate[1] = now->val.num;
843 /* The rest of the line must be empty. */
844 lr_ignore_rest (ldfile, 1);
845 break;
846
847 case tok_end:
848 /* Next we assume `LC_MONETARY'. */
849 now = lr_token (ldfile, charmap, result, NULL, verbose);
850 if (now->tok == tok_eof)
851 break;
852 if (now->tok == tok_eol)
853 lr_error (ldfile, _("%s: incomplete `END' line"), "LC_MONETARY");
854 else if (now->tok != tok_lc_monetary)
855 lr_error (ldfile, _("\
856 %1$s: definition does not end with `END %1$s'"), "LC_MONETARY");
857 lr_ignore_rest (ldfile, now->tok == tok_lc_monetary);
858 return;
859
860 default:
861 err_label:
862 SYNTAX_ERROR (_("%s: syntax error"), "LC_MONETARY");
863 }
864
865 /* Prepare for the next round. */
866 now = lr_token (ldfile, charmap, result, NULL, verbose);
867 nowtok = now->tok;
868 }
869
870 /* When we come here we reached the end of the file. */
871 lr_error (ldfile, _("%s: premature end of file"), "LC_MONETARY");
872 }
873