1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1985-2008 AT&T Intellectual Property * 5 * and is licensed under the * 6 * Common Public License, Version 1.0 * 7 * by AT&T Intellectual Property * 8 * * 9 * A copy of the License is available at * 10 * http://www.opensource.org/licenses/cpl1.0.txt * 11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 12 * * 13 * Information and Software Systems Research * 14 * AT&T Research * 15 * Florham Park NJ * 16 * * 17 * Glenn Fowler <gsf@research.att.com> * 18 * David Korn <dgk@research.att.com> * 19 * Phong Vo <kpv@research.att.com> * 20 * * 21 ***********************************************************************/ 22 #pragma prototyped 23 /* 24 * Glenn Fowler 25 * AT&T Research 26 * 27 * Time_t conversion support 28 * 29 * relative times inspired by Steve Bellovin's netnews getdate(3) 30 */ 31 32 #include <tmx.h> 33 #include <ctype.h> 34 #include <debug.h> 35 36 #define dig1(s,n) ((n)=((*(s)++)-'0')) 37 #define dig2(s,n) ((n)=((*(s)++)-'0')*10,(n)+=(*(s)++)-'0') 38 #define dig3(s,n) ((n)=((*(s)++)-'0')*100,(n)+=((*(s)++)-'0')*10,(n)+=(*(s)++)-'0') 39 #define dig4(s,n) ((n)=((*(s)++)-'0')*1000,(n)+=((*(s)++)-'0')*100,(n)+=((*(s)++)-'0')*10,(n)+=(*(s)++)-'0') 40 41 #define BREAK (1<<0) 42 #define CCYYMMDDHHMMSS (1<<1) 43 #define CRON (1<<2) 44 #define DAY (1<<3) 45 #define EXACT (1<<4) 46 #define FINAL (1<<5) 47 #define HOLD (1<<6) 48 #define HOUR (1<<7) 49 #define LAST (1<<8) 50 #define MDAY (1<<9) 51 #define MINUTE (1<<10) 52 #define MONTH (1<<11) 53 #define NEXT (1<<12) 54 #define NSEC (1<<13) 55 #define ORDINAL (1<<14) 56 #define SECOND (1<<15) 57 #define THIS (1L<<16) 58 #define WDAY (1L<<17) 59 #define YEAR (1L<<18) 60 #define ZONE (1L<<19) 61 62 /* 63 * parse cron range into set 64 * return: -1:error 0:* 1:some 65 */ 66 67 static int 68 range(register char* s, char** e, char* set, int lo, int hi) 69 { 70 int n; 71 int m; 72 int i; 73 char* t; 74 75 while (isspace(*s) || *s == '_') 76 s++; 77 if (*s == '*') 78 { 79 *e = s + 1; 80 return 0; 81 } 82 memset(set, 0, hi + 1); 83 for (;;) 84 { 85 n = strtol(s, &t, 10); 86 if (s == t || n < lo || n > hi) 87 return -1; 88 i = 1; 89 if (*(s = t) == '-') 90 { 91 m = strtol(++s, &t, 10); 92 if (s == t || m < n || m > hi) 93 return -1; 94 if (*(s = t) == '/') 95 { 96 i = strtol(++s, &t, 10); 97 if (s == t || i < 1) 98 return -1; 99 s = t; 100 } 101 } 102 else 103 m = n; 104 for (; n <= m; n += i) 105 set[n] = 1; 106 if (*s != ',') 107 break; 108 s++; 109 } 110 *e = s; 111 return 1; 112 } 113 114 /* 115 * parse date expression in s and return Time_t value 116 * 117 * if non-null, e points to the first invalid sequence in s 118 * now provides default values 119 */ 120 121 Time_t 122 tmxdate(register const char* s, char** e, Time_t now) 123 { 124 register Tm_t* tm; 125 register long n; 126 register int w; 127 unsigned long set; 128 unsigned long state; 129 unsigned long flags; 130 Time_t fix; 131 char* t; 132 char* u; 133 const char* x; 134 char* last; 135 char* type; 136 int day; 137 int dir; 138 int dst; 139 int zone; 140 int c; 141 int f; 142 int i; 143 int j; 144 int k; 145 int l; 146 long m; 147 long p; 148 long q; 149 Tm_zone_t* zp; 150 char skip[UCHAR_MAX + 1]; 151 152 /* 153 * check DATEMSK first 154 */ 155 156 debug((error(-1, "AHA tmxdate 2008-05-22"))); 157 fix = tmxscan(s, &last, NiL, &t, now, 0); 158 if (t && !*last) 159 { 160 if (e) 161 *e = last; 162 return fix; 163 } 164 165 reset: 166 167 /* 168 * use now for defaults 169 */ 170 171 tm = tmxmake(now); 172 tm_info.date = tm_info.zone; 173 day = -1; 174 dst = TM_DST; 175 set = state = 0; 176 type = 0; 177 zone = TM_LOCALZONE; 178 skip[0] = 0; 179 for (n = 1; n <= UCHAR_MAX; n++) 180 skip[n] = isspace(n) || strchr("_,;@=|!^()[]{}", n); 181 182 /* 183 * get <weekday year month day hour minutes seconds ?[ds]t [ap]m> 184 */ 185 186 for (;;) 187 { 188 state &= (state & HOLD) ? ~(HOLD) : ~(EXACT|LAST|NEXT|THIS); 189 if ((set|state) & (YEAR|MONTH|DAY)) 190 skip['/'] = 1; 191 message((-1, "AHA#%d state=%s%s%s%s| set=%s%s%s%s|", __LINE__, (state & EXACT) ? "|EXACT" : "", (state & LAST) ? "|LAST" : "", (state & THIS) ? "|THIS" : "", (state & NEXT) ? "|NEXT" : "", (set & EXACT) ? "|EXACT" : "", (set & LAST) ? "|LAST" : "", (set & THIS) ? "|THIS" : "", (set & NEXT) ? "|NEXT" : "")); 192 for (;;) 193 { 194 if (*s == '.' || *s == '-' || *s == '+') 195 { 196 if (((set|state) & (YEAR|MONTH|HOUR|MINUTE|ZONE)) == (YEAR|MONTH|HOUR|MINUTE) && (i = tmgoff(s, &t, TM_LOCALZONE)) != TM_LOCALZONE) 197 { 198 zone = i; 199 state |= ZONE; 200 if (!*(s = t)) 201 break; 202 } 203 else if (*s == '+') 204 break; 205 } 206 else if (!skip[*s]) 207 break; 208 s++; 209 } 210 if (!*(last = (char*)s)) 211 break; 212 if (*s == '#') 213 { 214 if (isdigit(*++s)) 215 { 216 now = strtoull(s, &t, 0); 217 sns: 218 if (*(s = t) == '.') 219 { 220 fix = 0; 221 m = 1000000000; 222 while (isdigit(*++s)) 223 fix += (*s - '0') * (m /= 10); 224 now = tmxsns(now, fix); 225 } 226 else if (now <= 0x7fffffff) 227 now = tmxsns(now, 0); 228 goto reset; 229 } 230 else if (*s++ == '#') 231 { 232 now = tmxtime(tm, zone); 233 goto reset; 234 } 235 break; 236 } 237 f = -1; 238 if (*s == '+') 239 { 240 while (isspace(*++s) || *s == '_'); 241 n = strtol(s, &t, 0); 242 if (w = t - s) 243 { 244 for (s = t; skip[*s]; s++); 245 state |= (f = n) ? NEXT : THIS; 246 set &= ~(EXACT|LAST|NEXT|THIS); 247 set |= state & (EXACT|LAST|NEXT|THIS); 248 } 249 else 250 s = last; 251 } 252 if (!(state & CRON)) 253 { 254 /* 255 * check for cron date 256 * 257 * min hour day-of-month month day-of-week 258 * 259 * if it's cron then determine the next time 260 * that satisfies the specification 261 * 262 * NOTE: the only spacing is ' '||'_'||';' 263 */ 264 265 i = 0; 266 n = *(t = (char*)s); 267 for (;;) 268 { 269 if (n == '*') 270 n = *++s; 271 else if (!isdigit(n)) 272 break; 273 else 274 while ((n = *++s) == ',' || n == '-' || n == '/' || isdigit(n)); 275 if (n != ' ' && n != '_' && n != ';') 276 { 277 if (!n) 278 i++; 279 break; 280 } 281 i++; 282 while ((n = *++s) == ' ' || n == '_'); 283 } 284 if (i == 5) 285 { 286 Time_t tt; 287 char hit[60]; 288 char mon[13]; 289 char day[7]; 290 291 state |= CRON; 292 flags = 0; 293 tm->tm_sec = 0; 294 tm->tm_min++; 295 tmfix(tm); 296 297 /* 298 * minute 299 */ 300 301 if ((k = range(t, &t, hit, 0, 59)) < 0) 302 break; 303 if (k && !hit[i = tm->tm_min]) 304 { 305 hit[i] = 1; 306 do if (++i > 59) 307 { 308 i = 0; 309 if (++tm->tm_hour > 59) 310 { 311 tm->tm_min = i; 312 tmfix(tm); 313 } 314 } while (!hit[i]); 315 tm->tm_min = i; 316 } 317 318 /* 319 * hour 320 */ 321 322 if ((k = range(t, &t, hit, 0, 23)) < 0) 323 break; 324 if (k && !hit[i = tm->tm_hour]) 325 { 326 hit[i] = 1; 327 do if (++i > 23) 328 { 329 i = 0; 330 if (++tm->tm_mday > 28) 331 { 332 tm->tm_hour = i; 333 tmfix(tm); 334 } 335 } while (!hit[i]); 336 tm->tm_hour = i; 337 } 338 339 /* 340 * day of month 341 */ 342 343 if ((k = range(t, &t, hit, 1, 31)) < 0) 344 break; 345 if (k) 346 flags |= DAY|MDAY; 347 348 /* 349 * month 350 */ 351 352 if ((k = range(t, &t, mon, 1, 12)) < 0) 353 break; 354 if (k) 355 flags |= MONTH; 356 else 357 for (i = 1; i <= 12; i++) 358 mon[i] = 1; 359 360 /* 361 * day of week 362 */ 363 364 if ((k = range(t, &t, day, 0, 6)) < 0) 365 break; 366 if (k) 367 flags |= WDAY; 368 s = t; 369 if (flags & (MONTH|MDAY|WDAY)) 370 { 371 fix = tmxtime(tm, zone); 372 tm = tmxmake(fix); 373 i = tm->tm_mon + 1; 374 j = tm->tm_mday; 375 k = tm->tm_wday; 376 for (;;) 377 { 378 if (!mon[i]) 379 { 380 if (++i > 12) 381 { 382 i = 1; 383 tm->tm_year++; 384 } 385 tm->tm_mon = i - 1; 386 tm->tm_mday = 1; 387 tt = tmxtime(tm, zone); 388 if (tt < fix) 389 goto done; 390 tm = tmxmake(tt); 391 i = tm->tm_mon + 1; 392 j = tm->tm_mday; 393 k = tm->tm_wday; 394 continue; 395 } 396 if (flags & (MDAY|WDAY)) 397 { 398 if ((flags & (MDAY|WDAY)) == (MDAY|WDAY)) 399 { 400 if (hit[j] && day[k]) 401 break; 402 } 403 else if ((flags & MDAY) && hit[j]) 404 break; 405 else if ((flags & WDAY) && day[k]) 406 break; 407 if (++j > 28) 408 { 409 tm->tm_mon = i - 1; 410 tm->tm_mday = j; 411 tm = tmxmake(tmxtime(tm, zone)); 412 i = tm->tm_mon + 1; 413 j = tm->tm_mday; 414 k = tm->tm_wday; 415 } 416 else if ((flags & WDAY) && ++k > 6) 417 k = 0; 418 } 419 else if (flags & MONTH) 420 break; 421 } 422 tm->tm_mon = i - 1; 423 tm->tm_mday = j; 424 tm->tm_wday = k; 425 } 426 continue; 427 } 428 s = t; 429 } 430 n = -1; 431 if (isdigit(*s)) 432 { 433 n = strtol(s, &t, 10); 434 if ((w = t - s) && *t == '.' && isdigit(*(t + 1)) && isdigit(*(t + 2)) && isdigit(*(t + 3))) 435 { 436 now = n; 437 goto sns; 438 } 439 if ((*t == 'T' || *t == 't') && ((set|state) & (YEAR|MONTH|DAY)) == (YEAR|MONTH) && isdigit(*(t + 1))) 440 t++; 441 u = t + (*t == '-'); 442 if ((w == 2 || w == 4) && (*u == 'W' || *u == 'w') && isdigit(*(u + 1))) 443 { 444 if (w == 4) 445 { 446 if ((n -= 1900) < TM_WINDOW) 447 break; 448 } 449 else if (n < TM_WINDOW) 450 n += 100; 451 m = n; 452 n = strtol(++u, &t, 10); 453 if ((i = (t - u)) < 2 || i > 3) 454 break; 455 if (i == 3) 456 { 457 k = n % 10; 458 n /= 10; 459 } 460 else if (*t != '-') 461 k = 1; 462 else if (*++t && dig1(t, k) < 1 || k > 7) 463 break; 464 if (n < 0 || n > 53) 465 break; 466 if (k == 7) 467 k = 0; 468 tm->tm_year = m; 469 tmweek(tm, 2, n, k); 470 set |= YEAR|MONTH|DAY; 471 s = t; 472 continue; 473 } 474 else if (w == 6 || w == 8 && (n / 1000000) > 12) 475 { 476 t = (char*)s; 477 flags = 0; 478 if (w == 8 || w == 6 && *u != 'T' && *u != 't') 479 { 480 dig4(t, m); 481 if ((m -= 1900) < TM_WINDOW) 482 break; 483 } 484 else 485 { 486 dig2(t, m); 487 if (m < TM_WINDOW) 488 m += 100; 489 } 490 flags |= YEAR; 491 if (dig2(t, l) <= 0 || l > 12) 492 break; 493 flags |= MONTH; 494 if (*t != 'T' && *t != 't' || !isdigit(*++t)) 495 { 496 if (w == 6) 497 goto save_yymm; 498 if (dig2(t, k) < 1 || k > 31) 499 break; 500 flags |= DAY; 501 goto save_yymmdd; 502 } 503 n = strtol(s = t, &t, 0); 504 if ((t - s) < 2) 505 break; 506 if (dig2(s, j) > 24) 507 break; 508 if ((t - s) < 2) 509 { 510 if ((t - s) == 1 || *t++ != '-') 511 break; 512 n = strtol(s = t, &t, 0); 513 if ((t - s) < 2) 514 break; 515 } 516 if (dig2(s, i) > 59) 517 break; 518 flags |= HOUR|MINUTE; 519 if ((t - s) == 2) 520 { 521 if (dig2(s, n) > (59 + TM_MAXLEAP)) 522 break; 523 flags |= SECOND; 524 } 525 else if (t - s) 526 break; 527 else 528 n = 0; 529 p = 0; 530 if (*t == '.') 531 { 532 q = 1000000000; 533 while (isdigit(*++t)) 534 p += (*t - '0') * (q /= 10); 535 set |= NSEC; 536 } 537 if (n > (59 + TM_MAXLEAP)) 538 break; 539 goto save; 540 } 541 else if (f == -1 && isalpha(*t) && tmlex(t, &t, tm_info.format + TM_ORDINAL, TM_ORDINALS - TM_ORDINAL, NiL, 0) >= 0) 542 { 543 message((-1, "AHA#%d n=%d", __LINE__, n)); 544 ordinal: 545 if (n) 546 n--; 547 message((-1, "AHA#%d n=%d", __LINE__, n)); 548 state |= ((f = n) ? NEXT : THIS)|ORDINAL; 549 set &= ~(EXACT|LAST|NEXT|THIS); 550 set |= state & (EXACT|LAST|NEXT|THIS); 551 for (s = t; skip[*s]; s++); 552 if (isdigit(*s)) 553 { 554 if (n = strtol(s, &t, 10)) 555 n--; 556 s = t; 557 if (*s == '_') 558 s++; 559 } 560 else 561 n = -1; 562 message((-1, "AHA#%d f=%d n=%d", __LINE__, f, n)); 563 } 564 else 565 { 566 if (!(state & (LAST|NEXT|THIS)) && ((i = t - s) == 4 && (*t == '.' && isdigit(*(t + 1)) && isdigit(*(t + 2)) && *(t + 3) != '.' || (!*t || isspace(*t) || *t == '_' || isalnum(*t)) && n >= 0 && (n % 100) < 60 && ((m = (n / 100)) < 20 || m < 24 && !((set|state) & (YEAR|MONTH|HOUR|MINUTE)))) || i > 4 && i <= 12)) 567 { 568 /* 569 * various { date(1) touch(1) } formats 570 * 571 * [[cc]yy[mm]]ddhhmm[.ss[.nn...]] 572 * [cc]yyjjj 573 * hhmm[.ss[.nn...]] 574 */ 575 576 flags = 0; 577 if (state & CCYYMMDDHHMMSS) 578 break; 579 state |= CCYYMMDDHHMMSS; 580 p = 0; 581 if ((i == 7 || i == 5) && (!*t || *t == 'Z' || *t == 'z')) 582 { 583 if (i == 7) 584 { 585 dig4(s, m); 586 if ((m -= 1900) < TM_WINDOW) 587 break; 588 } 589 else if (dig2(s, m) < TM_WINDOW) 590 m += 100; 591 dig3(s, k); 592 l = 1; 593 j = 0; 594 i = 0; 595 n = 0; 596 flags |= MONTH; 597 } 598 else if (i & 1) 599 break; 600 else 601 { 602 u = t; 603 if (i == 12) 604 { 605 x = s; 606 dig2(x, m); 607 if (m <= 12) 608 { 609 u -= 4; 610 i -= 4; 611 x = s + 8; 612 dig4(x, m); 613 } 614 else 615 dig4(s, m); 616 m -= 1900; 617 } 618 else if (i == 10) 619 { 620 x = s; 621 if (!dig2(x, m) || m > 12 || !dig2(x, m) || m > 31 || dig2(x, m) > 24 || dig2(x, m) > 60 || dig2(x, m) <= 60 && !(tm_info.flags & TM_DATESTYLE)) 622 dig2(s, m); 623 else 624 { 625 u -= 2; 626 i -= 2; 627 x = s + 8; 628 dig2(x, m); 629 } 630 if (m < TM_WINDOW) 631 m += 100; 632 } 633 else 634 m = tm->tm_year; 635 if ((u - s) < 8) 636 l = tm->tm_mon + 1; 637 else if (dig2(s, l) <= 0 || l > 12) 638 break; 639 else 640 flags |= MONTH; 641 if ((u - s) < 6) 642 k = tm->tm_mday; 643 else if (dig2(s, k) < 1 || k > 31) 644 break; 645 else 646 flags |= DAY; 647 if ((u - s) < 4) 648 break; 649 if (dig2(s, j) > 24) 650 break; 651 if (dig2(s, i) > 59) 652 break; 653 flags |= HOUR|MINUTE; 654 if ((u - s) == 2) 655 { 656 dig2(s, n); 657 flags |= SECOND; 658 } 659 else if (u - s) 660 break; 661 else if (*t != '.') 662 n = 0; 663 else 664 { 665 n = strtol(t + 1, &t, 10); 666 flags |= SECOND; 667 if (*t == '.') 668 { 669 q = 1000000000; 670 while (isdigit(*++t)) 671 p += (*t - '0') * (q /= 10); 672 set |= NSEC; 673 } 674 } 675 if (n > (59 + TM_MAXLEAP)) 676 break; 677 } 678 save: 679 tm->tm_hour = j; 680 tm->tm_min = i; 681 tm->tm_sec = n; 682 tm->tm_nsec = p; 683 save_yymmdd: 684 tm->tm_mday = k; 685 save_yymm: 686 tm->tm_mon = l - 1; 687 tm->tm_year = m; 688 s = t; 689 set |= flags; 690 continue; 691 } 692 for (s = t; skip[*s]; s++); 693 if (*s == ':' || *s == '.' && ((set|state) & (YEAR|MONTH|DAY|HOUR)) == (YEAR|MONTH|DAY)) 694 { 695 c = *s; 696 if ((state & HOUR) || n > 24) 697 break; 698 while (isspace(*++s) || *s == '_'); 699 if (!isdigit(*s)) 700 break; 701 i = n; 702 n = strtol(s, &t, 10); 703 for (s = t; isspace(*s) || *s == '_'; s++); 704 if (n > 59) 705 break; 706 j = n; 707 m = 0; 708 if (*s == c) 709 { 710 while (isspace(*++s) || *s == '_'); 711 if (!isdigit(*s)) 712 break; 713 n = strtol(s, &t, 10); 714 s = t; 715 if (n > (59 + TM_MAXLEAP)) 716 break; 717 set |= SECOND; 718 while (isspace(*s)) 719 s++; 720 if (*s == '.') 721 { 722 q = 1000000000; 723 while (isdigit(*++s)) 724 m += (*s - '0') * (q /= 10); 725 set |= NSEC; 726 } 727 } 728 else 729 n = 0; 730 set |= HOUR|MINUTE; 731 skip[':'] = 1; 732 k = tm->tm_hour; 733 tm->tm_hour = i; 734 l = tm->tm_min; 735 tm->tm_min = j; 736 tm->tm_sec = n; 737 tm->tm_nsec = m; 738 while (isspace(*s)) 739 s++; 740 switch (tmlex(s, &t, tm_info.format, TM_NFORM, tm_info.format + TM_MERIDIAN, 2)) 741 { 742 case TM_MERIDIAN: 743 s = t; 744 if (i == 12) 745 tm->tm_hour = i = 0; 746 break; 747 case TM_MERIDIAN+1: 748 if (i < 12) 749 tm->tm_hour = i += 12; 750 break; 751 } 752 if (f >= 0 || (state & (LAST|NEXT))) 753 { 754 message((-1, "AHA#%d f=%d i=%d j=%d k=%d l=%d", __LINE__, f, i, j, k, l)); 755 state &= ~HOLD; 756 if (f < 0) 757 { 758 if (state & LAST) 759 f = -1; 760 else if (state & NEXT) 761 f = 1; 762 else 763 f = 0; 764 } 765 if (f > 0) 766 { 767 if (i > k || i == k && j > l) 768 f--; 769 } 770 else if (i < k || i == k && j < l) 771 f++; 772 if (f > 0) 773 { 774 tm->tm_hour += f * 24; 775 while (tm->tm_hour >= 24) 776 { 777 tm->tm_hour -= 24; 778 tm->tm_mday++; 779 } 780 } 781 else if (f < 0) 782 { 783 tm->tm_hour += f * 24; 784 while (tm->tm_hour < 24) 785 { 786 tm->tm_hour += 24; 787 tm->tm_mday--; 788 } 789 } 790 } 791 continue; 792 } 793 } 794 } 795 for (;;) 796 { 797 if (*s == '-' || *s == '+') 798 { 799 if (((set|state) & (MONTH|DAY|HOUR|MINUTE)) == (MONTH|DAY|HOUR|MINUTE) || *s == '+' && (!isdigit(s[1]) || !isdigit(s[2]) || s[3] != ':' && (s[3] != '.' || ((set|state) & (YEAR|MONTH)) != (YEAR|MONTH)))) 800 break; 801 s++; 802 } 803 else if (skip[*s]) 804 s++; 805 else 806 break; 807 } 808 if (isalpha(*s) && n < 1000) 809 { 810 if ((j = tmlex(s, &t, tm_info.format, TM_NFORM, tm_info.format + TM_SUFFIXES, TM_PARTS - TM_SUFFIXES)) >= 0) 811 { 812 s = t; 813 switch (tm_data.lex[j]) 814 { 815 case TM_EXACT: 816 state |= HOLD|EXACT; 817 set &= ~(EXACT|LAST|NEXT|THIS); 818 set |= state & (EXACT|LAST|NEXT|THIS); 819 continue; 820 case TM_LAST: 821 state |= HOLD|LAST; 822 set &= ~(EXACT|LAST|NEXT|THIS); 823 set |= state & (EXACT|LAST|NEXT|THIS); 824 continue; 825 case TM_THIS: 826 state |= HOLD|THIS; 827 set &= ~(EXACT|LAST|NEXT|THIS); 828 set |= state & (EXACT|LAST|NEXT|THIS); 829 n = 0; 830 continue; 831 case TM_NEXT: 832 /* 833 * disambiguate english "last ... in" 834 */ 835 836 if (!((state|set) & LAST)) 837 { 838 state |= HOLD|NEXT; 839 set &= ~(EXACT|LAST|NEXT|THIS); 840 set |= state & (EXACT|LAST|NEXT|THIS); 841 continue; 842 } 843 /*FALLTHROUGH*/ 844 case TM_FINAL: 845 state |= HOLD|THIS|FINAL; 846 set &= ~(EXACT|LAST|NEXT|THIS); 847 set |= state & (EXACT|LAST|NEXT|THIS|FINAL); 848 continue; 849 case TM_ORDINAL: 850 j += TM_ORDINALS - TM_ORDINAL; 851 message((-1, "AHA#%d j=%d", __LINE__, j)); 852 /*FALLTHROUGH*/ 853 case TM_ORDINALS: 854 n = j - TM_ORDINALS + 1; 855 message((-1, "AHA#%d n=%d", __LINE__, n)); 856 goto ordinal; 857 case TM_MERIDIAN: 858 if (f >= 0) 859 f++; 860 else if (state & LAST) 861 f = -1; 862 else if (state & THIS) 863 f = 1; 864 else if (state & NEXT) 865 f = 2; 866 else 867 f = 0; 868 if (n > 0) 869 { 870 if (n > 24) 871 goto done; 872 tm->tm_hour = n; 873 } 874 for (k = tm->tm_hour; k < 0; k += 24); 875 k %= 24; 876 if (j == TM_MERIDIAN) 877 { 878 if (k == 12) 879 tm->tm_hour -= 12; 880 } 881 else if (k < 12) 882 tm->tm_hour += 12; 883 if (n > 0) 884 goto clear_min; 885 continue; 886 case TM_DAY_ABBREV: 887 j += TM_DAY - TM_DAY_ABBREV; 888 /*FALLTHROUGH*/ 889 case TM_DAY: 890 case TM_PARTS: 891 case TM_HOURS: 892 state |= set & (EXACT|LAST|NEXT|THIS); 893 if (!(state & (LAST|NEXT|THIS))) 894 for (;;) 895 { 896 while (skip[*s]) 897 s++; 898 if ((k = tmlex(s, &t, tm_info.format + TM_LAST, TM_NOISE - TM_LAST, NiL, 0)) >= 0) 899 { 900 s = t; 901 if (k <= 2) 902 state |= LAST; 903 else if (k <= 5) 904 state |= THIS; 905 else if (k <= 8) 906 state |= NEXT; 907 else 908 state |= EXACT; 909 } 910 else 911 { 912 state |= (n > 0) ? NEXT : THIS; 913 break; 914 } 915 set &= ~(EXACT|LAST|NEXT|THIS); 916 set |= state & (EXACT|LAST|NEXT|THIS); 917 } 918 /*FALLTHROUGH*/ 919 case TM_DAYS: 920 message((-1, "AHA#%d n=%d j=%d f=%d state=%s%s%s%s|", __LINE__, n, j, f, (state & EXACT) ? "|EXACT" : "", (state & LAST) ? "|LAST" : "", (state & THIS) ? "|THIS" : "", (state & NEXT) ? "|NEXT" : "")); 921 if (n == -1) 922 { 923 /* 924 * disambiguate english "second" 925 */ 926 927 if (j == TM_PARTS && f == -1) 928 { 929 state &= ~(LAST|NEXT|THIS|ORDINAL); /*AHA*/ 930 n = 2; 931 goto ordinal; 932 } 933 n = 1; 934 } 935 936 /* 937 * disambiguate "last" vs. { "previous" "final" } 938 */ 939 940 while (isspace(*s)) 941 s++; 942 message((-1, "AHA#%d disambiguate LAST s='%s'", __LINE__, s)); 943 if ((k = tmlex(s, &t, tm_info.format + TM_NEXT, TM_EXACT - TM_NEXT, NiL, 0)) >= 0) 944 { 945 s = t; 946 if (state & LAST) 947 { 948 state &= ~LAST; 949 set &= ~LAST; 950 state |= FINAL; 951 set |= FINAL; 952 message((-1, "AHA#%d LAST => FINAL", __LINE__)); 953 } 954 else 955 state &= ~(THIS|NEXT); 956 } 957 message((-1, "AHA#%d disambiguate LAST k=%d", __LINE__, k)); 958 if (state & LAST) 959 n = -n; 960 else if (!(state & NEXT)) 961 n--; 962 m = (f > 0) ? f * n : n; 963 message((-1, "AHA#%d f=%d n=%d i=%d j=%d k=%d l=%d m=%d state=%s%s%s%s|", __LINE__, f, n, i, j, k, l, m, (state & EXACT) ? "|EXACT" : "", (state & LAST) ? "|LAST" : "", (state & THIS) ? "|THIS" : "", (state & NEXT) ? "|NEXT" : "")); 964 switch (j) 965 { 966 case TM_DAYS+0: 967 tm->tm_mday--; 968 set |= DAY; 969 goto clear_hour; 970 case TM_DAYS+1: 971 set |= DAY; 972 goto clear_hour; 973 case TM_DAYS+2: 974 tm->tm_mday++; 975 set |= DAY; 976 goto clear_hour; 977 case TM_PARTS+0: 978 set |= SECOND; 979 if ((m < 0 ? -m : m) > (365L*24L*60L*60L)) 980 { 981 now = tmxtime(tm, zone) + tmxsns(m, 0); 982 goto reset; 983 } 984 tm->tm_sec += m; 985 goto clear_nsec; 986 case TM_PARTS+1: 987 tm->tm_min += m; 988 set |= MINUTE; 989 goto clear_sec; 990 case TM_PARTS+2: 991 tm->tm_hour += m; 992 set |= MINUTE; 993 goto clear_min; 994 case TM_PARTS+3: 995 tm->tm_mday += m; 996 if (!(set & FINAL)) 997 set |= HOUR; 998 goto clear_hour; 999 case TM_PARTS+4: 1000 tm = tmxmake(tmxtime(tm, zone)); 1001 tm->tm_mday += 7 * m - tm->tm_wday + 1; 1002 set |= DAY; 1003 goto clear_hour; 1004 case TM_PARTS+5: 1005 tm->tm_mon += m; 1006 set |= MONTH; 1007 goto clear_mday; 1008 case TM_PARTS+6: 1009 tm->tm_year += m; 1010 goto clear_mon; 1011 case TM_HOURS+0: 1012 tm->tm_mday += m; 1013 set |= DAY; 1014 goto clear_hour; 1015 case TM_HOURS+1: 1016 tm->tm_mday += m; 1017 tm->tm_hour = 6; 1018 set |= HOUR; 1019 goto clear_min; 1020 case TM_HOURS+2: 1021 tm->tm_mday += m; 1022 tm->tm_hour = 12; 1023 set |= HOUR; 1024 goto clear_min; 1025 case TM_HOURS+3: 1026 tm->tm_mday += m; 1027 tm->tm_hour = 18; 1028 set |= HOUR; 1029 goto clear_min; 1030 } 1031 if (m >= 0 && (state & ORDINAL)) 1032 tm->tm_mday = 1; 1033 tm = tmxmake(tmxtime(tm, zone)); 1034 day = j -= TM_DAY; 1035 dir = m; 1036 message((-1, "AHA#%d j=%d m=%d", __LINE__, j, m)); 1037 j -= tm->tm_wday; 1038 message((-1, "AHA#%d mday=%d wday=%d day=%d dir=%d f=%d i=%d j=%d l=%d m=%d", __LINE__, tm->tm_mday, tm->tm_wday, day, dir, f, i, j, l, m)); 1039 if (state & (LAST|NEXT|THIS)) 1040 { 1041 if (state & ORDINAL) 1042 { 1043 while (isspace(*s)) 1044 s++; 1045 if (isdigit(*s) || tmlex(s, &t, tm_info.format, TM_DAY_ABBREV, NiL, 0) >= 0) 1046 { 1047 state &= ~(LAST|NEXT|THIS); 1048 goto clear_hour; 1049 } 1050 } 1051 if (j < 0) 1052 j += 7; 1053 } 1054 else if (j > 0) 1055 j -= 7; 1056 message((-1, "AHA#%d day=%d mday=%d f=%d m=%d j=%d state=%s%s%s%s|", __LINE__, day, tm->tm_mday, f, m, j, (state & EXACT) ? "|EXACT" : "", (state & LAST) ? "|LAST" : "", (state & THIS) ? "|THIS" : "", (state & NEXT) ? "|NEXT" : "")); 1057 set |= DAY; 1058 if (set & FINAL) 1059 goto clear_hour; 1060 else if (state & (LAST|NEXT|THIS)) 1061 { 1062 if (f >= 0) 1063 day = -1; 1064 else if (m > 0 && (state & (NEXT|YEAR|MONTH)) == NEXT && j >= 0) 1065 m--; 1066 tm->tm_mday += j + m * 7; 1067 set &= ~(LAST|NEXT|THIS|ORDINAL); /*AHA*/ 1068 state &= ~(LAST|NEXT|THIS|ORDINAL); /*AHA*/ 1069 if (!(state & EXACT)) 1070 goto clear_hour; 1071 } 1072 continue; 1073 case TM_MONTH_ABBREV: 1074 j += TM_MONTH - TM_MONTH_ABBREV; 1075 /*FALLTHROUGH*/ 1076 case TM_MONTH: 1077 if (state & MONTH) 1078 goto done; 1079 state |= MONTH; 1080 i = tm->tm_mon; 1081 tm->tm_mon = j - TM_MONTH; 1082 if (n < 0) 1083 { 1084 while (skip[*s]) 1085 s++; 1086 if (isdigit(*s)) 1087 { 1088 n = strtol(s, &t, 10); 1089 if (n <= 31 && *t != ':') 1090 s = t; 1091 else 1092 n = -1; 1093 } 1094 } 1095 if (n >= 0) 1096 { 1097 if (n > 31) 1098 goto done; 1099 state |= DAY|MDAY; 1100 tm->tm_mday = n; 1101 if (f > 0) 1102 tm->tm_year += f; 1103 } 1104 if (state & (LAST|NEXT|THIS)) 1105 { 1106 n = i; 1107 goto rel_month; 1108 } 1109 continue; 1110 case TM_UT: 1111 if (state & ZONE) 1112 goto done; 1113 state |= ZONE; 1114 zone = tmgoff(s, &t, 0); 1115 s = t; 1116 continue; 1117 case TM_DT: 1118 if (!dst) 1119 goto done; 1120 if (!(state & ZONE)) 1121 { 1122 dst = tm_info.zone->dst; 1123 zone = tm_info.zone->west; 1124 } 1125 zone += tmgoff(s, &t, dst); 1126 s = t; 1127 dst = 0; 1128 state |= ZONE; 1129 continue; 1130 case TM_NOISE: 1131 continue; 1132 } 1133 } 1134 if (!(state & ZONE) && (zp = tmzone(s, &t, type, &dst))) 1135 { 1136 s = t; 1137 zone = zp->west + dst; 1138 tm_info.date = zp; 1139 state |= ZONE; 1140 if (n < 0) 1141 continue; 1142 } 1143 else if (!type && (zp = tmtype(s, &t))) 1144 { 1145 s = t; 1146 type = zp->type; 1147 if (n < 0) 1148 continue; 1149 } 1150 state |= BREAK; 1151 } 1152 else if (*s == '/') 1153 { 1154 if (!(state & (YEAR|MONTH)) && n >= 1900 && n < 3000 && (i = strtol(s + 1, &t, 10)) > 0 && i <= 12) 1155 { 1156 state |= YEAR; 1157 tm->tm_year = n - 1900; 1158 s = t; 1159 i--; 1160 } 1161 else 1162 { 1163 if ((state & MONTH) || n <= 0 || n > 31) 1164 break; 1165 if (isalpha(*++s)) 1166 { 1167 if ((i = tmlex(s, &t, tm_info.format, TM_DAY_ABBREV, NiL, 0)) < 0) 1168 break; 1169 if (i >= TM_MONTH) 1170 i -= TM_MONTH; 1171 s = t; 1172 } 1173 else 1174 { 1175 i = n - 1; 1176 n = strtol(s, &t, 10); 1177 s = t; 1178 if (n <= 0 || n > 31) 1179 break; 1180 if (*s == '/' && !isdigit(*(s + 1))) 1181 break; 1182 } 1183 state |= DAY; 1184 tm->tm_mday = n; 1185 } 1186 state |= MONTH; 1187 n = tm->tm_mon; 1188 tm->tm_mon = i; 1189 if (*s == '/') 1190 { 1191 n = strtol(++s, &t, 10); 1192 w = t - s; 1193 s = t; 1194 if (*s == '/' || *s == ':' || *s == '-' || *s == '_') 1195 s++; 1196 } 1197 else 1198 { 1199 if (state & (LAST|NEXT|THIS)) 1200 { 1201 rel_month: 1202 if (state & LAST) 1203 tm->tm_year -= (tm->tm_mon < n) ? 0 : 1; 1204 else 1205 tm->tm_year += ((state & NEXT) ? 1 : 0) + ((tm->tm_mon < n) ? 1 : 0); 1206 if (state & MDAY) 1207 goto clear_hour; 1208 set &= ~(LAST|NEXT|THIS); /*AHA*/ 1209 state &= ~(LAST|NEXT|THIS); /*AHA*/ 1210 goto clear_mday; 1211 } 1212 continue; 1213 } 1214 } 1215 if (n < 0 || w > 4) 1216 break; 1217 if (w == 4) 1218 { 1219 if ((state & YEAR) || n < 1900 || n >= 3000) 1220 break; 1221 state |= YEAR; 1222 tm->tm_year = n - 1900; 1223 } 1224 else if (w == 3) 1225 { 1226 if (state & (MONTH|MDAY|WDAY)) 1227 break; 1228 state |= MONTH|DAY|MDAY; 1229 tm->tm_mon = 0; 1230 tm->tm_mday = n; 1231 } 1232 else if (w == 2 && !(state & YEAR)) 1233 { 1234 state |= YEAR; 1235 if (n < TM_WINDOW) 1236 n += 100; 1237 tm->tm_year = n; 1238 } 1239 else if (!(state & MONTH) && n >= 1 && n <= 12) 1240 { 1241 state |= MONTH; 1242 tm->tm_mon = n - 1; 1243 } 1244 else if (!(state & (MDAY|WDAY)) && n >= 1 && n <= 31) 1245 { 1246 state |= DAY|MDAY|WDAY; 1247 tm->tm_mday = n; 1248 } 1249 else 1250 break; 1251 if (state & BREAK) 1252 { 1253 last = t; 1254 break; 1255 } 1256 continue; 1257 clear_mon: 1258 if ((set|state) & (EXACT|MONTH)) 1259 continue; 1260 tm->tm_mon = 0; 1261 clear_mday: 1262 set |= MONTH; 1263 if ((set|state) & (EXACT|DAY|HOUR)) 1264 continue; 1265 tm->tm_mday = 1; 1266 clear_hour: 1267 message((-1, "AHA#%d DAY", __LINE__)); 1268 set |= DAY; 1269 if ((set|state) & (EXACT|HOUR)) 1270 continue; 1271 tm->tm_hour = 0; 1272 clear_min: 1273 set |= HOUR; 1274 if ((set|state) & (EXACT|MINUTE)) 1275 continue; 1276 tm->tm_min = 0; 1277 clear_sec: 1278 set |= MINUTE; 1279 if ((set|state) & (EXACT|SECOND)) 1280 continue; 1281 tm->tm_sec = 0; 1282 clear_nsec: 1283 set |= SECOND; 1284 if ((set|state) & (EXACT|NSEC)) 1285 continue; 1286 tm->tm_nsec = 0; 1287 } 1288 done: 1289 if (day >= 0 && !(state & (MDAY|WDAY))) 1290 { 1291 message((-1, "AHA#%d day=%d dir=%d%s", __LINE__, day, dir, (state & FINAL) ? " FINAL" : "")); 1292 m = dir; 1293 if (state & MONTH) 1294 tm->tm_mday = 1; 1295 else if (m < 0) 1296 m++; 1297 tm = tmxmake(tmxtime(tm, zone)); 1298 j = day - tm->tm_wday; 1299 if (j < 0) 1300 j += 7; 1301 tm->tm_mday += j + m * 7; 1302 if (state & FINAL) 1303 for (n = tm_data.days[tm->tm_mon] + (tm->tm_mon == 1 && tmisleapyear(tm->tm_year)); (tm->tm_mday + 7) <= n; tm->tm_mday += 7); 1304 } 1305 else if (day < 0 && (state & FINAL) && (set & DAY)) 1306 tm->tm_mday = tm_data.days[tm->tm_mon] + (tm->tm_mon == 1 && tmisleapyear(tm->tm_year)); 1307 if (e) 1308 *e = last; 1309 return tmxtime(tm, zone); 1310 } 1311