| xf.li | bdd93d5 | 2023-05-12 07:10:14 -0700 | [diff] [blame] | 1 | /* Copyright (C) 1995-2016 Free Software Foundation, Inc. | 
|  | 2 | This file is part of the GNU C Library. | 
|  | 3 | Contributed by Ulrich Drepper <drepper@gnu.org>, 1995. | 
|  | 4 |  | 
|  | 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. | 
|  | 9 |  | 
|  | 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. | 
|  | 14 |  | 
|  | 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/>.  */ | 
|  | 17 |  | 
|  | 18 | #ifdef HAVE_CONFIG_H | 
|  | 19 | # include <config.h> | 
|  | 20 | #endif | 
|  | 21 |  | 
|  | 22 | #include <byteswap.h> | 
|  | 23 | #include <langinfo.h> | 
|  | 24 | #include <stdlib.h> | 
|  | 25 | #include <string.h> | 
|  | 26 | #include <wchar.h> | 
|  | 27 | #include <stdint.h> | 
|  | 28 | #include <sys/uio.h> | 
|  | 29 |  | 
|  | 30 | #include <assert.h> | 
|  | 31 |  | 
|  | 32 | #include "localedef.h" | 
|  | 33 | #include "linereader.h" | 
|  | 34 | #include "localeinfo.h" | 
|  | 35 | #include "locfile.h" | 
|  | 36 |  | 
|  | 37 |  | 
|  | 38 | /* Entry describing an entry of the era specification.  */ | 
|  | 39 | struct era_data | 
|  | 40 | { | 
|  | 41 | int32_t direction; | 
|  | 42 | int32_t offset; | 
|  | 43 | int32_t start_date[3]; | 
|  | 44 | int32_t stop_date[3]; | 
|  | 45 | const char *name; | 
|  | 46 | const char *format; | 
|  | 47 | uint32_t *wname; | 
|  | 48 | uint32_t *wformat; | 
|  | 49 | }; | 
|  | 50 |  | 
|  | 51 |  | 
|  | 52 | /* The real definition of the struct for the LC_TIME locale.  */ | 
|  | 53 | struct locale_time_t | 
|  | 54 | { | 
|  | 55 | const char *abday[7]; | 
|  | 56 | const uint32_t *wabday[7]; | 
|  | 57 | int abday_defined; | 
|  | 58 | const char *day[7]; | 
|  | 59 | const uint32_t *wday[7]; | 
|  | 60 | int day_defined; | 
|  | 61 | const char *abmon[12]; | 
|  | 62 | const uint32_t *wabmon[12]; | 
|  | 63 | int abmon_defined; | 
|  | 64 | const char *mon[12]; | 
|  | 65 | const uint32_t *wmon[12]; | 
|  | 66 | int mon_defined; | 
|  | 67 | const char *am_pm[2]; | 
|  | 68 | const uint32_t *wam_pm[2]; | 
|  | 69 | int am_pm_defined; | 
|  | 70 | const char *d_t_fmt; | 
|  | 71 | const uint32_t *wd_t_fmt; | 
|  | 72 | const char *d_fmt; | 
|  | 73 | const uint32_t *wd_fmt; | 
|  | 74 | const char *t_fmt; | 
|  | 75 | const uint32_t *wt_fmt; | 
|  | 76 | const char *t_fmt_ampm; | 
|  | 77 | const uint32_t *wt_fmt_ampm; | 
|  | 78 | const char **era; | 
|  | 79 | const uint32_t **wera; | 
|  | 80 | uint32_t num_era; | 
|  | 81 | const char *era_year; | 
|  | 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]; | 
|  | 91 | const char *date_fmt; | 
|  | 92 | const uint32_t *wdate_fmt; | 
|  | 93 | int alt_digits_defined; | 
|  | 94 | unsigned char week_ndays; | 
|  | 95 | uint32_t week_1stday; | 
|  | 96 | unsigned char week_1stweek; | 
|  | 97 | unsigned char first_weekday; | 
|  | 98 | unsigned char first_workday; | 
|  | 99 | unsigned char cal_direction; | 
|  | 100 | const char *timezone; | 
|  | 101 | const uint32_t *wtimezone; | 
|  | 102 |  | 
|  | 103 | struct era_data *era_entries; | 
|  | 104 | }; | 
|  | 105 |  | 
|  | 106 |  | 
|  | 107 | /* This constant is used to represent an empty wide character string.  */ | 
|  | 108 | static const uint32_t empty_wstr[1] = { 0 }; | 
|  | 109 |  | 
|  | 110 |  | 
|  | 111 | static void | 
|  | 112 | time_startup (struct linereader *lr, struct localedef_t *locale, | 
|  | 113 | int ignore_content) | 
|  | 114 | { | 
|  | 115 | if (!ignore_content) | 
|  | 116 | locale->categories[LC_TIME].time = | 
|  | 117 | (struct locale_time_t *) xcalloc (1, sizeof (struct locale_time_t)); | 
|  | 118 |  | 
|  | 119 | if (lr != NULL) | 
|  | 120 | { | 
|  | 121 | lr->translate_strings = 1; | 
|  | 122 | lr->return_widestr = 1; | 
|  | 123 | } | 
|  | 124 | } | 
|  | 125 |  | 
|  | 126 |  | 
|  | 127 | void | 
|  | 128 | time_finish (struct localedef_t *locale, const struct charmap_t *charmap) | 
|  | 129 | { | 
|  | 130 | struct locale_time_t *time = locale->categories[LC_TIME].time; | 
|  | 131 | int nothing = 0; | 
|  | 132 |  | 
|  | 133 | /* Now resolve copying and also handle completely missing definitions.  */ | 
|  | 134 | if (time == NULL) | 
|  | 135 | { | 
|  | 136 | /* First see whether we were supposed to copy.  If yes, find the | 
|  | 137 | actual definition.  */ | 
|  | 138 | if (locale->copy_name[LC_TIME] != NULL) | 
|  | 139 | { | 
|  | 140 | /* Find the copying locale.  This has to happen transitively since | 
|  | 141 | the locale we are copying from might also copying another one.  */ | 
|  | 142 | struct localedef_t *from = locale; | 
|  | 143 |  | 
|  | 144 | do | 
|  | 145 | from = find_locale (LC_TIME, from->copy_name[LC_TIME], | 
|  | 146 | from->repertoire_name, charmap); | 
|  | 147 | while (from->categories[LC_TIME].time == NULL | 
|  | 148 | && from->copy_name[LC_TIME] != NULL); | 
|  | 149 |  | 
|  | 150 | time = locale->categories[LC_TIME].time | 
|  | 151 | = from->categories[LC_TIME].time; | 
|  | 152 | } | 
|  | 153 |  | 
|  | 154 | /* If there is still no definition issue an warning and create an | 
|  | 155 | empty one.  */ | 
|  | 156 | if (time == NULL) | 
|  | 157 | { | 
|  | 158 | if (! be_quiet) | 
|  | 159 | WITH_CUR_LOCALE (error (0, 0, _("\ | 
|  | 160 | No definition for %s category found"), "LC_TIME")); | 
|  | 161 | time_startup (NULL, locale, 0); | 
|  | 162 | time = locale->categories[LC_TIME].time; | 
|  | 163 | nothing = 1; | 
|  | 164 | } | 
|  | 165 | } | 
|  | 166 |  | 
|  | 167 | #define noparen(arg1, argn...) arg1, ##argn | 
|  | 168 | #define TESTARR_ELEM(cat, val) \ | 
|  | 169 | if (!time->cat##_defined)						      \ | 
|  | 170 | {									      \ | 
|  | 171 | const char *initval[] = { noparen val };				      \ | 
|  | 172 | unsigned int i;							      \ | 
|  | 173 | \ | 
|  | 174 | if (! be_quiet && ! nothing)					      \ | 
|  | 175 | WITH_CUR_LOCALE (error (0, 0, _("%s: field `%s' not defined"),	      \ | 
|  | 176 | "LC_TIME", #cat));          		      \ | 
|  | 177 | \ | 
|  | 178 | for (i = 0; i < sizeof (initval) / sizeof (initval[0]); ++i)	      \ | 
|  | 179 | time->cat[i] = initval[i];					      \ | 
|  | 180 | } | 
|  | 181 |  | 
|  | 182 | TESTARR_ELEM (abday, ( "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" )); | 
|  | 183 | TESTARR_ELEM (day, ( "Sunday", "Monday", "Tuesday", "Wednesday", | 
|  | 184 | "Thursday", "Friday", "Saturday" )); | 
|  | 185 | TESTARR_ELEM (abmon, ( "Jan", "Feb", "Mar", "Apr", "May", "Jun", | 
|  | 186 | "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" )); | 
|  | 187 | TESTARR_ELEM (mon, ( "January", "February", "March", "April", | 
|  | 188 | "May", "June", "July", "August", | 
|  | 189 | "September", "October", "November", "December" )); | 
|  | 190 | TESTARR_ELEM (am_pm, ( "AM", "PM" )); | 
|  | 191 |  | 
|  | 192 | #define TEST_ELEM(cat, initval) \ | 
|  | 193 | if (time->cat == NULL)						      \ | 
|  | 194 | {									      \ | 
|  | 195 | if (! be_quiet && ! nothing)					      \ | 
|  | 196 | WITH_CUR_LOCALE (error (0, 0, _("%s: field `%s' not defined"),	      \ | 
|  | 197 | "LC_TIME", #cat));          		      \ | 
|  | 198 | \ | 
|  | 199 | time->cat = initval;						      \ | 
|  | 200 | } | 
|  | 201 |  | 
|  | 202 | TEST_ELEM (d_t_fmt, "%a %b %e %H:%M:%S %Y"); | 
|  | 203 | TEST_ELEM (d_fmt, "%m/%d/%y"); | 
|  | 204 | TEST_ELEM (t_fmt, "%H:%M:%S"); | 
|  | 205 |  | 
|  | 206 | /* According to C.Y.Alexis Cheng <alexis@vnet.ibm.com> the T_FMT_AMPM | 
|  | 207 | field is optional.  */ | 
|  | 208 | if (time->t_fmt_ampm == NULL) | 
|  | 209 | { | 
|  | 210 | if (time->am_pm[0][0] == '\0' && time->am_pm[1][0] == '\0') | 
|  | 211 | { | 
|  | 212 | /* No AM/PM strings defined, use the 24h format as default.  */ | 
|  | 213 | time->t_fmt_ampm = time->t_fmt; | 
|  | 214 | time->wt_fmt_ampm = time->wt_fmt; | 
|  | 215 | } | 
|  | 216 | else | 
|  | 217 | { | 
|  | 218 | time->t_fmt_ampm = "%I:%M:%S %p"; | 
|  | 219 | time->wt_fmt_ampm = (const uint32_t *) L"%I:%M:%S %p"; | 
|  | 220 | } | 
|  | 221 | } | 
|  | 222 |  | 
|  | 223 | /* Now process the era entries.  */ | 
|  | 224 | if (time->num_era != 0) | 
|  | 225 | { | 
|  | 226 | const int days_per_month[12] = { 31, 29, 31, 30, 31, 30, | 
|  | 227 | 31, 31, 30, 31 ,30, 31 }; | 
|  | 228 | size_t idx; | 
|  | 229 | wchar_t *wstr; | 
|  | 230 |  | 
|  | 231 | time->era_entries = | 
|  | 232 | (struct era_data *) xmalloc (time->num_era | 
|  | 233 | * sizeof (struct era_data)); | 
|  | 234 |  | 
|  | 235 | for (idx = 0; idx < time->num_era; ++idx) | 
|  | 236 | { | 
|  | 237 | size_t era_len = strlen (time->era[idx]); | 
|  | 238 | char *str = xmalloc ((era_len + 1 + 3) & ~3); | 
|  | 239 | char *endp; | 
|  | 240 |  | 
|  | 241 | memcpy (str, time->era[idx], era_len + 1); | 
|  | 242 |  | 
|  | 243 | /* First character must be + or - for the direction.  */ | 
|  | 244 | if (*str != '+' && *str != '-') | 
|  | 245 | { | 
|  | 246 | if (!be_quiet) | 
|  | 247 | WITH_CUR_LOCALE (error (0, 0, _("\ | 
|  | 248 | %s: direction flag in string %Zd in `era' field is not '+' nor '-'"), | 
|  | 249 | "LC_TIME", idx + 1)); | 
|  | 250 | /* Default arbitrarily to '+'.  */ | 
|  | 251 | time->era_entries[idx].direction = '+'; | 
|  | 252 | } | 
|  | 253 | else | 
|  | 254 | time->era_entries[idx].direction = *str; | 
|  | 255 | if (*++str != ':') | 
|  | 256 | { | 
|  | 257 | if (!be_quiet) | 
|  | 258 | WITH_CUR_LOCALE (error (0, 0, _("\ | 
|  | 259 | %s: direction flag in string %Zd in `era' field is not a single character"), | 
|  | 260 | "LC_TIME", idx + 1)); | 
|  | 261 | (void) strsep (&str, ":"); | 
|  | 262 | } | 
|  | 263 | else | 
|  | 264 | ++str; | 
|  | 265 |  | 
|  | 266 | /* Now the offset year.  */ | 
|  | 267 | time->era_entries[idx].offset = strtol (str, &endp, 10); | 
|  | 268 | if (endp == str) | 
|  | 269 | { | 
|  | 270 | if (!be_quiet) | 
|  | 271 | WITH_CUR_LOCALE (error (0, 0, _("\ | 
|  | 272 | %s: invalid number for offset in string %Zd in `era' field"), | 
|  | 273 | "LC_TIME", idx + 1)); | 
|  | 274 | (void) strsep (&str, ":"); | 
|  | 275 | } | 
|  | 276 | else if (*endp != ':') | 
|  | 277 | { | 
|  | 278 | if (!be_quiet) | 
|  | 279 | WITH_CUR_LOCALE (error (0, 0, _("\ | 
|  | 280 | %s: garbage at end of offset value in string %Zd in `era' field"), | 
|  | 281 | "LC_TIME", idx + 1)); | 
|  | 282 | (void) strsep (&str, ":"); | 
|  | 283 | } | 
|  | 284 | else | 
|  | 285 | str = endp + 1; | 
|  | 286 |  | 
|  | 287 | /* Next is the starting date in ISO format.  */ | 
|  | 288 | if (strncmp (str, "-*", 2) == 0) | 
|  | 289 | { | 
|  | 290 | time->era_entries[idx].start_date[0] = | 
|  | 291 | time->era_entries[idx].start_date[1] = | 
|  | 292 | time->era_entries[idx].start_date[2] = 0x80000000; | 
|  | 293 | if (str[2] != ':') | 
|  | 294 | goto garbage_start_date; | 
|  | 295 | str += 3; | 
|  | 296 | } | 
|  | 297 | else if (strncmp (str, "+*", 2) == 0) | 
|  | 298 | { | 
|  | 299 | time->era_entries[idx].start_date[0] = | 
|  | 300 | time->era_entries[idx].start_date[1] = | 
|  | 301 | time->era_entries[idx].start_date[2] = 0x7fffffff; | 
|  | 302 | if (str[2] != ':') | 
|  | 303 | goto garbage_start_date; | 
|  | 304 | str += 3; | 
|  | 305 | } | 
|  | 306 | else | 
|  | 307 | { | 
|  | 308 | time->era_entries[idx].start_date[0] = strtol (str, &endp, 10); | 
|  | 309 | if (endp == str || *endp != '/') | 
|  | 310 | goto invalid_start_date; | 
|  | 311 | else | 
|  | 312 | str = endp + 1; | 
|  | 313 | time->era_entries[idx].start_date[0] -= 1900; | 
|  | 314 | /* year -1 represent 1 B.C. (not -1 A.D.) */ | 
|  | 315 | if (time->era_entries[idx].start_date[0] < -1900) | 
|  | 316 | ++time->era_entries[idx].start_date[0]; | 
|  | 317 |  | 
|  | 318 | time->era_entries[idx].start_date[1] = strtol (str, &endp, 10); | 
|  | 319 | if (endp == str || *endp != '/') | 
|  | 320 | goto invalid_start_date; | 
|  | 321 | else | 
|  | 322 | str = endp + 1; | 
|  | 323 | time->era_entries[idx].start_date[1] -= 1; | 
|  | 324 |  | 
|  | 325 | time->era_entries[idx].start_date[2] = strtol (str, &endp, 10); | 
|  | 326 | if (endp == str) | 
|  | 327 | { | 
|  | 328 | invalid_start_date: | 
|  | 329 | if (!be_quiet) | 
|  | 330 | WITH_CUR_LOCALE (error (0, 0, _("\ | 
|  | 331 | %s: invalid starting date in string %Zd in `era' field"), | 
|  | 332 | "LC_TIME", idx + 1)); | 
|  | 333 | (void) strsep (&str, ":"); | 
|  | 334 | } | 
|  | 335 | else if (*endp != ':') | 
|  | 336 | { | 
|  | 337 | garbage_start_date: | 
|  | 338 | if (!be_quiet) | 
|  | 339 | WITH_CUR_LOCALE (error (0, 0, _("\ | 
|  | 340 | %s: garbage at end of starting date in string %Zd in `era' field "), | 
|  | 341 | "LC_TIME", idx + 1)); | 
|  | 342 | (void) strsep (&str, ":"); | 
|  | 343 | } | 
|  | 344 | else | 
|  | 345 | { | 
|  | 346 | str = endp + 1; | 
|  | 347 |  | 
|  | 348 | /* Check for valid value.  */ | 
|  | 349 | if ((time->era_entries[idx].start_date[1] < 0 | 
|  | 350 | || time->era_entries[idx].start_date[1] >= 12 | 
|  | 351 | || time->era_entries[idx].start_date[2] < 0 | 
|  | 352 | || (time->era_entries[idx].start_date[2] | 
|  | 353 | > days_per_month[time->era_entries[idx].start_date[1]]) | 
|  | 354 | || (time->era_entries[idx].start_date[1] == 2 | 
|  | 355 | && time->era_entries[idx].start_date[2] == 29 | 
|  | 356 | && !__isleap (time->era_entries[idx].start_date[0]))) | 
|  | 357 | && !be_quiet) | 
|  | 358 | WITH_CUR_LOCALE (error (0, 0, _("\ | 
|  | 359 | %s: starting date is invalid in string %Zd in `era' field"), | 
|  | 360 | "LC_TIME", idx + 1)); | 
|  | 361 | } | 
|  | 362 | } | 
|  | 363 |  | 
|  | 364 | /* Next is the stopping date in ISO format.  */ | 
|  | 365 | if (strncmp (str, "-*", 2) == 0) | 
|  | 366 | { | 
|  | 367 | time->era_entries[idx].stop_date[0] = | 
|  | 368 | time->era_entries[idx].stop_date[1] = | 
|  | 369 | time->era_entries[idx].stop_date[2] = 0x80000000; | 
|  | 370 | if (str[2] != ':') | 
|  | 371 | goto garbage_stop_date; | 
|  | 372 | str += 3; | 
|  | 373 | } | 
|  | 374 | else if (strncmp (str, "+*", 2) == 0) | 
|  | 375 | { | 
|  | 376 | time->era_entries[idx].stop_date[0] = | 
|  | 377 | time->era_entries[idx].stop_date[1] = | 
|  | 378 | time->era_entries[idx].stop_date[2] = 0x7fffffff; | 
|  | 379 | if (str[2] != ':') | 
|  | 380 | goto garbage_stop_date; | 
|  | 381 | str += 3; | 
|  | 382 | } | 
|  | 383 | else | 
|  | 384 | { | 
|  | 385 | time->era_entries[idx].stop_date[0] = strtol (str, &endp, 10); | 
|  | 386 | if (endp == str || *endp != '/') | 
|  | 387 | goto invalid_stop_date; | 
|  | 388 | else | 
|  | 389 | str = endp + 1; | 
|  | 390 | time->era_entries[idx].stop_date[0] -= 1900; | 
|  | 391 | /* year -1 represent 1 B.C. (not -1 A.D.) */ | 
|  | 392 | if (time->era_entries[idx].stop_date[0] < -1900) | 
|  | 393 | ++time->era_entries[idx].stop_date[0]; | 
|  | 394 |  | 
|  | 395 | time->era_entries[idx].stop_date[1] = strtol (str, &endp, 10); | 
|  | 396 | if (endp == str || *endp != '/') | 
|  | 397 | goto invalid_stop_date; | 
|  | 398 | else | 
|  | 399 | str = endp + 1; | 
|  | 400 | time->era_entries[idx].stop_date[1] -= 1; | 
|  | 401 |  | 
|  | 402 | time->era_entries[idx].stop_date[2] = strtol (str, &endp, 10); | 
|  | 403 | if (endp == str) | 
|  | 404 | { | 
|  | 405 | invalid_stop_date: | 
|  | 406 | if (!be_quiet) | 
|  | 407 | WITH_CUR_LOCALE (error (0, 0, _("\ | 
|  | 408 | %s: invalid stopping date in string %Zd in `era' field"), | 
|  | 409 | "LC_TIME", idx + 1)); | 
|  | 410 | (void) strsep (&str, ":"); | 
|  | 411 | } | 
|  | 412 | else if (*endp != ':') | 
|  | 413 | { | 
|  | 414 | garbage_stop_date: | 
|  | 415 | if (!be_quiet) | 
|  | 416 | WITH_CUR_LOCALE (error (0, 0, _("\ | 
|  | 417 | %s: garbage at end of stopping date in string %Zd in `era' field"), | 
|  | 418 | "LC_TIME", idx + 1)); | 
|  | 419 | (void) strsep (&str, ":"); | 
|  | 420 | } | 
|  | 421 | else | 
|  | 422 | { | 
|  | 423 | str = endp + 1; | 
|  | 424 |  | 
|  | 425 | /* Check for valid value.  */ | 
|  | 426 | if ((time->era_entries[idx].stop_date[1] < 0 | 
|  | 427 | || time->era_entries[idx].stop_date[1] >= 12 | 
|  | 428 | || time->era_entries[idx].stop_date[2] < 0 | 
|  | 429 | || (time->era_entries[idx].stop_date[2] | 
|  | 430 | > days_per_month[time->era_entries[idx].stop_date[1]]) | 
|  | 431 | || (time->era_entries[idx].stop_date[1] == 2 | 
|  | 432 | && time->era_entries[idx].stop_date[2] == 29 | 
|  | 433 | && !__isleap (time->era_entries[idx].stop_date[0]))) | 
|  | 434 | && !be_quiet) | 
|  | 435 | WITH_CUR_LOCALE (error (0, 0, _("\ | 
|  | 436 | %s: invalid stopping date in string %Zd in `era' field"), | 
|  | 437 | "LC_TIME", idx + 1)); | 
|  | 438 | } | 
|  | 439 | } | 
|  | 440 |  | 
|  | 441 | if (str == NULL || *str == '\0') | 
|  | 442 | { | 
|  | 443 | if (!be_quiet) | 
|  | 444 | WITH_CUR_LOCALE (error (0, 0, _("\ | 
|  | 445 | %s: missing era name in string %Zd in `era' field"), "LC_TIME", idx + 1)); | 
|  | 446 | time->era_entries[idx].name = | 
|  | 447 | time->era_entries[idx].format = ""; | 
|  | 448 | } | 
|  | 449 | else | 
|  | 450 | { | 
|  | 451 | time->era_entries[idx].name = strsep (&str, ":"); | 
|  | 452 |  | 
|  | 453 | if (str == NULL || *str == '\0') | 
|  | 454 | { | 
|  | 455 | if (!be_quiet) | 
|  | 456 | WITH_CUR_LOCALE (error (0, 0, _("\ | 
|  | 457 | %s: missing era format in string %Zd in `era' field"), | 
|  | 458 | "LC_TIME", idx + 1)); | 
|  | 459 | time->era_entries[idx].name = | 
|  | 460 | time->era_entries[idx].format = ""; | 
|  | 461 | } | 
|  | 462 | else | 
|  | 463 | time->era_entries[idx].format = str; | 
|  | 464 | } | 
|  | 465 |  | 
|  | 466 | /* Now generate the wide character name and format.  */ | 
|  | 467 | wstr = wcschr ((wchar_t *) time->wera[idx], L':');/* end direction */ | 
|  | 468 | wstr = wstr ? wcschr (wstr + 1, L':') : NULL;	/* end offset */ | 
|  | 469 | wstr = wstr ? wcschr (wstr + 1, L':') : NULL;	/* end start */ | 
|  | 470 | wstr = wstr ? wcschr (wstr + 1, L':') : NULL;	/* end end */ | 
|  | 471 | if (wstr != NULL) | 
|  | 472 | { | 
|  | 473 | time->era_entries[idx].wname = (uint32_t *) wstr + 1; | 
|  | 474 | wstr = wcschr (wstr + 1, L':');	/* end name */ | 
|  | 475 | if (wstr != NULL) | 
|  | 476 | { | 
|  | 477 | *wstr = L'\0'; | 
|  | 478 | time->era_entries[idx].wformat = (uint32_t *) wstr + 1; | 
|  | 479 | } | 
|  | 480 | else | 
|  | 481 | time->era_entries[idx].wname = | 
|  | 482 | time->era_entries[idx].wformat = (uint32_t *) L""; | 
|  | 483 | } | 
|  | 484 | else | 
|  | 485 | time->era_entries[idx].wname = | 
|  | 486 | time->era_entries[idx].wformat = (uint32_t *) L""; | 
|  | 487 | } | 
|  | 488 | } | 
|  | 489 |  | 
|  | 490 | if (time->week_ndays == 0) | 
|  | 491 | time->week_ndays = 7; | 
|  | 492 |  | 
|  | 493 | if (time->week_1stday == 0) | 
|  | 494 | time->week_1stday = 19971130; | 
|  | 495 |  | 
|  | 496 | if (time->week_1stweek > time->week_ndays) | 
|  | 497 | WITH_CUR_LOCALE (error (0, 0, _("\ | 
|  | 498 | %s: third operand for value of field `%s' must not be larger than %d"), | 
|  | 499 | "LC_TIME", "week", 7)); | 
|  | 500 |  | 
|  | 501 | if (time->first_weekday == '\0') | 
|  | 502 | /* The definition does not specify this so the default is used.  */ | 
|  | 503 | time->first_weekday = 1; | 
|  | 504 | else if (time->first_weekday > time->week_ndays) | 
|  | 505 | WITH_CUR_LOCALE (error (0, 0, _("\ | 
|  | 506 | %s: values for field `%s' must not be larger than %d"), | 
|  | 507 | "LC_TIME", "first_weekday", 7)); | 
|  | 508 |  | 
|  | 509 | if (time->first_workday == '\0') | 
|  | 510 | /* The definition does not specify this so the default is used.  */ | 
|  | 511 | time->first_workday = 2; | 
|  | 512 | else if (time->first_workday > time->week_ndays) | 
|  | 513 | WITH_CUR_LOCALE (error (0, 0, _("\ | 
|  | 514 | %s: values for field `%s' must not be larger than %d"), | 
|  | 515 | "LC_TIME", "first_workday", 7)); | 
|  | 516 |  | 
|  | 517 | if (time->cal_direction == '\0') | 
|  | 518 | /* The definition does not specify this so the default is used.  */ | 
|  | 519 | time->cal_direction = 1; | 
|  | 520 | else if (time->cal_direction > 3) | 
|  | 521 | WITH_CUR_LOCALE (error (0, 0, _("\ | 
|  | 522 | %s: values for field `%s' must not be larger than %d"), | 
|  | 523 | "LC_TIME", "cal_direction", 3)); | 
|  | 524 |  | 
|  | 525 | /* XXX We don't perform any tests on the timezone value since this is | 
|  | 526 | simply useless, stupid $&$!@...  */ | 
|  | 527 | if (time->timezone == NULL) | 
|  | 528 | time->timezone = ""; | 
|  | 529 |  | 
|  | 530 | if (time->date_fmt == NULL) | 
|  | 531 | time->date_fmt = "%a %b %e %H:%M:%S %Z %Y"; | 
|  | 532 | if (time->wdate_fmt == NULL) | 
|  | 533 | time->wdate_fmt = (const uint32_t *) L"%a %b %e %H:%M:%S %Z %Y"; | 
|  | 534 | } | 
|  | 535 |  | 
|  | 536 |  | 
|  | 537 | void | 
|  | 538 | time_output (struct localedef_t *locale, const struct charmap_t *charmap, | 
|  | 539 | const char *output_path) | 
|  | 540 | { | 
|  | 541 | struct locale_time_t *time = locale->categories[LC_TIME].time; | 
|  | 542 | struct locale_file file; | 
|  | 543 | size_t num, n; | 
|  | 544 |  | 
|  | 545 | init_locale_data (&file, _NL_ITEM_INDEX (_NL_NUM_LC_TIME)); | 
|  | 546 |  | 
|  | 547 | /* The ab'days.  */ | 
|  | 548 | for (n = 0; n < 7; ++n) | 
|  | 549 | add_locale_string (&file, time->abday[n] ?: ""); | 
|  | 550 |  | 
|  | 551 | /* The days.  */ | 
|  | 552 | for (n = 0; n < 7; ++n) | 
|  | 553 | add_locale_string (&file, time->day[n] ?: ""); | 
|  | 554 |  | 
|  | 555 | /* The ab'mons.  */ | 
|  | 556 | for (n = 0; n < 12; ++n) | 
|  | 557 | add_locale_string (&file, time->abmon[n] ?: ""); | 
|  | 558 |  | 
|  | 559 | /* The mons.  */ | 
|  | 560 | for (n = 0; n < 12; ++n) | 
|  | 561 | add_locale_string (&file, time->mon[n] ?: ""); | 
|  | 562 |  | 
|  | 563 | /* AM/PM.  */ | 
|  | 564 | for (n = 0; n < 2; ++n) | 
|  | 565 | add_locale_string (&file, time->am_pm[n]); | 
|  | 566 |  | 
|  | 567 | add_locale_string (&file, time->d_t_fmt ?: ""); | 
|  | 568 | add_locale_string (&file, time->d_fmt ?: ""); | 
|  | 569 | add_locale_string (&file, time->t_fmt ?: ""); | 
|  | 570 | add_locale_string (&file, time->t_fmt_ampm ?: ""); | 
|  | 571 |  | 
|  | 572 | start_locale_structure (&file); | 
|  | 573 | for (num = 0; num < time->num_era; ++num) | 
|  | 574 | add_locale_string (&file, time->era[num]); | 
|  | 575 | end_locale_structure (&file); | 
|  | 576 |  | 
|  | 577 | add_locale_string (&file, time->era_year ?: ""); | 
|  | 578 | add_locale_string (&file, time->era_d_fmt ?: ""); | 
|  | 579 |  | 
|  | 580 | start_locale_structure (&file); | 
|  | 581 | for (num = 0; num < 100; ++num) | 
|  | 582 | add_locale_string (&file, time->alt_digits[num] ?: ""); | 
|  | 583 | end_locale_structure (&file); | 
|  | 584 |  | 
|  | 585 | add_locale_string (&file, time->era_d_t_fmt ?: ""); | 
|  | 586 | add_locale_string (&file, time->era_t_fmt ?: ""); | 
|  | 587 | add_locale_uint32 (&file, time->num_era); | 
|  | 588 |  | 
|  | 589 | start_locale_structure (&file); | 
|  | 590 | for (num = 0; num < time->num_era; ++num) | 
|  | 591 | { | 
|  | 592 | add_locale_uint32 (&file, time->era_entries[num].direction); | 
|  | 593 | add_locale_uint32 (&file, time->era_entries[num].offset); | 
|  | 594 | add_locale_uint32 (&file, time->era_entries[num].start_date[0]); | 
|  | 595 | add_locale_uint32 (&file, time->era_entries[num].start_date[1]); | 
|  | 596 | add_locale_uint32 (&file, time->era_entries[num].start_date[2]); | 
|  | 597 | add_locale_uint32 (&file, time->era_entries[num].stop_date[0]); | 
|  | 598 | add_locale_uint32 (&file, time->era_entries[num].stop_date[1]); | 
|  | 599 | add_locale_uint32 (&file, time->era_entries[num].stop_date[2]); | 
|  | 600 | add_locale_string (&file, time->era_entries[num].name); | 
|  | 601 | add_locale_string (&file, time->era_entries[num].format); | 
|  | 602 | add_locale_wstring (&file, time->era_entries[num].wname); | 
|  | 603 | add_locale_wstring (&file, time->era_entries[num].wformat); | 
|  | 604 | } | 
|  | 605 | end_locale_structure (&file); | 
|  | 606 |  | 
|  | 607 | /* The wide character ab'days.  */ | 
|  | 608 | for (n = 0; n < 7; ++n) | 
|  | 609 | add_locale_wstring (&file, time->wabday[n] ?: empty_wstr); | 
|  | 610 |  | 
|  | 611 | /* The wide character days.  */ | 
|  | 612 | for (n = 0; n < 7; ++n) | 
|  | 613 | add_locale_wstring (&file, time->wday[n] ?: empty_wstr); | 
|  | 614 |  | 
|  | 615 | /* The wide character ab'mons.  */ | 
|  | 616 | for (n = 0; n < 12; ++n) | 
|  | 617 | add_locale_wstring (&file, time->wabmon[n] ?: empty_wstr); | 
|  | 618 |  | 
|  | 619 | /* The wide character mons.  */ | 
|  | 620 | for (n = 0; n < 12; ++n) | 
|  | 621 | add_locale_wstring (&file, time->wmon[n] ?: empty_wstr); | 
|  | 622 |  | 
|  | 623 | /* Wide character AM/PM.  */ | 
|  | 624 | for (n = 0; n < 2; ++n) | 
|  | 625 | add_locale_wstring (&file, time->wam_pm[n] ?: empty_wstr); | 
|  | 626 |  | 
|  | 627 | add_locale_wstring (&file, time->wd_t_fmt ?: empty_wstr); | 
|  | 628 | add_locale_wstring (&file, time->wd_fmt ?: empty_wstr); | 
|  | 629 | add_locale_wstring (&file, time->wt_fmt ?: empty_wstr); | 
|  | 630 | add_locale_wstring (&file, time->wt_fmt_ampm ?: empty_wstr); | 
|  | 631 | add_locale_wstring (&file, time->wera_year ?: empty_wstr); | 
|  | 632 | add_locale_wstring (&file, time->wera_d_fmt ?: empty_wstr); | 
|  | 633 |  | 
|  | 634 | start_locale_structure (&file); | 
|  | 635 | for (num = 0; num < 100; ++num) | 
|  | 636 | add_locale_wstring (&file, time->walt_digits[num] ?: empty_wstr); | 
|  | 637 | end_locale_structure (&file); | 
|  | 638 |  | 
|  | 639 | add_locale_wstring (&file, time->wera_d_t_fmt ?: empty_wstr); | 
|  | 640 | add_locale_wstring (&file, time->wera_t_fmt ?: empty_wstr); | 
|  | 641 | add_locale_char (&file, time->week_ndays); | 
|  | 642 | add_locale_uint32 (&file, time->week_1stday); | 
|  | 643 | add_locale_char (&file, time->week_1stweek); | 
|  | 644 | add_locale_char (&file, time->first_weekday); | 
|  | 645 | add_locale_char (&file, time->first_workday); | 
|  | 646 | add_locale_char (&file, time->cal_direction); | 
|  | 647 | add_locale_string (&file, time->timezone); | 
|  | 648 | add_locale_string (&file, time->date_fmt); | 
|  | 649 | add_locale_wstring (&file, time->wdate_fmt); | 
|  | 650 | add_locale_string (&file, charmap->code_set_name); | 
|  | 651 | write_locale_data (output_path, LC_TIME, "LC_TIME", &file); | 
|  | 652 | } | 
|  | 653 |  | 
|  | 654 |  | 
|  | 655 | /* The parser for the LC_TIME section of the locale definition.  */ | 
|  | 656 | void | 
|  | 657 | time_read (struct linereader *ldfile, struct localedef_t *result, | 
|  | 658 | const struct charmap_t *charmap, const char *repertoire_name, | 
|  | 659 | int ignore_content) | 
|  | 660 | { | 
|  | 661 | struct repertoire_t *repertoire = NULL; | 
|  | 662 | struct locale_time_t *time; | 
|  | 663 | struct token *now; | 
|  | 664 | enum token_t nowtok; | 
|  | 665 | size_t cnt; | 
|  | 666 |  | 
|  | 667 | /* Get the repertoire we have to use.  */ | 
|  | 668 | if (repertoire_name != NULL) | 
|  | 669 | repertoire = repertoire_read (repertoire_name); | 
|  | 670 |  | 
|  | 671 | /* The rest of the line containing `LC_TIME' must be free.  */ | 
|  | 672 | lr_ignore_rest (ldfile, 1); | 
|  | 673 |  | 
|  | 674 |  | 
|  | 675 | do | 
|  | 676 | { | 
|  | 677 | now = lr_token (ldfile, charmap, result, repertoire, verbose); | 
|  | 678 | nowtok = now->tok; | 
|  | 679 | } | 
|  | 680 | while (nowtok == tok_eol); | 
|  | 681 |  | 
|  | 682 | /* If we see `copy' now we are almost done.  */ | 
|  | 683 | if (nowtok == tok_copy) | 
|  | 684 | { | 
|  | 685 | handle_copy (ldfile, charmap, repertoire_name, result, tok_lc_time, | 
|  | 686 | LC_TIME, "LC_TIME", ignore_content); | 
|  | 687 | return; | 
|  | 688 | } | 
|  | 689 |  | 
|  | 690 | /* Prepare the data structures.  */ | 
|  | 691 | time_startup (ldfile, result, ignore_content); | 
|  | 692 | time = result->categories[LC_TIME].time; | 
|  | 693 |  | 
|  | 694 | while (1) | 
|  | 695 | { | 
|  | 696 | /* Of course we don't proceed beyond the end of file.  */ | 
|  | 697 | if (nowtok == tok_eof) | 
|  | 698 | break; | 
|  | 699 |  | 
|  | 700 | /* Ingore empty lines.  */ | 
|  | 701 | if (nowtok == tok_eol) | 
|  | 702 | { | 
|  | 703 | now = lr_token (ldfile, charmap, result, repertoire, verbose); | 
|  | 704 | nowtok = now->tok; | 
|  | 705 | continue; | 
|  | 706 | } | 
|  | 707 |  | 
|  | 708 | switch (nowtok) | 
|  | 709 | { | 
|  | 710 | #define STRARR_ELEM(cat, min, max) \ | 
|  | 711 | case tok_##cat:							      \ | 
|  | 712 | /* Ignore the rest of the line if we don't need the input of	      \ | 
|  | 713 | this line.  */						      \ | 
|  | 714 | if (ignore_content)						      \ | 
|  | 715 | {								      \ | 
|  | 716 | lr_ignore_rest (ldfile, 0);				      \ | 
|  | 717 | break;							      \ | 
|  | 718 | }								      \ | 
|  | 719 | \ | 
|  | 720 | for (cnt = 0; cnt < max; ++cnt)				      \ | 
|  | 721 | {								      \ | 
|  | 722 | now = lr_token (ldfile, charmap, result, repertoire, verbose);  \ | 
|  | 723 | if (now->tok == tok_eol)					      \ | 
|  | 724 | {							      \ | 
|  | 725 | if (cnt < min)					      \ | 
|  | 726 | lr_error (ldfile, _("%s: too few values for field `%s'"), \ | 
|  | 727 | "LC_TIME", #cat);				      \ | 
|  | 728 | if (!ignore_content)					      \ | 
|  | 729 | do							      \ | 
|  | 730 | {							      \ | 
|  | 731 | time->cat[cnt] = "";				      \ | 
|  | 732 | time->w##cat[cnt] = empty_wstr;			      \ | 
|  | 733 | }							      \ | 
|  | 734 | while (++cnt < max);				      \ | 
|  | 735 | break;						      \ | 
|  | 736 | }							      \ | 
|  | 737 | else if (now->tok != tok_string)				      \ | 
|  | 738 | goto err_label;						      \ | 
|  | 739 | else if (!ignore_content && (now->val.str.startmb == NULL	      \ | 
|  | 740 | || now->val.str.startwc == NULL))  \ | 
|  | 741 | {							      \ | 
|  | 742 | lr_error (ldfile, _("%s: unknown character in field `%s'"), \ | 
|  | 743 | "LC_TIME", #cat);				      \ | 
|  | 744 | time->cat[cnt] = "";					      \ | 
|  | 745 | time->w##cat[cnt] = empty_wstr;			      \ | 
|  | 746 | }							      \ | 
|  | 747 | else if (!ignore_content)					      \ | 
|  | 748 | {							      \ | 
|  | 749 | time->cat[cnt] = now->val.str.startmb;		      \ | 
|  | 750 | time->w##cat[cnt] = now->val.str.startwc;		      \ | 
|  | 751 | }							      \ | 
|  | 752 | \ | 
|  | 753 | /* Match the semicolon.  */				      \ | 
|  | 754 | now = lr_token (ldfile, charmap, result, repertoire, verbose);  \ | 
|  | 755 | if (now->tok != tok_semicolon && now->tok != tok_eol)	      \ | 
|  | 756 | break;							      \ | 
|  | 757 | }								      \ | 
|  | 758 | if (now->tok != tok_eol)					      \ | 
|  | 759 | {								      \ | 
|  | 760 | while (!ignore_content && cnt < min)			      \ | 
|  | 761 | {							      \ | 
|  | 762 | time->cat[cnt] = "";					      \ | 
|  | 763 | time->w##cat[cnt++] = empty_wstr;			      \ | 
|  | 764 | }							      \ | 
|  | 765 | \ | 
|  | 766 | if (now->tok == tok_semicolon)				      \ | 
|  | 767 | {							      \ | 
|  | 768 | now = lr_token (ldfile, charmap, result, repertoire,	      \ | 
|  | 769 | verbose);				      \ | 
|  | 770 | if (now->tok == tok_eol)				      \ | 
|  | 771 | lr_error (ldfile, _("extra trailing semicolon"));	      \ | 
|  | 772 | else if (now->tok == tok_string)			      \ | 
|  | 773 | {							      \ | 
|  | 774 | lr_error (ldfile, _("\ | 
|  | 775 | %s: too many values for field `%s'"),					      \ | 
|  | 776 | "LC_TIME", #cat);			      \ | 
|  | 777 | lr_ignore_rest (ldfile, 0);			      \ | 
|  | 778 | }							      \ | 
|  | 779 | else							      \ | 
|  | 780 | goto err_label;					      \ | 
|  | 781 | }							      \ | 
|  | 782 | else							      \ | 
|  | 783 | goto err_label;						      \ | 
|  | 784 | }								      \ | 
|  | 785 | time->cat##_defined = 1;					      \ | 
|  | 786 | break | 
|  | 787 |  | 
|  | 788 | STRARR_ELEM (abday, 7, 7); | 
|  | 789 | STRARR_ELEM (day, 7, 7); | 
|  | 790 | STRARR_ELEM (abmon, 12, 12); | 
|  | 791 | STRARR_ELEM (mon, 12, 12); | 
|  | 792 | STRARR_ELEM (am_pm, 2, 2); | 
|  | 793 | STRARR_ELEM (alt_digits, 0, 100); | 
|  | 794 |  | 
|  | 795 | case tok_era: | 
|  | 796 | /* Ignore the rest of the line if we don't need the input of | 
|  | 797 | this line.  */ | 
|  | 798 | if (ignore_content) | 
|  | 799 | { | 
|  | 800 | lr_ignore_rest (ldfile, 0); | 
|  | 801 | break; | 
|  | 802 | } | 
|  | 803 | do | 
|  | 804 | { | 
|  | 805 | now = lr_token (ldfile, charmap, result, repertoire, verbose); | 
|  | 806 | if (now->tok != tok_string) | 
|  | 807 | goto err_label; | 
|  | 808 | if (!ignore_content && (now->val.str.startmb == NULL | 
|  | 809 | || now->val.str.startwc == NULL)) | 
|  | 810 | { | 
|  | 811 | lr_error (ldfile, _("%s: unknown character in field `%s'"), | 
|  | 812 | "LC_TIME", "era"); | 
|  | 813 | lr_ignore_rest (ldfile, 0); | 
|  | 814 | break; | 
|  | 815 | } | 
|  | 816 | if (!ignore_content) | 
|  | 817 | { | 
|  | 818 | time->era = xrealloc (time->era, | 
|  | 819 | (time->num_era + 1) * sizeof (char *)); | 
|  | 820 | time->era[time->num_era] = now->val.str.startmb; | 
|  | 821 |  | 
|  | 822 | time->wera = xrealloc (time->wera, | 
|  | 823 | (time->num_era + 1) | 
|  | 824 | * sizeof (char *)); | 
|  | 825 | time->wera[time->num_era++] = now->val.str.startwc; | 
|  | 826 | } | 
|  | 827 | now = lr_token (ldfile, charmap, result, repertoire, verbose); | 
|  | 828 | if (now->tok != tok_eol && now->tok != tok_semicolon) | 
|  | 829 | goto err_label; | 
|  | 830 | } | 
|  | 831 | while (now->tok == tok_semicolon); | 
|  | 832 | break; | 
|  | 833 |  | 
|  | 834 | #define STR_ELEM(cat) \ | 
|  | 835 | case tok_##cat:							      \ | 
|  | 836 | /* Ignore the rest of the line if we don't need the input of	      \ | 
|  | 837 | this line.  */						      \ | 
|  | 838 | if (ignore_content)						      \ | 
|  | 839 | {								      \ | 
|  | 840 | lr_ignore_rest (ldfile, 0);				      \ | 
|  | 841 | break;							      \ | 
|  | 842 | }								      \ | 
|  | 843 | \ | 
|  | 844 | now = lr_token (ldfile, charmap, result, repertoire, verbose);      \ | 
|  | 845 | if (now->tok != tok_string)					      \ | 
|  | 846 | goto err_label;						      \ | 
|  | 847 | else if (time->cat != NULL)					      \ | 
|  | 848 | lr_error (ldfile, _("\ | 
|  | 849 | %s: field `%s' declared more than once"), "LC_TIME", #cat);		      \ | 
|  | 850 | else if (!ignore_content && (now->val.str.startmb == NULL	      \ | 
|  | 851 | || now->val.str.startwc == NULL))      \ | 
|  | 852 | {								      \ | 
|  | 853 | lr_error (ldfile, _("%s: unknown character in field `%s'"),     \ | 
|  | 854 | "LC_TIME", #cat);				      \ | 
|  | 855 | time->cat = "";						      \ | 
|  | 856 | time->w##cat = empty_wstr;				      \ | 
|  | 857 | }								      \ | 
|  | 858 | else if (!ignore_content)					      \ | 
|  | 859 | {								      \ | 
|  | 860 | time->cat = now->val.str.startmb;				      \ | 
|  | 861 | time->w##cat = now->val.str.startwc;			      \ | 
|  | 862 | }								      \ | 
|  | 863 | break | 
|  | 864 |  | 
|  | 865 | STR_ELEM (d_t_fmt); | 
|  | 866 | STR_ELEM (d_fmt); | 
|  | 867 | STR_ELEM (t_fmt); | 
|  | 868 | STR_ELEM (t_fmt_ampm); | 
|  | 869 | STR_ELEM (era_year); | 
|  | 870 | STR_ELEM (era_d_t_fmt); | 
|  | 871 | STR_ELEM (era_d_fmt); | 
|  | 872 | STR_ELEM (era_t_fmt); | 
|  | 873 | STR_ELEM (timezone); | 
|  | 874 | STR_ELEM (date_fmt); | 
|  | 875 |  | 
|  | 876 | #define INT_ELEM(cat) \ | 
|  | 877 | case tok_##cat:							      \ | 
|  | 878 | /* Ignore the rest of the line if we don't need the input of	      \ | 
|  | 879 | this line.  */						      \ | 
|  | 880 | if (ignore_content)						      \ | 
|  | 881 | {								      \ | 
|  | 882 | lr_ignore_rest (ldfile, 0);				      \ | 
|  | 883 | break;							      \ | 
|  | 884 | }								      \ | 
|  | 885 | \ | 
|  | 886 | now = lr_token (ldfile, charmap, result, repertoire, verbose);      \ | 
|  | 887 | if (now->tok != tok_number)					      \ | 
|  | 888 | goto err_label;						      \ | 
|  | 889 | else if (time->cat != 0)					      \ | 
|  | 890 | lr_error (ldfile, _("%s: field `%s' declared more than once"),    \ | 
|  | 891 | "LC_TIME", #cat);					      \ | 
|  | 892 | else if (!ignore_content)					      \ | 
|  | 893 | time->cat = now->val.num;					      \ | 
|  | 894 | break | 
|  | 895 |  | 
|  | 896 | INT_ELEM (first_weekday); | 
|  | 897 | INT_ELEM (first_workday); | 
|  | 898 | INT_ELEM (cal_direction); | 
|  | 899 |  | 
|  | 900 | case tok_week: | 
|  | 901 | /* Ignore the rest of the line if we don't need the input of | 
|  | 902 | this line.  */ | 
|  | 903 | if (ignore_content) | 
|  | 904 | { | 
|  | 905 | lr_ignore_rest (ldfile, 0); | 
|  | 906 | break; | 
|  | 907 | } | 
|  | 908 |  | 
|  | 909 | now = lr_token (ldfile, charmap, result, repertoire, verbose); | 
|  | 910 | if (now->tok != tok_number) | 
|  | 911 | goto err_label; | 
|  | 912 | time->week_ndays = now->val.num; | 
|  | 913 |  | 
|  | 914 | now = lr_token (ldfile, charmap, result, repertoire, verbose); | 
|  | 915 | if (now->tok != tok_semicolon) | 
|  | 916 | goto err_label; | 
|  | 917 |  | 
|  | 918 | now = lr_token (ldfile, charmap, result, repertoire, verbose); | 
|  | 919 | if (now->tok != tok_number) | 
|  | 920 | goto err_label; | 
|  | 921 | time->week_1stday = now->val.num; | 
|  | 922 |  | 
|  | 923 | now = lr_token (ldfile, charmap, result, repertoire, verbose); | 
|  | 924 | if (now->tok != tok_semicolon) | 
|  | 925 | goto err_label; | 
|  | 926 |  | 
|  | 927 | now = lr_token (ldfile, charmap, result, repertoire, verbose); | 
|  | 928 | if (now->tok != tok_number) | 
|  | 929 | goto err_label; | 
|  | 930 | time->week_1stweek = now->val.num; | 
|  | 931 |  | 
|  | 932 | lr_ignore_rest (ldfile,  1); | 
|  | 933 | break; | 
|  | 934 |  | 
|  | 935 | case tok_end: | 
|  | 936 | /* Next we assume `LC_TIME'.  */ | 
|  | 937 | now = lr_token (ldfile, charmap, result, repertoire, verbose); | 
|  | 938 | if (now->tok == tok_eof) | 
|  | 939 | break; | 
|  | 940 | if (now->tok == tok_eol) | 
|  | 941 | lr_error (ldfile, _("%s: incomplete `END' line"), "LC_TIME"); | 
|  | 942 | else if (now->tok != tok_lc_time) | 
|  | 943 | lr_error (ldfile, _("\ | 
|  | 944 | %1$s: definition does not end with `END %1$s'"), "LC_TIME"); | 
|  | 945 | lr_ignore_rest (ldfile, now->tok == tok_lc_time); | 
|  | 946 | return; | 
|  | 947 |  | 
|  | 948 | default: | 
|  | 949 | err_label: | 
|  | 950 | SYNTAX_ERROR (_("%s: syntax error"), "LC_TIME"); | 
|  | 951 | } | 
|  | 952 |  | 
|  | 953 | /* Prepare for the next round.  */ | 
|  | 954 | now = lr_token (ldfile, charmap, result, repertoire, verbose); | 
|  | 955 | nowtok = now->tok; | 
|  | 956 | } | 
|  | 957 |  | 
|  | 958 | /* When we come here we reached the end of the file.  */ | 
|  | 959 | lr_error (ldfile, _("%s: premature end of file"), "LC_TIME"); | 
|  | 960 | } |