| xf.li | bdd93d5 | 2023-05-12 07:10:14 -0700 | [diff] [blame] | 1 | /* Copyright (C) 1995-2016 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.  */ | 
 | 39 | struct 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, | 
 | 93 | static 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.  */ | 
 | 103 | static int curr_strcmp (const char *s1, const char **s2); | 
 | 104 |  | 
 | 105 |  | 
 | 106 | static void | 
 | 107 | monetary_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 |  | 
 | 160 | void | 
 | 161 | monetary_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, _("\ | 
 | 194 | No 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 \ | 
 | 238 | not 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 |  | 
 | 361 | void | 
 | 362 | monetary_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 |  | 
 | 421 | static int | 
 | 422 | curr_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.  */ | 
 | 429 | void | 
 | 430 | monetary_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 | } |