parsetime.c revision 87628
1295373Sdteske/* 2295373Sdteske * parsetime.c - parse time for at(1) 3295373Sdteske * Copyright (C) 1993, 1994 Thomas Koenig 4295373Sdteske * 5295373Sdteske * modifications for English-language times 6295373Sdteske * Copyright (C) 1993 David Parsons 7295373Sdteske * 8295373Sdteske * Redistribution and use in source and binary forms, with or without 9295373Sdteske * modification, are permitted provided that the following conditions 10295373Sdteske * are met: 11295373Sdteske * 1. Redistributions of source code must retain the above copyright 12295373Sdteske * notice, this list of conditions and the following disclaimer. 13295373Sdteske * 2. The name of the author(s) may not be used to endorse or promote 14295373Sdteske * products derived from this software without specific prior written 15295373Sdteske * permission. 16295373Sdteske * 17295373Sdteske * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 18295373Sdteske * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19295373Sdteske * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20295373Sdteske * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 21295373Sdteske * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22295373Sdteske * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23295373Sdteske * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24295373Sdteske * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25295373Sdteske * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26295373Sdteske * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27295373Sdteske * 28295373Sdteske * at [NOW] PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS 29295373Sdteske * /NUMBER [DOT NUMBER] [AM|PM]\ /[MONTH NUMBER [NUMBER]] \ 30295373Sdteske * |NOON | |[TOMORROW] | 31295373Sdteske * |MIDNIGHT | |[DAY OF WEEK] | 32295373Sdteske * \TEATIME / |NUMBER [SLASH NUMBER [SLASH NUMBER]]| 33295373Sdteske * \PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS/ 34295373Sdteske */ 35295373Sdteske 36295400Sdteske#include <sys/cdefs.h> 37295400Sdteske__FBSDID("$FreeBSD: head/usr.bin/at/parsetime.c 87628 2001-12-10 21:13:08Z dwmalone $"); 38295400Sdteske 39295373Sdteske/* System Headers */ 40295400Sdteske 41295400Sdteske#include <sys/types.h> 42295373Sdteske#include <ctype.h> 43295373Sdteske#include <err.h> 44295373Sdteske#include <errno.h> 45295373Sdteske#include <stdio.h> 46295373Sdteske#include <stdlib.h> 47295373Sdteske#include <string.h> 48295373Sdteske#include <time.h> 49295373Sdteske#include <unistd.h> 50295373Sdteske#ifndef __FreeBSD__ 51295373Sdteske#include <getopt.h> 52295373Sdteske#endif 53295373Sdteske 54295373Sdteske/* Local headers */ 55295373Sdteske 56295373Sdteske#include "at.h" 57295373Sdteske#include "panic.h" 58295373Sdteske#include "parsetime.h" 59295373Sdteske 60295373Sdteske 61295373Sdteske/* Structures and unions */ 62295373Sdteske 63295373Sdteskeenum { /* symbols */ 64295373Sdteske MIDNIGHT, NOON, TEATIME, 65295373Sdteske PM, AM, TOMORROW, TODAY, NOW, 66295373Sdteske MINUTES, HOURS, DAYS, WEEKS, MONTHS, YEARS, 67295373Sdteske NUMBER, PLUS, DOT, SLASH, ID, JUNK, 68295373Sdteske JAN, FEB, MAR, APR, MAY, JUN, 69295373Sdteske JUL, AUG, SEP, OCT, NOV, DEC, 70295373Sdteske SUN, MON, TUE, WED, THU, FRI, SAT 71295373Sdteske }; 72295373Sdteske 73295373Sdteske/* parse translation table - table driven parsers can be your FRIEND! 74295373Sdteske */ 75295373Sdteskestruct { 76295373Sdteske const char *name; /* token name */ 77295373Sdteske int value; /* token id */ 78295373Sdteske int plural; /* is this plural? */ 79295373Sdteske} Specials[] = { 80295373Sdteske { "midnight", MIDNIGHT,0 }, /* 00:00:00 of today or tomorrow */ 81295373Sdteske { "noon", NOON,0 }, /* 12:00:00 of today or tomorrow */ 82295373Sdteske { "teatime", TEATIME,0 }, /* 16:00:00 of today or tomorrow */ 83295373Sdteske { "am", AM,0 }, /* morning times for 0-12 clock */ 84295373Sdteske { "pm", PM,0 }, /* evening times for 0-12 clock */ 85295373Sdteske { "tomorrow", TOMORROW,0 }, /* execute 24 hours from time */ 86295373Sdteske { "today", TODAY, 0 }, /* execute today - don't advance time */ 87295373Sdteske { "now", NOW,0 }, /* opt prefix for PLUS */ 88295373Sdteske 89295373Sdteske { "minute", MINUTES,0 }, /* minutes multiplier */ 90295373Sdteske { "minutes", MINUTES,1 }, /* (pluralized) */ 91295373Sdteske { "hour", HOURS,0 }, /* hours ... */ 92295373Sdteske { "hours", HOURS,1 }, /* (pluralized) */ 93295373Sdteske { "day", DAYS,0 }, /* days ... */ 94295373Sdteske { "days", DAYS,1 }, /* (pluralized) */ 95295373Sdteske { "week", WEEKS,0 }, /* week ... */ 96295373Sdteske { "weeks", WEEKS,1 }, /* (pluralized) */ 97295373Sdteske { "month", MONTHS,0 }, /* month ... */ 98295373Sdteske { "months", MONTHS,1 }, /* (pluralized) */ 99295373Sdteske { "year", YEARS,0 }, /* year ... */ 100295373Sdteske { "years", YEARS,1 }, /* (pluralized) */ 101295373Sdteske { "jan", JAN,0 }, 102295373Sdteske { "feb", FEB,0 }, 103295373Sdteske { "mar", MAR,0 }, 104295373Sdteske { "apr", APR,0 }, 105295373Sdteske { "may", MAY,0 }, 106295373Sdteske { "jun", JUN,0 }, 107295373Sdteske { "jul", JUL,0 }, 108295373Sdteske { "aug", AUG,0 }, 109295373Sdteske { "sep", SEP,0 }, 110295373Sdteske { "oct", OCT,0 }, 111295373Sdteske { "nov", NOV,0 }, 112295373Sdteske { "dec", DEC,0 }, 113295373Sdteske { "january", JAN,0 }, 114295373Sdteske { "february", FEB,0 }, 115295373Sdteske { "march", MAR,0 }, 116295373Sdteske { "april", APR,0 }, 117295373Sdteske { "may", MAY,0 }, 118295373Sdteske { "june", JUN,0 }, 119295373Sdteske { "july", JUL,0 }, 120295373Sdteske { "august", AUG,0 }, 121295373Sdteske { "september", SEP,0 }, 122295373Sdteske { "october", OCT,0 }, 123295373Sdteske { "november", NOV,0 }, 124295373Sdteske { "december", DEC,0 }, 125295441Sdteske { "sunday", SUN, 0 }, 126295373Sdteske { "sun", SUN, 0 }, 127295373Sdteske { "monday", MON, 0 }, 128295373Sdteske { "mon", MON, 0 }, 129295373Sdteske { "tuesday", TUE, 0 }, 130295373Sdteske { "tue", TUE, 0 }, 131295373Sdteske { "wednesday", WED, 0 }, 132295373Sdteske { "wed", WED, 0 }, 133295373Sdteske { "thursday", THU, 0 }, 134295373Sdteske { "thu", THU, 0 }, 135295373Sdteske { "friday", FRI, 0 }, 136295373Sdteske { "fri", FRI, 0 }, 137295373Sdteske { "saturday", SAT, 0 }, 138295373Sdteske { "sat", SAT, 0 }, 139295373Sdteske} ; 140295373Sdteske 141295373Sdteske/* File scope variables */ 142295373Sdteske 143295373Sdteskestatic char **scp; /* scanner - pointer at arglist */ 144295373Sdteskestatic char scc; /* scanner - count of remaining arguments */ 145295373Sdteskestatic char *sct; /* scanner - next char pointer in current argument */ 146295373Sdteskestatic int need; /* scanner - need to advance to next argument */ 147295373Sdteske 148295373Sdteskestatic char *sc_token; /* scanner - token buffer */ 149295373Sdteskestatic size_t sc_len; /* scanner - length of token buffer */ 150295373Sdteskestatic int sc_tokid; /* scanner - token id */ 151295373Sdteskestatic int sc_tokplur; /* scanner - is token plural? */ 152295373Sdteske 153295373Sdteske/* Local functions */ 154295373Sdteske 155295373Sdteske/* 156295373Sdteske * parse a token, checking if it's something special to us 157295460Sdteske */ 158295373Sdteskestatic int 159295373Sdteskeparse_token(char *arg) 160295373Sdteske{ 161295373Sdteske size_t i; 162295373Sdteske 163295373Sdteske for (i=0; i<(sizeof Specials/sizeof Specials[0]); i++) 164295373Sdteske if (strcasecmp(Specials[i].name, arg) == 0) { 165295373Sdteske sc_tokplur = Specials[i].plural; 166295373Sdteske return sc_tokid = Specials[i].value; 167295373Sdteske } 168295373Sdteske 169295587Sdteske /* not special - must be some random id */ 170295373Sdteske return ID; 171295373Sdteske} /* parse_token */ 172295587Sdteske 173295587Sdteske 174295373Sdteske/* 175295373Sdteske * init_scanner() sets up the scanner to eat arguments 176295373Sdteske */ 177295546Sdteskestatic void 178295546Sdteskeinit_scanner(int argc, char **argv) 179295546Sdteske{ 180295546Sdteske scp = argv; 181295546Sdteske scc = argc; 182295546Sdteske need = 1; 183295546Sdteske sc_len = 1; 184295546Sdteske while (argc-- > 0) 185295546Sdteske sc_len += strlen(*argv++); 186295546Sdteske 187295546Sdteske if ((sc_token = malloc(sc_len)) == NULL) 188295546Sdteske errx(EXIT_FAILURE, "virtual memory exhausted"); 189295546Sdteske} /* init_scanner */ 190295546Sdteske 191295546Sdteske/* 192295546Sdteske * token() fetches a token from the input stream 193295546Sdteske */ 194295546Sdteskestatic int 195295546Sdtesketoken(void) 196295546Sdteske{ 197295546Sdteske int idx; 198295546Sdteske 199295546Sdteske while (1) { 200295546Sdteske memset(sc_token, 0, sc_len); 201295546Sdteske sc_tokid = EOF; 202295546Sdteske sc_tokplur = 0; 203295546Sdteske idx = 0; 204295546Sdteske 205295546Sdteske /* if we need to read another argument, walk along the argument list; 206295546Sdteske * when we fall off the arglist, we'll just return EOF forever 207295546Sdteske */ 208295546Sdteske if (need) { 209295546Sdteske if (scc < 1) 210295546Sdteske return sc_tokid; 211295546Sdteske sct = *scp; 212295546Sdteske scp++; 213295546Sdteske scc--; 214295546Sdteske need = 0; 215295546Sdteske } 216295546Sdteske /* eat whitespace now - if we walk off the end of the argument, 217295546Sdteske * we'll continue, which puts us up at the top of the while loop 218295546Sdteske * to fetch the next argument in 219295546Sdteske */ 220295546Sdteske while (isspace(*sct)) 221295553Sdteske ++sct; 222295553Sdteske if (!*sct) { 223295553Sdteske need = 1; 224295546Sdteske continue; 225295553Sdteske } 226295553Sdteske 227295553Sdteske /* preserve the first character of the new token 228295546Sdteske */ 229295546Sdteske sc_token[0] = *sct++; 230295546Sdteske 231295546Sdteske /* then see what it is 232295546Sdteske */ 233295553Sdteske if (isdigit(sc_token[0])) { 234295546Sdteske while (isdigit(*sct)) 235295546Sdteske sc_token[++idx] = *sct++; 236295546Sdteske sc_token[++idx] = 0; 237295546Sdteske return sc_tokid = NUMBER; 238295546Sdteske } 239295546Sdteske else if (isalpha(sc_token[0])) { 240295546Sdteske while (isalpha(*sct)) 241295546Sdteske sc_token[++idx] = *sct++; 242295546Sdteske sc_token[++idx] = 0; 243295546Sdteske return parse_token(sc_token); 244295546Sdteske } 245295546Sdteske else if (sc_token[0] == ':' || sc_token[0] == '.') 246295546Sdteske return sc_tokid = DOT; 247295546Sdteske else if (sc_token[0] == '+') 248295546Sdteske return sc_tokid = PLUS; 249295546Sdteske else if (sc_token[0] == '/') 250295546Sdteske return sc_tokid = SLASH; 251295546Sdteske else 252295546Sdteske return sc_tokid = JUNK; 253295546Sdteske } /* while (1) */ 254295546Sdteske} /* token */ 255295546Sdteske 256295373Sdteske 257295373Sdteske/* 258295373Sdteske * plonk() gives an appropriate error message if a token is incorrect 259295373Sdteske */ 260295373Sdteskestatic void 261295373Sdteskeplonk(int tok) 262295373Sdteske{ 263295373Sdteske panic((tok == EOF) ? "incomplete time" 264295554Sdteske : "garbled time"); 265295373Sdteske} /* plonk */ 266295373Sdteske 267295373Sdteske 268295373Sdteske/* 269295373Sdteske * expect() gets a token and dies most horribly if it's not the token we want 270295373Sdteske */ 271295373Sdteskestatic void 272295373Sdteskeexpect(int desired) 273295373Sdteske{ 274295373Sdteske if (token() != desired) 275295373Sdteske plonk(sc_tokid); /* and we die here... */ 276295373Sdteske} /* expect */ 277295373Sdteske 278295373Sdteske 279295373Sdteske/* 280295373Sdteske * plus() parses a now + time 281295373Sdteske * 282295373Sdteske * at [NOW] PLUS NUMBER [MINUTES|HOURS|DAYS|WEEKS|MONTHS|YEARS] 283295373Sdteske * 284295373Sdteske */ 285295548Sdteske 286295554Sdteskestatic void 287295373Sdteskeplus(struct tm *tm) 288295373Sdteske{ 289295548Sdteske int delay; 290295554Sdteske int expectplur; 291295548Sdteske 292295548Sdteske expect(NUMBER); 293295554Sdteske 294295548Sdteske delay = atoi(sc_token); 295295548Sdteske expectplur = (delay != 1) ? 1 : 0; 296295556Sdteske 297295373Sdteske switch (token()) { 298295556Sdteske case YEARS: 299295556Sdteske tm->tm_year += delay; 300295556Sdteske break; 301295556Sdteske case MONTHS: 302295373Sdteske tm->tm_mon += delay; 303295556Sdteske break; 304295373Sdteske case WEEKS: 305295373Sdteske delay *= 7; 306295556Sdteske case DAYS: 307295373Sdteske tm->tm_mday += delay; 308295373Sdteske break; 309295373Sdteske case HOURS: 310295556Sdteske tm->tm_hour += delay; 311295373Sdteske break; 312295373Sdteske case MINUTES: 313295373Sdteske tm->tm_min += delay; 314295373Sdteske break; 315295373Sdteske default: 316295373Sdteske plonk(sc_tokid); 317295373Sdteske break; 318295556Sdteske } 319295373Sdteske 320295373Sdteske if (expectplur != sc_tokplur) 321295373Sdteske warnx("pluralization is wrong"); 322295373Sdteske 323295373Sdteske tm->tm_isdst = -1; 324295373Sdteske if (mktime(tm) < 0) 325295373Sdteske plonk(sc_tokid); 326295373Sdteske 327295373Sdteske} /* plus */ 328295373Sdteske 329295373Sdteske 330295373Sdteske/* 331295373Sdteske * tod() computes the time of day 332295373Sdteske * [NUMBER [DOT NUMBER] [AM|PM]] 333295373Sdteske */ 334295556Sdteskestatic void 335295373Sdtesketod(struct tm *tm) 336295373Sdteske{ 337295373Sdteske int hour, minute = 0; 338295373Sdteske int tlen; 339295373Sdteske 340295373Sdteske hour = atoi(sc_token); 341295373Sdteske tlen = strlen(sc_token); 342295556Sdteske 343295373Sdteske /* first pick out the time of day - if it's 4 digits, we assume 344295373Sdteske * a HHMM time, otherwise it's HH DOT MM time 345295373Sdteske */ 346295373Sdteske if (token() == DOT) { 347295373Sdteske expect(NUMBER); 348295373Sdteske minute = atoi(sc_token); 349295373Sdteske if (minute > 59) 350295443Sdteske panic("garbled time"); 351295373Sdteske token(); 352295373Sdteske } 353295556Sdteske else if (tlen == 4) { 354295373Sdteske minute = hour%100; 355295373Sdteske if (minute > 59) 356295554Sdteske panic("garbled time"); 357295548Sdteske hour = hour/100; 358295554Sdteske } 359295554Sdteske 360295554Sdteske /* check if an AM or PM specifier was given 361295548Sdteske */ 362295548Sdteske if (sc_tokid == AM || sc_tokid == PM) { 363295554Sdteske if (hour > 12) 364295554Sdteske panic("garbled time"); 365295373Sdteske 366295556Sdteske if (sc_tokid == PM) { 367295373Sdteske if (hour != 12) /* 12:xx PM is 12:xx, not 24:xx */ 368295373Sdteske hour += 12; 369295373Sdteske } else { 370295373Sdteske if (hour == 12) /* 12:xx AM is 00:xx, not 12:xx */ 371295373Sdteske hour = 0; 372295373Sdteske } 373295373Sdteske token(); 374295373Sdteske } 375295373Sdteske else if (hour > 23) 376295373Sdteske panic("garbled time"); 377295373Sdteske 378295373Sdteske /* if we specify an absolute time, we don't want to bump the day even 379295373Sdteske * if we've gone past that time - but if we're specifying a time plus 380295373Sdteske * a relative offset, it's okay to bump things 381295373Sdteske */ 382295373Sdteske if ((sc_tokid == EOF || sc_tokid == PLUS) && tm->tm_hour > hour) { 383295373Sdteske tm->tm_mday++; 384295373Sdteske tm->tm_wday++; 385295373Sdteske } 386295373Sdteske 387295373Sdteske tm->tm_hour = hour; 388295373Sdteske tm->tm_min = minute; 389295373Sdteske if (tm->tm_hour == 24) { 390295373Sdteske tm->tm_hour = 0; 391295373Sdteske tm->tm_mday++; 392295373Sdteske } 393295373Sdteske} /* tod */ 394295373Sdteske 395295373Sdteske 396295373Sdteske/* 397295373Sdteske * assign_date() assigns a date, wrapping to next year if needed 398295373Sdteske */ 399295373Sdteskestatic void 400295373Sdteskeassign_date(struct tm *tm, long mday, long mon, long year) 401295373Sdteske{ 402295373Sdteske 403295373Sdteske /* 404295373Sdteske * Convert year into tm_year format (year - 1900). 405295373Sdteske * We may be given the year in 2 digit, 4 digit, or tm_year format. 406295373Sdteske */ 407295373Sdteske if (year != -1) { 408295373Sdteske if (year >= 1900) 409295373Sdteske year -= 1900; /* convert from 4 digit year */ 410295373Sdteske else if (year < 100) { 411295373Sdteske /* convert from 2 digit year */ 412295373Sdteske struct tm *lt; 413295373Sdteske time_t now; 414295373Sdteske 415295373Sdteske time(&now); 416295373Sdteske lt = localtime(&now); 417295373Sdteske 418295373Sdteske /* Convert to tm_year assuming current century */ 419295373Sdteske year += (lt->tm_year / 100) * 100; 420295373Sdteske 421295373Sdteske if (year == lt->tm_year - 1) year++; 422295373Sdteske else if (year < lt->tm_year) 423295373Sdteske year += 100; /* must be in next century */ 424295373Sdteske } 425295373Sdteske } 426295373Sdteske 427295373Sdteske if (year < 0 && 428295373Sdteske (tm->tm_mon > mon ||(tm->tm_mon == mon && tm->tm_mday > mday))) 429295373Sdteske year = tm->tm_year + 1; 430295373Sdteske 431295373Sdteske tm->tm_mday = mday; 432295373Sdteske tm->tm_mon = mon; 433295373Sdteske 434295373Sdteske if (year >= 0) 435295373Sdteske tm->tm_year = year; 436295373Sdteske} /* assign_date */ 437295373Sdteske 438295373Sdteske 439295373Sdteske/* 440295373Sdteske * month() picks apart a month specification 441295373Sdteske * 442295373Sdteske * /[<month> NUMBER [NUMBER]] \ 443295373Sdteske * |[TOMORROW] | 444295373Sdteske * |[DAY OF WEEK] | 445295373Sdteske * |NUMBER [SLASH NUMBER [SLASH NUMBER]]| 446295373Sdteske * \PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS/ 447295373Sdteske */ 448295373Sdteskestatic void 449295373Sdteskemonth(struct tm *tm) 450295460Sdteske{ 451295460Sdteske long year= (-1); 452295460Sdteske long mday = 0, wday, mon; 453295460Sdteske int tlen; 454295460Sdteske 455295460Sdteske switch (sc_tokid) { 456295460Sdteske case PLUS: 457295460Sdteske plus(tm); 458295460Sdteske break; 459295460Sdteske 460295460Sdteske case TOMORROW: 461295460Sdteske /* do something tomorrow */ 462295460Sdteske tm->tm_mday ++; 463295460Sdteske tm->tm_wday ++; 464295460Sdteske case TODAY: /* force ourselves to stay in today - no further processing */ 465295460Sdteske token(); 466295460Sdteske break; 467295460Sdteske 468295460Sdteske case JAN: case FEB: case MAR: case APR: case MAY: case JUN: 469295460Sdteske case JUL: case AUG: case SEP: case OCT: case NOV: case DEC: 470295460Sdteske /* do month mday [year] 471295460Sdteske */ 472295460Sdteske mon = (sc_tokid-JAN); 473295460Sdteske expect(NUMBER); 474295460Sdteske mday = atol(sc_token); 475295460Sdteske if (token() == NUMBER) { 476295460Sdteske year = atol(sc_token); 477295460Sdteske token(); 478295460Sdteske } 479295460Sdteske assign_date(tm, mday, mon, year); 480295460Sdteske break; 481295460Sdteske 482295460Sdteske case SUN: case MON: case TUE: 483295373Sdteske case WED: case THU: case FRI: 484295373Sdteske case SAT: 485295373Sdteske /* do a particular day of the week 486295373Sdteske */ 487295373Sdteske wday = (sc_tokid-SUN); 488295373Sdteske 489295373Sdteske mday = tm->tm_mday; 490295373Sdteske 491295373Sdteske /* if this day is < today, then roll to next week 492295373Sdteske */ 493295373Sdteske if (wday < tm->tm_wday) 494295373Sdteske mday += 7 - (tm->tm_wday - wday); 495295373Sdteske else 496295373Sdteske mday += (wday - tm->tm_wday); 497295373Sdteske 498295373Sdteske tm->tm_wday = wday; 499295373Sdteske 500295373Sdteske assign_date(tm, mday, tm->tm_mon, tm->tm_year); 501295373Sdteske break; 502295373Sdteske 503295373Sdteske case NUMBER: 504295373Sdteske /* get numeric MMDDYY, mm/dd/yy, or dd.mm.yy 505295373Sdteske */ 506295373Sdteske tlen = strlen(sc_token); 507295373Sdteske mon = atol(sc_token); 508295373Sdteske token(); 509295373Sdteske 510 if (sc_tokid == SLASH || sc_tokid == DOT) { 511 int sep; 512 513 sep = sc_tokid; 514 expect(NUMBER); 515 mday = atol(sc_token); 516 if (token() == sep) { 517 expect(NUMBER); 518 year = atol(sc_token); 519 token(); 520 } 521 522 /* flip months and days for European timing 523 */ 524 if (sep == DOT) { 525 int x = mday; 526 mday = mon; 527 mon = x; 528 } 529 } 530 else if (tlen == 6 || tlen == 8) { 531 if (tlen == 8) { 532 year = (mon % 10000) - 1900; 533 mon /= 10000; 534 } 535 else { 536 year = mon % 100; 537 mon /= 100; 538 } 539 mday = mon % 100; 540 mon /= 100; 541 } 542 else 543 panic("garbled time"); 544 545 mon--; 546 if (mon < 0 || mon > 11 || mday < 1 || mday > 31) 547 panic("garbled time"); 548 549 assign_date(tm, mday, mon, year); 550 break; 551 } /* case */ 552} /* month */ 553 554 555/* Global functions */ 556 557time_t 558parsetime(int argc, char **argv) 559{ 560/* Do the argument parsing, die if necessary, and return the time the job 561 * should be run. 562 */ 563 time_t nowtimer, runtimer; 564 struct tm nowtime, runtime; 565 int hr = 0; 566 /* this MUST be initialized to zero for midnight/noon/teatime */ 567 568 nowtimer = time(NULL); 569 nowtime = *localtime(&nowtimer); 570 571 runtime = nowtime; 572 runtime.tm_sec = 0; 573 runtime.tm_isdst = 0; 574 575 if (argc <= optind) 576 usage(); 577 578 init_scanner(argc-optind, argv+optind); 579 580 switch (token()) { 581 case NOW: 582 if (scc < 1) { 583 return nowtimer; 584 } 585 /* now is optional prefix for PLUS tree */ 586 expect(PLUS); 587 case PLUS: 588 plus(&runtime); 589 break; 590 591 case NUMBER: 592 tod(&runtime); 593 month(&runtime); 594 break; 595 596 /* evil coding for TEATIME|NOON|MIDNIGHT - we've initialised 597 * hr to zero up above, then fall into this case in such a 598 * way so we add +12 +4 hours to it for teatime, +12 hours 599 * to it for noon, and nothing at all for midnight, then 600 * set our runtime to that hour before leaping into the 601 * month scanner 602 */ 603 case TEATIME: 604 hr += 4; 605 case NOON: 606 hr += 12; 607 case MIDNIGHT: 608 if (runtime.tm_hour >= hr) { 609 runtime.tm_mday++; 610 runtime.tm_wday++; 611 } 612 runtime.tm_hour = hr; 613 runtime.tm_min = 0; 614 token(); 615 /* FALLTHROUGH to month setting */ 616 default: 617 month(&runtime); 618 break; 619 } /* ugly case statement */ 620 expect(EOF); 621 622 /* adjust for daylight savings time 623 */ 624 runtime.tm_isdst = -1; 625 runtimer = mktime(&runtime); 626 if (runtime.tm_isdst > 0) { 627 runtimer -= 3600; 628 runtimer = mktime(&runtime); 629 } 630 631 if (runtimer < 0) 632 panic("garbled time"); 633 634 if (nowtimer > runtimer) 635 panic("trying to travel back in time"); 636 637 return runtimer; 638} /* parsetime */ 639