1 /* Copyright (C) 1995, 1996 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Ulrich Drepper, <drepper@gnu.ai.mit.edu>.
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. */
27 /* Undefine following line in production version. */
28 /* #define NDEBUG 1 */
33 #include "localeinfo.h"
34 #include "stringtrans.h"
37 (((w) << 24) | (((w) & 0xff00) << 8) | (((w) >> 8) & 0xff00) | ((w) >> 24))
40 void *xmalloc (size_t __n);
41 void *xrealloc (void *__p, size_t __n);
44 /* Entry describing an entry of the era specification. */
49 int32_t start_date[3];
56 /* The real definition of the struct for the LC_TIME locale. */
63 const char *abmon[12];
72 const char *t_fmt_ampm;
74 u_int32_t cur_num_era;
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;
82 struct era_data *era_entries;
83 struct era_data *era_entries_ob;
88 time_startup (struct linereader *lr, struct localedef_t *locale,
89 struct charset_t *charset)
91 struct locale_time_t *time;
93 /* It is important that we always use UCS1 encoding for strings now. */
94 encoding_method = ENC_UCS1;
96 locale->categories[LC_TIME].time = time =
97 (struct locale_time_t *) xmalloc (sizeof (struct locale_time_t));
99 memset (time, '\0', sizeof (struct locale_time_t));
104 time_finish (struct localedef_t *locale)
106 struct locale_time_t *time = locale->categories[LC_TIME].time;
108 #define TESTARR_ELEM(cat, max) \
109 if (time->cur_num_##cat == 0) \
110 error (0, 0, _("field `%s' in category `%s' not defined"), \
112 else if (time->cur_num_##cat != max) \
113 error (0, 0, _("field `%s' in category `%s' has not enough values"), \
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);
122 #define TEST_ELEM(cat) \
123 if (time->cat == NULL) \
124 error (0, 0, _("field `%s' in category `%s' not defined"), \
130 TEST_ELEM (t_fmt_ampm);
132 /* Now process the era entries. */
133 if (time->cur_num_era != 0)
135 const int days_per_month[12] = { 31, 29, 31, 30, 31, 30,
136 31, 31, 30, 31 ,30, 31 };
140 (struct era_data *) xmalloc (time->cur_num_era
141 * sizeof (struct era_data));
143 for (idx = 0; idx < time->cur_num_era; ++idx)
145 size_t era_len = strlen (time->era[idx]);
146 char *str = xmalloc ((era_len + 1 + 3) & ~3);
149 memcpy (str, time->era[idx], era_len + 1);
151 /* First character must be + or - for the direction. */
152 if (*str != '+' && *str != '-')
154 error (0, 0, _("direction flag in string %d in `era' field"
155 " in category `%s' is not '+' nor '-'"),
157 /* Default arbitrarily to '+'. */
158 time->era_entries[idx].direction = '+';
161 time->era_entries[idx].direction = *str;
164 error (0, 0, _("direction flag in string %d in `era' field"
165 " in category `%s' is not a single character"),
167 (void) strsep (&str, ":");
172 /* Now the offset year. */
173 time->era_entries[idx].offset = strtol (str, &endp, 10);
176 error (0, 0, _("illegal number for offset in string %d in"
177 " `era' field in category `%s'"),
179 (void) strsep (&str, ":");
181 else if (*endp != ':')
183 error (0, 0, _("garbage at end of offset value in string %d in"
184 " `era' field in category `%s'"),
186 (void) strsep (&str, ":");
191 /* Next is the starting date in ISO format. */
192 if (strncmp (str, "-*", 2) == 0)
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;
198 goto garbage_start_date;
201 else if (strncmp (str, "+*", 2) == 0)
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;
207 goto garbage_start_date;
212 time->era_entries[idx].start_date[0] = strtol (str, &endp, 10);
213 if (endp == str || *endp != '/')
214 goto invalid_start_date;
217 time->era_entries[idx].start_date[0] -= 1900;
219 time->era_entries[idx].start_date[1] = strtol (str, &endp, 10);
220 if (endp == str || *endp != '/')
221 goto invalid_start_date;
224 time->era_entries[idx].start_date[1] -= 1;
226 time->era_entries[idx].start_date[2] = strtol (str, &endp, 10);
230 error (0, 0, _("illegal starting date in string %d in"
231 " `era' field in category `%s'"),
233 (void) strsep (&str, ":");
235 else if (*endp != ':')
238 error (0, 0, _("garbage at end of starting date in string %d"
239 " in `era' field in category `%s'"),
241 (void) strsep (&str, ":");
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"
263 /* Next is the stopping date in ISO format. */
264 if (strncmp (str, "-*", 2) == 0)
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;
270 goto garbage_stop_date;
273 else if (strncmp (str, "+*", 2) == 0)
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;
279 goto garbage_stop_date;
284 time->era_entries[idx].stop_date[0] = strtol (str, &endp, 10);
285 if (endp == str || *endp != '/')
286 goto invalid_stop_date;
289 time->era_entries[idx].stop_date[0] -= 1900;
291 time->era_entries[idx].stop_date[1] = strtol (str, &endp, 10);
292 if (endp == str || *endp != '/')
293 goto invalid_stop_date;
296 time->era_entries[idx].stop_date[1] -= 1;
298 time->era_entries[idx].stop_date[2] = strtol (str, &endp, 10);
302 error (0, 0, _("illegal stopping date in string %d in"
303 " `era' field in category `%s'"),
305 (void) strsep (&str, ":");
307 else if (*endp != ':')
310 error (0, 0, _("garbage at end of stopping date in string %d"
311 " in `era' field in category `%s'"),
313 (void) strsep (&str, ":");
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"
335 if (str == NULL || *str == '\0')
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 = "";
344 time->era_entries[idx].name = strsep (&str, ":");
346 if (str == NULL || *str == '\0')
348 error (0, 0, _("missing era format in string %d in `era'"
349 " field in category `%s'"),
351 time->era_entries[idx].name =
352 time->era_entries[idx].format = "";
355 time->era_entries[idx].format = str;
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));
364 for (idx = 0; idx < time->cur_num_era; ++idx)
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;
392 time_output (struct localedef_t *locale, const char *output_path)
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;
404 if ((locale->binary & (1 << LC_TIME)) != 0)
406 iov[0].iov_base = time;
407 iov[0].iov_len = locale->len[LC_TIME];
409 write_locale_data (output_path, "LC_TIME", 1, iov);
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);
419 iov[1].iov_base = (void *) idx;
420 iov[1].iov_len = sizeof (idx);
422 idx[0] = iov[0].iov_len + iov[1].iov_len;
425 for (cnt = 0; cnt <= _NL_ITEM_INDEX (ABDAY_7); ++cnt)
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;
434 for (; cnt <= _NL_ITEM_INDEX (DAY_7); ++cnt)
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;
443 for (; cnt <= _NL_ITEM_INDEX (ABMON_12); ++cnt)
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;
452 for (; cnt <= _NL_ITEM_INDEX (MON_12); ++cnt)
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;
461 for (; cnt <= _NL_ITEM_INDEX (PM_STR); ++cnt)
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;
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;
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;
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;
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;
489 idx[1 + last_idx] = idx[last_idx];
490 for (num = 0; num < time->cur_num_era; ++num, ++cnt)
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;
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;
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;
510 idx[1 + last_idx] = idx[last_idx];
511 for (num = 0; num < time->cur_num_alt_digits; ++num, ++cnt)
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;
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;
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;
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;
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;
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;
551 #if __BYTE_ORDER == __LITTLE_ENDIAN
552 # define ERA_B1 time->era_entries_ob
553 # define ERA_B2 time->era_entries
555 # define ERA_B1 time->era_entries
556 # define ERA_B2 time->era_entries_ob
558 idx[1 + last_idx] = idx[last_idx];
559 for (num = 0; num < time->cur_num_era; ++num)
563 iov[2 + cnt].iov_base = (void *) &ERA_B1[num].direction;
564 iov[2 + cnt].iov_len = sizeof (int32_t);
566 iov[2 + cnt].iov_base = (void *) &ERA_B1[num].offset;
567 iov[2 + cnt].iov_len = sizeof (int32_t);
569 iov[2 + cnt].iov_base = (void *) &ERA_B1[num].start_date[0];
570 iov[2 + cnt].iov_len = sizeof (int32_t);
572 iov[2 + cnt].iov_base = (void *) &ERA_B1[num].start_date[1];
573 iov[2 + cnt].iov_len = sizeof (int32_t);
575 iov[2 + cnt].iov_base = (void *) &ERA_B1[num].start_date[2];
576 iov[2 + cnt].iov_len = sizeof (int32_t);
578 iov[2 + cnt].iov_base = (void *) &ERA_B1[num].stop_date[0];
579 iov[2 + cnt].iov_len = sizeof (int32_t);
581 iov[2 + cnt].iov_base = (void *) &ERA_B1[num].stop_date[1];
582 iov[2 + cnt].iov_len = sizeof (int32_t);
584 iov[2 + cnt].iov_base = (void *) &ERA_B1[num].stop_date[2];
585 iov[2 + cnt].iov_len = sizeof (int32_t);
588 l = (strchr (ERA_B1[num].format, '\0') - ERA_B1[num].name) + 1;
590 iov[2 + cnt].iov_base = (void *) ERA_B1[num].name;
591 iov[2 + cnt].iov_len = l;
594 idx[1 + last_idx] += 8 * sizeof (int32_t) + l;
596 assert (idx[1 + last_idx] % 4 == 0);
600 /* idx[1 + last_idx] = idx[last_idx]; */
601 for (num = 0; num < time->cur_num_era; ++num)
605 iov[2 + cnt].iov_base = (void *) &ERA_B2[num].direction;
606 iov[2 + cnt].iov_len = sizeof (int32_t);
608 iov[2 + cnt].iov_base = (void *) &ERA_B2[num].offset;
609 iov[2 + cnt].iov_len = sizeof (int32_t);
611 iov[2 + cnt].iov_base = (void *) &ERA_B2[num].start_date[0];
612 iov[2 + cnt].iov_len = sizeof (int32_t);
614 iov[2 + cnt].iov_base = (void *) &ERA_B2[num].start_date[1];
615 iov[2 + cnt].iov_len = sizeof (int32_t);
617 iov[2 + cnt].iov_base = (void *) &ERA_B2[num].start_date[2];
618 iov[2 + cnt].iov_len = sizeof (int32_t);
620 iov[2 + cnt].iov_base = (void *) &ERA_B2[num].stop_date[0];
621 iov[2 + cnt].iov_len = sizeof (int32_t);
623 iov[2 + cnt].iov_base = (void *) &ERA_B2[num].stop_date[1];
624 iov[2 + cnt].iov_len = sizeof (int32_t);
626 iov[2 + cnt].iov_base = (void *) &ERA_B2[num].stop_date[2];
627 iov[2 + cnt].iov_len = sizeof (int32_t);
630 l = (strchr (ERA_B2[num].format, '\0') - ERA_B2[num].name) + 1;
632 iov[2 + cnt].iov_base = (void *) ERA_B2[num].name;
633 iov[2 + cnt].iov_len = l;
636 /* idx[1 + last_idx] += 8 * sizeof (int32_t) + l; */
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)
645 static u_int32_t dummy = 0;
646 iov[2 + cnt].iov_base = (void *) &dummy;
647 iov[2 + cnt].iov_len = 4;
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));
658 write_locale_data (output_path, "LC_TIME", 2 + cnt, iov);
663 time_add (struct linereader *lr, struct localedef_t *locale,
664 enum token_t tok, struct token *code,
665 struct charset_t *charset)
667 struct locale_time_t *time = locale->categories[LC_TIME].time;
671 #define STRARR_ELEM(cat, max) \
673 if (time->cur_num_##cat >= max) \
675 too many values for field `%s' in category `%s'"), \
677 else if (code->val.str.start == NULL) \
679 lr_error (lr, _("unknown character in field `%s' of category `%s'"),\
681 time->cat[time->cur_num_##cat++] = ""; \
684 time->cat[time->cur_num_##cat++] = code->val.str.start; \
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);
695 if (code->val.str.start == NULL)
696 lr_error (lr, _("unknown character in field `%s' of category `%s'"),
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;
707 #define STR_ELEM(cat) \
709 if (time->cat != NULL) \
711 field `%s' in category `%s' declared more than once"), \
713 else if (code->val.str.start == NULL) \
715 lr_error (lr, _("unknown character in field `%s' of category `%s'"),\
720 time->cat = code->val.str.start; \
726 STR_ELEM (t_fmt_ampm);
728 STR_ELEM (era_d_t_fmt);
729 STR_ELEM (era_d_fmt);
730 STR_ELEM (era_t_fmt);
733 assert (! "unknown token in category `LC_TIME': should not happen");