Update.
[glibc.git] / locale / programs / ld-time.c
blobb2c06cca7ce3d278781220f03c488822070079b2
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.
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.
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.
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. */
20 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif
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>
31 #include <assert.h>
33 #include "linereader.h"
34 #include "localedef.h"
35 #include "localeinfo.h"
36 #include "locfile.h"
39 /* Entry describing an entry of the era specification. */
40 struct era_data
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;
53 /* The real definition of the struct for the LC_TIME locale. */
54 struct locale_time_t
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;
102 struct era_data *era_entries;
106 /* This constant is used to represent an empty wide character string. */
107 static const uint32_t empty_wstr[1] = { 0 };
110 static void
111 time_startup (struct linereader *lr, struct localedef_t *locale,
112 int ignore_content)
114 if (!ignore_content)
115 locale->categories[LC_TIME].time =
116 (struct locale_time_t *) xcalloc (1, sizeof (struct locale_time_t));
118 if (lr != NULL)
120 lr->translate_strings = 1;
121 lr->return_widestr = 1;
126 void
127 time_finish (struct localedef_t *locale, struct charmap_t *charmap)
129 struct locale_time_t *time = locale->categories[LC_TIME].time;
130 int nothing = 0;
132 /* Now resolve copying and also handle completely missing definitions. */
133 if (time == NULL)
135 /* First see whether we were supposed to copy. If yes, find the
136 actual definition. */
137 if (locale->copy_name[LC_TIME] != NULL)
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;
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);
149 time = locale->categories[LC_TIME].time
150 = from->categories[LC_TIME].time;
153 /* If there is still no definition issue an warning and create an
154 empty one. */
155 if (time == NULL)
157 error (0, 0, _("No definition for %s category found"), "LC_TIME");
158 time_startup (NULL, locale, 0);
159 time = locale->categories[LC_TIME].time;
160 nothing = 1;
164 #define TESTARR_ELEM(cat) \
165 if (!time->cat##_defined) \
167 if(! be_quiet && ! nothing) \
168 error (0, 0, _("%s: field `%s' not defined"), "LC_TIME", #cat); \
171 TESTARR_ELEM (abday);
172 TESTARR_ELEM (day);
173 TESTARR_ELEM (abmon);
174 TESTARR_ELEM (mon);
175 TESTARR_ELEM (am_pm);
177 #define TEST_ELEM(cat) \
178 if (time->cat == NULL) \
180 if (! be_quiet && ! nothing) \
181 error (0, 0, _("%s: field `%s' not defined"), "LC_TIME", #cat); \
184 TEST_ELEM (d_t_fmt);
185 TEST_ELEM (d_fmt);
186 TEST_ELEM (t_fmt);
188 /* According to C.Y.Alexis Cheng <alexis@vnet.ibm.com> the T_FMT_AMPM
189 field is optional. */
190 if (time->t_fmt_ampm == NULL)
192 /* Use the 24h format as default. */
193 time->t_fmt_ampm = time->t_fmt;
194 time->wt_fmt_ampm = time->wt_fmt;
197 /* Now process the era entries. */
198 if (time->num_era != 0)
200 const int days_per_month[12] = { 31, 29, 31, 30, 31, 30,
201 31, 31, 30, 31 ,30, 31 };
202 size_t idx;
203 wchar_t *wstr;
205 time->era_entries =
206 (struct era_data *) xmalloc (time->num_era
207 * sizeof (struct era_data));
209 for (idx = 0; idx < time->num_era; ++idx)
211 size_t era_len = strlen (time->era[idx]);
212 char *str = xmalloc ((era_len + 1 + 3) & ~3);
213 char *endp;
215 memcpy (str, time->era[idx], era_len + 1);
217 /* First character must be + or - for the direction. */
218 if (*str != '+' && *str != '-')
220 if (!be_quiet)
221 error (0, 0, _("%s: direction flag in string %d in `era' field"
222 " is not '+' nor '-'"),
223 "LC_TIME", idx + 1);
224 /* Default arbitrarily to '+'. */
225 time->era_entries[idx].direction = '+';
227 else
228 time->era_entries[idx].direction = *str;
229 if (*++str != ':')
231 if (!be_quiet)
232 error (0, 0, _("%s: direction flag in string %d in `era' field"
233 " is not a single character"),
234 "LC_TIME", idx + 1);
235 (void) strsep (&str, ":");
237 else
238 ++str;
240 /* Now the offset year. */
241 time->era_entries[idx].offset = strtol (str, &endp, 10);
242 if (endp == str)
244 if (!be_quiet)
245 error (0, 0, _("%s: invalid number for offset in string %d in"
246 " `era' field"),
247 "LC_TIME", idx + 1);
248 (void) strsep (&str, ":");
250 else if (*endp != ':')
252 if (!be_quiet)
253 error (0, 0, _("%s: garbage at end of offset value in"
254 " string %d in `era' field"),
255 "LC_TIME", idx + 1);
256 (void) strsep (&str, ":");
258 else
259 str = endp + 1;
261 /* Next is the starting date in ISO format. */
262 if (strncmp (str, "-*", 2) == 0)
264 time->era_entries[idx].start_date[0] =
265 time->era_entries[idx].start_date[1] =
266 time->era_entries[idx].start_date[2] = 0x80000000;
267 if (str[2] != ':')
268 goto garbage_start_date;
269 str += 3;
271 else if (strncmp (str, "+*", 2) == 0)
273 time->era_entries[idx].start_date[0] =
274 time->era_entries[idx].start_date[1] =
275 time->era_entries[idx].start_date[2] = 0x7fffffff;
276 if (str[2] != ':')
277 goto garbage_start_date;
278 str += 3;
280 else
282 time->era_entries[idx].start_date[0] = strtol (str, &endp, 10);
283 if (endp == str || *endp != '/')
284 goto invalid_start_date;
285 else
286 str = endp + 1;
287 time->era_entries[idx].start_date[0] -= 1900;
289 time->era_entries[idx].start_date[1] = strtol (str, &endp, 10);
290 if (endp == str || *endp != '/')
291 goto invalid_start_date;
292 else
293 str = endp + 1;
294 time->era_entries[idx].start_date[1] -= 1;
296 time->era_entries[idx].start_date[2] = strtol (str, &endp, 10);
297 if (endp == str)
299 invalid_start_date:
300 if (!be_quiet)
301 error (0, 0, _("%s: invalid starting date in string %d in"
302 " `era' field"),
303 "LC_TIME", idx + 1);
304 (void) strsep (&str, ":");
306 else if (*endp != ':')
308 garbage_start_date:
309 if (!be_quiet)
310 error (0, 0, _("%s: garbage at end of starting date "
311 "in string %d in `era' field "),
312 "LC_TIME", idx + 1);
313 (void) strsep (&str, ":");
315 else
317 str = endp + 1;
319 /* Check for valid value. */
320 if ((time->era_entries[idx].start_date[1] < 0
321 || time->era_entries[idx].start_date[1] >= 12
322 || time->era_entries[idx].start_date[2] < 0
323 || (time->era_entries[idx].start_date[2]
324 > days_per_month[time->era_entries[idx].start_date[1]])
325 || (time->era_entries[idx].start_date[1] == 2
326 && time->era_entries[idx].start_date[2] == 29
327 && !__isleap (time->era_entries[idx].start_date[0])))
328 && !be_quiet)
329 error (0, 0, _("%s: starting date is invalid in"
330 " string %d in `era' field"),
331 "LC_TIME", idx + 1);
335 /* Next is the stopping date in ISO format. */
336 if (strncmp (str, "-*", 2) == 0)
338 time->era_entries[idx].stop_date[0] =
339 time->era_entries[idx].stop_date[1] =
340 time->era_entries[idx].stop_date[2] = 0x80000000;
341 if (str[2] != ':')
342 goto garbage_stop_date;
343 str += 3;
345 else if (strncmp (str, "+*", 2) == 0)
347 time->era_entries[idx].stop_date[0] =
348 time->era_entries[idx].stop_date[1] =
349 time->era_entries[idx].stop_date[2] = 0x7fffffff;
350 if (str[2] != ':')
351 goto garbage_stop_date;
352 str += 3;
354 else
356 time->era_entries[idx].stop_date[0] = strtol (str, &endp, 10);
357 if (endp == str || *endp != '/')
358 goto invalid_stop_date;
359 else
360 str = endp + 1;
361 time->era_entries[idx].stop_date[0] -= 1900;
363 time->era_entries[idx].stop_date[1] = strtol (str, &endp, 10);
364 if (endp == str || *endp != '/')
365 goto invalid_stop_date;
366 else
367 str = endp + 1;
368 time->era_entries[idx].stop_date[1] -= 1;
370 time->era_entries[idx].stop_date[2] = strtol (str, &endp, 10);
371 if (endp == str)
373 invalid_stop_date:
374 if (!be_quiet)
375 error (0, 0, _("%s: invalid stopping date in string %d in"
376 " `era' field"),
377 "LC_TIME", idx + 1);
378 (void) strsep (&str, ":");
380 else if (*endp != ':')
382 garbage_stop_date:
383 if (!be_quiet)
384 error (0, 0, _("%s: garbage at end of stopping date "
385 "in string %d in `era' field"),
386 "LC_TIME", idx + 1);
387 (void) strsep (&str, ":");
389 else
391 str = endp + 1;
393 /* Check for valid value. */
394 if ((time->era_entries[idx].stop_date[1] < 0
395 || time->era_entries[idx].stop_date[1] >= 12
396 || time->era_entries[idx].stop_date[2] < 0
397 || (time->era_entries[idx].stop_date[2]
398 > days_per_month[time->era_entries[idx].stop_date[1]])
399 || (time->era_entries[idx].stop_date[1] == 2
400 && time->era_entries[idx].stop_date[2] == 29
401 && !__isleap (time->era_entries[idx].stop_date[0])))
402 && !be_quiet)
403 error (0, 0, _("%s: stopping date is invalid in"
404 " string %d in `era' field"),
405 "LC_TIME", idx + 1);
409 if (str == NULL || *str == '\0')
411 if (!be_quiet)
412 error (0, 0, _("%s: missing era name in string %d in `era'"
413 " field"), "LC_TIME", idx + 1);
414 time->era_entries[idx].name =
415 time->era_entries[idx].format = "";
417 else
419 time->era_entries[idx].name = strsep (&str, ":");
421 if (str == NULL || *str == '\0')
423 if (!be_quiet)
424 error (0, 0, _("%s: missing era format in string %d"
425 " in `era' field"),
426 "LC_TIME", idx + 1);
427 time->era_entries[idx].name =
428 time->era_entries[idx].format = "";
430 else
431 time->era_entries[idx].format = str;
434 /* Now generate the wide character name and format. */
435 wstr = wcschr ((wchar_t *) time->wera[idx], L':');/* end direction */
436 wstr = wstr ? wcschr (wstr + 1, L':') : NULL; /* end offset */
437 wstr = wstr ? wcschr (wstr + 1, L':') : NULL; /* end start */
438 wstr = wstr ? wcschr (wstr + 1, L':') : NULL; /* end end */
439 time->era_entries[idx].wname = (uint32_t *) wstr + 1;
440 wstr = wstr ? wcschr (wstr + 1, L':') : NULL; /* end name */
441 *wstr = L'\0';
442 time->era_entries[idx].wformat = (uint32_t *) wstr + 1;
446 if (time->week_ndays == 0)
447 time->week_ndays = 7;
449 if (time->week_1stday == 0)
450 time->week_1stday = 19971130;
452 if (time->week_1stweek > time->week_ndays)
453 error (0, 0, _("\
454 %s: third operand for value of field `%s' must not be larger than %d"),
455 "LC_TIME", "week", 7);
457 if (time->first_weekday == '\0')
458 /* The definition does not specify this so the default is used. */
459 time->first_weekday = 1;
460 else if (time->first_weekday > time->week_ndays)
461 error (0, 0, _("\
462 %s: values of field `%s' must not be larger than %d"),
463 "LC_TIME", "first_weekday", 7);
465 if (time->first_workday == '\0')
466 /* The definition does not specify this so the default is used. */
467 time->first_workday = 1;
468 else if (time->first_workday > time->week_ndays)
469 error (0, 0, _("\
470 %s: values of field `%s' must not be larger than %d"),
471 "LC_TIME", "first_workday", 7);
473 if (time->cal_direction == '\0')
474 /* The definition does not specify this so the default is used. */
475 time->cal_direction = 1;
476 else if (time->cal_direction > 3)
477 error (0, 0, _("\
478 %s: values for field `%s' must not be larger than 3"),
479 "LC_TIME", "cal_direction", 3);
481 /* XXX We don't perform any tests on the timezone value since this is
482 simply useless, stupid $&$!@... */
483 if (time->timezone == NULL)
484 time->timezone = "";
488 void
489 time_output (struct localedef_t *locale, struct charmap_t *charmap,
490 const char *output_path)
492 struct locale_time_t *time = locale->categories[LC_TIME].time;
493 struct iovec iov[2 + _NL_ITEM_INDEX (_NL_NUM_LC_TIME)
494 + time->num_era - 1
495 + 2 * 99
496 + 1 + time->num_era * 10 - 1];
497 struct locale_file data;
498 uint32_t idx[_NL_ITEM_INDEX (_NL_NUM_LC_TIME)];
499 size_t cnt, last_idx, num, n;
501 data.magic = LIMAGIC (LC_TIME);
502 data.n = _NL_ITEM_INDEX (_NL_NUM_LC_TIME);
503 iov[0].iov_base = (void *) &data;
504 iov[0].iov_len = sizeof (data);
506 iov[1].iov_base = (void *) idx;
507 iov[1].iov_len = sizeof (idx);
509 idx[0] = iov[0].iov_len + iov[1].iov_len;
511 /* The ab'days. */
512 for (cnt = 0; cnt <= _NL_ITEM_INDEX (ABDAY_7); ++cnt)
514 iov[2 + cnt].iov_base =
515 (void *) (time->abday[cnt - _NL_ITEM_INDEX (ABDAY_1)] ?: "");
516 iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
517 idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
520 /* The days. */
521 for (; cnt <= _NL_ITEM_INDEX (DAY_7); ++cnt)
523 iov[2 + cnt].iov_base =
524 (void *) (time->day[cnt - _NL_ITEM_INDEX (DAY_1)] ?: "");
525 iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
526 idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
529 /* The ab'mons. */
530 for (; cnt <= _NL_ITEM_INDEX (ABMON_12); ++cnt)
532 iov[2 + cnt].iov_base =
533 (void *) (time->abmon[cnt - _NL_ITEM_INDEX (ABMON_1)] ?: "");
534 iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
535 idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
538 /* The mons. */
539 for (; cnt <= _NL_ITEM_INDEX (MON_12); ++cnt)
541 iov[2 + cnt].iov_base =
542 (void *) (time->mon[cnt - _NL_ITEM_INDEX (MON_1)] ?: "");
543 iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
544 idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
547 /* AM/PM. */
548 for (; cnt <= _NL_ITEM_INDEX (PM_STR); ++cnt)
550 iov[2 + cnt].iov_base =
551 (void *) (time->am_pm[cnt - _NL_ITEM_INDEX (AM_STR)] ?: "");
552 iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
553 idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
556 iov[2 + cnt].iov_base = (void *) (time->d_t_fmt ?: "");
557 iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
558 idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
559 ++cnt;
561 iov[2 + cnt].iov_base = (void *) (time->d_fmt ?: "");
562 iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
563 idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
564 ++cnt;
566 iov[2 + cnt].iov_base = (void *) (time->t_fmt ?: "");
567 iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
568 idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
569 ++cnt;
571 iov[2 + cnt].iov_base = (void *) (time->t_fmt_ampm ?: "");
572 iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
573 idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
574 last_idx = ++cnt;
576 idx[1 + last_idx] = idx[last_idx];
577 for (num = 0; num < time->num_era; ++num, ++cnt)
579 iov[2 + cnt].iov_base = (void *) time->era[num];
580 iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
581 idx[1 + last_idx] += iov[2 + cnt].iov_len;
583 ++last_idx;
585 iov[2 + cnt].iov_base = (void *) (time->era_year ?: "");
586 iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
587 idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
588 ++cnt;
589 ++last_idx;
591 iov[2 + cnt].iov_base = (void *) (time->era_d_fmt ?: "");
592 iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
593 idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
594 ++cnt;
595 ++last_idx;
597 idx[1 + last_idx] = idx[last_idx];
598 for (num = 0; num < 100; ++num, ++cnt)
600 iov[2 + cnt].iov_base = (void *) (time->alt_digits[num] ?: "");
601 iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
602 idx[1 + last_idx] += iov[2 + cnt].iov_len;
604 ++last_idx;
606 iov[2 + cnt].iov_base = (void *) (time->era_d_t_fmt ?: "");
607 iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
608 idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
609 ++cnt;
610 ++last_idx;
612 iov[2 + cnt].iov_base = (void *) (time->era_t_fmt ?: "");
613 iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
614 idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
615 ++cnt;
616 ++last_idx;
619 /* We must align the following data. */
620 iov[2 + cnt].iov_base = (void *) "\0\0";
621 iov[2 + cnt].iov_len = ((idx[last_idx] + 3) & ~3) - idx[last_idx];
622 idx[last_idx] = (idx[last_idx] + 3) & ~3;
623 ++cnt;
625 /* The `era' data in usable form. */
626 iov[2 + cnt].iov_base = (void *) &time->num_era;
627 iov[2 + cnt].iov_len = sizeof (uint32_t);
628 idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
629 ++cnt;
630 ++last_idx;
632 idx[1 + last_idx] = idx[last_idx];
633 for (num = 0; num < time->num_era; ++num)
635 size_t l;
637 iov[2 + cnt].iov_base = (void *) &time->era_entries[num].direction;
638 iov[2 + cnt].iov_len = sizeof (int32_t);
639 ++cnt;
640 iov[2 + cnt].iov_base = (void *) &time->era_entries[num].offset;
641 iov[2 + cnt].iov_len = sizeof (int32_t);
642 ++cnt;
643 iov[2 + cnt].iov_base = (void *) &time->era_entries[num].start_date[0];
644 iov[2 + cnt].iov_len = sizeof (int32_t);
645 ++cnt;
646 iov[2 + cnt].iov_base = (void *) &time->era_entries[num].start_date[1];
647 iov[2 + cnt].iov_len = sizeof (int32_t);
648 ++cnt;
649 iov[2 + cnt].iov_base = (void *) &time->era_entries[num].start_date[2];
650 iov[2 + cnt].iov_len = sizeof (int32_t);
651 ++cnt;
652 iov[2 + cnt].iov_base = (void *) &time->era_entries[num].stop_date[0];
653 iov[2 + cnt].iov_len = sizeof (int32_t);
654 ++cnt;
655 iov[2 + cnt].iov_base = (void *) &time->era_entries[num].stop_date[1];
656 iov[2 + cnt].iov_len = sizeof (int32_t);
657 ++cnt;
658 iov[2 + cnt].iov_base = (void *) &time->era_entries[num].stop_date[2];
659 iov[2 + cnt].iov_len = sizeof (int32_t);
660 ++cnt;
662 l = (strchr (time->era_entries[num].format, '\0')
663 - time->era_entries[num].name) + 1;
664 l = (l + 3) & ~3;
665 iov[2 + cnt].iov_base = (void *) time->era_entries[num].name;
666 iov[2 + cnt].iov_len = l;
667 ++cnt;
669 idx[1 + last_idx] += 8 * sizeof (int32_t) + l;
671 assert (idx[1 + last_idx] % 4 == 0);
673 iov[2 + cnt].iov_base = (void *) time->era_entries[num].wname;
674 iov[2 + cnt].iov_len = ((wcschr ((wchar_t *) time->era_entries[num].wformat, L'\0')
675 - (wchar_t *) time->era_entries[num].wname + 1)
676 * sizeof (uint32_t));
677 idx[1 + last_idx] += iov[2 + cnt].iov_len;
678 ++cnt;
680 ++last_idx;
682 /* The wide character ab'days. */
683 for (n = 0; n < 7; ++n, ++cnt, ++last_idx)
685 iov[2 + cnt].iov_base =
686 (void *) (time->wabday[n] ?: empty_wstr);
687 iov[2 + cnt].iov_len = ((wcslen (iov[2 + cnt].iov_base) + 1)
688 * sizeof (uint32_t));
689 idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
692 /* The wide character days. */
693 for (n = 0; n < 7; ++n, ++cnt, ++last_idx)
695 iov[2 + cnt].iov_base =
696 (void *) (time->wday[n] ?: empty_wstr);
697 iov[2 + cnt].iov_len = ((wcslen (iov[2 + cnt].iov_base) + 1)
698 * sizeof (uint32_t));
699 idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
702 /* The wide character ab'mons. */
703 for (n = 0; n < 12; ++n, ++cnt, ++last_idx)
705 iov[2 + cnt].iov_base =
706 (void *) (time->wabmon[n] ?: empty_wstr);
707 iov[2 + cnt].iov_len = ((wcslen (iov[2 + cnt].iov_base) + 1)
708 * sizeof (uint32_t));
709 idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
712 /* The wide character mons. */
713 for (n = 0; n < 12; ++n, ++cnt, ++last_idx)
715 iov[2 + cnt].iov_base =
716 (void *) (time->wmon[n] ?: empty_wstr);
717 iov[2 + cnt].iov_len = ((wcslen (iov[2 + cnt].iov_base) + 1)
718 * sizeof (uint32_t));
719 idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
722 /* Wide character AM/PM. */
723 for (n = 0; n < 2; ++n, ++cnt, ++last_idx)
725 iov[2 + cnt].iov_base =
726 (void *) (time->wam_pm[n] ?: empty_wstr);
727 iov[2 + cnt].iov_len = ((wcslen (iov[2 + cnt].iov_base) + 1)
728 * sizeof (uint32_t));
729 idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
732 iov[2 + cnt].iov_base = (void *) (time->wd_t_fmt ?: empty_wstr);
733 iov[2 + cnt].iov_len = ((wcslen (iov[2 + cnt].iov_base) + 1)
734 * sizeof (uint32_t));
735 idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
736 ++cnt;
737 ++last_idx;
739 iov[2 + cnt].iov_base = (void *) (time->wd_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;
746 iov[2 + cnt].iov_base = (void *) (time->wt_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;
753 iov[2 + cnt].iov_base = (void *) (time->wt_fmt_ampm ?: 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;
760 iov[2 + cnt].iov_base = (void *) (time->wera_year ?: 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;
767 iov[2 + cnt].iov_base = (void *) (time->wera_d_fmt ?: 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;
774 idx[1 + last_idx] = idx[last_idx];
775 for (num = 0; num < 100; ++num, ++cnt)
777 iov[2 + cnt].iov_base = (void *) (time->walt_digits[num]
778 ?: empty_wstr);
779 iov[2 + cnt].iov_len = ((wcslen (iov[2 + cnt].iov_base) + 1)
780 * sizeof (uint32_t));
781 idx[1 + last_idx] += iov[2 + cnt].iov_len;
783 ++last_idx;
785 iov[2 + cnt].iov_base = (void *) (time->wera_d_t_fmt ?: empty_wstr);
786 iov[2 + cnt].iov_len = ((wcslen (iov[2 + cnt].iov_base) + 1)
787 * sizeof (uint32_t));
788 idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
789 ++cnt;
790 ++last_idx;
792 iov[2 + cnt].iov_base = (void *) (time->wera_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;
799 iov[2 + cnt].iov_base = (void *) &time->week_ndays;
800 iov[2 + cnt].iov_len = 1;
801 idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
802 ++cnt;
803 ++last_idx;
805 iov[2 + cnt].iov_base = (void *) &time->week_1stday;
806 iov[2 + cnt].iov_len = 1;
807 idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
808 ++cnt;
809 ++last_idx;
811 iov[2 + cnt].iov_base = (void *) &time->week_1stweek;
812 iov[2 + cnt].iov_len = 1;
813 idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
814 ++cnt;
815 ++last_idx;
817 iov[2 + cnt].iov_base = (void *) &time->first_weekday;
818 iov[2 + cnt].iov_len = 1;
819 idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
820 ++cnt;
821 ++last_idx;
823 iov[2 + cnt].iov_base = (void *) &time->first_workday;
824 iov[2 + cnt].iov_len = 1;
825 idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
826 ++cnt;
827 ++last_idx;
829 iov[2 + cnt].iov_base = (void *) &time->cal_direction;
830 iov[2 + cnt].iov_len = 1;
831 idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
832 ++cnt;
833 ++last_idx;
835 iov[2 + cnt].iov_base = (void *) time->timezone;
836 iov[2 + cnt].iov_len = strlen (time->timezone) + 1;
837 ++cnt;
838 ++last_idx;
840 assert (cnt == (_NL_ITEM_INDEX (_NL_NUM_LC_TIME)
841 + time->num_era - 1
842 + 2 * 99
843 + 1 + time->num_era * 10 - 1));
844 assert (last_idx == _NL_ITEM_INDEX (_NL_NUM_LC_TIME));
846 write_locale_data (output_path, "LC_TIME", 2 + cnt, iov);
850 /* The parser for the LC_TIME section of the locale definition. */
851 void
852 time_read (struct linereader *ldfile, struct localedef_t *result,
853 struct charmap_t *charmap, const char *repertoire_name,
854 int ignore_content)
856 struct repertoire_t *repertoire = NULL;
857 struct locale_time_t *time;
858 struct token *now;
859 enum token_t nowtok;
860 size_t cnt;
862 /* Get the repertoire we have to use. */
863 if (repertoire_name != NULL)
864 repertoire = repertoire_read (repertoire_name);
866 /* The rest of the line containing `LC_TIME' must be free. */
867 lr_ignore_rest (ldfile, 1);
872 now = lr_token (ldfile, charmap, repertoire);
873 nowtok = now->tok;
875 while (nowtok == tok_eol);
877 /* If we see `copy' now we are almost done. */
878 if (nowtok == tok_copy)
880 handle_copy (ldfile, charmap, repertoire, result, tok_lc_time,
881 LC_TIME, "LC_TIME", ignore_content);
882 return;
885 /* Prepare the data structures. */
886 time_startup (ldfile, result, ignore_content);
887 time = result->categories[LC_TIME].time;
889 while (1)
891 /* Of course we don't proceed beyond the end of file. */
892 if (nowtok == tok_eof)
893 break;
895 /* Ingore empty lines. */
896 if (nowtok == tok_eol)
898 now = lr_token (ldfile, charmap, repertoire);
899 nowtok = now->tok;
900 continue;
903 switch (nowtok)
905 #define STRARR_ELEM(cat, min, max) \
906 case tok_##cat: \
907 /* Ignore the rest of the line if we don't need the input of \
908 this line. */ \
909 if (ignore_content) \
911 lr_ignore_rest (ldfile, 0); \
912 break; \
915 for (cnt = 0; cnt < max; ++cnt) \
917 now = lr_token (ldfile, charmap, repertoire); \
918 if (now->tok == tok_eol) \
920 if (cnt < min) \
921 lr_error (ldfile, _("%s: too few values for field `%s'"), \
922 "LC_TIME", #cat); \
923 if (!ignore_content) \
924 do \
926 time->cat[cnt] = ""; \
927 time->w##cat[cnt] = empty_wstr; \
929 while (++cnt < max); \
930 break; \
932 else if (now->tok != tok_string) \
933 goto err_label; \
934 else if (!ignore_content && (now->val.str.startmb == NULL \
935 || now->val.str.startwc == NULL)) \
937 lr_error (ldfile, _("%s: unknown character in field `%s'"), \
938 "LC_TIME", #cat); \
939 time->cat[cnt] = ""; \
940 time->w##cat[cnt] = empty_wstr; \
942 else if (!ignore_content) \
944 time->cat[cnt] = now->val.str.startmb; \
945 time->w##cat[cnt] = now->val.str.startwc; \
948 /* Match the semicolon. */ \
949 now = lr_token (ldfile, charmap, repertoire); \
950 if (now->tok != tok_semicolon && now->tok != tok_eol) \
951 break; \
953 if (now->tok != tok_eol) \
955 while (!ignore_content && cnt < min) \
957 time->cat[cnt] = ""; \
958 time->w##cat[cnt++] = empty_wstr; \
961 if (now->tok == tok_semicolon) \
963 now = lr_token (ldfile, charmap, repertoire); \
964 if (now->tok == tok_eol) \
965 lr_error (ldfile, _("extra trailing semicolon")); \
966 else if (now->tok == tok_string) \
968 lr_error (ldfile, _("\
969 %s: too many values for field `%s'"), \
970 "LC_TIME", #cat); \
971 lr_ignore_rest (ldfile, 0); \
973 else \
974 goto err_label; \
976 else \
977 goto err_label; \
979 time->cat##_defined = 1; \
980 break
982 STRARR_ELEM (abday, 7, 7);
983 STRARR_ELEM (day, 7, 7);
984 STRARR_ELEM (abmon, 12, 12);
985 STRARR_ELEM (mon, 12, 12);
986 STRARR_ELEM (am_pm, 2, 2);
987 STRARR_ELEM (alt_digits, 0, 100);
989 case tok_era:
990 /* Ignore the rest of the line if we don't need the input of
991 this line. */
992 if (ignore_content)
994 lr_ignore_rest (ldfile, 0);
995 break;
999 now = lr_token (ldfile, charmap, repertoire);
1000 if (now->tok != tok_string)
1001 goto err_label;
1002 if (!ignore_content && (now->val.str.startmb == NULL
1003 || now->val.str.startwc == NULL))
1005 lr_error (ldfile, _("%s: unknown character in field `%s'"),
1006 "LC_TIME", "era");
1007 lr_ignore_rest (ldfile, 0);
1008 break;
1010 if (!ignore_content)
1012 time->era = xrealloc (time->era,
1013 (time->num_era + 1) * sizeof (char *));
1014 time->era[time->num_era] = now->val.str.startmb;
1016 time->wera = xrealloc (time->wera,
1017 (time->num_era + 1)
1018 * sizeof (char *));
1019 time->wera[time->num_era++] = now->val.str.startwc;
1021 now = lr_token (ldfile, charmap, repertoire);
1022 if (now->tok != tok_eol && now->tok != tok_semicolon)
1023 goto err_label;
1025 while (now->tok == tok_semicolon);
1026 break;
1028 #define STR_ELEM(cat) \
1029 case tok_##cat: \
1030 /* Ignore the rest of the line if we don't need the input of \
1031 this line. */ \
1032 if (ignore_content) \
1034 lr_ignore_rest (ldfile, 0); \
1035 break; \
1038 now = lr_token (ldfile, charmap, repertoire); \
1039 if (now->tok != tok_string) \
1040 goto err_label; \
1041 else if (time->cat != NULL) \
1042 lr_error (ldfile, _("\
1043 %s: field `%s' declared more than once"), "LC_TIME", #cat); \
1044 else if (!ignore_content && (now->val.str.startmb == NULL \
1045 || now->val.str.startwc == NULL)) \
1047 lr_error (ldfile, _("%s: unknown character in field `%s'"), \
1048 "LC_TIME", #cat); \
1049 time->cat = ""; \
1050 time->w##cat = empty_wstr; \
1052 else if (!ignore_content) \
1054 time->cat = now->val.str.startmb; \
1055 time->w##cat = now->val.str.startwc; \
1057 break
1059 STR_ELEM (d_t_fmt);
1060 STR_ELEM (d_fmt);
1061 STR_ELEM (t_fmt);
1062 STR_ELEM (t_fmt_ampm);
1063 STR_ELEM (era_year);
1064 STR_ELEM (era_d_t_fmt);
1065 STR_ELEM (era_d_fmt);
1066 STR_ELEM (era_t_fmt);
1067 STR_ELEM (timezone);
1069 #define INT_ELEM(cat) \
1070 case tok_##cat: \
1071 /* Ignore the rest of the line if we don't need the input of \
1072 this line. */ \
1073 if (ignore_content) \
1075 lr_ignore_rest (ldfile, 0); \
1076 break; \
1079 now = lr_token (ldfile, charmap, repertoire); \
1080 if (now->tok != tok_number) \
1081 goto err_label; \
1082 else if (time->cat != 0) \
1083 lr_error (ldfile, _("%s: field `%s' declared more than once"), \
1084 "LC_TIME", #cat); \
1085 else if (!ignore_content) \
1086 time->cat = now->val.num; \
1087 break
1089 INT_ELEM (first_weekday);
1090 INT_ELEM (first_workday);
1091 INT_ELEM (cal_direction);
1093 case tok_week:
1094 /* Ignore the rest of the line if we don't need the input of
1095 this line. */
1096 if (ignore_content)
1098 lr_ignore_rest (ldfile, 0);
1099 break;
1102 now = lr_token (ldfile, charmap, repertoire);
1103 if (now->tok != tok_number)
1104 goto err_label;
1105 time->week_ndays = now->val.num;
1107 now = lr_token (ldfile, charmap, repertoire);
1108 if (now->tok != tok_semicolon)
1109 goto err_label;
1111 now = lr_token (ldfile, charmap, repertoire);
1112 if (now->tok != tok_number)
1113 goto err_label;
1114 time->week_1stday = now->val.num;
1116 now = lr_token (ldfile, charmap, repertoire);
1117 if (now->tok != tok_semicolon)
1118 goto err_label;
1120 now = lr_token (ldfile, charmap, repertoire);
1121 if (now->tok != tok_number)
1122 goto err_label;
1123 time->week_1stweek = now->val.num;
1125 lr_ignore_rest (ldfile, 1);
1126 break;
1128 case tok_end:
1129 /* Next we assume `LC_TIME'. */
1130 now = lr_token (ldfile, charmap, repertoire);
1131 if (now->tok == tok_eof)
1132 break;
1133 if (now->tok == tok_eol)
1134 lr_error (ldfile, _("%s: incomplete `END' line"), "LC_TIME");
1135 else if (now->tok != tok_lc_time)
1136 lr_error (ldfile, _("\
1137 %1$s: definition does not end with `END %1$s'"), "LC_TIME");
1138 lr_ignore_rest (ldfile, now->tok == tok_lc_time);
1139 return;
1141 default:
1142 err_label:
1143 SYNTAX_ERROR (_("%s: syntax error"), "LC_TIME");
1146 /* Prepare for the next round. */
1147 now = lr_token (ldfile, charmap, repertoire);
1148 nowtok = now->tok;
1151 /* When we come here we reached the end of the file. */
1152 lr_error (ldfile, _("%s: premature end of file"), "LC_TIME");