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