Fix handling of non-existing definitions for this category. Correctly
[kopensolaris-gnu/glibc.git] / locale / programs / ld-numeric.c
1 /* Copyright (C) 1995, 1996, 1997, 1998, 1999 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    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 not,
17    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 <string.h>
26 #include <sys/uio.h>
27
28 #include <assert.h>
29
30 #include "linereader.h"
31 #include "localedef.h"
32 #include "localeinfo.h"
33 #include "locfile.h"
34
35
36 /* The real definition of the struct for the LC_NUMERIC locale.  */
37 struct locale_numeric_t
38 {
39   const char *decimal_point;
40   const char *thousands_sep;
41   char *grouping;
42   size_t grouping_len;
43 };
44
45
46 static void
47 numeric_startup (struct linereader *lr, struct localedef_t *locale,
48                  int ignore_content)
49 {
50   if (!ignore_content)
51     {
52       struct locale_numeric_t *numeric;
53
54       locale->categories[LC_NUMERIC].numeric = numeric =
55         (struct locale_numeric_t *) xcalloc (1, sizeof (*numeric));
56
57       numeric->grouping = NULL;
58       numeric->grouping_len = 0;
59     }
60
61   if (lr != NULL)
62     {
63       lr->translate_strings = 1;
64       lr->return_widestr = 0;
65     }
66 }
67
68
69 void
70 numeric_finish (struct localedef_t *locale, struct charmap_t *charmap)
71 {
72   struct locale_numeric_t *numeric = locale->categories[LC_NUMERIC].numeric;
73   int nothing = 0;
74
75   /* Now resolve copying and also handle completely missing definitions.  */
76   if (numeric == NULL)
77     {
78       /* First see whether we were supposed to copy.  If yes, find the
79          actual definition.  */
80       if (locale->copy_name[LC_NUMERIC] != NULL)
81         {
82           /* Find the copying locale.  This has to happen transitively since
83              the locale we are copying from might also copying another one.  */
84           struct localedef_t *from = locale;
85
86           do
87             from = find_locale (LC_NUMERIC, from->copy_name[LC_NUMERIC],
88                                 from->repertoire_name, charmap);
89           while (from->categories[LC_NUMERIC].numeric == NULL
90                  && from->copy_name[LC_NUMERIC] != NULL);
91
92           numeric = locale->categories[LC_NUMERIC].numeric
93             = from->categories[LC_NUMERIC].numeric;
94         }
95
96       /* If there is still no definition issue an warning and create an
97          empty one.  */
98       if (numeric == NULL)
99         {
100           error (0, 0, _("No definition for %s category found"), "LC_NUMERIC");
101           numeric_startup (NULL, locale, 0);
102           numeric = locale->categories[LC_NUMERIC].numeric;
103           nothing = 1;
104         }
105     }
106
107 #define TEST_ELEM(cat)                                                        \
108   if (numeric->cat == NULL && ! be_quiet && ! nothing)                        \
109     error (0, 0, _("%s: field `%s' not defined"), "LC_NUMERIC", #cat)
110
111   TEST_ELEM (decimal_point);
112   TEST_ELEM (thousands_sep);
113
114   /* The decimal point must not be empty.  This is not said explicitly
115      in POSIX but ANSI C (ISO/IEC 9899) says in 4.4.2.1 it has to be
116      != "".  */
117   if (numeric->decimal_point[0] == '\0' && ! be_quiet && ! nothing)
118     {
119       error (0, 0, _("\
120 %s: value for field `%s' must not be the empty string"),
121              "LC_NUMERIC", "decimal_point");
122     }
123
124   if (numeric->grouping_len == 0 && ! be_quiet && ! nothing)
125     error (0, 0, _("%s: field `%s' not defined"), "LC_NUMERIC", "grouping");
126 }
127
128
129 void
130 numeric_output (struct localedef_t *locale, struct charmap_t *charmap,
131                 const char *output_path)
132 {
133   struct locale_numeric_t *numeric = locale->categories[LC_NUMERIC].numeric;
134   struct iovec iov[2 + _NL_ITEM_INDEX (_NL_NUM_LC_NUMERIC)];
135   struct locale_file data;
136   uint32_t idx[_NL_ITEM_INDEX (_NL_NUM_LC_NUMERIC)];
137   size_t cnt = 0;
138
139   data.magic = LIMAGIC (LC_NUMERIC);
140   data.n = _NL_ITEM_INDEX (_NL_NUM_LC_NUMERIC);
141   iov[cnt].iov_base = (void *) &data;
142   iov[cnt].iov_len = sizeof (data);
143   ++cnt;
144
145   iov[cnt].iov_base = (void *) idx;
146   iov[cnt].iov_len = sizeof (idx);
147   ++cnt;
148
149   idx[cnt - 2] = iov[0].iov_len + iov[1].iov_len;
150   iov[cnt].iov_base = (void *) (numeric->decimal_point ?: "");
151   iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
152   ++cnt;
153
154   idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
155   iov[cnt].iov_base = (void *) (numeric->thousands_sep ?: "");
156   iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
157   ++cnt;
158
159   idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
160   iov[cnt].iov_base = numeric->grouping;
161   iov[cnt].iov_len = numeric->grouping_len;
162
163   assert (cnt + 1 == 2 + _NL_ITEM_INDEX (_NL_NUM_LC_NUMERIC));
164
165   write_locale_data (output_path, "LC_NUMERIC",
166                      2 + _NL_ITEM_INDEX (_NL_NUM_LC_NUMERIC), iov);
167 }
168
169
170 /* The parser for the LC_NUMERIC section of the locale definition.  */
171 void
172 numeric_read (struct linereader *ldfile, struct localedef_t *result,
173               struct charmap_t *charmap, const char *repertoire_name,
174               int ignore_content)
175 {
176   struct repertoire_t *repertoire = NULL;
177   struct locale_numeric_t *numeric;
178   struct token *now;
179   enum token_t nowtok;
180
181   /* Get the repertoire we have to use.  */
182   if (repertoire_name != NULL)
183     repertoire = repertoire_read (repertoire_name);
184
185   /* The rest of the line containing `LC_NUMERIC' must be free.  */
186   lr_ignore_rest (ldfile, 1);
187
188
189   do
190     {
191       now = lr_token (ldfile, charmap, NULL);
192       nowtok = now->tok;
193     }
194   while (nowtok == tok_eol);
195
196   /* If we see `copy' now we are almost done.  */
197   if (nowtok == tok_copy)
198     {
199       handle_copy (ldfile, charmap, repertoire, result, tok_lc_numeric,
200                    LC_NUMERIC, "LC_NUMERIC", ignore_content);
201       return;
202     }
203
204   /* Prepare the data structures.  */
205   numeric_startup (ldfile, result, ignore_content);
206   numeric = result->categories[LC_NUMERIC].numeric;
207
208   while (1)
209     {
210       /* Of course we don't proceed beyond the end of file.  */
211       if (nowtok == tok_eof)
212         break;
213
214       /* Ingore empty lines.  */
215       if (nowtok == tok_eol)
216         {
217           now = lr_token (ldfile, charmap, NULL);
218           nowtok = now->tok;
219           continue;
220         }
221
222       switch (nowtok)
223         {
224 #define STR_ELEM(cat) \
225         case tok_##cat:                                                       \
226           /* Ignore the rest of the line if we don't need the input of        \
227              this line.  */                                                   \
228           if (ignore_content)                                                 \
229             {                                                                 \
230               lr_ignore_rest (ldfile, 0);                                     \
231               break;                                                          \
232             }                                                                 \
233                                                                               \
234           now = lr_token (ldfile, charmap, NULL);                             \
235           if (now->tok != tok_string)                                         \
236             goto err_label;                                                   \
237           if (numeric->cat != NULL)                                           \
238             lr_error (ldfile, _("\
239 %s: field `%s' declared more than once"), "LC_NUMERIC", #cat);                \
240           else if (!ignore_content && now->val.str.startmb == NULL)           \
241             {                                                                 \
242               lr_error (ldfile, _("\
243 %s: unknown character in field `%s'"), "LC_NUMERIC", #cat);                   \
244               numeric->cat = "";                                              \
245             }                                                                 \
246           else if (!ignore_content)                                           \
247             numeric->cat = now->val.str.startmb;                              \
248           break
249
250           STR_ELEM (decimal_point);
251           STR_ELEM (thousands_sep);
252
253         case tok_grouping:
254           /* Ignore the rest of the line if we don't need the input of
255              this line.  */
256           if (ignore_content)
257             {
258               lr_ignore_rest (ldfile, 0);
259               break;
260             }
261
262           now = lr_token (ldfile, charmap, NULL);
263           if (now->tok != tok_minus1 && now->tok != tok_number)
264             goto err_label;
265           else
266             {
267               size_t act = 0;
268               size_t max = 10;
269               char *grouping = ignore_content ? NULL : xmalloc (max);
270
271               do
272                 {
273                   if (act + 1 >= max)
274                     {
275                       max *= 2;
276                       grouping = xrealloc (grouping, max);
277                     }
278
279                   if (act > 0 && grouping[act - 1] == '\177')
280                     {
281                       lr_error (ldfile, _("\
282 %s: `-1' must be last entry in `%s' field"), "LC_NUMERIC", "grouping");
283                       lr_ignore_rest (ldfile, 0);
284                       break;
285                     }
286
287                   if (now->tok == tok_minus1)
288                     {
289                       if (!ignore_content)
290                         grouping[act++] = '\177';
291                     }
292                   else if (now->val.num == 0)
293                     {
294                       /* A value of 0 disables grouping from here on but
295                          we must not store a NUL character since this
296                          terminates the string.  Use something different
297                          which must not be used otherwise.  */
298                       if (!ignore_content)
299                         grouping[act++] = '\377';
300                     }
301                   else if (now->val.num > 126)
302                     lr_error (ldfile, _("\
303 %s: values for field `%s' must be smaller than 127"),
304                               "LC_NUMERIC", "grouping");
305                   else if (!ignore_content)
306                     grouping[act++] = now->val.num;
307
308                   /* Next must be semicolon.  */
309                   now = lr_token (ldfile, charmap, NULL);
310                   if (now->tok != tok_semicolon)
311                     break;
312
313                   now = lr_token (ldfile, charmap, NULL);
314                 }
315               while (now->tok == tok_minus1 || now->tok == tok_number);
316
317               if (now->tok != tok_eol)
318                 goto err_label;
319
320               if (!ignore_content)
321                 {
322                   grouping[act++] = '\0';
323
324                   numeric->grouping = xrealloc (grouping, act);
325                   numeric->grouping_len = act;
326                 }
327             }
328           break;
329
330         case tok_end:
331           /* Next we assume `LC_NUMERIC'.  */
332           now = lr_token (ldfile, charmap, NULL);
333           if (now->tok == tok_eof)
334             break;
335           if (now->tok == tok_eol)
336             lr_error (ldfile, _("%s: incomplete `END' line"), "LC_NUMERIC");
337           else if (now->tok != tok_lc_numeric)
338             lr_error (ldfile, _("\
339 %1$s: definition does not end with `END %1$s'"), "LC_NUMERIC");
340           lr_ignore_rest (ldfile, now->tok == tok_lc_numeric);
341           return;
342
343         default:
344         err_label:
345           SYNTAX_ERROR (_("%s: syntax error"), "LC_NUMERIC");
346         }
347
348       /* Prepare for the next round.  */
349       now = lr_token (ldfile, charmap, NULL);
350       nowtok = now->tok;
351     }
352
353   /* When we come here we reached the end of the file.  */
354   lr_error (ldfile, _("%s: premature end of file"), "LC_NUMERIC");
355 }