b9777634336798d15c39de31e82587d9127cb247
[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 && !be_quiet)                                  \
110     error (0, 0, _("field `%s' in category `%s' not defined"),                \
111            #cat, "LC_TIME");                                                  \
112   else if (time->cur_num_##cat != max && !be_quiet)                           \
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 && !be_quiet)                                         \
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 != '-' && !be_quiet)
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 != ':' && !be_quiet)
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 && !be_quiet)
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 != ':' && !be_quiet)
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 && !be_quiet)
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 != ':' && !be_quiet)
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                       && !be_quiet)
257                           error (0, 0, _("starting date is illegal in"
258                                          " string %d in `era' field in"
259                                          " category `%s'"),
260                                  idx + 1, "LC_TIME");
261                 }
262             }
263
264           /* Next is the stopping date in ISO format.  */
265           if (strncmp (str, "-*", 2) == 0)
266             {
267               time->era_entries[idx].stop_date[0] =
268                 time->era_entries[idx].stop_date[1] =
269                 time->era_entries[idx].stop_date[2] = 0x80000000;
270               if (str[2] != ':')
271                 goto garbage_stop_date;
272               str += 3;
273             }
274           else if (strncmp (str, "+*", 2) == 0)
275             {
276               time->era_entries[idx].stop_date[0] =
277                 time->era_entries[idx].stop_date[1] =
278                 time->era_entries[idx].stop_date[2] = 0x7fffffff;
279               if (str[2] != ':')
280                 goto garbage_stop_date;
281               str += 3;
282             }
283           else
284             {
285               time->era_entries[idx].stop_date[0] = strtol (str, &endp, 10);
286               if (endp == str || *endp != '/')
287                 goto invalid_stop_date;
288               else
289                 str = endp + 1;
290               time->era_entries[idx].stop_date[0] -= 1900;
291
292               time->era_entries[idx].stop_date[1] = strtol (str, &endp, 10);
293               if (endp == str || *endp != '/')
294                 goto invalid_stop_date;
295               else
296                 str = endp + 1;
297               time->era_entries[idx].stop_date[1] -= 1;
298
299               time->era_entries[idx].stop_date[2] = strtol (str, &endp, 10);
300               if (endp == str && !be_quiet)
301                 {
302                 invalid_stop_date:
303                   error (0, 0, _("illegal stopping date in string %d in"
304                                  " `era' field in category `%s'"),
305                          idx + 1, "LC_TIME");
306                   (void) strsep (&str, ":");
307                 }
308               else if (*endp != ':' && !be_quiet)
309                 {
310                 garbage_stop_date:
311                   error (0, 0, _("garbage at end of stopping date in string %d"
312                                  " in `era' field in category `%s'"),
313                          idx + 1, "LC_TIME");
314                   (void) strsep (&str, ":");
315                 }
316               else
317                 {
318                   str = endp + 1;
319
320                   /* Check for valid value.  */
321                   if ((time->era_entries[idx].stop_date[1] < 0
322                        || time->era_entries[idx].stop_date[1] >= 12
323                        || time->era_entries[idx].stop_date[2] < 0
324                        || (time->era_entries[idx].stop_date[2]
325                            > days_per_month[time->era_entries[idx].stop_date[1]])
326                        || (time->era_entries[idx].stop_date[1] == 2
327                            && time->era_entries[idx].stop_date[2] == 29
328                            && !__isleap (time->era_entries[idx].stop_date[0])))
329                       && !be_quiet)
330                           error (0, 0, _("stopping date is illegal in"
331                                          " string %d in `era' field in"
332                                          " category `%s'"),
333                                  idx + 1, "LC_TIME");
334                 }
335             }
336
337           if ((str == NULL || *str == '\0') && !be_quiet)
338             {
339               error (0, 0, _("missing era name in string %d in `era' field"
340                              " in category `%s'"), idx + 1, "LC_TIME");
341               time->era_entries[idx].name =
342                 time->era_entries[idx].format = "";
343             }
344           else
345             {
346               time->era_entries[idx].name = strsep (&str, ":");
347
348               if ((str == NULL || *str == '\0') && !be_quiet)
349                 {
350                   error (0, 0, _("missing era format in string %d in `era'"
351                                  " field in category `%s'"),
352                          idx + 1, "LC_TIME");
353                   time->era_entries[idx].name =
354                     time->era_entries[idx].format = "";
355                 }
356               else
357                 time->era_entries[idx].format = str;
358             }
359         }
360
361       /* Construct the array for the other byte order.  */
362       time->era_entries_ob =
363         (struct era_data *) xmalloc (time->cur_num_era
364                                       * sizeof (struct era_data));
365
366       for (idx = 0; idx < time->cur_num_era; ++idx)
367         {
368           time->era_entries_ob[idx].direction =
369             SWAPU32 (time->era_entries[idx].direction);
370           time->era_entries_ob[idx].offset =
371             SWAPU32 (time->era_entries[idx].offset);
372           time->era_entries_ob[idx].start_date[0] =
373             SWAPU32 (time->era_entries[idx].start_date[0]);
374           time->era_entries_ob[idx].start_date[1] =
375             SWAPU32 (time->era_entries[idx].start_date[1]);
376           time->era_entries_ob[idx].start_date[2] =
377             SWAPU32 (time->era_entries[idx].stop_date[2]);
378           time->era_entries_ob[idx].stop_date[0] =
379             SWAPU32 (time->era_entries[idx].stop_date[0]);
380           time->era_entries_ob[idx].stop_date[1] =
381             SWAPU32 (time->era_entries[idx].stop_date[1]);
382           time->era_entries_ob[idx].stop_date[2] =
383             SWAPU32 (time->era_entries[idx].stop_date[2]);
384           time->era_entries_ob[idx].name =
385             time->era_entries[idx].name;
386           time->era_entries_ob[idx].format =
387             time->era_entries[idx].format;
388         }
389     }
390 }
391
392
393 void
394 time_output (struct localedef_t *locale, const char *output_path)
395 {
396   struct locale_time_t *time = locale->categories[LC_TIME].time;
397   struct iovec iov[2 + _NL_ITEM_INDEX (_NL_NUM_LC_TIME)
398                   + time->cur_num_era - 1
399                   + time->cur_num_alt_digits - 1
400                   + 1 + (time->cur_num_era * 9 - 1) * 2
401                   + (time->cur_num_era == 0)];
402   struct locale_file data;
403   u_int32_t idx[_NL_ITEM_INDEX (_NL_NUM_LC_TIME)];
404   size_t cnt, last_idx, num;
405
406   if ((locale->binary & (1 << LC_TIME)) != 0)
407     {
408       iov[0].iov_base = time;
409       iov[0].iov_len = locale->len[LC_TIME];
410
411       write_locale_data (output_path, "LC_TIME", 1, iov);
412
413       return;
414     }
415
416   data.magic = LIMAGIC (LC_TIME);
417   data.n = _NL_ITEM_INDEX (_NL_NUM_LC_TIME);
418   iov[0].iov_base = (void *) &data;
419   iov[0].iov_len = sizeof (data);
420
421   iov[1].iov_base = (void *) idx;
422   iov[1].iov_len = sizeof (idx);
423
424   idx[0] = iov[0].iov_len + iov[1].iov_len;
425
426   /* The ab'days.  */
427   for (cnt = 0; cnt <= _NL_ITEM_INDEX (ABDAY_7); ++cnt)
428     {
429       iov[2 + cnt].iov_base =
430         (void *) (time->abday[cnt - _NL_ITEM_INDEX (ABDAY_1)] ?: "");
431       iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
432       idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
433     }
434
435   /* The days.  */
436   for (; cnt <= _NL_ITEM_INDEX (DAY_7); ++cnt)
437     {
438       iov[2 + cnt].iov_base =
439         (void *) (time->day[cnt - _NL_ITEM_INDEX (DAY_1)] ?: "");
440       iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
441       idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
442     }
443
444   /* The ab'mons.  */
445   for (; cnt <= _NL_ITEM_INDEX (ABMON_12); ++cnt)
446     {
447       iov[2 + cnt].iov_base =
448         (void *) (time->abmon[cnt - _NL_ITEM_INDEX (ABMON_1)] ?: "");
449       iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
450       idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
451     }
452
453   /* The mons.  */
454   for (; cnt <= _NL_ITEM_INDEX (MON_12); ++cnt)
455     {
456       iov[2 + cnt].iov_base =
457         (void *) (time->mon[cnt - _NL_ITEM_INDEX (MON_1)] ?: "");
458       iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
459       idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
460     }
461
462   /* AM/PM.  */
463   for (; cnt <= _NL_ITEM_INDEX (PM_STR); ++cnt)
464     {
465       iov[2 + cnt].iov_base =
466         (void *) (time->am_pm[cnt - _NL_ITEM_INDEX (AM_STR)] ?: "");
467       iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
468       idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
469     }
470
471   iov[2 + cnt].iov_base = (void *) (time->d_t_fmt ?: "");
472   iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
473   idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
474   ++cnt;
475
476   iov[2 + cnt].iov_base = (void *) (time->d_fmt ?: "");
477   iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
478   idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
479   ++cnt;
480
481   iov[2 + cnt].iov_base = (void *) (time->t_fmt ?: "");
482   iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
483   idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
484   ++cnt;
485
486   iov[2 + cnt].iov_base = (void *) (time->t_fmt_ampm ?: "");
487   iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
488   idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
489   last_idx = ++cnt;
490
491   idx[1 + last_idx] = idx[last_idx];
492   for (num = 0; num < time->cur_num_era; ++num, ++cnt)
493     {
494       iov[2 + cnt].iov_base = (void *) time->era[num];
495       iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
496       idx[1 + last_idx] += iov[2 + cnt].iov_len;
497     }
498   ++last_idx;
499
500   iov[2 + cnt].iov_base = (void *) (time->era_year ?: "");
501   iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
502   idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
503   ++cnt;
504   ++last_idx;
505
506   iov[2 + cnt].iov_base = (void *) (time->era_d_fmt ?: "");
507   iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
508   idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
509   ++cnt;
510   ++last_idx;
511
512   idx[1 + last_idx] = idx[last_idx];
513   for (num = 0; num < time->cur_num_alt_digits; ++num, ++cnt)
514     {
515       iov[2 + cnt].iov_base = (void *) (time->alt_digits[num] ?: "");
516       iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
517       idx[1 + last_idx] += iov[2 + cnt].iov_len;
518     }
519   ++last_idx;
520
521   iov[2 + cnt].iov_base = (void *) (time->era_d_t_fmt ?: "");
522   iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
523   idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
524   ++cnt;
525   ++last_idx;
526
527   iov[2 + cnt].iov_base = (void *) (time->era_t_fmt ?: "");
528   iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
529   idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
530   ++cnt;
531   ++last_idx;
532
533
534   /* We must align the following data.  */
535   iov[2 + cnt].iov_base = (void *) "\0\0";
536   iov[2 + cnt].iov_len = ((idx[last_idx] + 3) & ~3) - idx[last_idx];
537   idx[last_idx] = (idx[last_idx] + 3) & ~3;
538   ++cnt;
539
540   iov[2 + cnt].iov_base = (void *) &time->cur_num_alt_digits;
541   iov[2 + cnt].iov_len = sizeof (u_int32_t);
542   idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
543   ++cnt;
544   ++last_idx;
545
546   /* The `era' data in usable form.  */
547   iov[2 + cnt].iov_base = (void *) &time->cur_num_era;
548   iov[2 + cnt].iov_len = sizeof (u_int32_t);
549   idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
550   ++cnt;
551   ++last_idx;
552
553 #if __BYTE_ORDER == __LITTLE_ENDIAN
554 # define ERA_B1 time->era_entries_ob
555 # define ERA_B2 time->era_entries
556 #else
557 # define ERA_B1 time->era_entries
558 # define ERA_B2 time->era_entries_ob
559 #endif
560   idx[1 + last_idx] = idx[last_idx];
561   for (num = 0; num < time->cur_num_era; ++num)
562     {
563       size_t l;
564
565       iov[2 + cnt].iov_base = (void *) &ERA_B1[num].direction;
566       iov[2 + cnt].iov_len = sizeof (int32_t);
567       ++cnt;
568       iov[2 + cnt].iov_base = (void *) &ERA_B1[num].offset;
569       iov[2 + cnt].iov_len = sizeof (int32_t);
570       ++cnt;
571       iov[2 + cnt].iov_base = (void *) &ERA_B1[num].start_date[0];
572       iov[2 + cnt].iov_len = sizeof (int32_t);
573       ++cnt;
574       iov[2 + cnt].iov_base = (void *) &ERA_B1[num].start_date[1];
575       iov[2 + cnt].iov_len = sizeof (int32_t);
576       ++cnt;
577       iov[2 + cnt].iov_base = (void *) &ERA_B1[num].start_date[2];
578       iov[2 + cnt].iov_len = sizeof (int32_t);
579       ++cnt;
580       iov[2 + cnt].iov_base = (void *) &ERA_B1[num].stop_date[0];
581       iov[2 + cnt].iov_len = sizeof (int32_t);
582       ++cnt;
583       iov[2 + cnt].iov_base = (void *) &ERA_B1[num].stop_date[1];
584       iov[2 + cnt].iov_len = sizeof (int32_t);
585       ++cnt;
586       iov[2 + cnt].iov_base = (void *) &ERA_B1[num].stop_date[2];
587       iov[2 + cnt].iov_len = sizeof (int32_t);
588       ++cnt;
589
590       l = (strchr (ERA_B1[num].format, '\0') - ERA_B1[num].name) + 1;
591       l = (l + 3) & ~3;
592       iov[2 + cnt].iov_base = (void *) ERA_B1[num].name;
593       iov[2 + cnt].iov_len = l;
594       ++cnt;
595
596       idx[1 + last_idx] += 8 * sizeof (int32_t) + l;
597
598       assert (idx[1 + last_idx] % 4 == 0);
599     }
600   ++last_idx;
601
602   /* idx[1 + last_idx] = idx[last_idx]; */
603   for (num = 0; num < time->cur_num_era; ++num)
604     {
605       size_t l;
606
607       iov[2 + cnt].iov_base = (void *) &ERA_B2[num].direction;
608       iov[2 + cnt].iov_len = sizeof (int32_t);
609       ++cnt;
610       iov[2 + cnt].iov_base = (void *) &ERA_B2[num].offset;
611       iov[2 + cnt].iov_len = sizeof (int32_t);
612       ++cnt;
613       iov[2 + cnt].iov_base = (void *) &ERA_B2[num].start_date[0];
614       iov[2 + cnt].iov_len = sizeof (int32_t);
615       ++cnt;
616       iov[2 + cnt].iov_base = (void *) &ERA_B2[num].start_date[1];
617       iov[2 + cnt].iov_len = sizeof (int32_t);
618       ++cnt;
619       iov[2 + cnt].iov_base = (void *) &ERA_B2[num].start_date[2];
620       iov[2 + cnt].iov_len = sizeof (int32_t);
621       ++cnt;
622       iov[2 + cnt].iov_base = (void *) &ERA_B2[num].stop_date[0];
623       iov[2 + cnt].iov_len = sizeof (int32_t);
624       ++cnt;
625       iov[2 + cnt].iov_base = (void *) &ERA_B2[num].stop_date[1];
626       iov[2 + cnt].iov_len = sizeof (int32_t);
627       ++cnt;
628       iov[2 + cnt].iov_base = (void *) &ERA_B2[num].stop_date[2];
629       iov[2 + cnt].iov_len = sizeof (int32_t);
630       ++cnt;
631
632       l = (strchr (ERA_B2[num].format, '\0') - ERA_B2[num].name) + 1;
633       l = (l + 3) & ~3;
634       iov[2 + cnt].iov_base = (void *) ERA_B2[num].name;
635       iov[2 + cnt].iov_len = l;
636       ++cnt;
637
638       /* idx[1 + last_idx] += 8 * sizeof (int32_t) + l; */
639     }
640
641   /* We have a problem when no era data is present.  In this case the
642      data pointer for _NL_TIME_ERA_ENTRIES_EB and
643      _NL_TIME_ERA_ENTRIES_EL point after the end of the file.  So we
644      introduce some dummy data here.  */
645   if (time->cur_num_era == 0)
646     {
647       static u_int32_t dummy = 0;
648       iov[2 + cnt].iov_base = (void *) &dummy;
649       iov[2 + cnt].iov_len = 4;
650       ++cnt;
651     }
652
653   assert (cnt == (_NL_ITEM_INDEX (_NL_NUM_LC_TIME)
654                   + time->cur_num_era - 1
655                   + time->cur_num_alt_digits - 1
656                   + 1 + (time->cur_num_era * 9 - 1) * 2
657                   + (time->cur_num_era == 0))
658           && last_idx + 1 == _NL_ITEM_INDEX (_NL_NUM_LC_TIME));
659
660   write_locale_data (output_path, "LC_TIME", 2 + cnt, iov);
661 }
662
663
664 void
665 time_add (struct linereader *lr, struct localedef_t *locale,
666           enum token_t tok, struct token *code,
667           struct charset_t *charset)
668 {
669   struct locale_time_t *time = locale->categories[LC_TIME].time;
670
671   switch (tok)
672     {
673 #define STRARR_ELEM(cat, max)                                                 \
674     case tok_##cat:                                                           \
675       if (time->cur_num_##cat >= max)                                         \
676         lr_error (lr, _("\
677 too many values for field `%s' in category `%s'"),                            \
678                   #cat, "LC_TIME");                                           \
679       else if (code->val.str.start == NULL)                                   \
680         {                                                                     \
681           lr_error (lr, _("unknown character in field `%s' of category `%s'"),\
682                     #cat, "LC_TIME");                                         \
683           time->cat[time->cur_num_##cat++] = "";                              \
684         }                                                                     \
685       else                                                                    \
686         time->cat[time->cur_num_##cat++] = code->val.str.start;               \
687       break
688
689     STRARR_ELEM (abday, 7);
690     STRARR_ELEM (day, 7);
691     STRARR_ELEM (abmon, 12);
692     STRARR_ELEM (mon, 12);
693     STRARR_ELEM (am_pm, 2);
694     STRARR_ELEM (alt_digits, 100);
695
696     case tok_era:
697       if (code->val.str.start == NULL)
698         lr_error (lr, _("unknown character in field `%s' of category `%s'"),
699                   "era", "LC_TIME");
700       else
701         {
702           ++time->cur_num_era;
703           time->era = xrealloc (time->era,
704                                 time->cur_num_era * sizeof (char *));
705           time->era[time->cur_num_era - 1] = code->val.str.start;
706         }
707       break;
708
709 #define STR_ELEM(cat)                                                         \
710     case tok_##cat:                                                           \
711       if (time->cat != NULL)                                                  \
712         lr_error (lr, _("\
713 field `%s' in category `%s' declared more than once"),                        \
714                   #cat, "LC_TIME");                                           \
715       else if (code->val.str.start == NULL)                                   \
716         {                                                                     \
717           lr_error (lr, _("unknown character in field `%s' of category `%s'"),\
718                     #cat, "LC_TIME");                                         \
719           time->cat = "";                                                     \
720         }                                                                     \
721       else                                                                    \
722         time->cat = code->val.str.start;                                      \
723       break
724
725     STR_ELEM (d_t_fmt);
726     STR_ELEM (d_fmt);
727     STR_ELEM (t_fmt);
728     STR_ELEM (t_fmt_ampm);
729     STR_ELEM (era_year);
730     STR_ELEM (era_d_t_fmt);
731     STR_ELEM (era_d_fmt);
732     STR_ELEM (era_t_fmt);
733
734     default:
735       assert (! "unknown token in category `LC_TIME': should not happen");
736     }
737 }