1 /*- 2 * Copyright (c) 2014 Gary Mills 3 * Copyright 2011, Nexenta Systems, Inc. All rights reserved. 4 * Copyright (c) 1994 Powerdog Industries. All rights reserved. 5 * 6 * Copyright (c) 2011 The FreeBSD Foundation 7 * All rights reserved. 8 * Portions of this software were developed by David Chisnall 9 * under sponsorship from the FreeBSD Foundation. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer 18 * in the documentation and/or other materials provided with the 19 * distribution. 20 * 21 * THIS SOFTWARE IS PROVIDED BY POWERDOG INDUSTRIES ``AS IS'' AND ANY 22 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE POWERDOG INDUSTRIES BE 25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 28 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 30 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 31 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 * 33 * The views and conclusions contained in the software and documentation 34 * are those of the authors and should not be interpreted as representing 35 * official policies, either expressed or implied, of Powerdog Industries. 36 * 37 * @(#)strptime.c 0.1 (Powerdog) 94/03/27 38 * @(#) Copyright (c) 1994 Powerdog Industries. All rights reserved. 39 * $FreeBSD: head/lib/libc/stdtime/strptime.c 272679 2014-10-07 06:34:05Z ache $ 40 */ 41 42 #include "namespace.h" 43 #include <time.h> 44 #include <ctype.h> 45 #include <errno.h> 46 #include <stdlib.h> 47 #include <string.h> 48 #include <pthread.h> 49 #include "un-namespace.h" 50 #include "libc_private.h" 51 #include "timelocal.h" 52 #include "tzfile.h" 53 54 static char * _strptime(const char *, const char *, struct tm *, int *, locale_t); 55 56 #define asizeof(a) (sizeof(a) / sizeof((a)[0])) 57 58 #define FLAG_NONE (1 << 0) 59 #define FLAG_YEAR (1 << 1) 60 #define FLAG_MONTH (1 << 2) 61 #define FLAG_YDAY (1 << 3) 62 #define FLAG_MDAY (1 << 4) 63 #define FLAG_WDAY (1 << 5) 64 65 /* 66 * Calculate the week day of the first day of a year. Valid for 67 * the Gregorian calendar, which began Sept 14, 1752 in the UK 68 * and its colonies. Ref: 69 * http://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week 70 */ 71 72 static int 73 first_wday_of(int year) 74 { 75 return (((2 * (3 - (year / 100) % 4)) + (year % 100) + 76 ((year % 100) / 4) + (isleap(year) ? 6 : 0) + 1) % 7); 77 } 78 79 static char * 80 _strptime(const char *buf, const char *fmt, struct tm *tm, int *GMTp, 81 locale_t locale) 82 { 83 char c; 84 const char *ptr; 85 int day_offset = -1, wday_offset; 86 int week_offset; 87 int i, len; 88 int flags; 89 int Ealternative, Oalternative; 90 const struct lc_time_T *tptr = __get_current_time_locale(locale); 91 static int start_of_month[2][13] = { 92 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}, 93 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366} 94 }; 95 96 flags = FLAG_NONE; 97 98 ptr = fmt; 99 while (*ptr != 0) { 100 c = *ptr++; 101 102 if (c != '%') { 103 if (isspace_l((unsigned char)c, locale)) 104 while (*buf != 0 && 105 isspace_l((unsigned char)*buf, locale)) 106 buf++; 107 else if (c != *buf++) 108 return (NULL); 109 continue; 110 } 111 112 Ealternative = 0; 113 Oalternative = 0; 114 label: 115 c = *ptr++; 116 switch (c) { 117 case '%': 118 if (*buf++ != '%') 119 return (NULL); 120 break; 121 122 case '+': 123 buf = _strptime(buf, tptr->date_fmt, tm, GMTp, locale); 124 if (buf == NULL) 125 return (NULL); 126 flags |= FLAG_WDAY | FLAG_MONTH | FLAG_MDAY | FLAG_YEAR; 127 break; 128 129 case 'C': 130 if (!isdigit_l((unsigned char)*buf, locale)) 131 return (NULL); 132 133 /* XXX This will break for 3-digit centuries. */ 134 len = 2; 135 for (i = 0; len && *buf != 0 && 136 isdigit_l((unsigned char)*buf, locale); buf++) { 137 i *= 10; 138 i += *buf - '0'; 139 len--; 140 } 141 if (i < 19) 142 return (NULL); 143 144 tm->tm_year = i * 100 - TM_YEAR_BASE; 145 flags |= FLAG_YEAR; 146 147 break; 148 149 case 'c': 150 buf = _strptime(buf, tptr->c_fmt, tm, GMTp, locale); 151 if (buf == NULL) 152 return (NULL); 153 flags |= FLAG_WDAY | FLAG_MONTH | FLAG_MDAY | FLAG_YEAR; 154 break; 155 156 case 'D': 157 buf = _strptime(buf, "%m/%d/%y", tm, GMTp, locale); 158 if (buf == NULL) 159 return (NULL); 160 flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR; 161 break; 162 163 case 'E': 164 if (Ealternative || Oalternative) 165 break; 166 Ealternative++; 167 goto label; 168 169 case 'O': 170 if (Ealternative || Oalternative) 171 break; 172 Oalternative++; 173 goto label; 174 175 case 'F': 176 buf = _strptime(buf, "%Y-%m-%d", tm, GMTp, locale); 177 if (buf == NULL) 178 return (NULL); 179 flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR; 180 break; 181 182 case 'R': 183 buf = _strptime(buf, "%H:%M", tm, GMTp, locale); 184 if (buf == NULL) 185 return (NULL); 186 break; 187 188 case 'r': 189 buf = _strptime(buf, tptr->ampm_fmt, tm, GMTp, locale); 190 if (buf == NULL) 191 return (NULL); 192 break; 193 194 case 'T': 195 buf = _strptime(buf, "%H:%M:%S", tm, GMTp, locale); 196 if (buf == NULL) 197 return (NULL); 198 break; 199 200 case 'X': 201 buf = _strptime(buf, tptr->X_fmt, tm, GMTp, locale); 202 if (buf == NULL) 203 return (NULL); 204 break; 205 206 case 'x': 207 buf = _strptime(buf, tptr->x_fmt, tm, GMTp, locale); 208 if (buf == NULL) 209 return (NULL); 210 flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR; 211 break; 212 213 case 'j': 214 if (!isdigit_l((unsigned char)*buf, locale)) 215 return (NULL); 216 217 len = 3; 218 for (i = 0; len && *buf != 0 && 219 isdigit_l((unsigned char)*buf, locale); buf++){ 220 i *= 10; 221 i += *buf - '0'; 222 len--; 223 } 224 if (i < 1 || i > 366) 225 return (NULL); 226 227 tm->tm_yday = i - 1; 228 flags |= FLAG_YDAY; 229 230 break; 231 232 case 'M': 233 case 'S': 234 if (*buf == 0 || 235 isspace_l((unsigned char)*buf, locale)) 236 break; 237 238 if (!isdigit_l((unsigned char)*buf, locale)) 239 return (NULL); 240 241 len = 2; 242 for (i = 0; len && *buf != 0 && 243 isdigit_l((unsigned char)*buf, locale); buf++){ 244 i *= 10; 245 i += *buf - '0'; 246 len--; 247 } 248 249 if (c == 'M') { 250 if (i > 59) 251 return (NULL); 252 tm->tm_min = i; 253 } else { 254 if (i > 60) 255 return (NULL); 256 tm->tm_sec = i; 257 } 258 259 break; 260 261 case 'H': 262 case 'I': 263 case 'k': 264 case 'l': 265 /* 266 * Of these, %l is the only specifier explicitly 267 * documented as not being zero-padded. However, 268 * there is no harm in allowing zero-padding. 269 * 270 * XXX The %l specifier may gobble one too many 271 * digits if used incorrectly. 272 */ 273 if (!isdigit_l((unsigned char)*buf, locale)) 274 return (NULL); 275 276 len = 2; 277 for (i = 0; len && *buf != 0 && 278 isdigit_l((unsigned char)*buf, locale); buf++) { 279 i *= 10; 280 i += *buf - '0'; 281 len--; 282 } 283 if (c == 'H' || c == 'k') { 284 if (i > 23) 285 return (NULL); 286 } else if (i > 12) 287 return (NULL); 288 289 tm->tm_hour = i; 290 291 break; 292 293 case 'p': 294 /* 295 * XXX This is bogus if parsed before hour-related 296 * specifiers. 297 */ 298 len = strlen(tptr->am); 299 if (strncasecmp_l(buf, tptr->am, len, locale) == 0) { 300 if (tm->tm_hour > 12) 301 return (NULL); 302 if (tm->tm_hour == 12) 303 tm->tm_hour = 0; 304 buf += len; 305 break; 306 } 307 308 len = strlen(tptr->pm); 309 if (strncasecmp_l(buf, tptr->pm, len, locale) == 0) { 310 if (tm->tm_hour > 12) 311 return (NULL); 312 if (tm->tm_hour != 12) 313 tm->tm_hour += 12; 314 buf += len; 315 break; 316 } 317 318 return (NULL); 319 320 case 'A': 321 case 'a': 322 for (i = 0; i < asizeof(tptr->weekday); i++) { 323 len = strlen(tptr->weekday[i]); 324 if (strncasecmp_l(buf, tptr->weekday[i], 325 len, locale) == 0) 326 break; 327 len = strlen(tptr->wday[i]); 328 if (strncasecmp_l(buf, tptr->wday[i], 329 len, locale) == 0) 330 break; 331 } 332 if (i == asizeof(tptr->weekday)) 333 return (NULL); 334 335 buf += len; 336 tm->tm_wday = i; 337 flags |= FLAG_WDAY; 338 break; 339 340 case 'U': 341 case 'W': 342 /* 343 * XXX This is bogus, as we can not assume any valid 344 * information present in the tm structure at this 345 * point to calculate a real value, so just check the 346 * range for now. 347 */ 348 if (!isdigit_l((unsigned char)*buf, locale)) 349 return (NULL); 350 351 len = 2; 352 for (i = 0; len && *buf != 0 && 353 isdigit_l((unsigned char)*buf, locale); buf++) { 354 i *= 10; 355 i += *buf - '0'; 356 len--; 357 } 358 if (i > 53) 359 return (NULL); 360 361 if (c == 'U') 362 day_offset = TM_SUNDAY; 363 else 364 day_offset = TM_MONDAY; 365 366 367 week_offset = i; 368 369 break; 370 371 case 'w': 372 if (!isdigit_l((unsigned char)*buf, locale)) 373 return (NULL); 374 375 i = *buf - '0'; 376 if (i > 6) 377 return (NULL); 378 379 tm->tm_wday = i; 380 flags |= FLAG_WDAY; 381 382 break; 383 384 case 'e': 385 /* 386 * With %e format, our strftime(3) adds a blank space 387 * before single digits. 388 */ 389 if (*buf != 0 && 390 isspace_l((unsigned char)*buf, locale)) 391 buf++; 392 /* FALLTHROUGH */ 393 case 'd': 394 /* 395 * The %e specifier was once explicitly documented as 396 * not being zero-padded but was later changed to 397 * equivalent to %d. There is no harm in allowing 398 * such padding. 399 * 400 * XXX The %e specifier may gobble one too many 401 * digits if used incorrectly. 402 */ 403 if (!isdigit_l((unsigned char)*buf, locale)) 404 return (NULL); 405 406 len = 2; 407 for (i = 0; len && *buf != 0 && 408 isdigit_l((unsigned char)*buf, locale); buf++) { 409 i *= 10; 410 i += *buf - '0'; 411 len--; 412 } 413 if (i > 31) 414 return (NULL); 415 416 tm->tm_mday = i; 417 flags |= FLAG_MDAY; 418 419 break; 420 421 case 'B': 422 case 'b': 423 case 'h': 424 for (i = 0; i < asizeof(tptr->month); i++) { 425 if (Oalternative) { 426 if (c == 'B') { 427 len = strlen(tptr->alt_month[i]); 428 if (strncasecmp_l(buf, 429 tptr->alt_month[i], 430 len, locale) == 0) 431 break; 432 } 433 } else { 434 len = strlen(tptr->month[i]); 435 if (strncasecmp_l(buf, tptr->month[i], 436 len, locale) == 0) 437 break; 438 } 439 } 440 /* 441 * Try the abbreviated month name if the full name 442 * wasn't found and Oalternative was not requested. 443 */ 444 if (i == asizeof(tptr->month) && !Oalternative) { 445 for (i = 0; i < asizeof(tptr->month); i++) { 446 len = strlen(tptr->mon[i]); 447 if (strncasecmp_l(buf, tptr->mon[i], 448 len, locale) == 0) 449 break; 450 } 451 } 452 if (i == asizeof(tptr->month)) 453 return (NULL); 454 455 tm->tm_mon = i; 456 buf += len; 457 flags |= FLAG_MONTH; 458 459 break; 460 461 case 'm': 462 if (!isdigit_l((unsigned char)*buf, locale)) 463 return (NULL); 464 465 len = 2; 466 for (i = 0; len && *buf != 0 && 467 isdigit_l((unsigned char)*buf, locale); buf++) { 468 i *= 10; 469 i += *buf - '0'; 470 len--; 471 } 472 if (i < 1 || i > 12) 473 return (NULL); 474 475 tm->tm_mon = i - 1; 476 flags |= FLAG_MONTH; 477 478 break; 479 480 case 's': 481 { 482 char *cp; 483 int sverrno; 484 long n; 485 time_t t; 486 487 sverrno = errno; 488 errno = 0; 489 n = strtol_l(buf, &cp, 10, locale); 490 if (errno == ERANGE || (long)(t = n) != n) { 491 errno = sverrno; 492 return (NULL); 493 } 494 errno = sverrno; 495 buf = cp; 496 if (gmtime_r(&t, tm) == NULL) 497 return (NULL); 498 *GMTp = 1; 499 flags |= FLAG_YDAY | FLAG_WDAY | FLAG_MONTH | 500 FLAG_MDAY | FLAG_YEAR; 501 } 502 break; 503 504 case 'Y': 505 case 'y': 506 if (*buf == 0 || 507 isspace_l((unsigned char)*buf, locale)) 508 break; 509 510 if (!isdigit_l((unsigned char)*buf, locale)) 511 return (NULL); 512 513 len = (c == 'Y') ? 4 : 2; 514 for (i = 0; len && *buf != 0 && 515 isdigit_l((unsigned char)*buf, locale); buf++) { 516 i *= 10; 517 i += *buf - '0'; 518 len--; 519 } 520 if (c == 'Y') 521 i -= TM_YEAR_BASE; 522 if (c == 'y' && i < 69) 523 i += 100; 524 if (i < 0) 525 return (NULL); 526 527 tm->tm_year = i; 528 flags |= FLAG_YEAR; 529 530 break; 531 532 case 'Z': 533 { 534 const char *cp; 535 char *zonestr; 536 537 for (cp = buf; *cp && 538 isupper_l((unsigned char)*cp, locale); ++cp) { 539 /*empty*/} 540 if (cp - buf) { 541 zonestr = alloca(cp - buf + 1); 542 strncpy(zonestr, buf, cp - buf); 543 zonestr[cp - buf] = '\0'; 544 tzset(); 545 if (0 == strcmp(zonestr, "GMT") || 546 0 == strcmp(zonestr, "UTC")) { 547 *GMTp = 1; 548 } else if (0 == strcmp(zonestr, tzname[0])) { 549 tm->tm_isdst = 0; 550 } else if (0 == strcmp(zonestr, tzname[1])) { 551 tm->tm_isdst = 1; 552 } else { 553 return (NULL); 554 } 555 buf += cp - buf; 556 } 557 } 558 break; 559 560 case 'z': 561 { 562 int sign = 1; 563 564 if (*buf != '+') { 565 if (*buf == '-') 566 sign = -1; 567 else 568 return (NULL); 569 } 570 571 buf++; 572 i = 0; 573 for (len = 4; len > 0; len--) { 574 if (isdigit_l((unsigned char)*buf, locale)) { 575 i *= 10; 576 i += *buf - '0'; 577 buf++; 578 } else 579 return (NULL); 580 } 581 582 tm->tm_hour -= sign * (i / 100); 583 tm->tm_min -= sign * (i % 100); 584 *GMTp = 1; 585 } 586 break; 587 588 case 'n': 589 case 't': 590 while (isspace_l((unsigned char)*buf, locale)) 591 buf++; 592 break; 593 594 default: 595 return (NULL); 596 } 597 } 598 599 if (!(flags & FLAG_YDAY) && (flags & FLAG_YEAR)) { 600 if ((flags & (FLAG_MONTH | FLAG_MDAY)) == 601 (FLAG_MONTH | FLAG_MDAY)) { 602 tm->tm_yday = start_of_month[isleap(tm->tm_year + 603 TM_YEAR_BASE)][tm->tm_mon] + (tm->tm_mday - 1); 604 flags |= FLAG_YDAY; 605 } else if (day_offset != -1) { 606 /* Set the date to the first Sunday (or Monday) 607 * of the specified week of the year. 608 */ 609 if (!(flags & FLAG_WDAY)) { 610 tm->tm_wday = day_offset; 611 flags |= FLAG_WDAY; 612 } 613 tm->tm_yday = (7 - 614 first_wday_of(tm->tm_year + TM_YEAR_BASE) + 615 day_offset) % 7 + (week_offset - 1) * 7 + 616 tm->tm_wday - day_offset; 617 flags |= FLAG_YDAY; 618 } 619 } 620 621 if ((flags & (FLAG_YEAR | FLAG_YDAY)) == (FLAG_YEAR | FLAG_YDAY)) { 622 if (!(flags & FLAG_MONTH)) { 623 i = 0; 624 while (tm->tm_yday >= 625 start_of_month[isleap(tm->tm_year + 626 TM_YEAR_BASE)][i]) 627 i++; 628 if (i > 12) { 629 i = 1; 630 tm->tm_yday -= 631 start_of_month[isleap(tm->tm_year + 632 TM_YEAR_BASE)][12]; 633 tm->tm_year++; 634 } 635 tm->tm_mon = i - 1; 636 flags |= FLAG_MONTH; 637 } 638 if (!(flags & FLAG_MDAY)) { 639 tm->tm_mday = tm->tm_yday - 640 start_of_month[isleap(tm->tm_year + TM_YEAR_BASE)] 641 [tm->tm_mon] + 1; 642 flags |= FLAG_MDAY; 643 } 644 if (!(flags & FLAG_WDAY)) { 645 i = 0; 646 wday_offset = first_wday_of(tm->tm_year); 647 while (i++ <= tm->tm_yday) { 648 if (wday_offset++ >= 6) 649 wday_offset = 0; 650 } 651 tm->tm_wday = wday_offset; 652 flags |= FLAG_WDAY; 653 } 654 } 655 656 return ((char *)buf); 657 } 658 659 char * 660 strptime_l(const char * __restrict buf, const char * __restrict fmt, 661 struct tm * __restrict tm, locale_t loc) 662 { 663 char *ret; 664 int gmt; 665 FIX_LOCALE(loc); 666 667 gmt = 0; 668 ret = _strptime(buf, fmt, tm, &gmt, loc); 669 if (ret && gmt) { 670 time_t t = timegm(tm); 671 672 localtime_r(&t, tm); 673 } 674 675 return (ret); 676 } 677 678 char * 679 strptime(const char * __restrict buf, const char * __restrict fmt, 680 struct tm * __restrict tm) 681 { 682 return strptime_l(buf, fmt, tm, __get_locale()); 683 } 684