1/* $NetBSD: strptime.c,v 1.67 2024/06/07 13:53:23 riastradh 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.67 2024/06/07 13:53:23 riastradh 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 53static const u_char *conv_num(const unsigned char *, int *, uint, uint); 54static 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[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 82static char utc[] = { "UTC" }; 83/* RFC-822/RFC-2822 */ 84static const char * const nast[5] = { 85 "EST", "CST", "MST", "PST", "\0\0\0" 86}; 87static 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 */ 95static 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 109static int 110first_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 118static int 119fromzone(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 if (!isalnum((unsigned char)*buf)) 132 return 0; 133 tz = tzalloc(buf); 134 if (tz == NULL) 135 return 0; 136 137 *bp = rp; 138 tm->tm_isdst = 0; /* XXX */ 139#ifdef TM_GMTOFF 140 tm->TM_GMTOFF = tzgetgmtoff(tz, tm->tm_isdst); 141#endif 142#ifdef TM_ZONE 143 // Can't use tzgetname() here because we are going to free() 144 tm->TM_ZONE = NULL; /* XXX */ 145#endif 146 tzfree(tz); 147 return 1; 148} 149 150char * 151strptime(const char *buf, const char *fmt, struct tm *tm) 152{ 153 return strptime_l(buf, fmt, tm, _current_locale()); 154} 155 156char * 157strptime_l(const char *buf, const char *fmt, struct tm *tm, locale_t loc) 158{ 159 unsigned char c; 160 const unsigned char *bp, *ep, *zname; 161 int alt_format, i, split_year = 0, neg = 0, state = 0, 162 day_offset = -1, week_offset = 0, offs, mandatory; 163 const char *new_fmt; 164 165 bp = (const u_char *)buf; 166 167 while (bp != NULL && (c = *fmt++) != '\0') { 168 /* Clear `alternate' modifier prior to new conversion. */ 169 alt_format = 0; 170 i = 0; 171 172 /* Eat up white-space. */ 173 if (isspace(c)) { 174 while (isspace(*bp)) 175 bp++; 176 continue; 177 } 178 179 if (c != '%') 180 goto literal; 181 182 183again: switch (c = *fmt++) { 184 case '%': /* "%%" is converted to "%". */ 185literal: 186 if (c != *bp++) 187 return NULL; 188 LEGAL_ALT(0); 189 continue; 190 191 /* 192 * "Alternative" modifiers. Just set the appropriate flag 193 * and start over again. 194 */ 195 case 'E': /* "%E?" alternative conversion modifier. */ 196 LEGAL_ALT(0); 197 alt_format |= ALT_E; 198 goto again; 199 200 case 'O': /* "%O?" alternative conversion modifier. */ 201 LEGAL_ALT(0); 202 alt_format |= ALT_O; 203 goto again; 204 205 /* 206 * "Complex" conversion rules, implemented through recursion. 207 */ 208 case 'c': /* Date and time, using the locale's format. */ 209 new_fmt = _TIME_LOCALE(loc)->d_t_fmt; 210 state |= S_WDAY | S_MON | S_MDAY | S_YEAR; 211 goto recurse; 212 213 case 'D': /* The date as "%m/%d/%y". */ 214 new_fmt = "%m/%d/%y"; 215 LEGAL_ALT(0); 216 state |= S_MON | S_MDAY | S_YEAR; 217 goto recurse; 218 219 case 'F': /* The date as "%Y-%m-%d". */ 220 new_fmt = "%Y-%m-%d"; 221 LEGAL_ALT(0); 222 state |= S_MON | S_MDAY | S_YEAR; 223 goto recurse; 224 225 case 'R': /* The time as "%H:%M". */ 226 new_fmt = "%H:%M"; 227 LEGAL_ALT(0); 228 goto recurse; 229 230 case 'r': /* The time in 12-hour clock representation. */ 231 new_fmt = _TIME_LOCALE(loc)->t_fmt_ampm; 232 LEGAL_ALT(0); 233 goto recurse; 234 235 case 'T': /* The time as "%H:%M:%S". */ 236 new_fmt = "%H:%M:%S"; 237 LEGAL_ALT(0); 238 goto recurse; 239 240 case 'X': /* The time, using the locale's format. */ 241 new_fmt = _TIME_LOCALE(loc)->t_fmt; 242 goto recurse; 243 244 case 'x': /* The date, using the locale's format. */ 245 new_fmt = _TIME_LOCALE(loc)->d_fmt; 246 state |= S_MON | S_MDAY | S_YEAR; 247 recurse: 248 bp = (const u_char *)strptime((const char *)bp, 249 new_fmt, tm); 250 LEGAL_ALT(ALT_E); 251 continue; 252 253 /* 254 * "Elementary" conversion rules. 255 */ 256 case 'A': /* The day of week, using the locale's form. */ 257 case 'a': 258 bp = find_string(bp, &tm->tm_wday, 259 _TIME_LOCALE(loc)->day, _TIME_LOCALE(loc)->abday, 7); 260 LEGAL_ALT(0); 261 state |= S_WDAY; 262 continue; 263 264 case 'B': /* The month, using the locale's form. */ 265 case 'b': 266 case 'h': 267 bp = find_string(bp, &tm->tm_mon, 268 _TIME_LOCALE(loc)->mon, _TIME_LOCALE(loc)->abmon, 269 12); 270 LEGAL_ALT(0); 271 state |= S_MON; 272 continue; 273 274 case 'C': /* The century number. */ 275 i = 20; 276 bp = conv_num(bp, &i, 0, 99); 277 278 i = i * 100 - TM_YEAR_BASE; 279 if (split_year) 280 i += tm->tm_year % 100; 281 split_year = 1; 282 tm->tm_year = i; 283 LEGAL_ALT(ALT_E); 284 state |= S_YEAR; 285 continue; 286 287 case 'd': /* The day of month. */ 288 case 'e': 289 bp = conv_num(bp, &tm->tm_mday, 1, 31); 290 LEGAL_ALT(ALT_O); 291 state |= S_MDAY; 292 continue; 293 294 case 'k': /* The hour (24-hour clock representation). */ 295 LEGAL_ALT(0); 296 /* FALLTHROUGH */ 297 case 'H': 298 bp = conv_num(bp, &tm->tm_hour, 0, 23); 299 LEGAL_ALT(ALT_O); 300 state |= S_HOUR; 301 continue; 302 303 case 'l': /* The hour (12-hour clock representation). */ 304 LEGAL_ALT(0); 305 /* FALLTHROUGH */ 306 case 'I': 307 bp = conv_num(bp, &tm->tm_hour, 1, 12); 308 if (tm->tm_hour == 12) 309 tm->tm_hour = 0; 310 LEGAL_ALT(ALT_O); 311 state |= S_HOUR; 312 continue; 313 314 case 'j': /* The day of year. */ 315 i = 1; 316 bp = conv_num(bp, &i, 1, 366); 317 tm->tm_yday = i - 1; 318 LEGAL_ALT(0); 319 state |= S_YDAY; 320 continue; 321 322 case 'M': /* The minute. */ 323 bp = conv_num(bp, &tm->tm_min, 0, 59); 324 LEGAL_ALT(ALT_O); 325 continue; 326 327 case 'm': /* The month. */ 328 i = 1; 329 bp = conv_num(bp, &i, 1, 12); 330 tm->tm_mon = i - 1; 331 LEGAL_ALT(ALT_O); 332 state |= S_MON; 333 continue; 334 335 case 'p': /* The locale's equivalent of AM/PM. */ 336 bp = find_string(bp, &i, _TIME_LOCALE(loc)->am_pm, 337 NULL, 2); 338 if (HAVE_HOUR(state) && tm->tm_hour > 11) 339 return NULL; 340 tm->tm_hour += i * 12; 341 LEGAL_ALT(0); 342 continue; 343 344 case 'S': /* The seconds. */ 345 bp = conv_num(bp, &tm->tm_sec, 0, 61); 346 LEGAL_ALT(ALT_O); 347 continue; 348 349 case 's': { /* seconds since the epoch */ 350 const time_t TIME_MAX = __type_max(time_t); 351 time_t sse, d; 352 353 if (*bp < '0' || *bp > '9') { 354 bp = NULL; 355 continue; 356 } 357 358 sse = *bp++ - '0'; 359 while (*bp >= '0' && *bp <= '9') { 360 d = *bp++ - '0'; 361 if (sse > TIME_MAX/10) { 362 bp = NULL; 363 break; 364 } 365 sse *= 10; 366 if (sse > TIME_MAX - d) { 367 bp = NULL; 368 break; 369 } 370 sse += d; 371 } 372 if (bp == NULL) 373 continue; 374 375 if (localtime_r(&sse, tm) == NULL) 376 bp = NULL; 377 else 378 state |= S_YDAY | S_WDAY | 379 S_MON | S_MDAY | S_YEAR; 380 continue; 381 } 382 383 case 'U': /* The week of year, beginning on sunday. */ 384 case 'W': /* The week of year, beginning on monday. */ 385 /* 386 * This is bogus, as we can not assume any valid 387 * information present in the tm structure at this 388 * point to calculate a real value, so save the 389 * week for now in case it can be used later. 390 */ 391 bp = conv_num(bp, &i, 0, 53); 392 LEGAL_ALT(ALT_O); 393 if (c == 'U') 394 day_offset = TM_SUNDAY; 395 else 396 day_offset = TM_MONDAY; 397 week_offset = i; 398 continue; 399 400 case 'w': /* The day of week, beginning on sunday. */ 401 bp = conv_num(bp, &tm->tm_wday, 0, 6); 402 LEGAL_ALT(ALT_O); 403 state |= S_WDAY; 404 continue; 405 406 case 'u': /* The day of week, monday = 1. */ 407 bp = conv_num(bp, &i, 1, 7); 408 tm->tm_wday = i % 7; 409 LEGAL_ALT(ALT_O); 410 state |= S_WDAY; 411 continue; 412 413 case 'g': /* The year corresponding to the ISO week 414 * number but without the century. 415 */ 416 bp = conv_num(bp, &i, 0, 99); 417 continue; 418 419 case 'G': /* The year corresponding to the ISO week 420 * number with century. 421 */ 422 do 423 bp++; 424 while (isdigit(*bp)); 425 continue; 426 427 case 'V': /* The ISO 8601:1988 week number as decimal */ 428 bp = conv_num(bp, &i, 1, 53); 429 continue; 430 431 case 'Y': /* The year. */ 432 i = TM_YEAR_BASE; /* just for data sanity... */ 433 bp = conv_num(bp, &i, 0, 9999); 434 tm->tm_year = i - TM_YEAR_BASE; 435 LEGAL_ALT(ALT_E); 436 state |= S_YEAR; 437 continue; 438 439 case 'y': /* The year within 100 years of the epoch. */ 440 /* LEGAL_ALT(ALT_E | ALT_O); */ 441 bp = conv_num(bp, &i, 0, 99); 442 443 if (split_year) 444 /* preserve century */ 445 i += (tm->tm_year / 100) * 100; 446 else { 447 split_year = 1; 448 if (i <= 68) 449 i = i + 2000 - TM_YEAR_BASE; 450 else 451 i = i + 1900 - TM_YEAR_BASE; 452 } 453 tm->tm_year = i; 454 state |= S_YEAR; 455 continue; 456 457 case 'Z': 458 case 'z': 459 tzset(); 460 mandatory = c == 'z'; 461 /* 462 * We recognize all ISO 8601 formats: 463 * Z = Zulu time/UTC 464 * [+-]hhmm 465 * [+-]hh:mm 466 * [+-]hh 467 * We recognize all RFC-822/RFC-2822 formats: 468 * UT|GMT 469 * North American : UTC offsets 470 * E[DS]T = Eastern : -4 | -5 471 * C[DS]T = Central : -5 | -6 472 * M[DS]T = Mountain: -6 | -7 473 * P[DS]T = Pacific : -7 | -8 474 * Nautical/Military 475 * [A-IL-M] = -1 ... -9 (J not used) 476 * [N-Y] = +1 ... +12 477 * Note: J maybe used to denote non-nautical 478 * local time 479 */ 480 if (mandatory) 481 while (isspace(*bp)) 482 bp++; 483 484 zname = bp; 485 switch (*bp++) { 486 case 'G': 487 if (*bp++ != 'M') 488 goto namedzone; 489 /*FALLTHROUGH*/ 490 case 'U': 491 if (*bp++ != 'T') 492 goto namedzone; 493 else if (!delim(*bp) && *bp++ != 'C') 494 goto namedzone; 495 /*FALLTHROUGH*/ 496 case 'Z': 497 if (!delim(*bp)) 498 goto namedzone; 499 tm->tm_isdst = 0; 500#ifdef TM_GMTOFF 501 tm->TM_GMTOFF = 0; 502#endif 503#ifdef TM_ZONE 504 tm->TM_ZONE = utc; 505#endif 506 continue; 507 case '+': 508 neg = 0; 509 break; 510 case '-': 511 neg = 1; 512 break; 513 default: 514namedzone: 515 bp = zname; 516 517 /* Nautical / Military style */ 518 if (delim(bp[1]) && 519 ((*bp >= 'A' && *bp <= 'I') || 520 (*bp >= 'L' && *bp <= 'Y'))) { 521#ifdef TM_GMTOFF 522 /* Argh! No 'J'! */ 523 if (*bp >= 'A' && *bp <= 'I') 524 tm->TM_GMTOFF = 525 (int)*bp - ('A' - 1); 526 else if (*bp >= 'L' && *bp <= 'M') 527 tm->TM_GMTOFF = (int)*bp - 'A'; 528 else if (*bp >= 'N' && *bp <= 'Y') 529 tm->TM_GMTOFF = 'M' - (int)*bp; 530 tm->TM_GMTOFF *= SECSPERHOUR; 531#endif 532#ifdef TM_ZONE 533 tm->TM_ZONE = NULL; /* XXX */ 534#endif 535 bp++; 536 continue; 537 } 538 /* 'J' is local time */ 539 if (delim(bp[1]) && *bp == 'J') { 540#ifdef TM_GMTOFF 541 tm->TM_GMTOFF = -timezone; 542#endif 543#ifdef TM_ZONE 544 tm->TM_ZONE = NULL; /* XXX */ 545#endif 546 bp++; 547 continue; 548 } 549 550 /* 551 * From our 3 letter hard-coded table 552 * XXX: Can be removed, handled by tzload() 553 */ 554 if (delim(bp[0]) || delim(bp[1]) || 555 delim(bp[2]) || !delim(bp[3])) 556 goto loadzone; 557 ep = find_string(bp, &i, nast, NULL, 4); 558 if (ep != NULL) { 559#ifdef TM_GMTOFF 560 tm->TM_GMTOFF = (-5 - i) * SECSPERHOUR; 561#endif 562#ifdef TM_ZONE 563 tm->TM_ZONE = __UNCONST(nast[i]); 564#endif 565 bp = ep; 566 continue; 567 } 568 ep = find_string(bp, &i, nadt, NULL, 4); 569 if (ep != NULL) { 570 tm->tm_isdst = 1; 571#ifdef TM_GMTOFF 572 tm->TM_GMTOFF = (-4 - i) * SECSPERHOUR; 573#endif 574#ifdef TM_ZONE 575 tm->TM_ZONE = __UNCONST(nadt[i]); 576#endif 577 bp = ep; 578 continue; 579 } 580 /* 581 * Our current timezone 582 */ 583 ep = find_string(bp, &i, 584 (const char * const *)tzname, 585 NULL, 2); 586 if (ep != NULL) { 587 tm->tm_isdst = i; 588#ifdef TM_GMTOFF 589 tm->TM_GMTOFF = -timezone; 590#endif 591#ifdef TM_ZONE 592 tm->TM_ZONE = tzname[i]; 593#endif 594 bp = ep; 595 continue; 596 } 597loadzone: 598 /* 599 * The hard way, load the zone! 600 */ 601 if (fromzone(&bp, tm, mandatory)) 602 continue; 603 goto out; 604 } 605 offs = 0; 606 for (i = 0; i < 4; ) { 607 if (isdigit(*bp)) { 608 offs = offs * 10 + (*bp++ - '0'); 609 i++; 610 continue; 611 } 612 if (i == 2 && *bp == ':') { 613 bp++; 614 continue; 615 } 616 break; 617 } 618 if (isdigit(*bp)) 619 goto out; 620 switch (i) { 621 case 2: 622 offs *= SECSPERHOUR; 623 break; 624 case 4: 625 i = offs % 100; 626 offs /= 100; 627 if (i >= SECSPERMIN) 628 goto out; 629 /* Convert minutes into decimal */ 630 offs = offs * SECSPERHOUR + i * SECSPERMIN; 631 break; 632 default: 633 out: 634 if (mandatory) 635 return NULL; 636 bp = zname; 637 continue; 638 } 639 /* ISO 8601 & RFC 3339 limit to 23:59 max */ 640 if (offs >= (HOURSPERDAY * SECSPERHOUR)) 641 goto out; 642 if (neg) 643 offs = -offs; 644 tm->tm_isdst = 0; /* XXX */ 645#ifdef TM_GMTOFF 646 tm->TM_GMTOFF = offs; 647#endif 648#ifdef TM_ZONE 649 tm->TM_ZONE = NULL; /* XXX */ 650#endif 651 continue; 652 653 /* 654 * Miscellaneous conversions. 655 */ 656 case 'n': /* Any kind of white-space. */ 657 case 't': 658 while (isspace(*bp)) 659 bp++; 660 LEGAL_ALT(0); 661 continue; 662 663 664 default: /* Unknown/unsupported conversion. */ 665 return NULL; 666 } 667 } 668 669 if (!HAVE_YDAY(state) && HAVE_YEAR(state)) { 670 if (HAVE_MON(state) && HAVE_MDAY(state)) { 671 /* calculate day of year (ordinal date) */ 672 tm->tm_yday = start_of_month[isleap_sum(tm->tm_year, 673 TM_YEAR_BASE)][tm->tm_mon] + (tm->tm_mday - 1); 674 state |= S_YDAY; 675 } else if (day_offset != -1) { 676 /* 677 * Set the date to the first Sunday (or Monday) 678 * of the specified week of the year. 679 */ 680 if (!HAVE_WDAY(state)) { 681 tm->tm_wday = day_offset; 682 state |= S_WDAY; 683 } 684 tm->tm_yday = (7 - 685 first_wday_of(tm->tm_year + TM_YEAR_BASE) + 686 day_offset) % 7 + (week_offset - 1) * 7 + 687 tm->tm_wday - day_offset; 688 state |= S_YDAY; 689 } 690 } 691 692 if (HAVE_YDAY(state) && HAVE_YEAR(state)) { 693 int isleap; 694 695 if (!HAVE_MON(state)) { 696 /* calculate month of day of year */ 697 i = 0; 698 isleap = isleap_sum(tm->tm_year, TM_YEAR_BASE); 699 while (tm->tm_yday >= start_of_month[isleap][i]) 700 i++; 701 if (i > 12) { 702 i = 1; 703 tm->tm_yday -= start_of_month[isleap][12]; 704 tm->tm_year++; 705 } 706 tm->tm_mon = i - 1; 707 state |= S_MON; 708 } 709 710 if (!HAVE_MDAY(state)) { 711 /* calculate day of month */ 712 isleap = isleap_sum(tm->tm_year, TM_YEAR_BASE); 713 tm->tm_mday = tm->tm_yday - 714 start_of_month[isleap][tm->tm_mon] + 1; 715 state |= S_MDAY; 716 } 717 718 if (!HAVE_WDAY(state)) { 719 /* calculate day of week */ 720 i = 0; 721 week_offset = first_wday_of(tm->tm_year); 722 while (i++ <= tm->tm_yday) { 723 if (week_offset++ >= 6) 724 week_offset = 0; 725 } 726 tm->tm_wday = week_offset; 727 state |= S_WDAY; 728 } 729 } 730 731 return __UNCONST(bp); 732} 733 734 735static const u_char * 736conv_num(const unsigned char *buf, int *dest, uint llim, uint ulim) 737{ 738 uint result = 0; 739 unsigned char ch; 740 741 /* The limit also determines the number of valid digits. */ 742 uint rulim = ulim; 743 744 ch = *buf; 745 if (ch < '0' || ch > '9') 746 return NULL; 747 748 do { 749 result *= 10; 750 result += ch - '0'; 751 rulim /= 10; 752 ch = *++buf; 753 } while ((result * 10 <= ulim) && rulim && ch >= '0' && ch <= '9'); 754 755 if (result < llim || result > ulim) 756 return NULL; 757 758 *dest = result; 759 return buf; 760} 761 762static const u_char * 763find_string(const u_char *bp, int *tgt, const char * const *n1, 764 const char * const *n2, int c) 765{ 766 int i; 767 size_t len; 768 769 /* check full name - then abbreviated ones */ 770 for (; n1 != NULL; n1 = n2, n2 = NULL) { 771 for (i = 0; i < c; i++, n1++) { 772 len = strlen(*n1); 773 if (strncasecmp(*n1, (const char *)bp, len) == 0) { 774 *tgt = i; 775 return bp + len; 776 } 777 } 778 } 779 780 /* Nothing matched */ 781 return NULL; 782} 783