Thu May 23 00:01:10 1996 Ulrich Drepper <drepper@cygnus.com>
[kopensolaris-gnu/glibc.git] / locale / programs / locfile.c
1 /* Copyright (C) 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 <errno.h>
25 #include <fcntl.h>
26 #include <locale.h>
27 #include <malloc.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <sys/stat.h>
32 #include <sys/uio.h>
33
34 #include "locfile.h"
35 #include "linereader.h"
36 #include "localeinfo.h"
37 #include "locales.h"
38
39
40 /* Uncomment the following line in the production version. */
41 /* #define NDEBUG 1 */
42 #include <assert.h>
43
44 /* Define the lookup function.  */
45 #include "locfile-kw.h"
46
47
48 /* Some useful macros.  */
49 #define MIN(a, b) (__extension__ ({ typeof (a) _a = (a);                      \
50                                     typeof (b) _b = (b);                      \
51                                     _a < _b ? _a : _b; }))
52
53
54 void *xmalloc (size_t __n);
55 char *xstrdup (const char *__str);
56
57 struct localedef_t *
58 locfile_read (const char *filename, struct charset_t *charset)
59 {
60   struct linereader *ldfile;
61   struct localedef_t *result;
62   int state;
63   enum token_t expected_tok = tok_none;
64   const char *expected_str = NULL;
65   enum token_t ctype_tok_sym = tok_none;
66   const char *ctype_tok_str = NULL;
67   int copy_category = 0;
68   int cnt;
69
70   /* Allocate space for result.  */
71   result = (struct localedef_t *) xmalloc (sizeof (struct localedef_t));
72   memset (result, '\0', sizeof (struct localedef_t));
73
74   ldfile = lr_open (filename, locfile_hash);
75   if (ldfile == NULL)
76     {
77       if (filename[0] != '/')
78         {
79           char path[strlen (filename) + 1 + sizeof (LOCSRCDIR)];
80
81           stpcpy (stpcpy (stpcpy (path, LOCSRCDIR), "/"), filename);
82           ldfile = lr_open (path, locfile_hash);
83         }
84
85       if (ldfile == NULL)
86         {
87           result->failed = 1;
88           return result;
89         }
90     }
91
92 #define HANDLE_COPY(category, token, string)                                  \
93   if (nowtok == tok_copy)                                                     \
94     {                                                                         \
95       copy_category = category;                                               \
96       expected_tok = token;                                                   \
97       expected_str = string;                                                  \
98       state = 8;                                                              \
99       continue;                                                               \
100     }                                                                         \
101   ++state
102
103 #define LOCALE_PROLOG(token, string)                                          \
104   if (nowtok == tok_eol)                                                      \
105     /* Ignore empty lines.  */                                                \
106     continue;                                                                 \
107   if (nowtok == tok_end)                                                      \
108     {                                                                         \
109       expected_tok = token;                                                   \
110       expected_str = string;                                                  \
111       state = 4;                                                              \
112       continue;                                                               \
113     }                                                                         \
114   if (nowtok == tok_copy)                                                     \
115     goto only_copy;
116
117
118 #define READ_STRING(fn, errlabel)                                             \
119   do                                                                          \
120     {                                                                         \
121       arg = lr_token (ldfile, charset);                                       \
122       if (arg->tok != tok_string)                                             \
123         goto errlabel;                                                        \
124       fn (ldfile, result, nowtok, arg, charset);                              \
125       lr_ignore_rest (ldfile, 1);                                             \
126     }                                                                         \
127   while (0)
128
129 #define READ_STRING_LIST(fn, errlabel)                                        \
130   do                                                                          \
131     {                                                                         \
132       arg = lr_token (ldfile, charset);                                       \
133       while (arg->tok == tok_string)                                          \
134         {                                                                     \
135           fn (ldfile, result, nowtok, arg, charset);                          \
136           arg = lr_token (ldfile, charset);                                   \
137           if (arg->tok != tok_semicolon)                                      \
138             break;                                                            \
139           arg = lr_token (ldfile, charset);                                   \
140         }                                                                     \
141       if (arg->tok != tok_eol)                                                \
142         goto errlabel;                                                        \
143     }                                                                         \
144   while (0)
145
146 #define READ_NUMBER(fn, errlabel)                                             \
147   do                                                                          \
148     {                                                                         \
149       arg = lr_token (ldfile, charset);                                       \
150       if (arg->tok != tok_minus1 && arg->tok != tok_number)                   \
151         goto errlabel;                                                        \
152       fn (ldfile, result, nowtok, arg, charset);                              \
153       lr_ignore_rest (ldfile, 1);                                             \
154     }                                                                         \
155   while (0)
156
157 #define READ_NUMBER_LIST(fn, errlabel)                                        \
158   do                                                                          \
159     {                                                                         \
160       arg = lr_token (ldfile, charset);                                       \
161       while (arg->tok == tok_minus1 || arg->tok == tok_number)                \
162         {                                                                     \
163           fn (ldfile, result, nowtok, arg, charset);                          \
164           arg = lr_token (ldfile, charset);                                   \
165           if (arg->tok != tok_semicolon)                                      \
166             break;                                                            \
167           arg = lr_token (ldfile, charset);                                   \
168         }                                                                     \
169       if (arg->tok != tok_eol)                                                \
170         goto errlabel;                                                        \
171     }                                                                         \
172   while (0)
173
174 #define SYNTAX_ERROR(string)                                                  \
175   lr_error (ldfile, string);                                                  \
176   lr_ignore_rest (ldfile, 0);
177
178
179   /* Parse locale definition file and store result in RESULT.  */
180   state = 1;
181   while (1)
182     {
183       /* What's on?  */
184       struct token *now = lr_token (ldfile, charset);
185       enum token_t nowtok = now->tok;
186       struct token *arg;
187
188       if (nowtok == tok_eof)
189         break;
190
191       switch (state)
192         {
193         case 1:
194           /* The beginning.  We expect the special declarations, EOL or
195              the start of any locale.  */
196           if (nowtok == tok_eol)
197             /* Ignore empty lines.  */
198             continue;
199
200           switch (nowtok)
201             {
202             case tok_escape_char:
203             case tok_comment_char:
204               /* We need an argument.  */
205               arg = lr_token (ldfile, charset);
206
207               if (arg->tok != tok_ident)
208                 {
209                   SYNTAX_ERROR (_("bad argument"));
210                   continue;
211                 }
212
213               if (arg->val.str.len != 1)
214                 {
215                   lr_error (ldfile, _("\
216 argument to `%s' must be a single character"),
217                             nowtok == tok_escape_char ? "escape_char"
218                                                       : "comment_char");
219
220                   lr_ignore_rest (ldfile, 0);
221                   continue;
222                 }
223
224               if (nowtok == tok_escape_char)
225                 ldfile->escape_char = *arg->val.str.start;
226               else
227                 ldfile->comment_char = *arg->val.str.start;
228               break;
229
230             case tok_lc_ctype:
231               state = 2;
232               break;
233
234             case tok_lc_collate:
235               state = 10;
236               break;
237
238             case tok_lc_monetary:
239               state = 20;
240               break;
241
242             case tok_lc_numeric:
243               state = 30;
244               break;
245
246             case tok_lc_time:
247               state = 40;
248               break;
249
250             case tok_lc_messages:
251               state = 50;
252               break;
253
254             default:
255               SYNTAX_ERROR (_("\
256 syntax error: not inside a locale definition section"));
257               continue;
258             }
259           lr_ignore_rest (ldfile, 1);
260           continue;
261
262         case 2:
263           HANDLE_COPY (LC_CTYPE, tok_lc_ctype, "LC_CYTPE");
264
265           ctype_startup (ldfile, result, charset);
266           /* FALLTHROUGH */
267
268         case 3:
269           /* Here we accept all the character classes, tolower/toupper,
270              and following ANSI C:1995 self-defined classes.  */
271           LOCALE_PROLOG (tok_lc_ctype, "LC_CTYPE");
272
273           if (nowtok == tok_charclass)
274             {
275               READ_STRING_LIST (ctype_class_new, bad_new_charclass);
276               continue;
277             bad_new_charclass:
278               SYNTAX_ERROR (_("\
279 syntax error in definition of new character class"));
280               continue;
281             }
282
283           if (nowtok == tok_charmap)
284             {
285               READ_STRING_LIST (ctype_map_new, bad_new_charmap);
286               continue;
287             bad_new_charmap:
288               SYNTAX_ERROR (_("\
289 syntax error in definition of new character map"));
290               continue;
291             }
292
293           if (nowtok == tok_upper || nowtok == tok_lower
294               || nowtok == tok_alpha || nowtok == tok_digit
295               || nowtok == tok_alnum || nowtok == tok_space
296               || nowtok == tok_cntrl || nowtok == tok_punct
297               || nowtok == tok_graph || nowtok == tok_print
298               || nowtok == tok_xdigit || nowtok == tok_blank)
299             {
300               ctype_tok_sym = nowtok;
301               ctype_tok_str = NULL;
302               state = 5;
303               continue;
304             }
305
306           if (nowtok == tok_toupper|| nowtok == tok_tolower)
307             {
308               ctype_tok_sym = nowtok;
309               ctype_tok_str = NULL;
310               state = 6;
311               continue;
312             }
313
314           if (nowtok != tok_ident)
315             goto bad_charclass;
316
317           /* We possibly have a self-defined character class.  */
318           if (ctype_is_charclass (ldfile, result, now->val.str.start))
319             {
320               ctype_tok_sym = nowtok;
321               ctype_tok_str = now->val.str.start;
322               state = 5;
323               continue;
324             }
325
326           /* ...or a self-defined character map.  */
327           if (ctype_is_charmap (ldfile, result, now->val.str.start))
328             {
329               ctype_tok_sym = nowtok;
330               ctype_tok_str = now->val.str.start;
331               state = 6;
332               continue;
333             }
334
335           SYNTAX_ERROR (_("syntax error in definition of LC_CTYPE category"));
336           continue;
337
338         case 4:
339           /* Handle `END xxx'.  */
340           if (nowtok != expected_tok)
341             lr_error (ldfile, _("\
342 `%1$s' definition does not end with `END %1$s'"), expected_str);
343
344           lr_ignore_rest (ldfile, nowtok == expected_tok);
345           state = 1;
346           continue;
347
348         case 5:
349           /* Here we expect a semicolon separated list of bsymbols.  The
350              bit to be set in the word is given in CHARCLASS_BIT.  */
351           arg = now;
352
353           ctype_class_start (ldfile, result, ctype_tok_sym, ctype_tok_str,
354                              charset);
355
356           while (arg->tok != tok_eol)
357             {
358               /* Any token other than a bsymbol is an error.  */
359               if (arg->tok != tok_bsymbol)
360                 {
361                 bad_charclass:
362                   SYNTAX_ERROR (_("\
363 syntax error in character class definition"));
364                   break;
365                 }
366
367               /* Lookup value for token and write into array.  */
368               ctype_class_from (ldfile, result, arg, charset);
369
370               arg = lr_token (ldfile, charset);
371               if (arg->tok == tok_semicolon)
372                 arg = lr_token (ldfile, charset);
373               else if (arg->tok != tok_eol)
374                 goto bad_charclass;
375
376               /* Look for ellipsis.  */
377               if (arg->tok == tok_ellipsis)
378                 {
379                   arg = lr_token (ldfile, charset);
380                   if (arg->tok != tok_semicolon)
381                     goto bad_charclass;
382
383                   arg = lr_token (ldfile, charset);
384                   if (arg->tok != tok_bsymbol)
385                     goto bad_charclass;
386
387                   /* Write range starting at LAST to ARG->VAL.  */
388                   ctype_class_to (ldfile, result, arg, charset);
389
390                   arg = lr_token (ldfile, charset);
391                   if (arg->tok == tok_semicolon)
392                     arg = lr_token (ldfile, charset);
393                   else if (arg->tok != tok_eol)
394                     goto bad_charclass;
395                 }
396           }
397
398           /* Mark class as already seen.  */
399           ctype_class_end (ldfile, result);
400           state = 3;
401
402           continue;
403
404         case 6:
405           /* Here we expect a list of character mappings.  Note: the
406              first opening brace is already matched.  */
407           ctype_map_start (ldfile, result, ctype_tok_sym, ctype_tok_str,
408                            charset);
409
410           while (1)
411             {
412               /* Match ( bsymbol , bsymbol )  */
413               if (now->tok != tok_open_brace)
414                 goto bad_charmap;
415
416               now = lr_token (ldfile, charset);
417               if (now->tok != tok_bsymbol)
418                 {
419                 bad_charmap:
420                   SYNTAX_ERROR (_("\
421 syntax error in character mapping definition"));
422                   state = 3;
423                   break;
424                 }
425
426               /* Lookup arg and assign to FROM.  */
427               ctype_map_from (ldfile, result, now, charset);
428
429               now = lr_token (ldfile, charset);
430               if (now->tok != tok_comma)
431                 goto bad_charmap;
432
433               now = lr_token (ldfile, charset);
434               if (now->tok != tok_bsymbol)
435                 goto bad_charmap;
436
437               /* Lookup arg and assign to TO.  */
438               ctype_map_to (ldfile, result, now, charset);
439
440               now = lr_token (ldfile, charset);
441               if (now->tok != tok_close_brace)
442                 goto bad_charmap;
443
444               now = lr_token (ldfile, charset);
445               if (now->tok == tok_eol)
446                 {
447                   state = 3;
448                   break;
449                 }
450               if (now->tok != tok_semicolon)
451                 goto bad_charmap;
452
453               now = lr_token (ldfile, charset);
454             }
455
456           ctype_map_end (ldfile, result);
457           continue;
458
459         case 8:
460           {
461             /* We have seen `copy'.  First match the argument.  */
462             int warned = 0;
463
464             if (nowtok != tok_string)
465               lr_error (ldfile, _("expect string argument for `copy'"));
466             else
467               def_to_process (now->val.str.start, 1 << copy_category);
468
469             lr_ignore_rest (ldfile, nowtok == tok_string);
470
471             /* The rest of the line must be empty
472                and the next keyword must be `END xxx'.  */
473
474             while (lr_token (ldfile, charset)->tok != tok_end)
475               {
476                 if (warned == 0)
477                   {
478                   only_copy:
479                     lr_error (ldfile, _("\
480 no other keyword shall be specified when `copy' is used"));
481                     warned = 1;
482                   }
483
484                 lr_ignore_rest (ldfile, 0);
485               }
486
487             state = 4;
488           }
489           continue;
490
491         case 10:
492           HANDLE_COPY (LC_COLLATE, tok_lc_collate, "LC_COLLATE");
493
494           collate_startup (ldfile, result, charset);
495           /* FALLTHROUGH */
496
497         case 11:
498           /* Process the LC_COLLATE section.  We expect `END LC_COLLATE'
499              any of the collation specifications, or any bsymbol.  */
500           LOCALE_PROLOG (tok_lc_collate, "LC_COLLATE");
501
502           if (nowtok == tok_order_start)
503             {
504               state = 12;
505               continue;
506             }
507
508           if (nowtok != tok_collating_element
509               && nowtok != tok_collating_symbol)
510             {
511             bad_collation:
512               lr_error (ldfile, _("\
513 syntax error in collation definition"));
514               lr_ignore_rest (ldfile, 0);
515               continue;
516             }
517
518           /* Get argument.  */
519           arg = lr_token (ldfile, charset);
520           if (arg->tok != tok_bsymbol)
521             {
522               lr_error (ldfile, _("\
523 collation symbol expected after `%s'"),
524                         nowtok == tok_collating_element
525                         ? "collating-element" : "collating-symbol");
526               lr_ignore_rest (ldfile, 0);
527               continue;
528             }
529
530           if (nowtok == tok_collating_element)
531             {
532               /* Save to-value as new name.  */
533               collate_element_to (ldfile, result, arg, charset);
534
535               arg = lr_token (ldfile, charset);
536               if (arg->tok != tok_from)
537                 {
538                   lr_error (ldfile, _("\
539 `from' expected after first argument to `collating-element'"));
540                   lr_ignore_rest (ldfile, 0);
541                   continue;
542                 }
543
544               arg = lr_token (ldfile, charset);
545               if (arg->tok != tok_string)
546                 {
547                   lr_error (ldfile, _("\
548 from-value of `collating-element' must be a string"));
549                   lr_ignore_rest (ldfile, 0);
550                   continue;
551                 }
552
553               /* Enter new collating element.  */
554               collate_element_from (ldfile, result, arg, charset);
555             }
556           else
557             /* Enter new collating symbol into table.  */
558             collate_symbol (ldfile, result, arg, charset);
559
560           lr_ignore_rest (ldfile, 1);
561           continue;
562
563         case 12:
564           /* We parse the rest of the line containing `order_start'.
565              In any case we continue with parsing the symbols.  */
566           state = 13;
567
568           cnt = 0;
569           while (now->tok != tok_eol)
570             {
571               int collation_method = 0;
572
573               ++cnt;
574
575               do
576                 {
577                   if (now->tok == tok_forward)
578                     collation_method |= sort_forward;
579                   else if (now->tok == tok_backward)
580                     collation_method |= sort_backward;
581                   else if (now->tok == tok_position)
582                     collation_method |= sort_position;
583                   else
584                     {
585                       lr_error (ldfile, _("unknown collation directive"));
586                       lr_ignore_rest (ldfile, 0);
587                       continue;
588                     }
589
590                   now = lr_token (ldfile, charset);
591                 }
592               while (now->tok == tok_comma
593                      && (now == lr_token (ldfile, charset) != tok_none));
594
595               /* Check for consistency: forward and backwards are
596                  mutually exclusive.  */
597               if ((collation_method & sort_forward) != 0
598                   && (collation_method & sort_backward) != 0)
599                 {
600                   lr_error (ldfile, _("\
601 sorting order `forward' and `backward' are mutually exclusive"));
602                   /* The recover clear the backward flag.  */
603                   collation_method &= ~sort_backward;
604                 }
605
606               /* ??? I don't know whether this is correct but while
607                  thinking about the `strcoll' functions I found that I
608                  need a direction when performing position depended
609                  collation.  So I assume here that implicitly the
610                  direction `forward' is given when `position' alone is
611                  written.  --drepper  */
612               if (collation_method == sort_position)
613                 collation_method |= sort_forward;
614
615               /* Enter info about next collation order.  */
616               collate_new_order (ldfile, result, collation_method);
617
618               if (now->tok != tok_eol && now->tok != tok_semicolon)
619                 {
620                   lr_error (ldfile, _("\
621 syntax error in `order_start' directive"));
622                   lr_ignore_rest (ldfile, 0);
623                   break;
624                 }
625
626               if (now->tok == tok_semicolon)
627                 now = lr_token (ldfile, charset);
628             }
629
630           /* If no argument to `order_start' is given, one `forward'
631              argument is implicitely assumed.  */
632           if (cnt == 0)
633             collate_new_order (ldfile, result, sort_forward);
634
635
636           /* We now know about all sorting rules.  */
637           collate_build_arrays (ldfile, result);
638
639           continue;
640
641         case 13:
642           /* We read one symbol a line until `order_end' is found.  */
643           {
644             static int last_correct = 1;
645
646             if (nowtok == tok_order_end)
647               {
648                 state = 14;
649                 lr_ignore_rest (ldfile, 1);
650                 continue;
651               }
652
653             /* Ignore empty lines.  */
654             if (nowtok == tok_eol)
655               continue;
656
657             if (nowtok != tok_bsymbol && nowtok != tok_undefined
658                 && nowtok != tok_ellipsis)
659               {
660                 if (last_correct == 1)
661                   {
662                     lr_error (ldfile, _("\
663 syntax error in collating order definition"));
664                     last_correct = 0;
665                   }
666                 lr_ignore_rest (ldfile, 0);
667                 continue;
668               }
669             else
670               {
671                 last_correct = 1;
672
673                 /* Remember current token.  */
674                 if (collate_order_elem (ldfile, result, now, charset) < 0)
675                   continue;
676               }
677
678             /* Read optional arguments.  */
679             arg = lr_token (ldfile, charset);
680             while (arg->tok != tok_eol)
681               {
682                 if (arg->tok != tok_ignore && arg->tok != tok_ellipsis
683                     && arg->tok != tok_bsymbol && arg->tok != tok_string)
684                   break;
685
686                 if (arg->tok == tok_ignore || arg->tok == tok_ellipsis
687                     || arg->tok == tok_string)
688                   {
689                     /* Call handler for simple weights.  */
690                     if (collate_simple_weight (ldfile, result, arg, charset)
691                         < 0)
692                       goto illegal_weight;
693
694                     arg = lr_token (ldfile, charset);
695                   }
696                 else
697                   do
698                     {
699                       /* Collect char.  */
700                       int ok = collate_weight_bsymbol (ldfile, result, arg,
701                                                        charset);
702                       if (ok < 0)
703                         goto illegal_weight;
704
705                       arg = lr_token (ldfile, charset);
706                     }
707                   while (arg->tok == tok_bsymbol);
708
709                 /* Are there more weights?  */
710                 if (arg->tok != tok_semicolon)
711                   break;
712
713                 /* Yes, prepare next weight.  */
714                 if (collate_next_weight (ldfile, result) < 0)
715                   goto illegal_weight;
716
717                 arg = lr_token (ldfile, charset);
718               }
719
720             if (arg->tok != tok_eol)
721               {
722                 SYNTAX_ERROR (_("syntax error in order specification"));
723               }
724
725             collate_end_weight (ldfile, result);
726           illegal_weight:
727           }
728           continue;
729
730         case 14:
731           /* Following to the `order_end' keyword we don't expect
732              anything but the `END'.  */
733           if (nowtok == tok_eol)
734             continue;
735
736           if (nowtok != tok_end)
737             goto bad_collation;
738
739           expected_tok = tok_lc_collate;
740           expected_str = "LC_COLLATE";
741           state = 4;
742
743           ldfile->translate_strings = 1;
744           continue;
745
746         case 20:
747           HANDLE_COPY (LC_MONETARY, tok_lc_monetary, "LC_MONETARY");
748
749           monetary_startup (ldfile, result, charset);
750           /* FALLTHROUGH */
751
752         case 21:
753           LOCALE_PROLOG (tok_lc_monetary, "LC_MONETARY");
754
755           switch (nowtok)
756             {
757             case tok_int_curr_symbol:
758             case tok_currency_symbol:
759             case tok_mon_decimal_point:
760             case tok_mon_thousands_sep:
761             case tok_positive_sign:
762             case tok_negative_sign:
763               READ_STRING (monetary_add, bad_monetary);
764               break;
765
766             case tok_int_frac_digits:
767             case tok_frac_digits:
768             case tok_p_cs_precedes:
769             case tok_p_sep_by_space:
770             case tok_n_cs_precedes:
771             case tok_n_sep_by_space:
772             case tok_p_sign_posn:
773             case tok_n_sign_posn:
774               READ_NUMBER (monetary_add, bad_monetary);
775               break;
776
777             case tok_mon_grouping:
778               /* We have a semicolon separated list of integers.  */
779               READ_NUMBER_LIST (monetary_add, bad_monetary);
780               break;
781
782             default:
783             bad_monetary:
784               SYNTAX_ERROR (_("syntax error in monetary locale definition"));
785             }
786           continue;
787
788         case 30:
789           HANDLE_COPY (LC_NUMERIC, tok_lc_numeric, "LC_NUMERIC");
790
791           numeric_startup (ldfile, result, charset);
792           /* FALLTHROUGH */
793
794         case 31:
795           LOCALE_PROLOG (tok_lc_numeric, "LC_NUMERIC");
796
797           switch (nowtok)
798             {
799             case tok_decimal_point:
800             case tok_thousands_sep:
801               READ_STRING (numeric_add, bad_numeric);
802               break;
803
804             case tok_grouping:
805               /* We have a semicolon separated list of integers.  */
806               READ_NUMBER_LIST (numeric_add, bad_numeric);
807               break;
808
809             default:
810             bad_numeric:
811               SYNTAX_ERROR (_("syntax error in numeric locale definition"));
812             }
813           continue;
814
815         case 40:
816           HANDLE_COPY (LC_TIME, tok_lc_time, "LC_TIME");
817
818           time_startup (ldfile, result, charset);
819           /* FALLTHROUGH */
820
821         case 41:
822           LOCALE_PROLOG (tok_lc_time, "LC_TIME");
823
824           switch (nowtok)
825             {
826             case tok_abday:
827             case tok_day:
828             case tok_abmon:
829             case tok_mon:
830             case tok_am_pm:
831             case tok_alt_digits:
832               READ_STRING_LIST (time_add, bad_time);
833               continue;
834
835             case tok_d_t_fmt:
836             case tok_d_fmt:
837             case tok_t_fmt:
838             case tok_t_fmt_ampm:
839             case tok_era:
840             case tok_era_year:
841             case tok_era_d_t_fmt:
842             case tok_era_d_fmt:
843             case tok_era_t_fmt:
844               READ_STRING (time_add, bad_time);
845               break;
846
847             default:
848             bad_time:
849               SYNTAX_ERROR (_("syntax error in time locale definition"));
850             }
851           continue;
852
853         case 50:
854           HANDLE_COPY (LC_MESSAGES, tok_lc_messages, "LC_MESSAGES");
855
856           messages_startup (ldfile, result, charset);
857           /* FALLTHROUGH */
858
859         case 51:
860           LOCALE_PROLOG (tok_lc_messages, "LC_MESSAGES");
861
862           switch (nowtok)
863             {
864             case tok_yesexpr:
865             case tok_noexpr:
866             case tok_yesstr:
867             case tok_nostr:
868               READ_STRING (messages_add, bad_message);
869               break;
870
871             default:
872             bad_message:
873               SYNTAX_ERROR (_("syntax error in message locale definition"));
874             }
875           continue;
876
877         default:
878           error (5, 0, _("%s: error in state machine"), __FILE__);
879           /* NOTREACHED */
880         }
881
882       break;
883     }
884
885   /* We read all of the file.  */
886   lr_close (ldfile);
887
888   /* Let's see what information is available.  */
889   for (cnt = LC_CTYPE; cnt <= LC_MESSAGES; ++cnt)
890     if (result->categories[cnt].generic != NULL)
891       result->avail |= 1 << cnt;
892
893   return result;
894 }
895
896
897 void
898 check_all_categories (struct localedef_t *locale, struct charset_t *charset)
899 {
900  /* Call the finishing functions for all locales.  */
901   if ((locale->binary & (1 << LC_CTYPE)) == 0)
902     ctype_finish (locale, charset);
903   if ((locale->binary & (1 << LC_COLLATE)) == 0)
904     collate_finish (locale, charset);
905   if ((locale->binary & (1 << LC_MONETARY)) == 0)
906     monetary_finish (locale);
907   if ((locale->binary & (1 << LC_NUMERIC)) == 0)
908     numeric_finish (locale);
909   if ((locale->binary & (1 << LC_TIME)) == 0)
910     time_finish (locale);
911   if ((locale->binary & (1 << LC_MESSAGES)) == 0)
912     messages_finish (locale);
913 }
914
915
916 void
917 write_all_categories (struct localedef_t *locale, struct charset_t *charset,
918                       const char *output_path)
919 {
920   /* Call all functions to write locale data.  */
921   ctype_output (locale, charset, output_path);
922   collate_output (locale, output_path);
923   monetary_output (locale, output_path);
924   numeric_output (locale, output_path);
925   time_output (locale, output_path);
926   messages_output (locale, output_path);
927 }
928
929
930 void
931 write_locale_data (const char *output_path, const char *category,
932                    size_t n_elem, struct iovec *vec)
933 {
934   size_t cnt, step;
935   int fd;
936   char *fname;
937
938   asprintf (&fname, "%s%s", output_path, category);
939   fd = creat (fname, 0666);
940   if (fd == -1)
941     {
942       int save_err = errno;
943
944       if (errno == EISDIR)
945         {
946           free (fname);
947           asprintf (&fname, "%1$s%2$s/SYS_%2$s", output_path, category);
948           fd = creat (fname, 0666);
949           if (fd == -1)
950             save_err = errno;
951         }
952
953       if (fd == -1)
954         {
955           error (0, save_err, _("cannot open output file for category `%s'"),
956                  category);
957           return;
958         }
959     }
960   free (fname);
961
962   /* Write the data using writev.  But we must take care for the
963      limitation of the implementation.  */
964   for (cnt = 0; cnt < n_elem; cnt += step)
965     {
966       /* XXX Fixme: should be in libc header.  */
967 #ifndef MAX_IOVEC
968 # define MAX_IOVEC 8
969 #endif
970       step = MIN (MAX_IOVEC, n_elem - cnt);
971
972       if (writev (fd, &vec[cnt], step) < 0)
973         {
974           error (0, errno, _("failure while writing data for category `%s'"),
975                  category);
976           break;
977         }
978     }
979
980   close (fd);
981 }