update from main archive 970118
[kopensolaris-gnu/glibc.git] / locale / programs / ld-time.c
1 /* Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Library General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Library General Public License for more details.
14
15    You should have received a copy of the GNU Library General Public
16    License along with the GNU C Library; see the file COPYING.LIB.  If not,
17    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18    Boston, MA 02111-1307, USA.  */
19
20 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23
24 #include <langinfo.h>
25 #include <string.h>
26
27 /* Undefine following line in production version.  */
28 /* #define NDEBUG 1 */
29 #include <assert.h>
30 #include <stdlib.h>
31
32 #include "locales.h"
33 #include "localeinfo.h"
34 #include "stringtrans.h"
35
36 #define SWAPU32(w) \
37   (((w) << 24) | (((w) & 0xff00) << 8) | (((w) >> 8) & 0xff00) | ((w) >> 24))
38
39
40 void *xmalloc (size_t __n);
41 void *xrealloc (void *__p, size_t __n);
42
43
44 /* Entry describing an entry of the era specification.  */
45 struct era_data
46 {
47   int32_t direction;
48   int32_t offset;
49   int32_t start_date[3];
50   int32_t stop_date[3];
51   const char *name;
52   const char *format;
53 };
54
55
56 /* The real definition of the struct for the LC_TIME locale.  */
57 struct locale_time_t
58 {
59   const char *abday[7];
60   size_t cur_num_abday;
61   const char *day[7];
62   size_t cur_num_day;
63   const char *abmon[12];
64   size_t cur_num_abmon;
65   const char *mon[12];
66   size_t cur_num_mon;
67   const char *am_pm[2];
68   size_t cur_num_am_pm;
69   const char *d_t_fmt;
70   const char *d_fmt;
71   const char *t_fmt;
72   const char *t_fmt_ampm;
73   const char **era;
74   u_int32_t cur_num_era;
75   const char *era_year;
76   const char *era_d_t_fmt;
77   const char *era_t_fmt;
78   const char *era_d_fmt;
79   const char *alt_digits[100];
80   u_int32_t cur_num_alt_digits;
81
82   struct era_data *era_entries;
83   struct era_data *era_entries_ob;
84 };
85
86
87 void
88 time_startup (struct linereader *lr, struct localedef_t *locale,
89               struct charset_t *charset)
90 {
91   struct locale_time_t *time;
92
93   /* It is important that we always use UCS1 encoding for strings now.  */
94   encoding_method = ENC_UCS1;
95
96   locale->categories[LC_TIME].time = time =
97     (struct locale_time_t *) xmalloc (sizeof (struct locale_time_t));
98
99   memset (time, '\0', sizeof (struct locale_time_t));
100 }
101
102
103 void
104 time_finish (struct localedef_t *locale)
105 {
106   struct locale_time_t *time = locale->categories[LC_TIME].time;
107
108 #define TESTARR_ELEM(cat, max)                                                \
109   if (time->cur_num_##cat == 0)                                               \
110     error (0, 0, _("field `%s' in category `%s' not defined"),                \
111            #cat, "LC_TIME");                                                  \
112   else if (time->cur_num_##cat != max)                                        \
113     error (0, 0, _("field `%s' in category `%s' has not enough values"),      \
114            #cat, "LC_TIME")
115
116   TESTARR_ELEM (abday, 7);
117   TESTARR_ELEM (day, 7);
118   TESTARR_ELEM (abmon, 12);
119   TESTARR_ELEM (mon, 12);
120   TESTARR_ELEM (am_pm, 2);
121
122 #define TEST_ELEM(cat)                                                        \
123   if (time->cat == NULL)                                                      \
124     error (0, 0, _("field `%s' in category `%s' not defined"),                \
125            #cat, "LC_TIME")
126
127   TEST_ELEM (d_t_fmt);
128   TEST_ELEM (d_fmt);
129   TEST_ELEM (t_fmt);
130   TEST_ELEM (t_fmt_ampm);
131
132   /* Now process the era entries.  */
133   if (time->cur_num_era != 0)
134     {
135       const int days_per_month[12] = { 31, 29, 31, 30, 31, 30,
136                                        31, 31, 30, 31 ,30, 31 };
137       size_t idx;
138
139       time->era_entries =
140         (struct era_data *) xmalloc (time->cur_num_era
141                                      * sizeof (struct era_data));
142
143       for (idx = 0; idx < time->cur_num_era; ++idx)
144         {
145           size_t era_len = strlen (time->era[idx]);
146           char *str = xmalloc ((era_len + 1 + 3) & ~3);
147           char *endp;
148
149           memcpy (str, time->era[idx], era_len + 1);
150
151           /* First character must be + or - for the direction.  */
152           if (*str != '+' && *str != '-')
153             {
154               error (0, 0, _("direction flag in string %d in `era' field"
155                              " in category `%s' is not '+' nor '-'"),
156                      idx + 1, "LC_TIME");
157               /* Default arbitrarily to '+'.  */
158               time->era_entries[idx].direction = '+';
159             }
160           else
161             time->era_entries[idx].direction = *str;
162           if (*++str != ':')
163             {
164               error (0, 0, _("direction flag in string %d in `era' field"
165                              " in category `%s' is not a single character"),
166                      idx + 1, "LC_TIME");
167               (void) strsep (&str, ":");
168             }
169           else
170             ++str;
171
172           /* Now the offset year.  */
173           time->era_entries[idx].offset = strtol (str, &endp, 10);
174           if (endp == str)
175             {
176               error (0, 0, _("illegal number for offset in string %d in"
177                              " `era' field in category `%s'"),
178                      idx + 1, "LC_TIME");
179               (void) strsep (&str, ":");
180             }
181           else if (*endp != ':')
182             {
183               error (0, 0, _("garbage at end of offset value in string %d in"
184                              " `era' field in category `%s'"),
185                      idx + 1, "LC_TIME");
186               (void) strsep (&str, ":");
187             }
188           else
189             str = endp + 1;
190
191           /* Next is the starting date in ISO format.  */
192           if (strncmp (str, "-*", 2) == 0)
193             {
194               time->era_entries[idx].start_date[0] =
195                 time->era_entries[idx].start_date[1] =
196                 time->era_entries[idx].start_date[2] = 0x80000000;
197               if (str[2] != ':')
198                 goto garbage_start_date;
199               str += 3;
200             }
201           else if (strncmp (str, "+*", 2) == 0)
202             {
203               time->era_entries[idx].start_date[0] =
204                 time->era_entries[idx].start_date[1] =
205                 time->era_entries[idx].start_date[2] = 0x7fffffff;
206               if (str[2] != ':')
207                 goto garbage_start_date;
208               str += 3;
209             }
210           else
211             {
212               time->era_entries[idx].start_date[0] = strtol (str, &endp, 10);
213               if (endp == str || *endp != '/')
214                 goto invalid_start_date;
215               else
216                 str = endp + 1;
217               time->era_entries[idx].start_date[0] -= 1900;
218
219               time->era_entries[idx].start_date[1] = strtol (str, &endp, 10);
220               if (endp == str || *endp != '/')
221                 goto invalid_start_date;
222               else
223                 str = endp + 1;
224               time->era_entries[idx].start_date[1] -= 1;
225
226               time->era_entries[idx].start_date[2] = strtol (str, &endp, 10);
227               if (endp == str)
228                 {
229                 invalid_start_date:
230                   error (0, 0, _("illegal starting date in string %d in"
231                                  " `era' field in category `%s'"),
232                          idx + 1, "LC_TIME");
233                   (void) strsep (&str, ":");
234                 }
235               else if (*endp != ':')
236                 {
237                 garbage_start_date:
238                   error (0, 0, _("garbage at end of starting date in string %d"
239                                  " in `era' field in category `%s'"),
240                          idx + 1, "LC_TIME");
241                   (void) strsep (&str, ":");
242                 }
243               else
244                 {
245                   str = endp + 1;
246
247                   /* Check for valid value.  */
248                   if (time->era_entries[idx].start_date[1] < 0
249                       || time->era_entries[idx].start_date[1] >= 12
250                       || time->era_entries[idx].start_date[2] < 0
251                       || (time->era_entries[idx].start_date[2]
252                           > days_per_month[time->era_entries[idx].start_date[1]])
253                       || (time->era_entries[idx].start_date[1] == 2
254                           && time->era_entries[idx].start_date[2] == 29
255                           && !__isleap (time->era_entries[idx].start_date[0])))
256                           error (0, 0, _("starting date is illegal in"
257                                          " string %d in `era' field in"
258                                          " category `%s'"),
259                                  idx + 1, "LC_TIME");
260                 }
261             }
262
263           /* Next is the stopping date in ISO format.  */
264           if (strncmp (str, "-*", 2) == 0)
265             {
266               time->era_entries[idx].stop_date[0] =
267                 time->era_entries[idx].stop_date[1] =
268                 time->era_entries[idx].stop_date[2] = 0x80000000;
269               if (str[2] != ':')
270                 goto garbage_stop_date;
271               str += 3;
272             }
273           else if (strncmp (str, "+*", 2) == 0)
274             {
275               time->era_entries[idx].stop_date[0] =
276                 time->era_entries[idx].stop_date[1] =
277                 time->era_entries[idx].stop_date[2] = 0x7fffffff;
278               if (str[2] != ':')
279                 goto garbage_stop_date;
280               str += 3;
281             }
282           else
283             {
284               time->era_entries[idx].stop_date[0] = strtol (str, &endp, 10);
285               if (endp == str || *endp != '/')
286                 goto invalid_stop_date;
287               else
288                 str = endp + 1;
289               time->era_entries[idx].stop_date[0] -= 1900;
290
291               time->era_entries[idx].stop_date[1] = strtol (str, &endp, 10);
292               if (endp == str || *endp != '/')
293                 goto invalid_stop_date;
294               else
295                 str = endp + 1;
296               time->era_entries[idx].stop_date[1] -= 1;
297
298               time->era_entries[idx].stop_date[2] = strtol (str, &endp, 10);
299               if (endp == str)
300                 {
301                 invalid_stop_date:
302                   error (0, 0, _("illegal stopping date in string %d in"
303                                  " `era' field in category `%s'"),
304                          idx + 1, "LC_TIME");
305                   (void) strsep (&str, ":");
306                 }
307               else if (*endp != ':')
308                 {
309                 garbage_stop_date:
310                   error (0, 0, _("garbage at end of stopping date in string %d"
311                                  " in `era' field in category `%s'"),
312                          idx + 1, "LC_TIME");
313                   (void) strsep (&str, ":");
314                 }
315               else
316                 {
317                   str = endp + 1;
318
319                   /* Check for valid value.  */
320                   if (time->era_entries[idx].stop_date[1] < 0
321                       || time->era_entries[idx].stop_date[1] >= 12
322                       || time->era_entries[idx].stop_date[2] < 0
323                       || (time->era_entries[idx].stop_date[2]
324                           > days_per_month[time->era_entries[idx].stop_date[1]])
325                       || (time->era_entries[idx].stop_date[1] == 2
326                           && time->era_entries[idx].stop_date[2] == 29
327                           && !__isleap (time->era_entries[idx].stop_date[0])))
328                           error (0, 0, _("stopping date is illegal in"
329                                          " string %d in `era' field in"
330                                          " category `%s'"),
331                                  idx + 1, "LC_TIME");
332                 }
333             }
334
335           if (str == NULL || *str == '\0')
336             {
337               error (0, 0, _("missing era name in string %d in `era' field"
338                              " in category `%s'"), idx + 1, "LC_TIME");
339               time->era_entries[idx].name =
340                 time->era_entries[idx].format = "";
341             }
342           else
343             {
344               time->era_entries[idx].name = strsep (&str, ":");
345
346               if (str == NULL || *str == '\0')
347                 {
348                   error (0, 0, _("missing era format in string %d in `era'"
349                                  " field in category `%s'"),
350                          idx + 1, "LC_TIME");
351                   time->era_entries[idx].name =
352                     time->era_entries[idx].format = "";
353                 }
354               else
355                 time->era_entries[idx].format = str;
356             }
357         }
358
359       /* Construct the array for the other byte order.  */
360       time->era_entries_ob =
361         (struct era_data *) xmalloc (time->cur_num_era
362                                       * sizeof (struct era_data));
363
364       for (idx = 0; idx < time->cur_num_era; ++idx)
365         {
366           time->era_entries_ob[idx].direction =
367             SWAPU32 (time->era_entries[idx].direction);
368           time->era_entries_ob[idx].offset =
369             SWAPU32 (time->era_entries[idx].offset);
370           time->era_entries_ob[idx].start_date[0] =
371             SWAPU32 (time->era_entries[idx].start_date[0]);
372           time->era_entries_ob[idx].start_date[1] =
373             SWAPU32 (time->era_entries[idx].start_date[1]);
374           time->era_entries_ob[idx].start_date[2] =
375             SWAPU32 (time->era_entries[idx].stop_date[2]);
376           time->era_entries_ob[idx].stop_date[0] =
377             SWAPU32 (time->era_entries[idx].stop_date[0]);
378           time->era_entries_ob[idx].stop_date[1] =
379             SWAPU32 (time->era_entries[idx].stop_date[1]);
380           time->era_entries_ob[idx].stop_date[2] =
381             SWAPU32 (time->era_entries[idx].stop_date[2]);
382           time->era_entries_ob[idx].name =
383             time->era_entries[idx].name;
384           time->era_entries_ob[idx].format =
385             time->era_entries[idx].format;
386         }
387     }
388 }
389
390
391 void
392 time_output (struct localedef_t *locale, const char *output_path)
393 {
394   struct locale_time_t *time = locale->categories[LC_TIME].time;
395   struct iovec iov[2 + _NL_ITEM_INDEX (_NL_NUM_LC_TIME)
396                   + time->cur_num_era - 1
397                   + time->cur_num_alt_digits - 1
398                   + 1 + (time->cur_num_era * 9 - 1) * 2
399                   + (time->cur_num_era == 0)];
400   struct locale_file data;
401   u_int32_t idx[_NL_ITEM_INDEX (_NL_NUM_LC_TIME)];
402   size_t cnt, last_idx, num;
403
404   if ((locale->binary & (1 << LC_TIME)) != 0)
405     {
406       iov[0].iov_base = time;
407       iov[0].iov_len = locale->len[LC_TIME];
408
409       write_locale_data (output_path, "LC_TIME", 1, iov);
410
411       return;
412     }
413
414   data.magic = LIMAGIC (LC_TIME);
415   data.n = _NL_ITEM_INDEX (_NL_NUM_LC_TIME);
416   iov[0].iov_base = (void *) &data;
417   iov[0].iov_len = sizeof (data);
418
419   iov[1].iov_base = (void *) idx;
420   iov[1].iov_len = sizeof (idx);
421
422   idx[0] = iov[0].iov_len + iov[1].iov_len;
423
424   /* The ab'days.  */
425   for (cnt = 0; cnt <= _NL_ITEM_INDEX (ABDAY_7); ++cnt)
426     {
427       iov[2 + cnt].iov_base =
428         (void *) (time->abday[cnt - _NL_ITEM_INDEX (ABDAY_1)] ?: "");
429       iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
430       idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
431     }
432
433   /* The days.  */
434   for (; cnt <= _NL_ITEM_INDEX (DAY_7); ++cnt)
435     {
436       iov[2 + cnt].iov_base =
437         (void *) (time->day[cnt - _NL_ITEM_INDEX (DAY_1)] ?: "");
438       iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
439       idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
440     }
441
442   /* The ab'mons.  */
443   for (; cnt <= _NL_ITEM_INDEX (ABMON_12); ++cnt)
444     {
445       iov[2 + cnt].iov_base =
446         (void *) (time->abmon[cnt - _NL_ITEM_INDEX (ABMON_1)] ?: "");
447       iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
448       idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
449     }
450
451   /* The mons.  */
452   for (; cnt <= _NL_ITEM_INDEX (MON_12); ++cnt)
453     {
454       iov[2 + cnt].iov_base =
455         (void *) (time->mon[cnt - _NL_ITEM_INDEX (MON_1)] ?: "");
456       iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
457       idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
458     }
459
460   /* AM/PM.  */
461   for (; cnt <= _NL_ITEM_INDEX (PM_STR); ++cnt)
462     {
463       iov[2 + cnt].iov_base =
464         (void *) (time->am_pm[cnt - _NL_ITEM_INDEX (AM_STR)] ?: "");
465       iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
466       idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
467     }
468
469   iov[2 + cnt].iov_base = (void *) (time->d_t_fmt ?: "");
470   iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
471   idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
472   ++cnt;
473
474   iov[2 + cnt].iov_base = (void *) (time->d_fmt ?: "");
475   iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
476   idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
477   ++cnt;
478
479   iov[2 + cnt].iov_base = (void *) (time->t_fmt ?: "");
480   iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
481   idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
482   ++cnt;
483
484   iov[2 + cnt].iov_base = (void *) (time->t_fmt_ampm ?: "");
485   iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
486   idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
487   last_idx = ++cnt;
488
489   idx[1 + last_idx] = idx[last_idx];
490   for (num = 0; num < time->cur_num_era; ++num, ++cnt)
491     {
492       iov[2 + cnt].iov_base = (void *) time->era[num];
493       iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
494       idx[1 + last_idx] += iov[2 + cnt].iov_len;
495     }
496   ++last_idx;
497
498   iov[2 + cnt].iov_base = (void *) (time->era_year ?: "");
499   iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
500   idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
501   ++cnt;
502   ++last_idx;
503
504   iov[2 + cnt].iov_base = (void *) (time->era_d_fmt ?: "");
505   iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
506   idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
507   ++cnt;
508   ++last_idx;
509
510   idx[1 + last_idx] = idx[last_idx];
511   for (num = 0; num < time->cur_num_alt_digits; ++num, ++cnt)
512     {
513       iov[2 + cnt].iov_base = (void *) (time->alt_digits[num] ?: "");
514       iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
515       idx[1 + last_idx] += iov[2 + cnt].iov_len;
516     }
517   ++last_idx;
518
519   iov[2 + cnt].iov_base = (void *) (time->era_d_t_fmt ?: "");
520   iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
521   idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
522   ++cnt;
523   ++last_idx;
524
525   iov[2 + cnt].iov_base = (void *) (time->era_t_fmt ?: "");
526   iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
527   idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
528   ++cnt;
529   ++last_idx;
530
531
532   /* We must align the following data.  */
533   iov[2 + cnt].iov_base = (void *) "\0\0";
534   iov[2 + cnt].iov_len = ((idx[last_idx] + 3) & ~3) - idx[last_idx];
535   idx[last_idx] = (idx[last_idx] + 3) & ~3;
536   ++cnt;
537
538   iov[2 + cnt].iov_base = (void *) &time->cur_num_alt_digits;
539   iov[2 + cnt].iov_len = sizeof (u_int32_t);
540   idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
541   ++cnt;
542   ++last_idx;
543
544   /* The `era' data in usable form.  */
545   iov[2 + cnt].iov_base = (void *) &time->cur_num_era;
546   iov[2 + cnt].iov_len = sizeof (u_int32_t);
547   idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
548   ++cnt;
549   ++last_idx;
550
551 #if __BYTE_ORDER == __LITTLE_ENDIAN
552 # define ERA_B1 time->era_entries_ob
553 # define ERA_B2 time->era_entries
554 #else
555 # define ERA_B1 time->era_entries
556 # define ERA_B2 time->era_entries_ob
557 #endif
558   idx[1 + last_idx] = idx[last_idx];
559   for (num = 0; num < time->cur_num_era; ++num)
560     {
561       size_t l;
562
563       iov[2 + cnt].iov_base = (void *) &ERA_B1[num].direction;
564       iov[2 + cnt].iov_len = sizeof (int32_t);
565       ++cnt;
566       iov[2 + cnt].iov_base = (void *) &ERA_B1[num].offset;
567       iov[2 + cnt].iov_len = sizeof (int32_t);
568       ++cnt;
569       iov[2 + cnt].iov_base = (void *) &ERA_B1[num].start_date[0];
570       iov[2 + cnt].iov_len = sizeof (int32_t);
571       ++cnt;
572       iov[2 + cnt].iov_base = (void *) &ERA_B1[num].start_date[1];
573       iov[2 + cnt].iov_len = sizeof (int32_t);
574       ++cnt;
575       iov[2 + cnt].iov_base = (void *) &ERA_B1[num].start_date[2];
576       iov[2 + cnt].iov_len = sizeof (int32_t);
577       ++cnt;
578       iov[2 + cnt].iov_base = (void *) &ERA_B1[num].stop_date[0];
579       iov[2 + cnt].iov_len = sizeof (int32_t);
580       ++cnt;
581       iov[2 + cnt].iov_base = (void *) &ERA_B1[num].stop_date[1];
582       iov[2 + cnt].iov_len = sizeof (int32_t);
583       ++cnt;
584       iov[2 + cnt].iov_base = (void *) &ERA_B1[num].stop_date[2];
585       iov[2 + cnt].iov_len = sizeof (int32_t);
586       ++cnt;
587
588       l = (strchr (ERA_B1[num].format, '\0') - ERA_B1[num].name) + 1;
589       l = (l + 3) & ~3;
590       iov[2 + cnt].iov_base = (void *) ERA_B1[num].name;
591       iov[2 + cnt].iov_len = l;
592       ++cnt;
593
594       idx[1 + last_idx] += 8 * sizeof (int32_t) + l;
595
596       assert (idx[1 + last_idx] % 4 == 0);
597     }
598   ++last_idx;
599
600   /* idx[1 + last_idx] = idx[last_idx]; */
601   for (num = 0; num < time->cur_num_era; ++num)
602     {
603       size_t l;
604
605       iov[2 + cnt].iov_base = (void *) &ERA_B2[num].direction;
606       iov[2 + cnt].iov_len = sizeof (int32_t);
607       ++cnt;
608       iov[2 + cnt].iov_base = (void *) &ERA_B2[num].offset;
609       iov[2 + cnt].iov_len = sizeof (int32_t);
610       ++cnt;
611       iov[2 + cnt].iov_base = (void *) &ERA_B2[num].start_date[0];
612       iov[2 + cnt].iov_len = sizeof (int32_t);
613       ++cnt;
614       iov[2 + cnt].iov_base = (void *) &ERA_B2[num].start_date[1];
615       iov[2 + cnt].iov_len = sizeof (int32_t);
616       ++cnt;
617       iov[2 + cnt].iov_base = (void *) &ERA_B2[num].start_date[2];
618       iov[2 + cnt].iov_len = sizeof (int32_t);
619       ++cnt;
620       iov[2 + cnt].iov_base = (void *) &ERA_B2[num].stop_date[0];
621       iov[2 + cnt].iov_len = sizeof (int32_t);
622       ++cnt;
623       iov[2 + cnt].iov_base = (void *) &ERA_B2[num].stop_date[1];
624       iov[2 + cnt].iov_len = sizeof (int32_t);
625       ++cnt;
626       iov[2 + cnt].iov_base = (void *) &ERA_B2[num].stop_date[2];
627       iov[2 + cnt].iov_len = sizeof (int32_t);
628       ++cnt;
629
630       l = (strchr (ERA_B2[num].format, '\0') - ERA_B2[num].name) + 1;
631       l = (l + 3) & ~3;
632       iov[2 + cnt].iov_base = (void *) ERA_B2[num].name;
633       iov[2 + cnt].iov_len = l;
634       ++cnt;
635
636       /* idx[1 + last_idx] += 8 * sizeof (int32_t) + l; */
637     }
638
639   /* We have a problem when no era data is present.  In this case the
640      data pointer for _NL_TIME_ERA_ENTRIES_EB and
641      _NL_TIME_ERA_ENTRIES_EL point after the end of the file.  So we
642      introduce some dummy data here.  */
643   if (time->cur_num_era == 0)
644     {
645       static u_int32_t dummy = 0;
646       iov[2 + cnt].iov_base = (void *) &dummy;
647       iov[2 + cnt].iov_len = 4;
648       ++cnt;
649     }
650
651   assert (cnt == (_NL_ITEM_INDEX (_NL_NUM_LC_TIME)
652                   + time->cur_num_era - 1
653                   + time->cur_num_alt_digits - 1
654                   + 1 + (time->cur_num_era * 9 - 1) * 2
655                   + (time->cur_num_era == 0))
656           && last_idx + 1 == _NL_ITEM_INDEX (_NL_NUM_LC_TIME));
657
658   write_locale_data (output_path, "LC_TIME", 2 + cnt, iov);
659 }
660
661
662 void
663 time_add (struct linereader *lr, struct localedef_t *locale,
664           enum token_t tok, struct token *code,
665           struct charset_t *charset)
666 {
667   struct locale_time_t *time = locale->categories[LC_TIME].time;
668
669   switch (tok)
670     {
671 #define STRARR_ELEM(cat, max)                                                 \
672     case tok_##cat:                                                           \
673       if (time->cur_num_##cat >= max)                                         \
674         lr_error (lr, _("\
675 too many values for field `%s' in category `%s'"),                            \
676                   #cat, "LC_TIME");                                           \
677       else if (code->val.str.start == NULL)                                   \
678         {                                                                     \
679           lr_error (lr, _("unknown character in field `%s' of category `%s'"),\
680                     #cat, "LC_TIME");                                         \
681           time->cat[time->cur_num_##cat++] = "";                              \
682         }                                                                     \
683       else                                                                    \
684         time->cat[time->cur_num_##cat++] = code->val.str.start;               \
685       break
686
687     STRARR_ELEM (abday, 7);
688     STRARR_ELEM (day, 7);
689     STRARR_ELEM (abmon, 12);
690     STRARR_ELEM (mon, 12);
691     STRARR_ELEM (am_pm, 2);
692     STRARR_ELEM (alt_digits, 100);
693
694     case tok_era:
695       if (code->val.str.start == NULL)
696         lr_error (lr, _("unknown character in field `%s' of category `%s'"),
697                   "era", "LC_TIME");
698       else
699         {
700           ++time->cur_num_era;
701           time->era = xrealloc (time->era,
702                                 time->cur_num_era * sizeof (char *));
703           time->era[time->cur_num_era - 1] = code->val.str.start;
704         }
705       break;
706
707 #define STR_ELEM(cat)                                                         \
708     case tok_##cat:                                                           \
709       if (time->cat != NULL)                                                  \
710         lr_error (lr, _("\
711 field `%s' in category `%s' declared more than once"),                        \
712                   #cat, "LC_TIME");                                           \
713       else if (code->val.str.start == NULL)                                   \
714         {                                                                     \
715           lr_error (lr, _("unknown character in field `%s' of category `%s'"),\
716                     #cat, "LC_TIME");                                         \
717           time->cat = "";                                                     \
718         }                                                                     \
719       else                                                                    \
720         time->cat = code->val.str.start;                                      \
721       break
722
723     STR_ELEM (d_t_fmt);
724     STR_ELEM (d_fmt);
725     STR_ELEM (t_fmt);
726     STR_ELEM (t_fmt_ampm);
727     STR_ELEM (era_year);
728     STR_ELEM (era_d_t_fmt);
729     STR_ELEM (era_d_fmt);
730     STR_ELEM (era_t_fmt);
731
732     default:
733       assert (! "unknown token in category `LC_TIME': should not happen");
734     }
735 }