]>
Commit | Line | Data |
---|---|---|
19bc17a9 RM |
1 | /* Copyright (C) 1995, 1996 Free Software Foundation, Inc. |
2 | This file is part of the GNU C Library. | |
3 | Contributed by Ulrich Drepper, <drepper@gnu.ai.mit.edu>. | |
4 | ||
5 | The GNU C Library is free software; you can redistribute it and/or | |
6 | modify it under the terms of the GNU Library General Public License as | |
7 | published by the Free Software Foundation; either version 2 of the | |
8 | License, or (at your option) any later version. | |
9 | ||
10 | The GNU C Library 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 GNU | |
13 | Library General Public License for more details. | |
14 | ||
15 | You should have received a copy of the GNU Library General Public | |
16 | License along with the GNU C Library; see the file COPYING.LIB. If | |
17 | not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
18 | Boston, MA 02111-1307, USA. */ | |
19 | ||
20 | #ifdef HAVE_CONFIG_H | |
21 | # include <config.h> | |
22 | #endif | |
23 | ||
24 | #include <langinfo.h> | |
25 | #include <limits.h> | |
26 | #include <stdlib.h> | |
27 | #include <string.h> | |
28 | ||
29 | ||
30 | /* Undefine following line in production version. */ | |
31 | /* #define NDEBUG 1 */ | |
32 | #include <assert.h> | |
33 | ||
34 | #include "locales.h" | |
35 | #include "localeinfo.h" | |
36 | #include "stringtrans.h" | |
37 | ||
38 | void *xmalloc (size_t __n); | |
39 | void *xrealloc (void *__ptr, size_t __n); | |
40 | ||
41 | ||
42 | /* The real definition of the struct for the LC_NUMERIC locale. */ | |
43 | struct locale_monetary_t | |
44 | { | |
45 | const char *int_curr_symbol; | |
46 | const char *currency_symbol; | |
47 | const char *mon_decimal_point; | |
48 | const char *mon_thousands_sep; | |
49 | char *mon_grouping; | |
50 | size_t mon_grouping_max; | |
51 | size_t mon_grouping_act; | |
52 | const char *positive_sign; | |
53 | const char *negative_sign; | |
54 | signed char int_frac_digits; | |
55 | signed char frac_digits; | |
56 | signed char p_cs_precedes; | |
57 | signed char p_sep_by_space; | |
58 | signed char n_cs_precedes; | |
59 | signed char n_sep_by_space; | |
60 | signed char p_sign_posn; | |
61 | signed char n_sign_posn; | |
62 | }; | |
63 | ||
64 | ||
65 | /* The content iof the field int_curr_symbol has to be taken from | |
66 | ISO-4217. We test for correct values. */ | |
67 | #define DEFINE_INT_CURR(str) str, | |
68 | static const char *const valid_int_curr[] = | |
69 | { | |
70 | # include "../iso-4217.def" | |
71 | }; | |
72 | #define NR_VALID_INT_CURR ((sizeof (valid_int_curr) \ | |
73 | / sizeof (valid_int_curr[0]))) | |
74 | #undef DEFINE_INT_CURR | |
75 | ||
76 | ||
77 | /* Prototypes for local functions. */ | |
78 | static int curr_strcmp(const char *s1, const char **s2); | |
79 | ||
80 | ||
81 | void | |
82 | monetary_startup (struct linereader *lr, struct localedef_t *locale, | |
83 | struct charset_t *charset) | |
84 | { | |
85 | struct locale_monetary_t *monetary; | |
86 | ||
87 | /* It is important that we always use UCS1 encoding for strings now. */ | |
88 | encoding_method = ENC_UCS1; | |
89 | ||
90 | locale->categories[LC_MONETARY].monetary = monetary = | |
91 | (struct locale_monetary_t *) xmalloc (sizeof (struct locale_monetary_t)); | |
92 | ||
93 | memset (monetary, '\0', sizeof (struct locale_monetary_t)); | |
94 | ||
95 | monetary->mon_grouping_max = 80; | |
96 | monetary->mon_grouping = | |
97 | (char *) xmalloc (monetary->mon_grouping_max); | |
98 | monetary->mon_grouping_act = 0; | |
99 | ||
100 | monetary->int_frac_digits = -2; | |
101 | monetary->frac_digits = -2; | |
102 | monetary->p_cs_precedes = -2; | |
103 | monetary->p_sep_by_space = -2; | |
104 | monetary->n_cs_precedes = -2; | |
105 | monetary->n_sep_by_space = -2; | |
106 | monetary->p_sign_posn = -2; | |
107 | monetary->n_sign_posn = -2; | |
108 | } | |
109 | ||
110 | ||
111 | void | |
112 | monetary_finish (struct localedef_t *locale) | |
113 | { | |
114 | struct locale_monetary_t *monetary | |
115 | = locale->categories[LC_MONETARY].monetary; | |
116 | ||
117 | #define TEST_ELEM(cat) \ | |
118 | if (monetary->cat == NULL) \ | |
119 | error (0, 0, _("field `%s' in category `%s' not defined"), \ | |
120 | #cat, "LC_MONETARY") | |
121 | ||
122 | TEST_ELEM (int_curr_symbol); | |
123 | TEST_ELEM (currency_symbol); | |
124 | TEST_ELEM (mon_decimal_point); | |
125 | TEST_ELEM (mon_thousands_sep); | |
126 | TEST_ELEM (positive_sign); | |
127 | TEST_ELEM (negative_sign); | |
128 | ||
129 | /* The international currency symbol must come from ISO 4217. */ | |
130 | if (monetary->int_curr_symbol != NULL) | |
131 | { | |
132 | if (strlen (monetary->int_curr_symbol) != 4) | |
133 | error (0, 0, _("\ | |
134 | value of field `int_curr_symbol' in category `LC_MONETARY' has wrong length")); | |
135 | else if (bsearch (monetary->int_curr_symbol, valid_int_curr, | |
136 | NR_VALID_INT_CURR, sizeof (const char *), | |
137 | (comparison_fn_t) curr_strcmp) == NULL) | |
138 | error (0, 0, _("\ | |
139 | value of field `int_curr_symbol' in category `LC_MONETARY' does \ | |
140 | not correspond to a valid name in ISO 4217")); | |
141 | } | |
142 | ||
143 | /* The decimal point must not be empty. This is not said explicitly | |
144 | in POSIX but ANSI C (ISO/IEC 9899) says in 4.4.2.1 it has to be | |
145 | != "". */ | |
146 | if (monetary->mon_decimal_point[0] == '\0') | |
147 | { | |
148 | error (0, 0, _("\ | |
149 | value for field `%s' in category `%s' must not be the empty string"), | |
150 | "mon_decimal_point", "LC_MONETARY"); | |
151 | } | |
152 | ||
153 | if (monetary->mon_grouping_act == 0) | |
154 | error (0, 0, _("field `%s' in category `%s' not defined"), | |
155 | "mon_grouping", "LC_MONETARY"); | |
156 | ||
157 | #undef TEST_ELEM | |
158 | #define TEST_ELEM(cat, min, max) \ | |
159 | if (monetary->cat == -2) \ | |
160 | error (0, 0, _("field `%s' in category `%s' not defined"), \ | |
161 | #cat, "LC_MONETARY"); \ | |
162 | else if (monetary->cat < min || monetary->cat > max) \ | |
163 | error (0, 0, _("\ | |
164 | value for field `%s' in category `%s' must be in range %d...%d"), \ | |
165 | #cat, "LC_MONETARY", min, max) | |
166 | ||
167 | TEST_ELEM (int_frac_digits, -128, 127); /* No range check. */ | |
168 | TEST_ELEM (frac_digits, -128, 127); /* No range check. */ | |
169 | TEST_ELEM (p_cs_precedes, -1, 1); | |
170 | TEST_ELEM (p_sep_by_space, -1, 2); | |
171 | TEST_ELEM (n_cs_precedes, -1, 1); | |
172 | TEST_ELEM (n_sep_by_space, -1, 2); | |
173 | TEST_ELEM (p_sign_posn, -1, 4); | |
174 | TEST_ELEM (n_sign_posn, -1, 4); | |
175 | } | |
176 | ||
177 | ||
178 | void | |
179 | monetary_output (struct localedef_t *locale, const char *output_path) | |
180 | { | |
181 | struct locale_monetary_t *monetary | |
182 | = locale->categories[LC_MONETARY].monetary; | |
183 | struct iovec iov[2 + _NL_ITEM_INDEX (_NL_NUM_LC_MONETARY)]; | |
184 | struct locale_file data; | |
185 | u32_t idx[_NL_ITEM_INDEX (_NL_NUM_LC_MONETARY)]; | |
186 | size_t cnt = 0; | |
187 | ||
188 | if ((locale->binary & (1 << LC_MONETARY)) != 0) | |
189 | { | |
190 | iov[0].iov_base = monetary; | |
191 | iov[0].iov_len = locale->len[LC_MONETARY]; | |
192 | ||
193 | write_locale_data (output_path, "LC_MONETARY", 1, iov); | |
194 | ||
195 | return; | |
196 | } | |
197 | ||
198 | data.magic = LIMAGIC (LC_MONETARY); | |
199 | data.n = _NL_ITEM_INDEX (_NL_NUM_LC_MONETARY); | |
200 | iov[cnt].iov_base = (void *) &data; | |
201 | iov[cnt].iov_len = sizeof (data); | |
202 | ++cnt; | |
203 | ||
204 | iov[cnt].iov_base = (void *) idx; | |
205 | iov[cnt].iov_len = sizeof (idx); | |
206 | ++cnt; | |
207 | ||
208 | idx[cnt - 2] = iov[0].iov_len + iov[1].iov_len; | |
209 | iov[cnt].iov_base = (void *) (monetary->int_curr_symbol ?: ""); | |
210 | iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1; | |
211 | ++cnt; | |
212 | ||
213 | idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len; | |
214 | iov[cnt].iov_base = (void *) (monetary->currency_symbol ?: ""); | |
215 | iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1; | |
216 | ++cnt; | |
217 | ||
218 | idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len; | |
219 | iov[cnt].iov_base = (void *) (monetary->mon_decimal_point ?: ""); | |
220 | iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1; | |
221 | ++cnt; | |
222 | ||
223 | idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len; | |
224 | iov[cnt].iov_base = (void *) (monetary->mon_thousands_sep ?: ""); | |
225 | iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1; | |
226 | ++cnt; | |
227 | ||
228 | idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len; | |
229 | iov[cnt].iov_base = alloca (monetary->mon_grouping_act + 1); | |
230 | iov[cnt].iov_len = monetary->mon_grouping_act + 1; | |
231 | memcpy (iov[cnt].iov_base, monetary->mon_grouping, | |
232 | monetary->mon_grouping_act); | |
233 | ((char *) iov[cnt].iov_base)[monetary->mon_grouping_act] = '\0'; | |
234 | ++cnt; | |
235 | ||
236 | idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len; | |
237 | iov[cnt].iov_base = (void *) (monetary->positive_sign ?: ""); | |
238 | iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1; | |
239 | ++cnt; | |
240 | ||
241 | idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len; | |
242 | iov[cnt].iov_base = (void *) (monetary->negative_sign ?: ""); | |
243 | iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1; | |
244 | ++cnt; | |
245 | ||
246 | idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len; | |
247 | iov[cnt].iov_base = (void *) &monetary->int_frac_digits; | |
248 | iov[cnt].iov_len = 1; | |
249 | ++cnt; | |
250 | ||
251 | idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len; | |
252 | iov[cnt].iov_base = (void *) &monetary->frac_digits; | |
253 | iov[cnt].iov_len = 1; | |
254 | ++cnt; | |
255 | ||
256 | idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len; | |
257 | iov[cnt].iov_base = (void *) &monetary->p_cs_precedes; | |
258 | iov[cnt].iov_len = 1; | |
259 | ++cnt; | |
260 | ||
261 | idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len; | |
262 | iov[cnt].iov_base = (void *) &monetary->p_sep_by_space; | |
263 | iov[cnt].iov_len = 1; | |
264 | ++cnt; | |
265 | ||
266 | idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len; | |
267 | iov[cnt].iov_base = (void *) &monetary->n_cs_precedes; | |
268 | iov[cnt].iov_len = 1; | |
269 | ++cnt; | |
270 | ||
271 | idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len; | |
272 | iov[cnt].iov_base = (void *) &monetary->n_sep_by_space; | |
273 | iov[cnt].iov_len = 1; | |
274 | ++cnt; | |
275 | ||
276 | idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len; | |
277 | iov[cnt].iov_base = (void *) &monetary->p_sign_posn; | |
278 | iov[cnt].iov_len = 1; | |
279 | ++cnt; | |
280 | ||
281 | idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len; | |
282 | iov[cnt].iov_base = (void *) &monetary->n_sign_posn; | |
283 | iov[cnt].iov_len = 1; | |
284 | ||
285 | assert (cnt + 1 == 2 + _NL_ITEM_INDEX (_NL_NUM_LC_MONETARY)); | |
286 | ||
287 | write_locale_data (output_path, "LC_MONETARY", | |
288 | 2 + _NL_ITEM_INDEX (_NL_NUM_LC_MONETARY), iov); | |
289 | } | |
290 | ||
291 | ||
292 | void | |
293 | monetary_add (struct linereader *lr, struct localedef_t *locale, | |
294 | enum token_t tok, struct token *code, | |
295 | struct charset_t *charset) | |
296 | { | |
297 | struct locale_monetary_t *monetary | |
298 | = locale->categories[LC_MONETARY].monetary; | |
299 | ||
300 | switch (tok) | |
301 | { | |
302 | #define STR_ELEM(cat) \ | |
303 | case tok_##cat: \ | |
304 | if (monetary->cat != NULL) \ | |
305 | lr_error (lr, _("\ | |
306 | field `%s' in category `%s' declared more than once"), \ | |
307 | #cat, "LC_MONETARY"); \ | |
308 | else if (code->val.str.start == NULL) \ | |
309 | { \ | |
310 | lr_error (lr, _("unknown character in field `%s' of category `%s'"),\ | |
311 | #cat, "LC_MONETARY"); \ | |
312 | monetary->cat = ""; \ | |
313 | } \ | |
314 | else \ | |
315 | monetary->cat = code->val.str.start; \ | |
316 | break | |
317 | ||
318 | STR_ELEM (int_curr_symbol); | |
319 | STR_ELEM (currency_symbol); | |
320 | STR_ELEM (mon_decimal_point); | |
321 | STR_ELEM (mon_thousands_sep); | |
322 | STR_ELEM (positive_sign); | |
323 | STR_ELEM (negative_sign); | |
324 | ||
325 | #define INT_ELEM(cat) \ | |
326 | case tok_##cat: \ | |
327 | if (monetary->cat != -2) \ | |
328 | lr_error (lr, _("\ | |
329 | field `%s' in category `%s' declared more than once"), \ | |
330 | #cat, "LC_MONETARY"); \ | |
331 | else \ | |
332 | monetary->cat = code->val.num; \ | |
333 | break | |
334 | ||
335 | INT_ELEM (int_frac_digits); | |
336 | INT_ELEM (frac_digits); | |
337 | INT_ELEM (p_cs_precedes); | |
338 | INT_ELEM (p_sep_by_space); | |
339 | INT_ELEM (n_cs_precedes); | |
340 | INT_ELEM (n_sep_by_space); | |
341 | INT_ELEM (p_sign_posn); | |
342 | INT_ELEM (n_sign_posn); | |
343 | ||
344 | case tok_mon_grouping: | |
345 | if (monetary->mon_grouping_act == monetary->mon_grouping_max) | |
346 | { | |
347 | monetary->mon_grouping_max *= 2; | |
348 | monetary->mon_grouping = | |
349 | (char *) xrealloc (monetary->mon_grouping, | |
350 | monetary->mon_grouping_max); | |
351 | } | |
352 | if (monetary->mon_grouping[monetary->mon_grouping_act - 1] | |
353 | == '\177') | |
354 | lr_error (lr, _("\ | |
355 | `-1' must be last entry in `%s' field in `%s' category"), | |
356 | "mon_grouping", "LC_MONETARY"); | |
357 | else | |
358 | { | |
359 | if (code->tok == tok_minus1) | |
360 | monetary->mon_grouping[monetary->mon_grouping_act++] = '\177'; | |
361 | else if (code->val.num == 0) | |
362 | lr_error (lr, _("\ | |
363 | values for field `%s' in category `%s' must not be zero"), | |
364 | "mon_grouping", "LC_MONETARY"); | |
365 | else if (code->val.num > 126) | |
366 | lr_error (lr, _("\ | |
367 | values for field `%s' in category `%s' must be smaller than 127"), | |
368 | "mon_grouping", "LC_MONETARY"); | |
369 | else | |
370 | monetary->mon_grouping[monetary->mon_grouping_act++] | |
371 | = code->val.num; | |
372 | } | |
373 | break; | |
374 | ||
375 | default: | |
376 | assert (! "unknown token in category `LC_MONETARY': should not happen"); | |
377 | } | |
378 | } | |
379 | ||
380 | ||
381 | static int | |
382 | curr_strcmp(const char *s1, const char **s2) | |
383 | { | |
384 | return strcmp (s1, *s2); | |
385 | } |