110154Sache/* 27767Sache * parsetime.c - parse time for at(1) 37767Sache * Copyright (C) 1993, 1994 Thomas Koenig 4941Snate * 554158Scharnier * modifications for English-language times 67767Sache * Copyright (C) 1993 David Parsons 7941Snate * 8941Snate * Redistribution and use in source and binary forms, with or without 9941Snate * modification, are permitted provided that the following conditions 10941Snate * are met: 11941Snate * 1. Redistributions of source code must retain the above copyright 12941Snate * notice, this list of conditions and the following disclaimer. 13941Snate * 2. The name of the author(s) may not be used to endorse or promote 14941Snate * products derived from this software without specific prior written 15941Snate * permission. 16941Snate * 17941Snate * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 18941Snate * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19941Snate * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2010154Sache * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 21941Snate * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22941Snate * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23941Snate * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24941Snate * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25941Snate * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26941Snate * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27941Snate * 28941Snate * at [NOW] PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS 29941Snate * /NUMBER [DOT NUMBER] [AM|PM]\ /[MONTH NUMBER [NUMBER]] \ 30941Snate * |NOON | |[TOMORROW] | 317767Sache * |MIDNIGHT | |[DAY OF WEEK] | 327767Sache * \TEATIME / |NUMBER [SLASH NUMBER [SLASH NUMBER]]| 337767Sache * \PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS/ 34941Snate */ 35941Snate 3687230Smarkm#include <sys/cdefs.h> 3787230Smarkm__FBSDID("$FreeBSD$"); 3887230Smarkm 39941Snate/* System Headers */ 40941Snate 41941Snate#include <sys/types.h> 4254158Scharnier#include <ctype.h> 4326872Scharnier#include <err.h> 44941Snate#include <errno.h> 45941Snate#include <stdio.h> 46941Snate#include <stdlib.h> 47941Snate#include <string.h> 48941Snate#include <time.h> 49941Snate#include <unistd.h> 507767Sache#ifndef __FreeBSD__ 517767Sache#include <getopt.h> 527767Sache#endif 53941Snate 54941Snate/* Local headers */ 55941Snate 56941Snate#include "at.h" 57941Snate#include "panic.h" 5887208Smarkm#include "parsetime.h" 59941Snate 60941Snate 61941Snate/* Structures and unions */ 62941Snate 6310154Sacheenum { /* symbols */ 64941Snate MIDNIGHT, NOON, TEATIME, 65941Snate PM, AM, TOMORROW, TODAY, NOW, 6650411Snsayer MINUTES, HOURS, DAYS, WEEKS, MONTHS, YEARS, 67241230Scracauer NUMBER, PLUS, MINUS, DOT, SLASH, ID, JUNK, 68941Snate JAN, FEB, MAR, APR, MAY, JUN, 697767Sache JUL, AUG, SEP, OCT, NOV, DEC, 707767Sache SUN, MON, TUE, WED, THU, FRI, SAT 717767Sache }; 72941Snate 737767Sache/* parse translation table - table driven parsers can be your FRIEND! 74941Snate */ 75227233Sedstatic const struct { 7687208Smarkm const char *name; /* token name */ 7710154Sache int value; /* token id */ 787767Sache int plural; /* is this plural? */ 79941Snate} Specials[] = { 807767Sache { "midnight", MIDNIGHT,0 }, /* 00:00:00 of today or tomorrow */ 817767Sache { "noon", NOON,0 }, /* 12:00:00 of today or tomorrow */ 827767Sache { "teatime", TEATIME,0 }, /* 16:00:00 of today or tomorrow */ 837767Sache { "am", AM,0 }, /* morning times for 0-12 clock */ 847767Sache { "pm", PM,0 }, /* evening times for 0-12 clock */ 857767Sache { "tomorrow", TOMORROW,0 }, /* execute 24 hours from time */ 867767Sache { "today", TODAY, 0 }, /* execute today - don't advance time */ 877767Sache { "now", NOW,0 }, /* opt prefix for PLUS */ 88941Snate 897767Sache { "minute", MINUTES,0 }, /* minutes multiplier */ 907767Sache { "minutes", MINUTES,1 }, /* (pluralized) */ 917767Sache { "hour", HOURS,0 }, /* hours ... */ 927767Sache { "hours", HOURS,1 }, /* (pluralized) */ 937767Sache { "day", DAYS,0 }, /* days ... */ 947767Sache { "days", DAYS,1 }, /* (pluralized) */ 957767Sache { "week", WEEKS,0 }, /* week ... */ 967767Sache { "weeks", WEEKS,1 }, /* (pluralized) */ 9750411Snsayer { "month", MONTHS,0 }, /* month ... */ 9850411Snsayer { "months", MONTHS,1 }, /* (pluralized) */ 9950411Snsayer { "year", YEARS,0 }, /* year ... */ 10050411Snsayer { "years", YEARS,1 }, /* (pluralized) */ 1017767Sache { "jan", JAN,0 }, 1027767Sache { "feb", FEB,0 }, 1037767Sache { "mar", MAR,0 }, 1047767Sache { "apr", APR,0 }, 1057767Sache { "may", MAY,0 }, 1067767Sache { "jun", JUN,0 }, 1077767Sache { "jul", JUL,0 }, 1087767Sache { "aug", AUG,0 }, 1097767Sache { "sep", SEP,0 }, 1107767Sache { "oct", OCT,0 }, 1117767Sache { "nov", NOV,0 }, 1127767Sache { "dec", DEC,0 }, 11337538Sdes { "january", JAN,0 }, 11437538Sdes { "february", FEB,0 }, 11537538Sdes { "march", MAR,0 }, 11637538Sdes { "april", APR,0 }, 11737538Sdes { "may", MAY,0 }, 11837538Sdes { "june", JUN,0 }, 11937538Sdes { "july", JUL,0 }, 12037538Sdes { "august", AUG,0 }, 12137538Sdes { "september", SEP,0 }, 12237538Sdes { "october", OCT,0 }, 12337538Sdes { "november", NOV,0 }, 12437538Sdes { "december", DEC,0 }, 1257767Sache { "sunday", SUN, 0 }, 1267767Sache { "sun", SUN, 0 }, 1277767Sache { "monday", MON, 0 }, 1287767Sache { "mon", MON, 0 }, 1297767Sache { "tuesday", TUE, 0 }, 1307767Sache { "tue", TUE, 0 }, 1317767Sache { "wednesday", WED, 0 }, 1327767Sache { "wed", WED, 0 }, 1337767Sache { "thursday", THU, 0 }, 1347767Sache { "thu", THU, 0 }, 1357767Sache { "friday", FRI, 0 }, 1367767Sache { "fri", FRI, 0 }, 1377767Sache { "saturday", SAT, 0 }, 1387767Sache { "sat", SAT, 0 }, 139941Snate} ; 140941Snate 141941Snate/* File scope variables */ 142941Snate 143941Snatestatic char **scp; /* scanner - pointer at arglist */ 144941Snatestatic char scc; /* scanner - count of remaining arguments */ 145941Snatestatic char *sct; /* scanner - next char pointer in current argument */ 146941Snatestatic int need; /* scanner - need to advance to next argument */ 147941Snate 148941Snatestatic char *sc_token; /* scanner - token buffer */ 14954158Scharnierstatic size_t sc_len; /* scanner - length of token buffer */ 15010154Sachestatic int sc_tokid; /* scanner - token id */ 1517767Sachestatic int sc_tokplur; /* scanner - is token plural? */ 152941Snate 153941Snate/* Local functions */ 154941Snate 155941Snate/* 156941Snate * parse a token, checking if it's something special to us 157941Snate */ 15810154Sachestatic int 15910154Sacheparse_token(char *arg) 160941Snate{ 16187208Smarkm size_t i; 162941Snate 163941Snate for (i=0; i<(sizeof Specials/sizeof Specials[0]); i++) 164941Snate if (strcasecmp(Specials[i].name, arg) == 0) { 1657767Sache sc_tokplur = Specials[i].plural; 166941Snate return sc_tokid = Specials[i].value; 167941Snate } 168941Snate 169941Snate /* not special - must be some random id */ 170941Snate return ID; 171941Snate} /* parse_token */ 172941Snate 173941Snate 174941Snate/* 175941Snate * init_scanner() sets up the scanner to eat arguments 176941Snate */ 177941Snatestatic void 1787767Sacheinit_scanner(int argc, char **argv) 179941Snate{ 180941Snate scp = argv; 181941Snate scc = argc; 182941Snate need = 1; 183941Snate sc_len = 1; 18410154Sache while (argc-- > 0) 18510154Sache sc_len += strlen(*argv++); 186941Snate 18780294Sobrien if ((sc_token = malloc(sc_len)) == NULL) 18880294Sobrien errx(EXIT_FAILURE, "virtual memory exhausted"); 189941Snate} /* init_scanner */ 190941Snate 191941Snate/* 192941Snate * token() fetches a token from the input stream 193941Snate */ 19410154Sachestatic int 19587208Smarkmtoken(void) 196941Snate{ 197941Snate int idx; 198941Snate 199941Snate while (1) { 200941Snate memset(sc_token, 0, sc_len); 201941Snate sc_tokid = EOF; 2027767Sache sc_tokplur = 0; 203941Snate idx = 0; 204941Snate 2057767Sache /* if we need to read another argument, walk along the argument list; 206941Snate * when we fall off the arglist, we'll just return EOF forever 207941Snate */ 208941Snate if (need) { 209941Snate if (scc < 1) 210941Snate return sc_tokid; 211941Snate sct = *scp; 212941Snate scp++; 213941Snate scc--; 214941Snate need = 0; 215941Snate } 2167767Sache /* eat whitespace now - if we walk off the end of the argument, 217941Snate * we'll continue, which puts us up at the top of the while loop 218941Snate * to fetch the next argument in 219941Snate */ 220941Snate while (isspace(*sct)) 221941Snate ++sct; 222941Snate if (!*sct) { 223941Snate need = 1; 224941Snate continue; 225941Snate } 226941Snate 2277767Sache /* preserve the first character of the new token 228941Snate */ 229941Snate sc_token[0] = *sct++; 230941Snate 2317767Sache /* then see what it is 232941Snate */ 233941Snate if (isdigit(sc_token[0])) { 234941Snate while (isdigit(*sct)) 235941Snate sc_token[++idx] = *sct++; 236941Snate sc_token[++idx] = 0; 237941Snate return sc_tokid = NUMBER; 2387767Sache } 2397767Sache else if (isalpha(sc_token[0])) { 240941Snate while (isalpha(*sct)) 241941Snate sc_token[++idx] = *sct++; 242941Snate sc_token[++idx] = 0; 243941Snate return parse_token(sc_token); 244941Snate } 245941Snate else if (sc_token[0] == ':' || sc_token[0] == '.') 246941Snate return sc_tokid = DOT; 247941Snate else if (sc_token[0] == '+') 248941Snate return sc_tokid = PLUS; 249241230Scracauer else if (sc_token[0] == '-') 250241230Scracauer return sc_tokid = MINUS; 2517767Sache else if (sc_token[0] == '/') 252941Snate return sc_tokid = SLASH; 253941Snate else 254941Snate return sc_tokid = JUNK; 255941Snate } /* while (1) */ 256941Snate} /* token */ 257941Snate 258941Snate 259941Snate/* 260941Snate * plonk() gives an appropriate error message if a token is incorrect 261941Snate */ 262941Snatestatic void 2637767Sacheplonk(int tok) 264941Snate{ 265941Snate panic((tok == EOF) ? "incomplete time" 266941Snate : "garbled time"); 267941Snate} /* plonk */ 268941Snate 269941Snate 27010154Sache/* 271941Snate * expect() gets a token and dies most horribly if it's not the token we want 272941Snate */ 273941Snatestatic void 27410154Sacheexpect(int desired) 275941Snate{ 276941Snate if (token() != desired) 277941Snate plonk(sc_tokid); /* and we die here... */ 278941Snate} /* expect */ 279941Snate 280941Snate 281941Snate/* 282241230Scracauer * plus_or_minus() holds functionality common to plus() and minus() 283941Snate */ 284941Snatestatic void 285241230Scracauerplus_or_minus(struct tm *tm, int delay) 286941Snate{ 2877767Sache int expectplur; 288941Snate 289241230Scracauer expectplur = (delay != 1 && delay != -1) ? 1 : 0; 290941Snate 291941Snate switch (token()) { 29250411Snsayer case YEARS: 29350411Snsayer tm->tm_year += delay; 29450411Snsayer break; 29550411Snsayer case MONTHS: 29650411Snsayer tm->tm_mon += delay; 29750411Snsayer break; 298941Snate case WEEKS: 299941Snate delay *= 7; 300941Snate case DAYS: 30150411Snsayer tm->tm_mday += delay; 30250411Snsayer break; 303941Snate case HOURS: 30450411Snsayer tm->tm_hour += delay; 30550411Snsayer break; 306941Snate case MINUTES: 30750411Snsayer tm->tm_min += delay; 30850411Snsayer break; 30950411Snsayer default: 31050411Snsayer plonk(sc_tokid); 31150411Snsayer break; 312941Snate } 31350411Snsayer 31450411Snsayer if (expectplur != sc_tokplur) 31550411Snsayer warnx("pluralization is wrong"); 31650411Snsayer 31750411Snsayer tm->tm_isdst = -1; 31850411Snsayer if (mktime(tm) < 0) 31950411Snsayer plonk(sc_tokid); 320241230Scracauer} /* plus_or_minus */ 32150411Snsayer 322241230Scracauer 323241230Scracauer/* 324241230Scracauer * plus() parses a now + time 325241230Scracauer * 326241230Scracauer * at [NOW] PLUS NUMBER [MINUTES|HOURS|DAYS|WEEKS|MONTHS|YEARS] 327241230Scracauer * 328241230Scracauer */ 329241230Scracauerstatic void 330241230Scracauerplus(struct tm *tm) 331241230Scracauer{ 332241230Scracauer int delay; 333241230Scracauer 334241230Scracauer expect(NUMBER); 335241230Scracauer 336241230Scracauer delay = atoi(sc_token); 337241230Scracauer plus_or_minus(tm, delay); 338941Snate} /* plus */ 339941Snate 340941Snate 341941Snate/* 342241230Scracauer * minus() is like plus but can not be used with NOW 343241230Scracauer */ 344241230Scracauerstatic void 345241230Scracauerminus(struct tm *tm) 346241230Scracauer{ 347241230Scracauer int delay; 348241230Scracauer 349241230Scracauer expect(NUMBER); 350241230Scracauer 351241230Scracauer delay = -atoi(sc_token); 352241230Scracauer plus_or_minus(tm, delay); 353241230Scracauer} /* minus */ 354241230Scracauer 355241230Scracauer 356241230Scracauer/* 357941Snate * tod() computes the time of day 358941Snate * [NUMBER [DOT NUMBER] [AM|PM]] 359941Snate */ 360941Snatestatic void 3617767Sachetod(struct tm *tm) 362941Snate{ 363941Snate int hour, minute = 0; 364941Snate int tlen; 365941Snate 366941Snate hour = atoi(sc_token); 367941Snate tlen = strlen(sc_token); 368941Snate 3697767Sache /* first pick out the time of day - if it's 4 digits, we assume 370941Snate * a HHMM time, otherwise it's HH DOT MM time 371941Snate */ 372941Snate if (token() == DOT) { 373941Snate expect(NUMBER); 374941Snate minute = atoi(sc_token); 375941Snate if (minute > 59) 376941Snate panic("garbled time"); 377941Snate token(); 3787767Sache } 3797767Sache else if (tlen == 4) { 380941Snate minute = hour%100; 381941Snate if (minute > 59) 38238646Ssteve panic("garbled time"); 383941Snate hour = hour/100; 384941Snate } 385941Snate 3867767Sache /* check if an AM or PM specifier was given 387941Snate */ 388941Snate if (sc_tokid == AM || sc_tokid == PM) { 389941Snate if (hour > 12) 390941Snate panic("garbled time"); 391941Snate 39217221Sjdp if (sc_tokid == PM) { 39317221Sjdp if (hour != 12) /* 12:xx PM is 12:xx, not 24:xx */ 39426835Scharnier hour += 12; 39517221Sjdp } else { 39617221Sjdp if (hour == 12) /* 12:xx AM is 00:xx, not 12:xx */ 39726835Scharnier hour = 0; 39817221Sjdp } 399941Snate token(); 4007767Sache } 4017767Sache else if (hour > 23) 402941Snate panic("garbled time"); 403941Snate 4047767Sache /* if we specify an absolute time, we don't want to bump the day even 405941Snate * if we've gone past that time - but if we're specifying a time plus 406941Snate * a relative offset, it's okay to bump things 407941Snate */ 408241230Scracauer if ((sc_tokid == EOF || sc_tokid == PLUS || sc_tokid == MINUS) && 409241230Scracauer tm->tm_hour > hour) { 410941Snate tm->tm_mday++; 4117767Sache tm->tm_wday++; 4127767Sache } 413941Snate 414941Snate tm->tm_hour = hour; 415941Snate tm->tm_min = minute; 416941Snate if (tm->tm_hour == 24) { 417941Snate tm->tm_hour = 0; 418941Snate tm->tm_mday++; 419941Snate } 420941Snate} /* tod */ 421941Snate 422941Snate 423941Snate/* 424941Snate * assign_date() assigns a date, wrapping to next year if needed 425941Snate */ 426941Snatestatic void 4277767Sacheassign_date(struct tm *tm, long mday, long mon, long year) 428941Snate{ 42935729Salex 43058660Ssheldonh /* 43158660Ssheldonh * Convert year into tm_year format (year - 1900). 43258660Ssheldonh * We may be given the year in 2 digit, 4 digit, or tm_year format. 43358660Ssheldonh */ 43458660Ssheldonh if (year != -1) { 43558660Ssheldonh if (year >= 1900) 43658660Ssheldonh year -= 1900; /* convert from 4 digit year */ 43758660Ssheldonh else if (year < 100) { 43858660Ssheldonh /* convert from 2 digit year */ 43958660Ssheldonh struct tm *lt; 44058660Ssheldonh time_t now; 44135729Salex 44258660Ssheldonh time(&now); 44358660Ssheldonh lt = localtime(&now); 44458660Ssheldonh 44558660Ssheldonh /* Convert to tm_year assuming current century */ 44658660Ssheldonh year += (lt->tm_year / 100) * 100; 44758660Ssheldonh 44858660Ssheldonh if (year == lt->tm_year - 1) year++; 44958660Ssheldonh else if (year < lt->tm_year) 45058660Ssheldonh year += 100; /* must be in next century */ 45158660Ssheldonh } 452941Snate } 453941Snate 454941Snate if (year < 0 && 455941Snate (tm->tm_mon > mon ||(tm->tm_mon == mon && tm->tm_mday > mday))) 456941Snate year = tm->tm_year + 1; 457941Snate 458941Snate tm->tm_mday = mday; 459941Snate tm->tm_mon = mon; 460941Snate 461941Snate if (year >= 0) 462941Snate tm->tm_year = year; 463941Snate} /* assign_date */ 464941Snate 465941Snate 46610154Sache/* 467941Snate * month() picks apart a month specification 468941Snate * 469941Snate * /[<month> NUMBER [NUMBER]] \ 470941Snate * |[TOMORROW] | 4717767Sache * |[DAY OF WEEK] | 472941Snate * |NUMBER [SLASH NUMBER [SLASH NUMBER]]| 473941Snate * \PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS/ 474941Snate */ 475941Snatestatic void 4767767Sachemonth(struct tm *tm) 477941Snate{ 478941Snate long year= (-1); 47941556Sarchie long mday = 0, wday, mon; 480941Snate int tlen; 481941Snate 482941Snate switch (sc_tokid) { 483941Snate case PLUS: 484941Snate plus(tm); 485941Snate break; 486241230Scracauer case MINUS: 487241230Scracauer minus(tm); 488241230Scracauer break; 489941Snate 490941Snate case TOMORROW: 491941Snate /* do something tomorrow */ 492941Snate tm->tm_mday ++; 4937767Sache tm->tm_wday ++; 494941Snate case TODAY: /* force ourselves to stay in today - no further processing */ 495941Snate token(); 496941Snate break; 497941Snate 498941Snate case JAN: case FEB: case MAR: case APR: case MAY: case JUN: 499941Snate case JUL: case AUG: case SEP: case OCT: case NOV: case DEC: 5007767Sache /* do month mday [year] 501941Snate */ 502941Snate mon = (sc_tokid-JAN); 503941Snate expect(NUMBER); 5046079Sbde mday = atol(sc_token); 505941Snate if (token() == NUMBER) { 506941Snate year = atol(sc_token); 507941Snate token(); 508941Snate } 509941Snate assign_date(tm, mday, mon, year); 510941Snate break; 511941Snate 5127767Sache case SUN: case MON: case TUE: 5137767Sache case WED: case THU: case FRI: 5147767Sache case SAT: 5157767Sache /* do a particular day of the week 5167767Sache */ 5177767Sache wday = (sc_tokid-SUN); 5187767Sache 5197767Sache mday = tm->tm_mday; 5207767Sache 5217767Sache /* if this day is < today, then roll to next week 5227767Sache */ 5237767Sache if (wday < tm->tm_wday) 5247767Sache mday += 7 - (tm->tm_wday - wday); 5257767Sache else 5267767Sache mday += (wday - tm->tm_wday); 5277767Sache 5287767Sache tm->tm_wday = wday; 5297767Sache 5307767Sache assign_date(tm, mday, tm->tm_mon, tm->tm_year); 5317767Sache break; 5327767Sache 533941Snate case NUMBER: 5347767Sache /* get numeric MMDDYY, mm/dd/yy, or dd.mm.yy 535941Snate */ 536941Snate tlen = strlen(sc_token); 537941Snate mon = atol(sc_token); 538941Snate token(); 539941Snate 540941Snate if (sc_tokid == SLASH || sc_tokid == DOT) { 54110154Sache int sep; 542941Snate 543941Snate sep = sc_tokid; 544941Snate expect(NUMBER); 545941Snate mday = atol(sc_token); 546941Snate if (token() == sep) { 547941Snate expect(NUMBER); 548941Snate year = atol(sc_token); 549941Snate token(); 550941Snate } 551941Snate 55254158Scharnier /* flip months and days for European timing 553941Snate */ 554941Snate if (sep == DOT) { 555941Snate int x = mday; 556941Snate mday = mon; 557941Snate mon = x; 558941Snate } 5597767Sache } 5607767Sache else if (tlen == 6 || tlen == 8) { 561941Snate if (tlen == 8) { 562941Snate year = (mon % 10000) - 1900; 563941Snate mon /= 10000; 5647767Sache } 5657767Sache else { 566941Snate year = mon % 100; 567941Snate mon /= 100; 568941Snate } 569941Snate mday = mon % 100; 570941Snate mon /= 100; 5717767Sache } 5727767Sache else 573941Snate panic("garbled time"); 574941Snate 575941Snate mon--; 576941Snate if (mon < 0 || mon > 11 || mday < 1 || mday > 31) 577941Snate panic("garbled time"); 578941Snate 579941Snate assign_date(tm, mday, mon, year); 580941Snate break; 581941Snate } /* case */ 582941Snate} /* month */ 583941Snate 584941Snate 585941Snate/* Global functions */ 586941Snate 587941Snatetime_t 5887767Sacheparsetime(int argc, char **argv) 589941Snate{ 5907767Sache/* Do the argument parsing, die if necessary, and return the time the job 591941Snate * should be run. 592941Snate */ 593941Snate time_t nowtimer, runtimer; 594941Snate struct tm nowtime, runtime; 595941Snate int hr = 0; 596941Snate /* this MUST be initialized to zero for midnight/noon/teatime */ 597941Snate 598941Snate nowtimer = time(NULL); 599941Snate nowtime = *localtime(&nowtimer); 600941Snate 601941Snate runtime = nowtime; 602941Snate runtime.tm_sec = 0; 603941Snate runtime.tm_isdst = 0; 604941Snate 605941Snate if (argc <= optind) 606941Snate usage(); 607941Snate 608941Snate init_scanner(argc-optind, argv+optind); 609941Snate 610941Snate switch (token()) { 61186848Sbrian case NOW: 61286848Sbrian if (scc < 1) { 61386848Sbrian return nowtimer; 61486848Sbrian } 61586848Sbrian /* now is optional prefix for PLUS tree */ 616941Snate expect(PLUS); 617941Snate case PLUS: 618941Snate plus(&runtime); 619941Snate break; 620941Snate 621241230Scracauer /* MINUS is different from PLUS in that NOW is not 622241230Scracauer * an optional prefix for it 623241230Scracauer */ 624241230Scracauer case MINUS: 625241230Scracauer minus(&runtime); 626241230Scracauer break; 627941Snate case NUMBER: 628941Snate tod(&runtime); 629941Snate month(&runtime); 630941Snate break; 631941Snate 6327767Sache /* evil coding for TEATIME|NOON|MIDNIGHT - we've initialised 633941Snate * hr to zero up above, then fall into this case in such a 634941Snate * way so we add +12 +4 hours to it for teatime, +12 hours 635941Snate * to it for noon, and nothing at all for midnight, then 636941Snate * set our runtime to that hour before leaping into the 637941Snate * month scanner 638941Snate */ 639941Snate case TEATIME: 640941Snate hr += 4; 641941Snate case NOON: 642941Snate hr += 12; 643941Snate case MIDNIGHT: 6447767Sache if (runtime.tm_hour >= hr) { 645941Snate runtime.tm_mday++; 6467767Sache runtime.tm_wday++; 6477767Sache } 648941Snate runtime.tm_hour = hr; 649941Snate runtime.tm_min = 0; 650941Snate token(); 65154158Scharnier /* FALLTHROUGH to month setting */ 652941Snate default: 653941Snate month(&runtime); 654941Snate break; 655941Snate } /* ugly case statement */ 656941Snate expect(EOF); 657941Snate 658149215Sstefanf /* convert back to time_t 659941Snate */ 660941Snate runtime.tm_isdst = -1; 661941Snate runtimer = mktime(&runtime); 662941Snate 663941Snate if (runtimer < 0) 664941Snate panic("garbled time"); 665941Snate 666941Snate if (nowtimer > runtimer) 66754158Scharnier panic("trying to travel back in time"); 668941Snate 669941Snate return runtimer; 670941Snate} /* parsetime */ 671