Update.
[platform/upstream/glibc.git] / locale / programs / ld-time.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 <byteswap.h>
25 #include <langinfo.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <wchar.h>
29 #include <sys/uio.h>
30
31 #include <assert.h>
32
33 #include "linereader.h"
34 #include "localedef.h"
35 #include "localeinfo.h"
36 #include "locfile.h"
37
38
39 /* Entry describing an entry of the era specification.  */
40 struct era_data
41 {
42   int32_t direction;
43   int32_t offset;
44   int32_t start_date[3];
45   int32_t stop_date[3];
46   const char *name;
47   const char *format;
48   uint32_t *wname;
49   uint32_t *wformat;
50 };
51
52
53 /* The real definition of the struct for the LC_TIME locale.  */
54 struct locale_time_t
55 {
56   const char *abday[7];
57   const uint32_t *wabday[7];
58   int abday_defined;
59   const char *day[7];
60   const uint32_t *wday[7];
61   int day_defined;
62   const char *abmon[12];
63   const uint32_t *wabmon[12];
64   int abmon_defined;
65   const char *mon[12];
66   const uint32_t *wmon[12];
67   int mon_defined;
68   const char *am_pm[2];
69   const uint32_t *wam_pm[2];
70   int am_pm_defined;
71   const char *d_t_fmt;
72   const uint32_t *wd_t_fmt;
73   const char *d_fmt;
74   const uint32_t *wd_fmt;
75   const char *t_fmt;
76   const uint32_t *wt_fmt;
77   const char *t_fmt_ampm;
78   const uint32_t *wt_fmt_ampm;
79   const char **era;
80   const uint32_t **wera;
81   uint32_t num_era;
82   const char *era_year;
83   const uint32_t *wera_year;
84   const char *era_d_t_fmt;
85   const uint32_t *wera_d_t_fmt;
86   const char *era_t_fmt;
87   const uint32_t *wera_t_fmt;
88   const char *era_d_fmt;
89   const uint32_t *wera_d_fmt;
90   const char *alt_digits[100];
91   const uint32_t *walt_digits[100];
92   int alt_digits_defined;
93   unsigned char week_ndays;
94   uint32_t week_1stday;
95   unsigned char week_1stweek;
96   unsigned char first_weekday;
97   unsigned char first_workday;
98   unsigned char cal_direction;
99   const char *timezone;
100   const uint32_t *wtimezone;
101
102   struct era_data *era_entries;
103 };
104
105
106 /* This constant is used to represent an empty wide character string.  */
107 static const uint32_t empty_wstr[1] = { 0 };
108
109
110 static void
111 time_startup (struct linereader *lr, struct localedef_t *locale,
112               int ignore_content)
113 {
114   if (!ignore_content)
115     locale->categories[LC_TIME].time =
116       (struct locale_time_t *) xcalloc (1, sizeof (struct locale_time_t));
117
118   if (time != NULL)
119     {
120       lr->translate_strings = 1;
121       lr->return_widestr = 1;
122     }
123 }
124
125
126 void
127 time_finish (struct localedef_t *locale, struct charmap_t *charmap)
128 {
129   struct locale_time_t *time = locale->categories[LC_TIME].time;
130   int nothing = 0;
131
132   /* Now resolve copying and also handle completely missing definitions.  */
133   if (time == NULL)
134     {
135       /* First see whether we were supposed to copy.  If yes, find the
136          actual definition.  */
137       if (locale->copy_name[LC_TIME] != NULL)
138         {
139           /* Find the copying locale.  This has to happen transitively since
140              the locale we are copying from might also copying another one.  */
141           struct localedef_t *from = locale;
142
143           do
144             from = find_locale (LC_TIME, from->copy_name[LC_TIME],
145                                 from->repertoire_name, charmap);
146           while (from->categories[LC_TIME].time == NULL
147                  && from->copy_name[LC_TIME] != NULL);
148
149           time = locale->categories[LC_TIME].time
150             = from->categories[LC_TIME].time;
151         }
152
153       /* If there is still no definition issue an warning and create an
154          empty one.  */
155       if (time == NULL)
156         {
157           error (0, 0, _("No definition for %s category found"), "LC_TIME");
158           time_startup (NULL, locale, 0);
159           time = locale->categories[LC_TIME].time;
160           nothing = 1;
161         }
162     }
163
164 #define TESTARR_ELEM(cat) \
165   if (!time->cat##_defined)                                                   \
166     {                                                                         \
167       if(! be_quiet && ! nothing)                                             \
168         error (0, 0, _("%s: field `%s' not defined"), "LC_TIME", #cat);       \
169     }
170
171   TESTARR_ELEM (abday);
172   TESTARR_ELEM (day);
173   TESTARR_ELEM (abmon);
174   TESTARR_ELEM (mon);
175   TESTARR_ELEM (am_pm);
176
177 #define TEST_ELEM(cat) \
178   if (time->cat == NULL)                                                      \
179     {                                                                         \
180       if (! be_quiet && ! nothing)                                            \
181         error (0, 0, _("%s: field `%s' not defined"), "LC_TIME", #cat);       \
182     }
183
184   TEST_ELEM (d_t_fmt);
185   TEST_ELEM (d_fmt);
186   TEST_ELEM (t_fmt);
187
188   /* According to C.Y.Alexis Cheng <alexis@vnet.ibm.com> the T_FMT_AMPM
189      field is optional.  */
190   if (time->t_fmt_ampm == NULL)
191     {
192       /* Use the 24h format as default.  */
193       time->t_fmt_ampm = time->t_fmt;
194       time->wt_fmt_ampm = time->wt_fmt;
195     }
196
197   /* Now process the era entries.  */
198   if (time->num_era != 0)
199     {
200       const int days_per_month[12] = { 31, 29, 31, 30, 31, 30,
201                                        31, 31, 30, 31 ,30, 31 };
202       size_t idx;
203       wchar_t *wstr;
204
205       time->era_entries =
206         (struct era_data *) xmalloc (time->num_era
207                                      * sizeof (struct era_data));
208
209       for (idx = 0; idx < time->num_era; ++idx)
210         {
211           size_t era_len = strlen (time->era[idx]);
212           char *str = xmalloc ((era_len + 1 + 3) & ~3);
213           char *endp;
214
215           memcpy (str, time->era[idx], era_len + 1);
216
217           /* First character must be + or - for the direction.  */
218           if (*str != '+' && *str != '-')
219             {
220               if (!be_quiet)
221                 error (0, 0, _("%s: direction flag in string %d in `era' field"
222                                " is not '+' nor '-'"),
223                        "LC_TIME", idx + 1);
224               /* Default arbitrarily to '+'.  */
225               time->era_entries[idx].direction = '+';
226             }
227           else
228             time->era_entries[idx].direction = *str;
229           if (*++str != ':')
230             {
231               if (!be_quiet)
232                 error (0, 0, _("%s: direction flag in string %d in `era' field"
233                                " is not a single character"),
234                        "LC_TIME", idx + 1);
235               (void) strsep (&str, ":");
236             }
237           else
238             ++str;
239
240           /* Now the offset year.  */
241           time->era_entries[idx].offset = strtol (str, &endp, 10);
242           if (endp == str)
243             {
244               if (!be_quiet)
245                 error (0, 0, _("%s: invalid number for offset in string %d in"
246                                " `era' field"),
247                        "LC_TIME", idx + 1);
248               (void) strsep (&str, ":");
249             }
250           else if (*endp != ':')
251             {
252               if (!be_quiet)
253                 error (0, 0, _("%s: garbage at end of offset value in"
254                                " string %d in `era' field"),
255                        "LC_TIME", idx + 1);
256               (void) strsep (&str, ":");
257             }
258           else
259             str = endp + 1;
260
261           /* Next is the starting date in ISO format.  */
262           if (strncmp (str, "-*", 2) == 0)
263             {
264               time->era_entries[idx].start_date[0] =
265                 time->era_entries[idx].start_date[1] =
266                 time->era_entries[idx].start_date[2] = 0x80000000;
267               if (str[2] != ':')
268                 goto garbage_start_date;
269               str += 3;
270             }
271           else if (strncmp (str, "+*", 2) == 0)
272             {
273               time->era_entries[idx].start_date[0] =
274                 time->era_entries[idx].start_date[1] =
275                 time->era_entries[idx].start_date[2] = 0x7fffffff;
276               if (str[2] != ':')
277                 goto garbage_start_date;
278               str += 3;
279             }
280           else
281             {
282               time->era_entries[idx].start_date[0] = strtol (str, &endp, 10);
283               if (endp == str || *endp != '/')
284                 goto invalid_start_date;
285               else
286                 str = endp + 1;
287               time->era_entries[idx].start_date[0] -= 1900;
288
289               time->era_entries[idx].start_date[1] = strtol (str, &endp, 10);
290               if (endp == str || *endp != '/')
291                 goto invalid_start_date;
292               else
293                 str = endp + 1;
294               time->era_entries[idx].start_date[1] -= 1;
295
296               time->era_entries[idx].start_date[2] = strtol (str, &endp, 10);
297               if (endp == str)
298                 {
299                 invalid_start_date:
300                   if (!be_quiet)
301                     error (0, 0, _("%s: invalid starting date in string %d in"
302                                    " `era' field"),
303                            "LC_TIME", idx + 1);
304                   (void) strsep (&str, ":");
305                 }
306               else if (*endp != ':')
307                 {
308                 garbage_start_date:
309                   if (!be_quiet)
310                     error (0, 0, _("%s: garbage at end of starting date "
311                                    "in string %d in `era' field "),
312                            "LC_TIME", idx + 1);
313                   (void) strsep (&str, ":");
314                 }
315               else
316                 {
317                   str = endp + 1;
318
319                   /* Check for valid value.  */
320                   if ((time->era_entries[idx].start_date[1] < 0
321                        || time->era_entries[idx].start_date[1] >= 12
322                        || time->era_entries[idx].start_date[2] < 0
323                        || (time->era_entries[idx].start_date[2]
324                            > days_per_month[time->era_entries[idx].start_date[1]])
325                        || (time->era_entries[idx].start_date[1] == 2
326                            && time->era_entries[idx].start_date[2] == 29
327                            && !__isleap (time->era_entries[idx].start_date[0])))
328                       && !be_quiet)
329                           error (0, 0, _("%s: starting date is invalid in"
330                                          " string %d in `era' field"),
331                                  "LC_TIME", idx + 1);
332                 }
333             }
334
335           /* Next is the stopping date in ISO format.  */
336           if (strncmp (str, "-*", 2) == 0)
337             {
338               time->era_entries[idx].stop_date[0] =
339                 time->era_entries[idx].stop_date[1] =
340                 time->era_entries[idx].stop_date[2] = 0x80000000;
341               if (str[2] != ':')
342                 goto garbage_stop_date;
343               str += 3;
344             }
345           else if (strncmp (str, "+*", 2) == 0)
346             {
347               time->era_entries[idx].stop_date[0] =
348                 time->era_entries[idx].stop_date[1] =
349                 time->era_entries[idx].stop_date[2] = 0x7fffffff;
350               if (str[2] != ':')
351                 goto garbage_stop_date;
352               str += 3;
353             }
354           else
355             {
356               time->era_entries[idx].stop_date[0] = strtol (str, &endp, 10);
357               if (endp == str || *endp != '/')
358                 goto invalid_stop_date;
359               else
360                 str = endp + 1;
361               time->era_entries[idx].stop_date[0] -= 1900;
362
363               time->era_entries[idx].stop_date[1] = strtol (str, &endp, 10);
364               if (endp == str || *endp != '/')
365                 goto invalid_stop_date;
366               else
367                 str = endp + 1;
368               time->era_entries[idx].stop_date[1] -= 1;
369
370               time->era_entries[idx].stop_date[2] = strtol (str, &endp, 10);
371               if (endp == str)
372                 {
373                 invalid_stop_date:
374                   if (!be_quiet)
375                     error (0, 0, _("%s: invalid stopping date in string %d in"
376                                    " `era' field"),
377                            "LC_TIME", idx + 1);
378                   (void) strsep (&str, ":");
379                 }
380               else if (*endp != ':')
381                 {
382                 garbage_stop_date:
383                   if (!be_quiet)
384                     error (0, 0, _("%s: garbage at end of stopping date "
385                                    "in string %d in `era' field"),
386                            "LC_TIME", idx + 1);
387                   (void) strsep (&str, ":");
388                 }
389               else
390                 {
391                   str = endp + 1;
392
393                   /* Check for valid value.  */
394                   if ((time->era_entries[idx].stop_date[1] < 0
395                        || time->era_entries[idx].stop_date[1] >= 12
396                        || time->era_entries[idx].stop_date[2] < 0
397                        || (time->era_entries[idx].stop_date[2]
398                            > days_per_month[time->era_entries[idx].stop_date[1]])
399                        || (time->era_entries[idx].stop_date[1] == 2
400                            && time->era_entries[idx].stop_date[2] == 29
401                            && !__isleap (time->era_entries[idx].stop_date[0])))
402                       && !be_quiet)
403                           error (0, 0, _("%s: stopping date is invalid in"
404                                          " string %d in `era' field"),
405                                  "LC_TIME", idx + 1);
406                 }
407             }
408
409           if (str == NULL || *str == '\0')
410             {
411               if (!be_quiet)
412                 error (0, 0, _("%s: missing era name in string %d in `era'"
413                                " field"), "LC_TIME", idx + 1);
414               time->era_entries[idx].name =
415                 time->era_entries[idx].format = "";
416             }
417           else
418             {
419               time->era_entries[idx].name = strsep (&str, ":");
420
421               if (str == NULL || *str == '\0')
422                 {
423                   if (!be_quiet)
424                     error (0, 0, _("%s: missing era format in string %d"
425                                    " in `era' field"),
426                            "LC_TIME", idx + 1);
427                   time->era_entries[idx].name =
428                     time->era_entries[idx].format = "";
429                 }
430               else
431                 time->era_entries[idx].format = str;
432             }
433
434           /* Now generate the wide character name and format.  */
435           wstr = wcschr ((wchar_t *) time->wera[idx], L':');/* end direction */
436           wstr = wstr ? wcschr (wstr + 1, L':') : NULL; /* end offset */
437           wstr = wstr ? wcschr (wstr + 1, L':') : NULL; /* end start */
438           wstr = wstr ? wcschr (wstr + 1, L':') : NULL; /* end end */
439           time->era_entries[idx].wname = (uint32_t *) wstr;
440           wstr = wstr ? wcschr (wstr + 1, L':') : NULL; /* end name */
441           time->era_entries[idx].wformat = (uint32_t *) wstr;
442         }
443     }
444
445   if (time->week_ndays == 0)
446     time->week_ndays = 7;
447
448   if (time->week_1stday == 0)
449     time->week_1stday = 19971130;
450
451   if (time->week_1stweek > time->week_ndays)
452     error (0, 0, _("\
453 %s: third operand for value of field `%s' must not be larger than %d"),
454            "LC_TIME", "week", 7);
455
456   if (time->first_weekday == '\0')
457     /* The definition does not specify this so the default is used.  */
458     time->first_weekday = 1;
459   else if (time->first_weekday > time->week_ndays)
460     error (0, 0, _("\
461 %s: values of field `%s' must not be larger than %d"),
462            "LC_TIME", "first_weekday", 7);
463
464   if (time->first_workday == '\0')
465     /* The definition does not specify this so the default is used.  */
466     time->first_workday = 1;
467   else if (time->first_workday > time->week_ndays)
468     error (0, 0, _("\
469 %s: values of field `%s' must not be larger than %d"),
470            "LC_TIME", "first_workday", 7);
471
472   if (time->cal_direction == '\0')
473     /* The definition does not specify this so the default is used.  */
474     time->cal_direction = 1;
475   else if (time->cal_direction > 3)
476     error (0, 0, _("\
477 %s: values for field `%s' must not be larger than 3"),
478            "LC_TIME", "cal_direction", 3);
479
480   /* XXX We don't perform any tests on the timezone value since this is
481      simply useless, stupid $&$!@...  */
482   if (time->timezone == NULL)
483     time->timezone = "";
484 }
485
486
487 void
488 time_output (struct localedef_t *locale, struct charmap_t *charmap,
489              const char *output_path)
490 {
491   struct locale_time_t *time = locale->categories[LC_TIME].time;
492   struct iovec iov[2 + _NL_ITEM_INDEX (_NL_NUM_LC_TIME)
493                   + time->num_era - 1
494                   + 2 * 99
495                   + 1 + time->num_era * 10 - 1];
496   struct locale_file data;
497   uint32_t idx[_NL_ITEM_INDEX (_NL_NUM_LC_TIME)];
498   size_t cnt, last_idx, num, n;
499
500   data.magic = LIMAGIC (LC_TIME);
501   data.n = _NL_ITEM_INDEX (_NL_NUM_LC_TIME);
502   iov[0].iov_base = (void *) &data;
503   iov[0].iov_len = sizeof (data);
504
505   iov[1].iov_base = (void *) idx;
506   iov[1].iov_len = sizeof (idx);
507
508   idx[0] = iov[0].iov_len + iov[1].iov_len;
509
510   /* The ab'days.  */
511   for (cnt = 0; cnt <= _NL_ITEM_INDEX (ABDAY_7); ++cnt)
512     {
513       iov[2 + cnt].iov_base =
514         (void *) (time->abday[cnt - _NL_ITEM_INDEX (ABDAY_1)] ?: "");
515       iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
516       idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
517     }
518
519   /* The days.  */
520   for (; cnt <= _NL_ITEM_INDEX (DAY_7); ++cnt)
521     {
522       iov[2 + cnt].iov_base =
523         (void *) (time->day[cnt - _NL_ITEM_INDEX (DAY_1)] ?: "");
524       iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
525       idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
526     }
527
528   /* The ab'mons.  */
529   for (; cnt <= _NL_ITEM_INDEX (ABMON_12); ++cnt)
530     {
531       iov[2 + cnt].iov_base =
532         (void *) (time->abmon[cnt - _NL_ITEM_INDEX (ABMON_1)] ?: "");
533       iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
534       idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
535     }
536
537   /* The mons.  */
538   for (; cnt <= _NL_ITEM_INDEX (MON_12); ++cnt)
539     {
540       iov[2 + cnt].iov_base =
541         (void *) (time->mon[cnt - _NL_ITEM_INDEX (MON_1)] ?: "");
542       iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
543       idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
544     }
545
546   /* AM/PM.  */
547   for (; cnt <= _NL_ITEM_INDEX (PM_STR); ++cnt)
548     {
549       iov[2 + cnt].iov_base =
550         (void *) (time->am_pm[cnt - _NL_ITEM_INDEX (AM_STR)] ?: "");
551       iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
552       idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
553     }
554
555   iov[2 + cnt].iov_base = (void *) (time->d_t_fmt ?: "");
556   iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
557   idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
558   ++cnt;
559
560   iov[2 + cnt].iov_base = (void *) (time->d_fmt ?: "");
561   iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
562   idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
563   ++cnt;
564
565   iov[2 + cnt].iov_base = (void *) (time->t_fmt ?: "");
566   iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
567   idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
568   ++cnt;
569
570   iov[2 + cnt].iov_base = (void *) (time->t_fmt_ampm ?: "");
571   iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
572   idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
573   last_idx = ++cnt;
574
575   idx[1 + last_idx] = idx[last_idx];
576   for (num = 0; num < time->num_era; ++num, ++cnt)
577     {
578       iov[2 + cnt].iov_base = (void *) time->era[num];
579       iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
580       idx[1 + last_idx] += iov[2 + cnt].iov_len;
581     }
582   ++last_idx;
583
584   iov[2 + cnt].iov_base = (void *) (time->era_year ?: "");
585   iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
586   idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
587   ++cnt;
588   ++last_idx;
589
590   iov[2 + cnt].iov_base = (void *) (time->era_d_fmt ?: "");
591   iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
592   idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
593   ++cnt;
594   ++last_idx;
595
596   idx[1 + last_idx] = idx[last_idx];
597   for (num = 0; num < 100; ++num, ++cnt)
598     {
599       iov[2 + cnt].iov_base = (void *) (time->alt_digits[num] ?: "");
600       iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
601       idx[1 + last_idx] += iov[2 + cnt].iov_len;
602     }
603   ++last_idx;
604
605   iov[2 + cnt].iov_base = (void *) (time->era_d_t_fmt ?: "");
606   iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
607   idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
608   ++cnt;
609   ++last_idx;
610
611   iov[2 + cnt].iov_base = (void *) (time->era_t_fmt ?: "");
612   iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
613   idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
614   ++cnt;
615   ++last_idx;
616
617
618   /* We must align the following data.  */
619   iov[2 + cnt].iov_base = (void *) "\0\0";
620   iov[2 + cnt].iov_len = ((idx[last_idx] + 3) & ~3) - idx[last_idx];
621   idx[last_idx] = (idx[last_idx] + 3) & ~3;
622   ++cnt;
623
624   /* The `era' data in usable form.  */
625   iov[2 + cnt].iov_base = (void *) &time->num_era;
626   iov[2 + cnt].iov_len = sizeof (uint32_t);
627   idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
628   ++cnt;
629   ++last_idx;
630
631   idx[1 + last_idx] = idx[last_idx];
632   for (num = 0; num < time->num_era; ++num)
633     {
634       size_t l;
635
636       iov[2 + cnt].iov_base = (void *) &time->era_entries[num].direction;
637       iov[2 + cnt].iov_len = sizeof (int32_t);
638       ++cnt;
639       iov[2 + cnt].iov_base = (void *) &time->era_entries[num].offset;
640       iov[2 + cnt].iov_len = sizeof (int32_t);
641       ++cnt;
642       iov[2 + cnt].iov_base = (void *) &time->era_entries[num].start_date[0];
643       iov[2 + cnt].iov_len = sizeof (int32_t);
644       ++cnt;
645       iov[2 + cnt].iov_base = (void *) &time->era_entries[num].start_date[1];
646       iov[2 + cnt].iov_len = sizeof (int32_t);
647       ++cnt;
648       iov[2 + cnt].iov_base = (void *) &time->era_entries[num].start_date[2];
649       iov[2 + cnt].iov_len = sizeof (int32_t);
650       ++cnt;
651       iov[2 + cnt].iov_base = (void *) &time->era_entries[num].stop_date[0];
652       iov[2 + cnt].iov_len = sizeof (int32_t);
653       ++cnt;
654       iov[2 + cnt].iov_base = (void *) &time->era_entries[num].stop_date[1];
655       iov[2 + cnt].iov_len = sizeof (int32_t);
656       ++cnt;
657       iov[2 + cnt].iov_base = (void *) &time->era_entries[num].stop_date[2];
658       iov[2 + cnt].iov_len = sizeof (int32_t);
659       ++cnt;
660
661       l = (strchr (time->era_entries[num].format, '\0')
662            - time->era_entries[num].name) + 1;
663       l = (l + 3) & ~3;
664       iov[2 + cnt].iov_base = (void *) time->era_entries[num].name;
665       iov[2 + cnt].iov_len = l;
666       ++cnt;
667
668       idx[1 + last_idx] += 8 * sizeof (int32_t) + l;
669
670       assert (idx[1 + last_idx] % 4 == 0);
671
672       iov[2 + cnt].iov_base = (void *) time->era_entries[num].wname;
673       iov[2 + cnt].iov_len = ((wcschr ((wchar_t *) time->era_entries[num].wformat, L'\0')
674                                - (wchar_t *) time->era_entries[num].wname + 1)
675                               * sizeof (uint32_t));
676       idx[1 + last_idx] += iov[2 + cnt].iov_len;
677       ++cnt;
678     }
679   ++last_idx;
680
681   /* The wide character ab'days.  */
682   for (n = 0; n < 7; ++n, ++cnt, ++last_idx)
683     {
684       iov[2 + cnt].iov_base =
685         (void *) (time->wabday[n] ?: empty_wstr);
686       iov[2 + cnt].iov_len = ((wcslen (iov[2 + cnt].iov_base) + 1)
687                               * sizeof (uint32_t));
688       idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
689     }
690
691   /* The wide character days.  */
692   for (n = 0; n < 7; ++n, ++cnt, ++last_idx)
693     {
694       iov[2 + cnt].iov_base =
695         (void *) (time->wday[n] ?: empty_wstr);
696       iov[2 + cnt].iov_len = ((wcslen (iov[2 + cnt].iov_base) + 1)
697                               * sizeof (uint32_t));
698       idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
699     }
700
701   /* The wide character ab'mons.  */
702   for (n = 0; n < 12; ++n, ++cnt, ++last_idx)
703     {
704       iov[2 + cnt].iov_base =
705         (void *) (time->wabmon[n] ?: empty_wstr);
706       iov[2 + cnt].iov_len = ((wcslen (iov[2 + cnt].iov_base) + 1)
707                               * sizeof (uint32_t));
708       idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
709     }
710
711   /* The wide character mons.  */
712   for (n = 0; n < 12; ++n, ++cnt, ++last_idx)
713     {
714       iov[2 + cnt].iov_base =
715         (void *) (time->wmon[n] ?: empty_wstr);
716       iov[2 + cnt].iov_len = ((wcslen (iov[2 + cnt].iov_base) + 1)
717                               * sizeof (uint32_t));
718       idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
719     }
720
721   /* Wide character AM/PM.  */
722   for (n = 0; n < 2; ++n, ++cnt, ++last_idx)
723     {
724       iov[2 + cnt].iov_base =
725         (void *) (time->wam_pm[n] ?: empty_wstr);
726       iov[2 + cnt].iov_len = ((wcslen (iov[2 + cnt].iov_base) + 1)
727                               * sizeof (uint32_t));
728       idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
729     }
730
731   iov[2 + cnt].iov_base = (void *) (time->wd_t_fmt ?: empty_wstr);
732   iov[2 + cnt].iov_len = ((wcslen (iov[2 + cnt].iov_base) + 1)
733                           * sizeof (uint32_t));
734   idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
735   ++cnt;
736   ++last_idx;
737
738   iov[2 + cnt].iov_base = (void *) (time->wd_fmt ?: empty_wstr);
739   iov[2 + cnt].iov_len = ((wcslen (iov[2 + cnt].iov_base) + 1)
740                           * sizeof (uint32_t));
741   idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
742   ++cnt;
743   ++last_idx;
744
745   iov[2 + cnt].iov_base = (void *) (time->wt_fmt ?: empty_wstr);
746   iov[2 + cnt].iov_len = ((wcslen (iov[2 + cnt].iov_base) + 1)
747                           * sizeof (uint32_t));
748   idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
749   ++cnt;
750   ++last_idx;
751
752   iov[2 + cnt].iov_base = (void *) (time->wt_fmt_ampm ?: empty_wstr);
753   iov[2 + cnt].iov_len = ((wcslen (iov[2 + cnt].iov_base) + 1)
754                           * sizeof (uint32_t));
755   idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
756   ++cnt;
757   ++last_idx;
758
759   iov[2 + cnt].iov_base = (void *) (time->wera_year ?: empty_wstr);
760   iov[2 + cnt].iov_len = ((wcslen (iov[2 + cnt].iov_base) + 1)
761                           * sizeof (uint32_t));
762   idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
763   ++cnt;
764   ++last_idx;
765
766   iov[2 + cnt].iov_base = (void *) (time->wera_d_fmt ?: empty_wstr);
767   iov[2 + cnt].iov_len = ((wcslen (iov[2 + cnt].iov_base) + 1)
768                           * sizeof (uint32_t));
769   idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
770   ++cnt;
771   ++last_idx;
772
773   idx[1 + last_idx] = idx[last_idx];
774   for (num = 0; num < 100; ++num, ++cnt)
775     {
776       iov[2 + cnt].iov_base = (void *) (time->walt_digits[num]
777                                         ?: empty_wstr);
778       iov[2 + cnt].iov_len = ((wcslen (iov[2 + cnt].iov_base) + 1)
779                               * sizeof (uint32_t));
780       idx[1 + last_idx] += iov[2 + cnt].iov_len;
781     }
782   ++last_idx;
783
784   iov[2 + cnt].iov_base = (void *) (time->wera_d_t_fmt ?: empty_wstr);
785   iov[2 + cnt].iov_len = ((wcslen (iov[2 + cnt].iov_base) + 1)
786                           * sizeof (uint32_t));
787   idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
788   ++cnt;
789   ++last_idx;
790
791   iov[2 + cnt].iov_base = (void *) (time->wera_t_fmt ?: empty_wstr);
792   iov[2 + cnt].iov_len = ((wcslen (iov[2 + cnt].iov_base) + 1)
793                           * sizeof (uint32_t));
794   idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
795   ++cnt;
796   ++last_idx;
797
798   iov[2 + cnt].iov_base = (void *) &time->week_ndays;
799   iov[2 + cnt].iov_len = 1;
800   idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
801   ++cnt;
802   ++last_idx;
803
804   iov[2 + cnt].iov_base = (void *) &time->week_1stday;
805   iov[2 + cnt].iov_len = 1;
806   idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
807   ++cnt;
808   ++last_idx;
809
810   iov[2 + cnt].iov_base = (void *) &time->week_1stweek;
811   iov[2 + cnt].iov_len = 1;
812   idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
813   ++cnt;
814   ++last_idx;
815
816   iov[2 + cnt].iov_base = (void *) &time->first_weekday;
817   iov[2 + cnt].iov_len = 1;
818   idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
819   ++cnt;
820   ++last_idx;
821
822   iov[2 + cnt].iov_base = (void *) &time->first_workday;
823   iov[2 + cnt].iov_len = 1;
824   idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
825   ++cnt;
826   ++last_idx;
827
828   iov[2 + cnt].iov_base = (void *) &time->cal_direction;
829   iov[2 + cnt].iov_len = 1;
830   idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
831   ++cnt;
832   ++last_idx;
833
834   iov[2 + cnt].iov_base = (void *) time->timezone;
835   iov[2 + cnt].iov_len = strlen (time->timezone) + 1;
836   ++cnt;
837   ++last_idx;
838
839   assert (cnt == (_NL_ITEM_INDEX (_NL_NUM_LC_TIME)
840                   + time->num_era - 1
841                   + 2 * 99
842                   + 1 + time->num_era * 10 - 1));
843   assert (last_idx  == _NL_ITEM_INDEX (_NL_NUM_LC_TIME));
844
845   write_locale_data (output_path, "LC_TIME", 2 + cnt, iov);
846 }
847
848
849 /* The parser for the LC_TIME section of the locale definition.  */
850 void
851 time_read (struct linereader *ldfile, struct localedef_t *result,
852            struct charmap_t *charmap, const char *repertoire_name,
853            int ignore_content)
854 {
855   struct repertoire_t *repertoire = NULL;
856   struct locale_time_t *time;
857   struct token *now;
858   enum token_t nowtok;
859   size_t cnt;
860
861   /* Get the repertoire we have to use.  */
862   if (repertoire_name != NULL)
863     repertoire = repertoire_read (repertoire_name);
864
865   /* The rest of the line containing `LC_TIME' must be free.  */
866   lr_ignore_rest (ldfile, 1);
867
868
869   do
870     {
871       now = lr_token (ldfile, charmap, repertoire);
872       nowtok = now->tok;
873     }
874   while (nowtok == tok_eol);
875
876   /* If we see `copy' now we are almost done.  */
877   if (nowtok == tok_copy)
878     {
879       handle_copy (ldfile, charmap, repertoire, result, tok_lc_time,
880                    LC_TIME, "LC_TIME", ignore_content);
881       return;
882     }
883
884   /* Prepare the data structures.  */
885   time_startup (ldfile, result, ignore_content);
886   time = result->categories[LC_TIME].time;
887
888   while (1)
889     {
890       /* Of course we don't proceed beyond the end of file.  */
891       if (nowtok == tok_eof)
892         break;
893
894       /* Ingore empty lines.  */
895       if (nowtok == tok_eol)
896         {
897           now = lr_token (ldfile, charmap, repertoire);
898           nowtok = now->tok;
899           continue;
900         }
901
902       switch (nowtok)
903         {
904 #define STRARR_ELEM(cat, min, max) \
905         case tok_##cat:                                                       \
906           /* Ignore the rest of the line if we don't need the input of        \
907              this line.  */                                                   \
908           if (ignore_content)                                                 \
909             {                                                                 \
910               lr_ignore_rest (ldfile, 0);                                     \
911               break;                                                          \
912             }                                                                 \
913                                                                               \
914           for (cnt = 0; cnt < max; ++cnt)                                     \
915             {                                                                 \
916               now = lr_token (ldfile, charmap, repertoire);                   \
917               if (now->tok == tok_eol)                                        \
918                 {                                                             \
919                   if (cnt < min)                                              \
920                     lr_error (ldfile, _("%s: too few values for field `%s'"), \
921                               "LC_TIME", #cat);                               \
922                   if (!ignore_content)                                        \
923                     do                                                        \
924                       {                                                       \
925                         time->cat[cnt] = "";                                  \
926                         time->w##cat[cnt] = empty_wstr;                       \
927                       }                                                       \
928                     while (++cnt < max);                                      \
929                   break;                                                      \
930                 }                                                             \
931               else if (now->tok != tok_string)                                \
932                 goto err_label;                                               \
933               else if (!ignore_content && (now->val.str.startmb == NULL       \
934                                            || now->val.str.startwc == NULL))  \
935                 {                                                             \
936                   lr_error (ldfile, _("%s: unknown character in field `%s'"), \
937                             "LC_TIME", #cat);                                 \
938                   time->cat[cnt] = "";                                        \
939                   time->w##cat[cnt] = empty_wstr;                             \
940                 }                                                             \
941               else if (!ignore_content)                                       \
942                 {                                                             \
943                   time->cat[cnt] = now->val.str.startmb;                      \
944                   time->w##cat[cnt] = now->val.str.startwc;                   \
945                 }                                                             \
946                                                                               \
947               /* Match the semicolon.  */                                     \
948               now = lr_token (ldfile, charmap, repertoire);                   \
949               if (now->tok != tok_semicolon && now->tok != tok_eol)           \
950                 break;                                                        \
951             }                                                                 \
952           if (now->tok != tok_eol)                                            \
953             {                                                                 \
954               while (!ignore_content && cnt < min)                            \
955                 {                                                             \
956                   time->cat[cnt] = "";                                        \
957                   time->w##cat[cnt++] = empty_wstr;                           \
958                 }                                                             \
959                                                                               \
960               if (now->tok == tok_semicolon)                                  \
961                 {                                                             \
962                   now = lr_token (ldfile, charmap, repertoire);               \
963                   if (now->tok == tok_eol)                                    \
964                     lr_error (ldfile, _("extra trailing semicolon"));         \
965                   else if (now->tok == tok_string)                            \
966                     {                                                         \
967                       lr_error (ldfile, _("\
968 %s: too many values for field `%s'"),                                         \
969                                 "LC_TIME", #cat);                             \
970                       lr_ignore_rest (ldfile, 0);                             \
971                     }                                                         \
972                   else                                                        \
973                     goto err_label;                                           \
974                 }                                                             \
975               else                                                            \
976                 goto err_label;                                               \
977             }                                                                 \
978           time->cat##_defined = 1;                                            \
979           break
980
981           STRARR_ELEM (abday, 7, 7);
982           STRARR_ELEM (day, 7, 7);
983           STRARR_ELEM (abmon, 12, 12);
984           STRARR_ELEM (mon, 12, 12);
985           STRARR_ELEM (am_pm, 2, 2);
986           STRARR_ELEM (alt_digits, 0, 100);
987
988         case tok_era:
989           /* Ignore the rest of the line if we don't need the input of
990              this line.  */
991           if (ignore_content)
992             {
993               lr_ignore_rest (ldfile, 0);
994               break;
995             }
996           do
997             {
998               now = lr_token (ldfile, charmap, repertoire);
999               if (now->tok != tok_string)
1000                 goto err_label;
1001               if (!ignore_content && (now->val.str.startmb == NULL
1002                                       || now->val.str.startwc == NULL))
1003                 {
1004                   lr_error (ldfile, _("%s: unknown character in field `%s'"),
1005                             "LC_TIME", "era");
1006                   lr_ignore_rest (ldfile, 0);
1007                   break;
1008                 }
1009               if (!ignore_content)
1010                 {
1011                   time->era = xrealloc (time->era,
1012                                         (time->num_era + 1) * sizeof (char *));
1013                   time->era[time->num_era] = now->val.str.startmb;
1014
1015                   time->wera = xrealloc (time->wera,
1016                                          (time->num_era + 1)
1017                                          * sizeof (char *));
1018                   time->wera[time->num_era++] = now->val.str.startwc;
1019                 }
1020               now = lr_token (ldfile, charmap, repertoire);
1021               if (now->tok != tok_eol && now->tok != tok_semicolon)
1022                 goto err_label;
1023             }
1024           while (now->tok == tok_semicolon);
1025           break;
1026
1027 #define STR_ELEM(cat) \
1028         case tok_##cat:                                                       \
1029           /* Ignore the rest of the line if we don't need the input of        \
1030              this line.  */                                                   \
1031           if (ignore_content)                                                 \
1032             {                                                                 \
1033               lr_ignore_rest (ldfile, 0);                                     \
1034               break;                                                          \
1035             }                                                                 \
1036                                                                               \
1037           now = lr_token (ldfile, charmap, repertoire);                       \
1038           if (now->tok != tok_string)                                         \
1039             goto err_label;                                                   \
1040           else if (time->cat != NULL)                                         \
1041             lr_error (ldfile, _("\
1042 %s: field `%s' declared more than once"), "LC_TIME", #cat);                   \
1043           else if (!ignore_content && (now->val.str.startmb == NULL           \
1044                                        || now->val.str.startwc == NULL))      \
1045             {                                                                 \
1046               lr_error (ldfile, _("%s: unknown character in field `%s'"),     \
1047                         "LC_TIME", #cat);                                     \
1048               time->cat = "";                                                 \
1049               time->w##cat = empty_wstr;                                      \
1050             }                                                                 \
1051           else if (!ignore_content)                                           \
1052             {                                                                 \
1053               time->cat = now->val.str.startmb;                               \
1054               time->w##cat = now->val.str.startwc;                            \
1055             }                                                                 \
1056           break
1057
1058           STR_ELEM (d_t_fmt);
1059           STR_ELEM (d_fmt);
1060           STR_ELEM (t_fmt);
1061           STR_ELEM (t_fmt_ampm);
1062           STR_ELEM (era_year);
1063           STR_ELEM (era_d_t_fmt);
1064           STR_ELEM (era_d_fmt);
1065           STR_ELEM (era_t_fmt);
1066           STR_ELEM (timezone);
1067
1068 #define INT_ELEM(cat) \
1069         case tok_##cat:                                                       \
1070           /* Ignore the rest of the line if we don't need the input of        \
1071              this line.  */                                                   \
1072           if (ignore_content)                                                 \
1073             {                                                                 \
1074               lr_ignore_rest (ldfile, 0);                                     \
1075               break;                                                          \
1076             }                                                                 \
1077                                                                               \
1078           now = lr_token (ldfile, charmap, repertoire);                       \
1079           if (now->tok != tok_number)                                         \
1080             goto err_label;                                                   \
1081           else if (time->cat != 0)                                            \
1082             lr_error (ldfile, _("%s: field `%s' declared more than once"),    \
1083                       "LC_TIME", #cat);                                       \
1084           else if (!ignore_content)                                           \
1085             time->cat = now->val.num;                                         \
1086           break
1087
1088           INT_ELEM (first_weekday);
1089           INT_ELEM (first_workday);
1090           INT_ELEM (cal_direction);
1091
1092         case tok_week:
1093           /* Ignore the rest of the line if we don't need the input of
1094              this line.  */
1095           if (ignore_content)
1096             {
1097               lr_ignore_rest (ldfile, 0);
1098               break;
1099             }
1100
1101           now = lr_token (ldfile, charmap, repertoire);
1102           if (now->tok != tok_number)
1103             goto err_label;
1104           time->week_ndays = now->val.num;
1105
1106           now = lr_token (ldfile, charmap, repertoire);
1107           if (now->tok != tok_semicolon)
1108             goto err_label;
1109
1110           now = lr_token (ldfile, charmap, repertoire);
1111           if (now->tok != tok_number)
1112             goto err_label;
1113           time->week_1stday = now->val.num;
1114
1115           now = lr_token (ldfile, charmap, repertoire);
1116           if (now->tok != tok_semicolon)
1117             goto err_label;
1118
1119           now = lr_token (ldfile, charmap, repertoire);
1120           if (now->tok != tok_number)
1121             goto err_label;
1122           time->week_1stweek = now->val.num;
1123
1124           lr_ignore_rest (ldfile,  1);
1125           break;
1126
1127         case tok_end:
1128           /* Next we assume `LC_TIME'.  */
1129           now = lr_token (ldfile, charmap, repertoire);
1130           if (now->tok == tok_eof)
1131             break;
1132           if (now->tok == tok_eol)
1133             lr_error (ldfile, _("%s: incomplete `END' line"), "LC_TIME");
1134           else if (now->tok != tok_lc_time)
1135             lr_error (ldfile, _("\
1136 %1$s: definition does not end with `END %1$s'"), "LC_TIME");
1137           lr_ignore_rest (ldfile, now->tok == tok_lc_time);
1138           return;
1139
1140         default:
1141         err_label:
1142           SYNTAX_ERROR (_("%s: syntax error"), "LC_TIME");
1143         }
1144
1145       /* Prepare for the next round.  */
1146       now = lr_token (ldfile, charmap, repertoire);
1147       nowtok = now->tok;
1148     }
1149
1150   /* When we come here we reached the end of the file.  */
1151   lr_error (ldfile, _("%s: premature end of file"), "LC_TIME");
1152 }