1 /* $NetBSD: strptime.c,v 1.58 2015/10/31 03:42:00 ginsbach Exp $ */ 2 3 /*- 4 * Copyright (c) 1997, 1998, 2005, 2008 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code was contributed to The NetBSD Foundation by Klaus Klein. 8 * Heavily optimised by David Laight 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #if defined(LIBC_SCCS) && !defined(lint) 34 __RCSID("$NetBSD: strptime.c,v 1.58 2015/10/31 03:42:00 ginsbach Exp $"); 35 #endif 36 37 #include "namespace.h" 38 #include <sys/localedef.h> 39 #include <sys/types.h> 40 #include <ctype.h> 41 #include <locale.h> 42 #include <string.h> 43 #include <time.h> 44 #include <tzfile.h> 45 #include "private.h" 46 #include "setlocale_local.h" 47 48 #ifdef __weak_alias 49 __weak_alias(strptime,_strptime) 50 __weak_alias(strptime_l, _strptime_l) 51 #endif 52 53 static const u_char *conv_num(const unsigned char *, int *, uint, uint); 54 static const u_char *find_string(const u_char *, int *, const char * const *, 55 const char * const *, int); 56 57 #define _TIME_LOCALE(loc) \ 58 ((_TimeLocale *)((loc)->part_impl[(size_t)LC_TIME])) 59 60 /* 61 * We do not implement alternate representations. However, we always 62 * check whether a given modifier is allowed for a certain conversion. 63 */ 64 #define ALT_E 0x01 65 #define ALT_O 0x02 66 #define LEGAL_ALT(x) { if (alt_format & ~(x)) return NULL; } 67 68 #define S_YEAR (1 << 0) 69 #define S_MON (1 << 1) 70 #define S_YDAY (1 << 2) 71 #define S_MDAY (1 << 3) 72 #define S_WDAY (1 << 4) 73 #define S_HOUR (1 << 5) 74 75 #define HAVE_MDAY(s) (s & S_MDAY) 76 #define HAVE_MON(s) (s & S_MON) 77 #define HAVE_WDAY(s) (s & S_WDAY) 78 #define HAVE_YDAY(s) (s & S_YDAY) 79 #define HAVE_YEAR(s) (s & S_YEAR) 80 #define HAVE_HOUR(s) (s & S_HOUR) 81 82 static char utc[] = { "UTC" }; 83 /* RFC-822/RFC-2822 */ 84 static const char * const nast[5] = { 85 "EST", "CST", "MST", "PST", "\0\0\0" 86 }; 87 static const char * const nadt[5] = { 88 "EDT", "CDT", "MDT", "PDT", "\0\0\0" 89 }; 90 91 /* 92 * Table to determine the ordinal date for the start of a month. 93 * Ref: http://en.wikipedia.org/wiki/ISO_week_date 94 */ 95 static const int start_of_month[2][13] = { 96 /* non-leap year */ 97 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, 98 /* leap year */ 99 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } 100 }; 101 102 /* 103 * Calculate the week day of the first day of a year. Valid for 104 * the Gregorian calendar, which began Sept 14, 1752 in the UK 105 * and its colonies. Ref: 106 * http://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week 107 */ 108 109 static int 110 first_wday_of(int yr) 111 { 112 return ((2 * (3 - (yr / 100) % 4)) + (yr % 100) + ((yr % 100) / 4) + 113 (isleap(yr) ? 6 : 0) + 1) % 7; 114 } 115 116 #define delim(p) ((p) == '\0' || isspace((unsigned char)(p))) 117 118 static int 119 fromzone(const unsigned char **bp, struct tm *tm, int mandatory) 120 { 121 timezone_t tz; 122 char buf[512], *p; 123 const unsigned char *rp; 124 125 for (p = buf, rp = *bp; !delim(*rp) && p < &buf[sizeof(buf) - 1]; rp++) 126 *p++ = *rp; 127 *p = '\0'; 128 129 if (mandatory) 130 *bp = rp; 131 tz = tzalloc(buf); 132 if (tz == NULL) 133 return 0; 134 135 *bp = rp; 136 tm->tm_isdst = 0; /* XXX */ 137 #ifdef TM_GMTOFF 138 tm->TM_GMTOFF = tzgetgmtoff(tz, tm->tm_isdst); 139 #endif 140 #ifdef TM_ZONE 141 // Can't use tzgetname() here because we are going to free() 142 tm->TM_ZONE = NULL; /* XXX */ 143 #endif 144 tzfree(tz); 145 return 1; 146 } 147 148 char * 149 strptime(const char *buf, const char *fmt, struct tm *tm) 150 { 151 return strptime_l(buf, fmt, tm, _current_locale()); 152 } 153 154 char * 155 strptime_l(const char *buf, const char *fmt, struct tm *tm, locale_t loc) 156 { 157 unsigned char c; 158 const unsigned char *bp, *ep, *zname; 159 int alt_format, i, split_year = 0, neg = 0, state = 0, 160 day_offset = -1, week_offset = 0, offs, mandatory; 161 const char *new_fmt; 162 163 bp = (const u_char *)buf; 164 165 while (bp != NULL && (c = *fmt++) != '\0') { 166 /* Clear `alternate' modifier prior to new conversion. */ 167 alt_format = 0; 168 i = 0; 169 170 /* Eat up white-space. */ 171 if (isspace(c)) { 172 while (isspace(*bp)) 173 bp++; 174 continue; 175 } 176 177 if (c != '%') 178 goto literal; 179 180 181 again: switch (c = *fmt++) { 182 case '%': /* "%%" is converted to "%". */ 183 literal: 184 if (c != *bp++) 185 return NULL; 186 LEGAL_ALT(0); 187 continue; 188 189 /* 190 * "Alternative" modifiers. Just set the appropriate flag 191 * and start over again. 192 */ 193 case 'E': /* "%E?" alternative conversion modifier. */ 194 LEGAL_ALT(0); 195 alt_format |= ALT_E; 196 goto again; 197 198 case 'O': /* "%O?" alternative conversion modifier. */ 199 LEGAL_ALT(0); 200 alt_format |= ALT_O; 201 goto again; 202 203 /* 204 * "Complex" conversion rules, implemented through recursion. 205 */ 206 case 'c': /* Date and time, using the locale's format. */ 207 new_fmt = _TIME_LOCALE(loc)->d_t_fmt; 208 state |= S_WDAY | S_MON | S_MDAY | S_YEAR; 209 goto recurse; 210 211 case 'D': /* The date as "%m/%d/%y". */ 212 new_fmt = "%m/%d/%y"; 213 LEGAL_ALT(0); 214 state |= S_MON | S_MDAY | S_YEAR; 215 goto recurse; 216 217 case 'F': /* The date as "%Y-%m-%d". */ 218 new_fmt = "%Y-%m-%d"; 219 LEGAL_ALT(0); 220 state |= S_MON | S_MDAY | S_YEAR; 221 goto recurse; 222 223 case 'R': /* The time as "%H:%M". */ 224 new_fmt = "%H:%M"; 225 LEGAL_ALT(0); 226 goto recurse; 227 228 case 'r': /* The time in 12-hour clock representation. */ 229 new_fmt = _TIME_LOCALE(loc)->t_fmt_ampm; 230 LEGAL_ALT(0); 231 goto recurse; 232 233 case 'T': /* The time as "%H:%M:%S". */ 234 new_fmt = "%H:%M:%S"; 235 LEGAL_ALT(0); 236 goto recurse; 237 238 case 'X': /* The time, using the locale's format. */ 239 new_fmt = _TIME_LOCALE(loc)->t_fmt; 240 goto recurse; 241 242 case 'x': /* The date, using the locale's format. */ 243 new_fmt = _TIME_LOCALE(loc)->d_fmt; 244 state |= S_MON | S_MDAY | S_YEAR; 245 recurse: 246 bp = (const u_char *)strptime((const char *)bp, 247 new_fmt, tm); 248 LEGAL_ALT(ALT_E); 249 continue; 250 251 /* 252 * "Elementary" conversion rules. 253 */ 254 case 'A': /* The day of week, using the locale's form. */ 255 case 'a': 256 bp = find_string(bp, &tm->tm_wday, 257 _TIME_LOCALE(loc)->day, _TIME_LOCALE(loc)->abday, 7); 258 LEGAL_ALT(0); 259 state |= S_WDAY; 260 continue; 261 262 case 'B': /* The month, using the locale's form. */ 263 case 'b': 264 case 'h': 265 bp = find_string(bp, &tm->tm_mon, 266 _TIME_LOCALE(loc)->mon, _TIME_LOCALE(loc)->abmon, 267 12); 268 LEGAL_ALT(0); 269 state |= S_MON; 270 continue; 271 272 case 'C': /* The century number. */ 273 i = 20; 274 bp = conv_num(bp, &i, 0, 99); 275 276 i = i * 100 - TM_YEAR_BASE; 277 if (split_year) 278 i += tm->tm_year % 100; 279 split_year = 1; 280 tm->tm_year = i; 281 LEGAL_ALT(ALT_E); 282 state |= S_YEAR; 283 continue; 284 285 case 'd': /* The day of month. */ 286 case 'e': 287 bp = conv_num(bp, &tm->tm_mday, 1, 31); 288 LEGAL_ALT(ALT_O); 289 state |= S_MDAY; 290 continue; 291 292 case 'k': /* The hour (24-hour clock representation). */ 293 LEGAL_ALT(0); 294 /* FALLTHROUGH */ 295 case 'H': 296 bp = conv_num(bp, &tm->tm_hour, 0, 23); 297 LEGAL_ALT(ALT_O); 298 state |= S_HOUR; 299 continue; 300 301 case 'l': /* The hour (12-hour clock representation). */ 302 LEGAL_ALT(0); 303 /* FALLTHROUGH */ 304 case 'I': 305 bp = conv_num(bp, &tm->tm_hour, 1, 12); 306 if (tm->tm_hour == 12) 307 tm->tm_hour = 0; 308 LEGAL_ALT(ALT_O); 309 state |= S_HOUR; 310 continue; 311 312 case 'j': /* The day of year. */ 313 i = 1; 314 bp = conv_num(bp, &i, 1, 366); 315 tm->tm_yday = i - 1; 316 LEGAL_ALT(0); 317 state |= S_YDAY; 318 continue; 319 320 case 'M': /* The minute. */ 321 bp = conv_num(bp, &tm->tm_min, 0, 59); 322 LEGAL_ALT(ALT_O); 323 continue; 324 325 case 'm': /* The month. */ 326 i = 1; 327 bp = conv_num(bp, &i, 1, 12); 328 tm->tm_mon = i - 1; 329 LEGAL_ALT(ALT_O); 330 state |= S_MON; 331 continue; 332 333 case 'p': /* The locale's equivalent of AM/PM. */ 334 bp = find_string(bp, &i, _TIME_LOCALE(loc)->am_pm, 335 NULL, 2); 336 if (HAVE_HOUR(state) && tm->tm_hour > 11) 337 return NULL; 338 tm->tm_hour += i * 12; 339 LEGAL_ALT(0); 340 continue; 341 342 case 'S': /* The seconds. */ 343 bp = conv_num(bp, &tm->tm_sec, 0, 61); 344 LEGAL_ALT(ALT_O); 345 continue; 346 347 #ifndef TIME_MAX 348 #define TIME_MAX INT64_MAX 349 #endif 350 case 's': /* seconds since the epoch */ 351 { 352 time_t sse = 0; 353 uint64_t rulim = TIME_MAX; 354 355 if (*bp < '0' || *bp > '9') { 356 bp = NULL; 357 continue; 358 } 359 360 do { 361 sse *= 10; 362 sse += *bp++ - '0'; 363 rulim /= 10; 364 } while ((sse * 10 <= TIME_MAX) && 365 rulim && *bp >= '0' && *bp <= '9'); 366 367 if (sse < 0 || (uint64_t)sse > TIME_MAX) { 368 bp = NULL; 369 continue; 370 } 371 372 if (localtime_r(&sse, tm) == NULL) 373 bp = NULL; 374 else 375 state |= S_YDAY | S_WDAY | 376 S_MON | S_MDAY | S_YEAR; 377 } 378 continue; 379 380 case 'U': /* The week of year, beginning on sunday. */ 381 case 'W': /* The week of year, beginning on monday. */ 382 /* 383 * XXX This is bogus, as we can not assume any valid 384 * information present in the tm structure at this 385 * point to calculate a real value, so just check the 386 * range for now. 387 */ 388 bp = conv_num(bp, &i, 0, 53); 389 LEGAL_ALT(ALT_O); 390 if (c == 'U') 391 day_offset = TM_SUNDAY; 392 else 393 day_offset = TM_MONDAY; 394 week_offset = i; 395 continue; 396 397 case 'w': /* The day of week, beginning on sunday. */ 398 bp = conv_num(bp, &tm->tm_wday, 0, 6); 399 LEGAL_ALT(ALT_O); 400 state |= S_WDAY; 401 continue; 402 403 case 'u': /* The day of week, monday = 1. */ 404 bp = conv_num(bp, &i, 1, 7); 405 tm->tm_wday = i % 7; 406 LEGAL_ALT(ALT_O); 407 state |= S_WDAY; 408 continue; 409 410 case 'g': /* The year corresponding to the ISO week 411 * number but without the century. 412 */ 413 bp = conv_num(bp, &i, 0, 99); 414 continue; 415 416 case 'G': /* The year corresponding to the ISO week 417 * number with century. 418 */ 419 do 420 bp++; 421 while (isdigit(*bp)); 422 continue; 423 424 case 'V': /* The ISO 8601:1988 week number as decimal */ 425 bp = conv_num(bp, &i, 0, 53); 426 continue; 427 428 case 'Y': /* The year. */ 429 i = TM_YEAR_BASE; /* just for data sanity... */ 430 bp = conv_num(bp, &i, 0, 9999); 431 tm->tm_year = i - TM_YEAR_BASE; 432 LEGAL_ALT(ALT_E); 433 state |= S_YEAR; 434 continue; 435 436 case 'y': /* The year within 100 years of the epoch. */ 437 /* LEGAL_ALT(ALT_E | ALT_O); */ 438 bp = conv_num(bp, &i, 0, 99); 439 440 if (split_year) 441 /* preserve century */ 442 i += (tm->tm_year / 100) * 100; 443 else { 444 split_year = 1; 445 if (i <= 68) 446 i = i + 2000 - TM_YEAR_BASE; 447 else 448 i = i + 1900 - TM_YEAR_BASE; 449 } 450 tm->tm_year = i; 451 state |= S_YEAR; 452 continue; 453 454 case 'Z': 455 case 'z': 456 tzset(); 457 mandatory = c == 'z'; 458 /* 459 * We recognize all ISO 8601 formats: 460 * Z = Zulu time/UTC 461 * [+-]hhmm 462 * [+-]hh:mm 463 * [+-]hh 464 * We recognize all RFC-822/RFC-2822 formats: 465 * UT|GMT 466 * North American : UTC offsets 467 * E[DS]T = Eastern : -4 | -5 468 * C[DS]T = Central : -5 | -6 469 * M[DS]T = Mountain: -6 | -7 470 * P[DS]T = Pacific : -7 | -8 471 * Nautical/Military 472 * [A-IL-M] = -1 ... -9 (J not used) 473 * [N-Y] = +1 ... +12 474 * Note: J maybe used to denote non-nautical 475 * local time 476 */ 477 if (mandatory) 478 while (isspace(*bp)) 479 bp++; 480 481 zname = bp; 482 switch (*bp++) { 483 case 'G': 484 if (*bp++ != 'M') 485 goto namedzone; 486 /*FALLTHROUGH*/ 487 case 'U': 488 if (*bp++ != 'T') 489 goto namedzone; 490 else if (!delim(*bp) && *bp++ != 'C') 491 goto namedzone; 492 /*FALLTHROUGH*/ 493 case 'Z': 494 if (!delim(*bp)) 495 goto namedzone; 496 tm->tm_isdst = 0; 497 #ifdef TM_GMTOFF 498 tm->TM_GMTOFF = 0; 499 #endif 500 #ifdef TM_ZONE 501 tm->TM_ZONE = utc; 502 #endif 503 continue; 504 case '+': 505 neg = 0; 506 break; 507 case '-': 508 neg = 1; 509 break; 510 default: 511 namedzone: 512 bp = zname; 513 514 /* Nautical / Military style */ 515 if (delim(bp[1]) && 516 ((*bp >= 'A' && *bp <= 'I') || 517 (*bp >= 'L' && *bp <= 'Y'))) { 518 #ifdef TM_GMTOFF 519 /* Argh! No 'J'! */ 520 if (*bp >= 'A' && *bp <= 'I') 521 tm->TM_GMTOFF = 522 ('A' - 1) - (int)*bp; 523 else if (*bp >= 'L' && *bp <= 'M') 524 tm->TM_GMTOFF = 'A' - (int)*bp; 525 else if (*bp >= 'N' && *bp <= 'Y') 526 tm->TM_GMTOFF = (int)*bp - 'M'; 527 tm->TM_GMTOFF *= SECSPERHOUR; 528 #endif 529 #ifdef TM_ZONE 530 tm->TM_ZONE = NULL; /* XXX */ 531 #endif 532 bp++; 533 continue; 534 } 535 /* 'J' is local time */ 536 if (delim(bp[1]) && *bp == 'J') { 537 #ifdef TM_GMTOFF 538 tm->TM_GMTOFF = -timezone; 539 #endif 540 #ifdef TM_ZONE 541 tm->TM_ZONE = NULL; /* XXX */ 542 #endif 543 bp++; 544 continue; 545 } 546 547 /* 548 * From our 3 letter hard-coded table 549 * XXX: Can be removed, handled by tzload() 550 */ 551 if (delim(bp[0]) || delim(bp[1]) || 552 delim(bp[2]) || !delim(bp[3])) 553 goto loadzone; 554 ep = find_string(bp, &i, nast, NULL, 4); 555 if (ep != NULL) { 556 #ifdef TM_GMTOFF 557 tm->TM_GMTOFF = (-5 - i) * SECSPERHOUR; 558 #endif 559 #ifdef TM_ZONE 560 tm->TM_ZONE = __UNCONST(nast[i]); 561 #endif 562 bp = ep; 563 continue; 564 } 565 ep = find_string(bp, &i, nadt, NULL, 4); 566 if (ep != NULL) { 567 tm->tm_isdst = 1; 568 #ifdef TM_GMTOFF 569 tm->TM_GMTOFF = (-4 - i) * SECSPERHOUR; 570 #endif 571 #ifdef TM_ZONE 572 tm->TM_ZONE = __UNCONST(nadt[i]); 573 #endif 574 bp = ep; 575 continue; 576 } 577 /* 578 * Our current timezone 579 */ 580 ep = find_string(bp, &i, 581 (const char * const *)tzname, 582 NULL, 2); 583 if (ep != NULL) { 584 tm->tm_isdst = i; 585 #ifdef TM_GMTOFF 586 tm->TM_GMTOFF = -timezone; 587 #endif 588 #ifdef TM_ZONE 589 tm->TM_ZONE = tzname[i]; 590 #endif 591 bp = ep; 592 continue; 593 } 594 loadzone: 595 /* 596 * The hard way, load the zone! 597 */ 598 if (fromzone(&bp, tm, mandatory)) 599 continue; 600 goto out; 601 } 602 offs = 0; 603 for (i = 0; i < 4; ) { 604 if (isdigit(*bp)) { 605 offs = offs * 10 + (*bp++ - '0'); 606 i++; 607 continue; 608 } 609 if (i == 2 && *bp == ':') { 610 bp++; 611 continue; 612 } 613 break; 614 } 615 if (isdigit(*bp)) 616 goto out; 617 switch (i) { 618 case 2: 619 offs *= SECSPERHOUR; 620 break; 621 case 4: 622 i = offs % 100; 623 offs /= 100; 624 if (i >= SECSPERMIN) 625 goto out; 626 /* Convert minutes into decimal */ 627 offs = offs * SECSPERHOUR + i * SECSPERMIN; 628 break; 629 default: 630 out: 631 if (mandatory) 632 return NULL; 633 bp = zname; 634 continue; 635 } 636 if (offs >= (HOURSPERDAY * SECSPERHOUR)) 637 goto out; 638 if (neg) 639 offs = -offs; 640 tm->tm_isdst = 0; /* XXX */ 641 #ifdef TM_GMTOFF 642 tm->TM_GMTOFF = offs; 643 #endif 644 #ifdef TM_ZONE 645 tm->TM_ZONE = NULL; /* XXX */ 646 #endif 647 continue; 648 649 /* 650 * Miscellaneous conversions. 651 */ 652 case 'n': /* Any kind of white-space. */ 653 case 't': 654 while (isspace(*bp)) 655 bp++; 656 LEGAL_ALT(0); 657 continue; 658 659 660 default: /* Unknown/unsupported conversion. */ 661 return NULL; 662 } 663 } 664 665 if (!HAVE_YDAY(state) && HAVE_YEAR(state)) { 666 if (HAVE_MON(state) && HAVE_MDAY(state)) { 667 /* calculate day of year (ordinal date) */ 668 tm->tm_yday = start_of_month[isleap_sum(tm->tm_year, 669 TM_YEAR_BASE)][tm->tm_mon] + (tm->tm_mday - 1); 670 state |= S_YDAY; 671 } else if (day_offset != -1) { 672 /* 673 * Set the date to the first Sunday (or Monday) 674 * of the specified week of the year. 675 */ 676 if (!HAVE_WDAY(state)) { 677 tm->tm_wday = day_offset; 678 state |= S_WDAY; 679 } 680 tm->tm_yday = (7 - 681 first_wday_of(tm->tm_year + TM_YEAR_BASE) + 682 day_offset) % 7 + (week_offset - 1) * 7 + 683 tm->tm_wday - day_offset; 684 state |= S_YDAY; 685 } 686 } 687 688 if (HAVE_YDAY(state) && HAVE_YEAR(state)) { 689 int isleap; 690 691 if (!HAVE_MON(state)) { 692 /* calculate month of day of year */ 693 i = 0; 694 isleap = isleap_sum(tm->tm_year, TM_YEAR_BASE); 695 while (tm->tm_yday >= start_of_month[isleap][i]) 696 i++; 697 if (i > 12) { 698 i = 1; 699 tm->tm_yday -= start_of_month[isleap][12]; 700 tm->tm_year++; 701 } 702 tm->tm_mon = i - 1; 703 state |= S_MON; 704 } 705 706 if (!HAVE_MDAY(state)) { 707 /* calculate day of month */ 708 isleap = isleap_sum(tm->tm_year, TM_YEAR_BASE); 709 tm->tm_mday = tm->tm_yday - 710 start_of_month[isleap][tm->tm_mon] + 1; 711 state |= S_MDAY; 712 } 713 714 if (!HAVE_WDAY(state)) { 715 /* calculate day of week */ 716 i = 0; 717 week_offset = first_wday_of(tm->tm_year); 718 while (i++ <= tm->tm_yday) { 719 if (week_offset++ >= 6) 720 week_offset = 0; 721 } 722 tm->tm_wday = week_offset; 723 state |= S_WDAY; 724 } 725 } 726 727 return __UNCONST(bp); 728 } 729 730 731 static const u_char * 732 conv_num(const unsigned char *buf, int *dest, uint llim, uint ulim) 733 { 734 uint result = 0; 735 unsigned char ch; 736 737 /* The limit also determines the number of valid digits. */ 738 uint rulim = ulim; 739 740 ch = *buf; 741 if (ch < '0' || ch > '9') 742 return NULL; 743 744 do { 745 result *= 10; 746 result += ch - '0'; 747 rulim /= 10; 748 ch = *++buf; 749 } while ((result * 10 <= ulim) && rulim && ch >= '0' && ch <= '9'); 750 751 if (result < llim || result > ulim) 752 return NULL; 753 754 *dest = result; 755 return buf; 756 } 757 758 static const u_char * 759 find_string(const u_char *bp, int *tgt, const char * const *n1, 760 const char * const *n2, int c) 761 { 762 int i; 763 size_t len; 764 765 /* check full name - then abbreviated ones */ 766 for (; n1 != NULL; n1 = n2, n2 = NULL) { 767 for (i = 0; i < c; i++, n1++) { 768 len = strlen(*n1); 769 if (strncasecmp(*n1, (const char *)bp, len) == 0) { 770 *tgt = i; 771 return bp + len; 772 } 773 } 774 } 775 776 /* Nothing matched */ 777 return NULL; 778 } 779