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