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