1 /* Copyright (C) 1995-2018 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Ulrich Drepper <drepper@gnu.org>, 1995.
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; version 2 of the License, or
8 (at your option) any later version.
10 This program 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
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, see <http://www.gnu.org/licenses/>. */
32 #include "localedef.h"
33 #include "linereader.h"
34 #include "localeinfo.h"
38 /* Entry describing an entry of the era specification. */
43 int32_t start_date
[3];
52 /* The real definition of the struct for the LC_TIME locale. */
56 const uint32_t *wabday
[7];
59 const uint32_t *wday
[7];
61 const char *abmon
[12];
62 const uint32_t *wabmon
[12];
65 const uint32_t *wmon
[12];
68 const uint32_t *wam_pm
[2];
71 const uint32_t *wd_t_fmt
;
73 const uint32_t *wd_fmt
;
75 const uint32_t *wt_fmt
;
76 const char *t_fmt_ampm
;
77 const uint32_t *wt_fmt_ampm
;
79 const uint32_t **wera
;
82 const uint32_t *wera_year
;
83 const char *era_d_t_fmt
;
84 const uint32_t *wera_d_t_fmt
;
85 const char *era_t_fmt
;
86 const uint32_t *wera_t_fmt
;
87 const char *era_d_fmt
;
88 const uint32_t *wera_d_fmt
;
89 const char *alt_digits
[100];
90 const uint32_t *walt_digits
[100];
92 const uint32_t *wdate_fmt
;
93 int alt_digits_defined
;
94 const char *alt_mon
[12];
95 const uint32_t *walt_mon
[12];
97 const char *ab_alt_mon
[12];
98 const uint32_t *wab_alt_mon
[12];
99 int ab_alt_mon_defined
;
100 unsigned char week_ndays
;
101 uint32_t week_1stday
;
102 unsigned char week_1stweek
;
103 unsigned char first_weekday
;
104 unsigned char first_workday
;
105 unsigned char cal_direction
;
106 const char *timezone
;
107 const uint32_t *wtimezone
;
109 struct era_data
*era_entries
;
113 /* This constant is used to represent an empty wide character string. */
114 static const uint32_t empty_wstr
[1] = { 0 };
118 time_startup (struct linereader
*lr
, struct localedef_t
*locale
,
122 locale
->categories
[LC_TIME
].time
=
123 (struct locale_time_t
*) xcalloc (1, sizeof (struct locale_time_t
));
127 lr
->translate_strings
= 1;
128 lr
->return_widestr
= 1;
134 time_finish (struct localedef_t
*locale
, const struct charmap_t
*charmap
)
136 struct locale_time_t
*time
= locale
->categories
[LC_TIME
].time
;
139 /* Now resolve copying and also handle completely missing definitions. */
142 /* First see whether we were supposed to copy. If yes, find the
143 actual definition. */
144 if (locale
->copy_name
[LC_TIME
] != NULL
)
146 /* Find the copying locale. This has to happen transitively since
147 the locale we are copying from might also copying another one. */
148 struct localedef_t
*from
= locale
;
151 from
= find_locale (LC_TIME
, from
->copy_name
[LC_TIME
],
152 from
->repertoire_name
, charmap
);
153 while (from
->categories
[LC_TIME
].time
== NULL
154 && from
->copy_name
[LC_TIME
] != NULL
);
156 time
= locale
->categories
[LC_TIME
].time
157 = from
->categories
[LC_TIME
].time
;
160 /* If there is still no definition issue an warning and create an
165 No definition for %s category found"), "LC_TIME");
166 time_startup (NULL
, locale
, 0);
167 time
= locale
->categories
[LC_TIME
].time
;
172 #define noparen(arg1, argn...) arg1, ##argn
173 #define TESTARR_ELEM(cat, val) \
174 if (!time->cat##_defined) \
176 const char *initval[] = { noparen val }; \
180 record_error (0, 0, _("%s: field `%s' not defined"), \
183 for (i = 0; i < sizeof (initval) / sizeof (initval[0]); ++i) \
184 time->cat[i] = initval[i]; \
187 TESTARR_ELEM (abday
, ( "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ));
188 TESTARR_ELEM (day
, ( "Sunday", "Monday", "Tuesday", "Wednesday",
189 "Thursday", "Friday", "Saturday" ));
190 TESTARR_ELEM (abmon
, ( "Jan", "Feb", "Mar", "Apr", "May", "Jun",
191 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ));
192 TESTARR_ELEM (mon
, ( "January", "February", "March", "April",
193 "May", "June", "July", "August",
194 "September", "October", "November", "December" ));
195 TESTARR_ELEM (am_pm
, ( "AM", "PM" ));
197 #define TEST_ELEM(cat, initval) \
198 if (time->cat == NULL) \
201 record_error (0, 0, _("%s: field `%s' not defined"), \
204 time->cat = initval; \
207 TEST_ELEM (d_t_fmt
, "%a %b %e %H:%M:%S %Y");
208 TEST_ELEM (d_fmt
, "%m/%d/%y");
209 TEST_ELEM (t_fmt
, "%H:%M:%S");
211 /* According to C.Y.Alexis Cheng <alexis@vnet.ibm.com> the T_FMT_AMPM
212 field is optional. */
213 if (time
->t_fmt_ampm
== NULL
)
215 if (time
->am_pm
[0][0] == '\0' && time
->am_pm
[1][0] == '\0')
217 /* No AM/PM strings defined, use the 24h format as default. */
218 time
->t_fmt_ampm
= time
->t_fmt
;
219 time
->wt_fmt_ampm
= time
->wt_fmt
;
223 time
->t_fmt_ampm
= "%I:%M:%S %p";
224 time
->wt_fmt_ampm
= (const uint32_t *) L
"%I:%M:%S %p";
228 /* Now process the era entries. */
229 if (time
->num_era
!= 0)
231 const int days_per_month
[12] = { 31, 29, 31, 30, 31, 30,
232 31, 31, 30, 31 ,30, 31 };
237 (struct era_data
*) xmalloc (time
->num_era
238 * sizeof (struct era_data
));
240 for (idx
= 0; idx
< time
->num_era
; ++idx
)
242 size_t era_len
= strlen (time
->era
[idx
]);
243 char *str
= xmalloc ((era_len
+ 1 + 3) & ~3);
246 memcpy (str
, time
->era
[idx
], era_len
+ 1);
248 /* First character must be + or - for the direction. */
249 if (*str
!= '+' && *str
!= '-')
251 record_error (0, 0, _("\
252 %s: direction flag in string %Zd in `era' field is not '+' nor '-'"),
254 /* Default arbitrarily to '+'. */
255 time
->era_entries
[idx
].direction
= '+';
258 time
->era_entries
[idx
].direction
= *str
;
261 record_error (0, 0, _("\
262 %s: direction flag in string %Zd in `era' field is not a single character"),
264 (void) strsep (&str
, ":");
269 /* Now the offset year. */
270 time
->era_entries
[idx
].offset
= strtol (str
, &endp
, 10);
273 record_error (0, 0, _("\
274 %s: invalid number for offset in string %Zd in `era' field"),
276 (void) strsep (&str
, ":");
278 else if (*endp
!= ':')
280 record_error (0, 0, _("\
281 %s: garbage at end of offset value in string %Zd in `era' field"),
283 (void) strsep (&str
, ":");
288 /* Next is the starting date in ISO format. */
289 if (strncmp (str
, "-*", 2) == 0)
291 time
->era_entries
[idx
].start_date
[0] =
292 time
->era_entries
[idx
].start_date
[1] =
293 time
->era_entries
[idx
].start_date
[2] = 0x80000000;
295 goto garbage_start_date
;
298 else if (strncmp (str
, "+*", 2) == 0)
300 time
->era_entries
[idx
].start_date
[0] =
301 time
->era_entries
[idx
].start_date
[1] =
302 time
->era_entries
[idx
].start_date
[2] = 0x7fffffff;
304 goto garbage_start_date
;
309 time
->era_entries
[idx
].start_date
[0] = strtol (str
, &endp
, 10);
310 if (endp
== str
|| *endp
!= '/')
311 goto invalid_start_date
;
314 time
->era_entries
[idx
].start_date
[0] -= 1900;
315 /* year -1 represent 1 B.C. (not -1 A.D.) */
316 if (time
->era_entries
[idx
].start_date
[0] < -1900)
317 ++time
->era_entries
[idx
].start_date
[0];
319 time
->era_entries
[idx
].start_date
[1] = strtol (str
, &endp
, 10);
320 if (endp
== str
|| *endp
!= '/')
321 goto invalid_start_date
;
324 time
->era_entries
[idx
].start_date
[1] -= 1;
326 time
->era_entries
[idx
].start_date
[2] = strtol (str
, &endp
, 10);
330 record_error (0, 0, _("\
331 %s: invalid starting date in string %Zd in `era' field"),
333 (void) strsep (&str
, ":");
335 else if (*endp
!= ':')
338 record_error (0, 0, _("\
339 %s: garbage at end of starting date in string %Zd in `era' field "),
341 (void) strsep (&str
, ":");
347 /* Check for valid value. */
348 if ((time
->era_entries
[idx
].start_date
[1] < 0
349 || time
->era_entries
[idx
].start_date
[1] >= 12
350 || time
->era_entries
[idx
].start_date
[2] < 0
351 || (time
->era_entries
[idx
].start_date
[2]
352 > days_per_month
[time
->era_entries
[idx
].start_date
[1]])
353 || (time
->era_entries
[idx
].start_date
[1] == 2
354 && time
->era_entries
[idx
].start_date
[2] == 29
355 && !__isleap (time
->era_entries
[idx
].start_date
[0]))))
356 record_error (0, 0, _("\
357 %s: starting date is invalid in string %Zd in `era' field"),
362 /* Next is the stopping date in ISO format. */
363 if (strncmp (str
, "-*", 2) == 0)
365 time
->era_entries
[idx
].stop_date
[0] =
366 time
->era_entries
[idx
].stop_date
[1] =
367 time
->era_entries
[idx
].stop_date
[2] = 0x80000000;
369 goto garbage_stop_date
;
372 else if (strncmp (str
, "+*", 2) == 0)
374 time
->era_entries
[idx
].stop_date
[0] =
375 time
->era_entries
[idx
].stop_date
[1] =
376 time
->era_entries
[idx
].stop_date
[2] = 0x7fffffff;
378 goto garbage_stop_date
;
383 time
->era_entries
[idx
].stop_date
[0] = strtol (str
, &endp
, 10);
384 if (endp
== str
|| *endp
!= '/')
385 goto invalid_stop_date
;
388 time
->era_entries
[idx
].stop_date
[0] -= 1900;
389 /* year -1 represent 1 B.C. (not -1 A.D.) */
390 if (time
->era_entries
[idx
].stop_date
[0] < -1900)
391 ++time
->era_entries
[idx
].stop_date
[0];
393 time
->era_entries
[idx
].stop_date
[1] = strtol (str
, &endp
, 10);
394 if (endp
== str
|| *endp
!= '/')
395 goto invalid_stop_date
;
398 time
->era_entries
[idx
].stop_date
[1] -= 1;
400 time
->era_entries
[idx
].stop_date
[2] = strtol (str
, &endp
, 10);
404 record_error (0, 0, _("\
405 %s: invalid stopping date in string %Zd in `era' field"),
407 (void) strsep (&str
, ":");
409 else if (*endp
!= ':')
412 record_error (0, 0, _("\
413 %s: garbage at end of stopping date in string %Zd in `era' field"),
415 (void) strsep (&str
, ":");
421 /* Check for valid value. */
422 if ((time
->era_entries
[idx
].stop_date
[1] < 0
423 || time
->era_entries
[idx
].stop_date
[1] >= 12
424 || time
->era_entries
[idx
].stop_date
[2] < 0
425 || (time
->era_entries
[idx
].stop_date
[2]
426 > days_per_month
[time
->era_entries
[idx
].stop_date
[1]])
427 || (time
->era_entries
[idx
].stop_date
[1] == 2
428 && time
->era_entries
[idx
].stop_date
[2] == 29
429 && !__isleap (time
->era_entries
[idx
].stop_date
[0]))))
430 record_error (0, 0, _("\
431 %s: invalid stopping date in string %Zd in `era' field"),
436 if (str
== NULL
|| *str
== '\0')
438 record_error (0, 0, _("\
439 %s: missing era name in string %Zd in `era' field"), "LC_TIME", idx
+ 1);
440 time
->era_entries
[idx
].name
=
441 time
->era_entries
[idx
].format
= "";
445 time
->era_entries
[idx
].name
= strsep (&str
, ":");
447 if (str
== NULL
|| *str
== '\0')
449 record_error (0, 0, _("\
450 %s: missing era format in string %Zd in `era' field"),
452 time
->era_entries
[idx
].name
=
453 time
->era_entries
[idx
].format
= "";
456 time
->era_entries
[idx
].format
= str
;
459 /* Now generate the wide character name and format. */
460 wstr
= wcschr ((wchar_t *) time
->wera
[idx
], L
':');/* end direction */
461 wstr
= wstr
? wcschr (wstr
+ 1, L
':') : NULL
; /* end offset */
462 wstr
= wstr
? wcschr (wstr
+ 1, L
':') : NULL
; /* end start */
463 wstr
= wstr
? wcschr (wstr
+ 1, L
':') : NULL
; /* end end */
466 time
->era_entries
[idx
].wname
= (uint32_t *) wstr
+ 1;
467 wstr
= wcschr (wstr
+ 1, L
':'); /* end name */
471 time
->era_entries
[idx
].wformat
= (uint32_t *) wstr
+ 1;
474 time
->era_entries
[idx
].wname
=
475 time
->era_entries
[idx
].wformat
= (uint32_t *) L
"";
478 time
->era_entries
[idx
].wname
=
479 time
->era_entries
[idx
].wformat
= (uint32_t *) L
"";
483 /* Set up defaults based on ISO 30112 WD10 [2014]. */
484 if (time
->week_ndays
== 0)
485 time
->week_ndays
= 7;
487 if (time
->week_1stday
== 0)
488 time
->week_1stday
= 19971130;
490 if (time
->week_1stweek
== 0)
491 time
->week_1stweek
= 7;
493 if (time
->week_1stweek
> time
->week_ndays
)
494 record_error (0, 0, _("\
495 %s: third operand for value of field `%s' must not be larger than %d"),
496 "LC_TIME", "week", 7);
498 if (time
->first_weekday
== '\0')
499 /* The definition does not specify this so the default is used. */
500 time
->first_weekday
= 1;
501 else if (time
->first_weekday
> time
->week_ndays
)
502 record_error (0, 0, _("\
503 %s: values for field `%s' must not be larger than %d"),
504 "LC_TIME", "first_weekday", 7);
506 if (time
->first_workday
== '\0')
507 /* The definition does not specify this so the default is used. */
508 time
->first_workday
= 2;
509 else if (time
->first_workday
> time
->week_ndays
)
510 record_error (0, 0, _("\
511 %s: values for field `%s' must not be larger than %d"),
512 "LC_TIME", "first_workday", 7);
514 if (time
->cal_direction
== '\0')
515 /* The definition does not specify this so the default is used. */
516 time
->cal_direction
= 1;
517 else if (time
->cal_direction
> 3)
518 record_error (0, 0, _("\
519 %s: values for field `%s' must not be larger than %d"),
520 "LC_TIME", "cal_direction", 3);
522 /* XXX We don't perform any tests on the timezone value since this is
523 simply useless, stupid $&$!@... */
524 if (time
->timezone
== NULL
)
527 if (time
->date_fmt
== NULL
)
528 time
->date_fmt
= "%a %b %e %H:%M:%S %Z %Y";
529 if (time
->wdate_fmt
== NULL
)
530 time
->wdate_fmt
= (const uint32_t *) L
"%a %b %e %H:%M:%S %Z %Y";
535 time_output (struct localedef_t
*locale
, const struct charmap_t
*charmap
,
536 const char *output_path
)
538 struct locale_time_t
*time
= locale
->categories
[LC_TIME
].time
;
539 struct locale_file file
;
542 init_locale_data (&file
, _NL_ITEM_INDEX (_NL_NUM_LC_TIME
));
545 for (n
= 0; n
< 7; ++n
)
546 add_locale_string (&file
, time
->abday
[n
] ?: "");
549 for (n
= 0; n
< 7; ++n
)
550 add_locale_string (&file
, time
->day
[n
] ?: "");
553 for (n
= 0; n
< 12; ++n
)
554 add_locale_string (&file
, time
->abmon
[n
] ?: "");
557 for (n
= 0; n
< 12; ++n
)
558 add_locale_string (&file
, time
->mon
[n
] ?: "");
561 for (n
= 0; n
< 2; ++n
)
562 add_locale_string (&file
, time
->am_pm
[n
]);
564 add_locale_string (&file
, time
->d_t_fmt
?: "");
565 add_locale_string (&file
, time
->d_fmt
?: "");
566 add_locale_string (&file
, time
->t_fmt
?: "");
567 add_locale_string (&file
, time
->t_fmt_ampm
?: "");
569 start_locale_structure (&file
);
570 for (num
= 0; num
< time
->num_era
; ++num
)
571 add_locale_string (&file
, time
->era
[num
]);
572 end_locale_structure (&file
);
574 add_locale_string (&file
, time
->era_year
?: "");
575 add_locale_string (&file
, time
->era_d_fmt
?: "");
577 start_locale_structure (&file
);
578 for (num
= 0; num
< 100; ++num
)
579 add_locale_string (&file
, time
->alt_digits
[num
] ?: "");
580 end_locale_structure (&file
);
582 add_locale_string (&file
, time
->era_d_t_fmt
?: "");
583 add_locale_string (&file
, time
->era_t_fmt
?: "");
584 add_locale_uint32 (&file
, time
->num_era
);
586 start_locale_structure (&file
);
587 for (num
= 0; num
< time
->num_era
; ++num
)
589 add_locale_uint32 (&file
, time
->era_entries
[num
].direction
);
590 add_locale_uint32 (&file
, time
->era_entries
[num
].offset
);
591 add_locale_uint32 (&file
, time
->era_entries
[num
].start_date
[0]);
592 add_locale_uint32 (&file
, time
->era_entries
[num
].start_date
[1]);
593 add_locale_uint32 (&file
, time
->era_entries
[num
].start_date
[2]);
594 add_locale_uint32 (&file
, time
->era_entries
[num
].stop_date
[0]);
595 add_locale_uint32 (&file
, time
->era_entries
[num
].stop_date
[1]);
596 add_locale_uint32 (&file
, time
->era_entries
[num
].stop_date
[2]);
597 add_locale_string (&file
, time
->era_entries
[num
].name
);
598 add_locale_string (&file
, time
->era_entries
[num
].format
);
599 add_locale_wstring (&file
, time
->era_entries
[num
].wname
);
600 add_locale_wstring (&file
, time
->era_entries
[num
].wformat
);
602 end_locale_structure (&file
);
604 /* The wide character ab'days. */
605 for (n
= 0; n
< 7; ++n
)
606 add_locale_wstring (&file
, time
->wabday
[n
] ?: empty_wstr
);
608 /* The wide character days. */
609 for (n
= 0; n
< 7; ++n
)
610 add_locale_wstring (&file
, time
->wday
[n
] ?: empty_wstr
);
612 /* The wide character ab'mons. */
613 for (n
= 0; n
< 12; ++n
)
614 add_locale_wstring (&file
, time
->wabmon
[n
] ?: empty_wstr
);
616 /* The wide character mons. */
617 for (n
= 0; n
< 12; ++n
)
618 add_locale_wstring (&file
, time
->wmon
[n
] ?: empty_wstr
);
620 /* Wide character AM/PM. */
621 for (n
= 0; n
< 2; ++n
)
622 add_locale_wstring (&file
, time
->wam_pm
[n
] ?: empty_wstr
);
624 add_locale_wstring (&file
, time
->wd_t_fmt
?: empty_wstr
);
625 add_locale_wstring (&file
, time
->wd_fmt
?: empty_wstr
);
626 add_locale_wstring (&file
, time
->wt_fmt
?: empty_wstr
);
627 add_locale_wstring (&file
, time
->wt_fmt_ampm
?: empty_wstr
);
628 add_locale_wstring (&file
, time
->wera_year
?: empty_wstr
);
629 add_locale_wstring (&file
, time
->wera_d_fmt
?: empty_wstr
);
631 start_locale_structure (&file
);
632 for (num
= 0; num
< 100; ++num
)
633 add_locale_wstring (&file
, time
->walt_digits
[num
] ?: empty_wstr
);
634 end_locale_structure (&file
);
636 add_locale_wstring (&file
, time
->wera_d_t_fmt
?: empty_wstr
);
637 add_locale_wstring (&file
, time
->wera_t_fmt
?: empty_wstr
);
638 add_locale_char (&file
, time
->week_ndays
);
639 add_locale_uint32 (&file
, time
->week_1stday
);
640 add_locale_char (&file
, time
->week_1stweek
);
641 add_locale_char (&file
, time
->first_weekday
);
642 add_locale_char (&file
, time
->first_workday
);
643 add_locale_char (&file
, time
->cal_direction
);
644 add_locale_string (&file
, time
->timezone
);
645 add_locale_string (&file
, time
->date_fmt
);
646 add_locale_wstring (&file
, time
->wdate_fmt
);
647 add_locale_string (&file
, charmap
->code_set_name
);
650 for (n
= 0; n
< 12; ++n
)
651 add_locale_string (&file
, time
->alt_mon
[n
] ?: "");
653 /* The wide character alt'mons. */
654 for (n
= 0; n
< 12; ++n
)
655 add_locale_wstring (&file
, time
->walt_mon
[n
] ?: empty_wstr
);
657 /* The ab'alt'mons. */
658 for (n
= 0; n
< 12; ++n
)
659 add_locale_string (&file
, time
->ab_alt_mon
[n
] ?: "");
661 /* The wide character ab'alt'mons. */
662 for (n
= 0; n
< 12; ++n
)
663 add_locale_wstring (&file
, time
->wab_alt_mon
[n
] ?: empty_wstr
);
665 write_locale_data (output_path
, LC_TIME
, "LC_TIME", &file
);
669 /* The parser for the LC_TIME section of the locale definition. */
671 time_read (struct linereader
*ldfile
, struct localedef_t
*result
,
672 const struct charmap_t
*charmap
, const char *repertoire_name
,
675 struct repertoire_t
*repertoire
= NULL
;
676 struct locale_time_t
*time
;
681 /* Get the repertoire we have to use. */
682 if (repertoire_name
!= NULL
)
683 repertoire
= repertoire_read (repertoire_name
);
685 /* The rest of the line containing `LC_TIME' must be free. */
686 lr_ignore_rest (ldfile
, 1);
691 now
= lr_token (ldfile
, charmap
, result
, repertoire
, verbose
);
694 while (nowtok
== tok_eol
);
696 /* If we see `copy' now we are almost done. */
697 if (nowtok
== tok_copy
)
699 handle_copy (ldfile
, charmap
, repertoire_name
, result
, tok_lc_time
,
700 LC_TIME
, "LC_TIME", ignore_content
);
704 /* Prepare the data structures. */
705 time_startup (ldfile
, result
, ignore_content
);
706 time
= result
->categories
[LC_TIME
].time
;
710 /* Of course we don't proceed beyond the end of file. */
711 if (nowtok
== tok_eof
)
714 /* Ingore empty lines. */
715 if (nowtok
== tok_eol
)
717 now
= lr_token (ldfile
, charmap
, result
, repertoire
, verbose
);
724 #define STRARR_ELEM(cat, min, max) \
726 /* Ignore the rest of the line if we don't need the input of \
728 if (ignore_content) \
730 lr_ignore_rest (ldfile, 0); \
734 for (cnt = 0; cnt < max; ++cnt) \
736 now = lr_token (ldfile, charmap, result, repertoire, verbose); \
737 if (now->tok == tok_eol) \
740 lr_error (ldfile, _("%s: too few values for field `%s'"), \
742 if (!ignore_content) \
745 time->cat[cnt] = ""; \
746 time->w##cat[cnt] = empty_wstr; \
748 while (++cnt < max); \
751 else if (now->tok != tok_string) \
753 else if (!ignore_content && (now->val.str.startmb == NULL \
754 || now->val.str.startwc == NULL)) \
756 lr_error (ldfile, _("%s: unknown character in field `%s'"), \
758 time->cat[cnt] = ""; \
759 time->w##cat[cnt] = empty_wstr; \
761 else if (!ignore_content) \
763 time->cat[cnt] = now->val.str.startmb; \
764 time->w##cat[cnt] = now->val.str.startwc; \
767 /* Match the semicolon. */ \
768 now = lr_token (ldfile, charmap, result, repertoire, verbose); \
769 if (now->tok != tok_semicolon && now->tok != tok_eol) \
772 if (now->tok != tok_eol) \
774 while (!ignore_content && cnt < min) \
776 time->cat[cnt] = ""; \
777 time->w##cat[cnt++] = empty_wstr; \
780 if (now->tok == tok_semicolon) \
782 now = lr_token (ldfile, charmap, result, repertoire, \
784 if (now->tok == tok_eol) \
785 lr_error (ldfile, _("extra trailing semicolon")); \
786 else if (now->tok == tok_string) \
788 lr_error (ldfile, _("\
789 %s: too many values for field `%s'"), \
791 lr_ignore_rest (ldfile, 0); \
799 time->cat##_defined = 1; \
802 STRARR_ELEM (abday
, 7, 7);
803 STRARR_ELEM (day
, 7, 7);
804 STRARR_ELEM (abmon
, 12, 12);
805 STRARR_ELEM (mon
, 12, 12);
806 STRARR_ELEM (am_pm
, 2, 2);
807 STRARR_ELEM (alt_digits
, 0, 100);
808 STRARR_ELEM (alt_mon
, 12, 12);
809 STRARR_ELEM (ab_alt_mon
, 12, 12);
812 /* Ignore the rest of the line if we don't need the input of
816 lr_ignore_rest (ldfile
, 0);
821 now
= lr_token (ldfile
, charmap
, result
, repertoire
, verbose
);
822 if (now
->tok
!= tok_string
)
824 if (!ignore_content
&& (now
->val
.str
.startmb
== NULL
825 || now
->val
.str
.startwc
== NULL
))
827 lr_error (ldfile
, _("%s: unknown character in field `%s'"),
829 lr_ignore_rest (ldfile
, 0);
834 time
->era
= xrealloc (time
->era
,
835 (time
->num_era
+ 1) * sizeof (char *));
836 time
->era
[time
->num_era
] = now
->val
.str
.startmb
;
838 time
->wera
= xrealloc (time
->wera
,
841 time
->wera
[time
->num_era
++] = now
->val
.str
.startwc
;
843 now
= lr_token (ldfile
, charmap
, result
, repertoire
, verbose
);
844 if (now
->tok
!= tok_eol
&& now
->tok
!= tok_semicolon
)
847 while (now
->tok
== tok_semicolon
);
850 #define STR_ELEM(cat) \
852 /* Ignore the rest of the line if we don't need the input of \
854 if (ignore_content) \
856 lr_ignore_rest (ldfile, 0); \
860 now = lr_token (ldfile, charmap, result, repertoire, verbose); \
861 if (now->tok != tok_string) \
863 else if (time->cat != NULL) \
864 lr_error (ldfile, _("\
865 %s: field `%s' declared more than once"), "LC_TIME", #cat); \
866 else if (!ignore_content && (now->val.str.startmb == NULL \
867 || now->val.str.startwc == NULL)) \
869 lr_error (ldfile, _("%s: unknown character in field `%s'"), \
872 time->w##cat = empty_wstr; \
874 else if (!ignore_content) \
876 time->cat = now->val.str.startmb; \
877 time->w##cat = now->val.str.startwc; \
884 STR_ELEM (t_fmt_ampm
);
886 STR_ELEM (era_d_t_fmt
);
887 STR_ELEM (era_d_fmt
);
888 STR_ELEM (era_t_fmt
);
892 #define INT_ELEM(cat) \
894 /* Ignore the rest of the line if we don't need the input of \
896 if (ignore_content) \
898 lr_ignore_rest (ldfile, 0); \
902 now = lr_token (ldfile, charmap, result, repertoire, verbose); \
903 if (now->tok != tok_number) \
905 else if (time->cat != 0) \
906 lr_error (ldfile, _("%s: field `%s' declared more than once"), \
908 else if (!ignore_content) \
909 time->cat = now->val.num; \
912 INT_ELEM (first_weekday
);
913 INT_ELEM (first_workday
);
914 INT_ELEM (cal_direction
);
917 /* Ignore the rest of the line if we don't need the input of
921 lr_ignore_rest (ldfile
, 0);
925 now
= lr_token (ldfile
, charmap
, result
, repertoire
, verbose
);
926 if (now
->tok
!= tok_number
)
928 time
->week_ndays
= now
->val
.num
;
930 now
= lr_token (ldfile
, charmap
, result
, repertoire
, verbose
);
931 if (now
->tok
!= tok_semicolon
)
934 now
= lr_token (ldfile
, charmap
, result
, repertoire
, verbose
);
935 if (now
->tok
!= tok_number
)
937 time
->week_1stday
= now
->val
.num
;
939 now
= lr_token (ldfile
, charmap
, result
, repertoire
, verbose
);
940 if (now
->tok
!= tok_semicolon
)
943 now
= lr_token (ldfile
, charmap
, result
, repertoire
, verbose
);
944 if (now
->tok
!= tok_number
)
946 time
->week_1stweek
= now
->val
.num
;
948 lr_ignore_rest (ldfile
, 1);
952 /* Next we assume `LC_TIME'. */
953 now
= lr_token (ldfile
, charmap
, result
, repertoire
, verbose
);
954 if (now
->tok
== tok_eof
)
956 if (now
->tok
== tok_eol
)
957 lr_error (ldfile
, _("%s: incomplete `END' line"), "LC_TIME");
958 else if (now
->tok
!= tok_lc_time
)
959 lr_error (ldfile
, _("\
960 %1$s: definition does not end with `END %1$s'"), "LC_TIME");
961 lr_ignore_rest (ldfile
, now
->tok
== tok_lc_time
);
963 /* If alt_mon was not specified, make it a copy of mon. */
964 if (!ignore_content
&& !time
->alt_mon_defined
)
966 memcpy (time
->alt_mon
, time
->mon
, sizeof (time
->mon
));
967 memcpy (time
->walt_mon
, time
->wmon
, sizeof (time
->wmon
));
968 time
->alt_mon_defined
= 1;
970 /* The same for abbreviated versions. */
971 if (!ignore_content
&& !time
->ab_alt_mon_defined
)
973 memcpy (time
->ab_alt_mon
, time
->abmon
, sizeof (time
->abmon
));
974 memcpy (time
->wab_alt_mon
, time
->wabmon
, sizeof (time
->wabmon
));
975 time
->ab_alt_mon_defined
= 1;
981 SYNTAX_ERROR (_("%s: syntax error"), "LC_TIME");
984 /* Prepare for the next round. */
985 now
= lr_token (ldfile
, charmap
, result
, repertoire
, verbose
);
989 /* When we come here we reached the end of the file. */
990 lr_error (ldfile
, _("%s: premature end of file"), "LC_TIME");