parsetime.c revision 41556
110154Sache/* 27767Sache * parsetime.c - parse time for at(1) 37767Sache * Copyright (C) 1993, 1994 Thomas Koenig 4941Snate * 57767Sache * 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 36941Snate/* System Headers */ 37941Snate 387767Sache 39941Snate#include <sys/types.h> 4026872Scharnier#include <err.h> 41941Snate#include <errno.h> 42941Snate#include <stdio.h> 43941Snate#include <stdlib.h> 44941Snate#include <string.h> 45941Snate#include <time.h> 46941Snate#include <unistd.h> 47941Snate#include <ctype.h> 487767Sache#ifndef __FreeBSD__ 497767Sache#include <getopt.h> 507767Sache#endif 51941Snate 52941Snate/* Local headers */ 53941Snate 54941Snate#include "at.h" 55941Snate#include "panic.h" 56941Snate 57941Snate 58941Snate/* Structures and unions */ 59941Snate 6010154Sacheenum { /* symbols */ 61941Snate MIDNIGHT, NOON, TEATIME, 62941Snate PM, AM, TOMORROW, TODAY, NOW, 63941Snate MINUTES, HOURS, DAYS, WEEKS, 64941Snate NUMBER, PLUS, DOT, SLASH, ID, JUNK, 65941Snate JAN, FEB, MAR, APR, MAY, JUN, 667767Sache JUL, AUG, SEP, OCT, NOV, DEC, 677767Sache SUN, MON, TUE, WED, THU, FRI, SAT 687767Sache }; 69941Snate 707767Sache/* parse translation table - table driven parsers can be your FRIEND! 71941Snate */ 72941Snatestruct { 7310154Sache char *name; /* token name */ 7410154Sache int value; /* token id */ 757767Sache int plural; /* is this plural? */ 76941Snate} Specials[] = { 777767Sache { "midnight", MIDNIGHT,0 }, /* 00:00:00 of today or tomorrow */ 787767Sache { "noon", NOON,0 }, /* 12:00:00 of today or tomorrow */ 797767Sache { "teatime", TEATIME,0 }, /* 16:00:00 of today or tomorrow */ 807767Sache { "am", AM,0 }, /* morning times for 0-12 clock */ 817767Sache { "pm", PM,0 }, /* evening times for 0-12 clock */ 827767Sache { "tomorrow", TOMORROW,0 }, /* execute 24 hours from time */ 837767Sache { "today", TODAY, 0 }, /* execute today - don't advance time */ 847767Sache { "now", NOW,0 }, /* opt prefix for PLUS */ 85941Snate 867767Sache { "minute", MINUTES,0 }, /* minutes multiplier */ 877767Sache { "minutes", MINUTES,1 }, /* (pluralized) */ 887767Sache { "hour", HOURS,0 }, /* hours ... */ 897767Sache { "hours", HOURS,1 }, /* (pluralized) */ 907767Sache { "day", DAYS,0 }, /* days ... */ 917767Sache { "days", DAYS,1 }, /* (pluralized) */ 927767Sache { "week", WEEKS,0 }, /* week ... */ 937767Sache { "weeks", WEEKS,1 }, /* (pluralized) */ 947767Sache { "jan", JAN,0 }, 957767Sache { "feb", FEB,0 }, 967767Sache { "mar", MAR,0 }, 977767Sache { "apr", APR,0 }, 987767Sache { "may", MAY,0 }, 997767Sache { "jun", JUN,0 }, 1007767Sache { "jul", JUL,0 }, 1017767Sache { "aug", AUG,0 }, 1027767Sache { "sep", SEP,0 }, 1037767Sache { "oct", OCT,0 }, 1047767Sache { "nov", NOV,0 }, 1057767Sache { "dec", DEC,0 }, 10637538Sdes { "january", JAN,0 }, 10737538Sdes { "february", FEB,0 }, 10837538Sdes { "march", MAR,0 }, 10937538Sdes { "april", APR,0 }, 11037538Sdes { "may", MAY,0 }, 11137538Sdes { "june", JUN,0 }, 11237538Sdes { "july", JUL,0 }, 11337538Sdes { "august", AUG,0 }, 11437538Sdes { "september", SEP,0 }, 11537538Sdes { "october", OCT,0 }, 11637538Sdes { "november", NOV,0 }, 11737538Sdes { "december", DEC,0 }, 1187767Sache { "sunday", SUN, 0 }, 1197767Sache { "sun", SUN, 0 }, 1207767Sache { "monday", MON, 0 }, 1217767Sache { "mon", MON, 0 }, 1227767Sache { "tuesday", TUE, 0 }, 1237767Sache { "tue", TUE, 0 }, 1247767Sache { "wednesday", WED, 0 }, 1257767Sache { "wed", WED, 0 }, 1267767Sache { "thursday", THU, 0 }, 1277767Sache { "thu", THU, 0 }, 1287767Sache { "friday", FRI, 0 }, 1297767Sache { "fri", FRI, 0 }, 1307767Sache { "saturday", SAT, 0 }, 1317767Sache { "sat", SAT, 0 }, 132941Snate} ; 133941Snate 134941Snate/* File scope variables */ 135941Snate 136941Snatestatic char **scp; /* scanner - pointer at arglist */ 137941Snatestatic char scc; /* scanner - count of remaining arguments */ 138941Snatestatic char *sct; /* scanner - next char pointer in current argument */ 139941Snatestatic int need; /* scanner - need to advance to next argument */ 140941Snate 141941Snatestatic char *sc_token; /* scanner - token buffer */ 142941Snatestatic size_t sc_len; /* scanner - lenght of token buffer */ 14310154Sachestatic int sc_tokid; /* scanner - token id */ 1447767Sachestatic int sc_tokplur; /* scanner - is token plural? */ 145941Snate 14641556Sarchiestatic const char rcsid[] = 14741556Sarchie "$Id: parsetime.c,v 1.15 1998/08/30 17:33:05 steve Exp $"; 148941Snate 149941Snate/* Local functions */ 150941Snate 151941Snate/* 152941Snate * parse a token, checking if it's something special to us 153941Snate */ 15410154Sachestatic int 15510154Sacheparse_token(char *arg) 156941Snate{ 157941Snate int i; 158941Snate 159941Snate for (i=0; i<(sizeof Specials/sizeof Specials[0]); i++) 160941Snate if (strcasecmp(Specials[i].name, arg) == 0) { 1617767Sache sc_tokplur = Specials[i].plural; 162941Snate return sc_tokid = Specials[i].value; 163941Snate } 164941Snate 165941Snate /* not special - must be some random id */ 166941Snate return ID; 167941Snate} /* parse_token */ 168941Snate 169941Snate 170941Snate/* 171941Snate * init_scanner() sets up the scanner to eat arguments 172941Snate */ 173941Snatestatic void 1747767Sacheinit_scanner(int argc, char **argv) 175941Snate{ 176941Snate scp = argv; 177941Snate scc = argc; 178941Snate need = 1; 179941Snate sc_len = 1; 18010154Sache while (argc-- > 0) 18110154Sache sc_len += strlen(*argv++); 182941Snate 1837767Sache sc_token = (char *) mymalloc(sc_len); 184941Snate} /* init_scanner */ 185941Snate 186941Snate/* 187941Snate * token() fetches a token from the input stream 188941Snate */ 18910154Sachestatic int 190941Snatetoken() 191941Snate{ 192941Snate int idx; 193941Snate 194941Snate while (1) { 195941Snate memset(sc_token, 0, sc_len); 196941Snate sc_tokid = EOF; 1977767Sache sc_tokplur = 0; 198941Snate idx = 0; 199941Snate 2007767Sache /* if we need to read another argument, walk along the argument list; 201941Snate * when we fall off the arglist, we'll just return EOF forever 202941Snate */ 203941Snate if (need) { 204941Snate if (scc < 1) 205941Snate return sc_tokid; 206941Snate sct = *scp; 207941Snate scp++; 208941Snate scc--; 209941Snate need = 0; 210941Snate } 2117767Sache /* eat whitespace now - if we walk off the end of the argument, 212941Snate * we'll continue, which puts us up at the top of the while loop 213941Snate * to fetch the next argument in 214941Snate */ 215941Snate while (isspace(*sct)) 216941Snate ++sct; 217941Snate if (!*sct) { 218941Snate need = 1; 219941Snate continue; 220941Snate } 221941Snate 2227767Sache /* preserve the first character of the new token 223941Snate */ 224941Snate sc_token[0] = *sct++; 225941Snate 2267767Sache /* then see what it is 227941Snate */ 228941Snate if (isdigit(sc_token[0])) { 229941Snate while (isdigit(*sct)) 230941Snate sc_token[++idx] = *sct++; 231941Snate sc_token[++idx] = 0; 232941Snate return sc_tokid = NUMBER; 2337767Sache } 2347767Sache else if (isalpha(sc_token[0])) { 235941Snate while (isalpha(*sct)) 236941Snate sc_token[++idx] = *sct++; 237941Snate sc_token[++idx] = 0; 238941Snate return parse_token(sc_token); 239941Snate } 240941Snate else if (sc_token[0] == ':' || sc_token[0] == '.') 241941Snate return sc_tokid = DOT; 242941Snate else if (sc_token[0] == '+') 243941Snate return sc_tokid = PLUS; 2447767Sache else if (sc_token[0] == '/') 245941Snate return sc_tokid = SLASH; 246941Snate else 247941Snate return sc_tokid = JUNK; 248941Snate } /* while (1) */ 249941Snate} /* token */ 250941Snate 251941Snate 252941Snate/* 253941Snate * plonk() gives an appropriate error message if a token is incorrect 254941Snate */ 255941Snatestatic void 2567767Sacheplonk(int tok) 257941Snate{ 258941Snate panic((tok == EOF) ? "incomplete time" 259941Snate : "garbled time"); 260941Snate} /* plonk */ 261941Snate 262941Snate 26310154Sache/* 264941Snate * expect() gets a token and dies most horribly if it's not the token we want 265941Snate */ 266941Snatestatic void 26710154Sacheexpect(int desired) 268941Snate{ 269941Snate if (token() != desired) 270941Snate plonk(sc_tokid); /* and we die here... */ 271941Snate} /* expect */ 272941Snate 273941Snate 274941Snate/* 275941Snate * dateadd() adds a number of minutes to a date. It is extraordinarily 276941Snate * stupid regarding day-of-month overflow, and will most likely not 277941Snate * work properly 278941Snate */ 279941Snatestatic void 2807767Sachedateadd(int minutes, struct tm *tm) 281941Snate{ 282941Snate /* increment days */ 283941Snate 284941Snate while (minutes > 24*60) { 285941Snate minutes -= 24*60; 286941Snate tm->tm_mday++; 287941Snate } 288941Snate 289941Snate /* increment hours */ 290941Snate while (minutes > 60) { 291941Snate minutes -= 60; 292941Snate tm->tm_hour++; 293941Snate if (tm->tm_hour > 23) { 294941Snate tm->tm_mday++; 295941Snate tm->tm_hour = 0; 296941Snate } 297941Snate } 298941Snate 299941Snate /* increment minutes */ 300941Snate tm->tm_min += minutes; 301941Snate 302941Snate if (tm->tm_min > 59) { 303941Snate tm->tm_hour++; 304941Snate tm->tm_min -= 60; 305941Snate 306941Snate if (tm->tm_hour > 23) { 307941Snate tm->tm_mday++; 308941Snate tm->tm_hour = 0; 309941Snate } 310941Snate } 311941Snate} /* dateadd */ 312941Snate 313941Snate 314941Snate/* 315941Snate * plus() parses a now + time 316941Snate * 317941Snate * at [NOW] PLUS NUMBER [MINUTES|HOURS|DAYS|WEEKS] 318941Snate * 319941Snate */ 320941Snatestatic void 3217767Sacheplus(struct tm *tm) 322941Snate{ 323941Snate int delay; 3247767Sache int expectplur; 325941Snate 326941Snate expect(NUMBER); 327941Snate 328941Snate delay = atoi(sc_token); 3297767Sache expectplur = (delay != 1) ? 1 : 0; 330941Snate 331941Snate switch (token()) { 332941Snate case WEEKS: 333941Snate delay *= 7; 334941Snate case DAYS: 335941Snate delay *= 24; 336941Snate case HOURS: 337941Snate delay *= 60; 338941Snate case MINUTES: 3397767Sache if (expectplur != sc_tokplur) 34026835Scharnier warnx("pluralization is wrong"); 341941Snate dateadd(delay, tm); 342941Snate return; 343941Snate } 344941Snate plonk(sc_tokid); 345941Snate} /* plus */ 346941Snate 347941Snate 348941Snate/* 349941Snate * tod() computes the time of day 350941Snate * [NUMBER [DOT NUMBER] [AM|PM]] 351941Snate */ 352941Snatestatic void 3537767Sachetod(struct tm *tm) 354941Snate{ 355941Snate int hour, minute = 0; 356941Snate int tlen; 357941Snate 358941Snate hour = atoi(sc_token); 359941Snate tlen = strlen(sc_token); 360941Snate 3617767Sache /* first pick out the time of day - if it's 4 digits, we assume 362941Snate * a HHMM time, otherwise it's HH DOT MM time 363941Snate */ 364941Snate if (token() == DOT) { 365941Snate expect(NUMBER); 366941Snate minute = atoi(sc_token); 367941Snate if (minute > 59) 368941Snate panic("garbled time"); 369941Snate token(); 3707767Sache } 3717767Sache else if (tlen == 4) { 372941Snate minute = hour%100; 373941Snate if (minute > 59) 37438646Ssteve panic("garbled time"); 375941Snate hour = hour/100; 376941Snate } 377941Snate 3787767Sache /* check if an AM or PM specifier was given 379941Snate */ 380941Snate if (sc_tokid == AM || sc_tokid == PM) { 381941Snate if (hour > 12) 382941Snate panic("garbled time"); 383941Snate 38417221Sjdp if (sc_tokid == PM) { 38517221Sjdp if (hour != 12) /* 12:xx PM is 12:xx, not 24:xx */ 38626835Scharnier hour += 12; 38717221Sjdp } else { 38817221Sjdp if (hour == 12) /* 12:xx AM is 00:xx, not 12:xx */ 38926835Scharnier hour = 0; 39017221Sjdp } 391941Snate token(); 3927767Sache } 3937767Sache else if (hour > 23) 394941Snate panic("garbled time"); 395941Snate 3967767Sache /* if we specify an absolute time, we don't want to bump the day even 397941Snate * if we've gone past that time - but if we're specifying a time plus 398941Snate * a relative offset, it's okay to bump things 399941Snate */ 4007767Sache if ((sc_tokid == EOF || sc_tokid == PLUS) && tm->tm_hour > hour) { 401941Snate tm->tm_mday++; 4027767Sache tm->tm_wday++; 4037767Sache } 404941Snate 405941Snate tm->tm_hour = hour; 406941Snate tm->tm_min = minute; 407941Snate if (tm->tm_hour == 24) { 408941Snate tm->tm_hour = 0; 409941Snate tm->tm_mday++; 410941Snate } 411941Snate} /* tod */ 412941Snate 413941Snate 414941Snate/* 415941Snate * assign_date() assigns a date, wrapping to next year if needed 416941Snate */ 417941Snatestatic void 4187767Sacheassign_date(struct tm *tm, long mday, long mon, long year) 419941Snate{ 420941Snate if (year > 99) { 421941Snate if (year > 1899) 422941Snate year -= 1900; 423941Snate else 424941Snate panic("garbled time"); 42538188Salex } else if (year != -1) { 42635729Salex struct tm *lt; 42735729Salex time_t now; 42835729Salex 42935729Salex time(&now); 43035729Salex lt = localtime(&now); 43135729Salex 43235729Salex /* 43335729Salex * check if the specified year is in the next century. 43435729Salex * allow for one year of user error as many people will 43535729Salex * enter n - 1 at the start of year n. 43635729Salex */ 43735729Salex if (year < (lt->tm_year % 100) - 1) 43835729Salex year += 100; 43935729Salex /* adjust for the year 2000 and beyond */ 44035729Salex year += lt->tm_year - (lt->tm_year % 100); 441941Snate } 442941Snate 443941Snate if (year < 0 && 444941Snate (tm->tm_mon > mon ||(tm->tm_mon == mon && tm->tm_mday > mday))) 445941Snate year = tm->tm_year + 1; 446941Snate 447941Snate tm->tm_mday = mday; 448941Snate tm->tm_mon = mon; 449941Snate 450941Snate if (year >= 0) 451941Snate tm->tm_year = year; 452941Snate} /* assign_date */ 453941Snate 454941Snate 45510154Sache/* 456941Snate * month() picks apart a month specification 457941Snate * 458941Snate * /[<month> NUMBER [NUMBER]] \ 459941Snate * |[TOMORROW] | 4607767Sache * |[DAY OF WEEK] | 461941Snate * |NUMBER [SLASH NUMBER [SLASH NUMBER]]| 462941Snate * \PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS/ 463941Snate */ 464941Snatestatic void 4657767Sachemonth(struct tm *tm) 466941Snate{ 467941Snate long year= (-1); 46841556Sarchie long mday = 0, wday, mon; 469941Snate int tlen; 470941Snate 471941Snate switch (sc_tokid) { 472941Snate case PLUS: 473941Snate plus(tm); 474941Snate break; 475941Snate 476941Snate case TOMORROW: 477941Snate /* do something tomorrow */ 478941Snate tm->tm_mday ++; 4797767Sache tm->tm_wday ++; 480941Snate case TODAY: /* force ourselves to stay in today - no further processing */ 481941Snate token(); 482941Snate break; 483941Snate 484941Snate case JAN: case FEB: case MAR: case APR: case MAY: case JUN: 485941Snate case JUL: case AUG: case SEP: case OCT: case NOV: case DEC: 4867767Sache /* do month mday [year] 487941Snate */ 488941Snate mon = (sc_tokid-JAN); 489941Snate expect(NUMBER); 4906079Sbde mday = atol(sc_token); 491941Snate if (token() == NUMBER) { 492941Snate year = atol(sc_token); 493941Snate token(); 494941Snate } 495941Snate assign_date(tm, mday, mon, year); 496941Snate break; 497941Snate 4987767Sache case SUN: case MON: case TUE: 4997767Sache case WED: case THU: case FRI: 5007767Sache case SAT: 5017767Sache /* do a particular day of the week 5027767Sache */ 5037767Sache wday = (sc_tokid-SUN); 5047767Sache 5057767Sache mday = tm->tm_mday; 5067767Sache 5077767Sache /* if this day is < today, then roll to next week 5087767Sache */ 5097767Sache if (wday < tm->tm_wday) 5107767Sache mday += 7 - (tm->tm_wday - wday); 5117767Sache else 5127767Sache mday += (wday - tm->tm_wday); 5137767Sache 5147767Sache tm->tm_wday = wday; 5157767Sache 5167767Sache assign_date(tm, mday, tm->tm_mon, tm->tm_year); 5177767Sache break; 5187767Sache 519941Snate case NUMBER: 5207767Sache /* get numeric MMDDYY, mm/dd/yy, or dd.mm.yy 521941Snate */ 522941Snate tlen = strlen(sc_token); 523941Snate mon = atol(sc_token); 524941Snate token(); 525941Snate 526941Snate if (sc_tokid == SLASH || sc_tokid == DOT) { 52710154Sache int sep; 528941Snate 529941Snate sep = sc_tokid; 530941Snate expect(NUMBER); 531941Snate mday = atol(sc_token); 532941Snate if (token() == sep) { 533941Snate expect(NUMBER); 534941Snate year = atol(sc_token); 535941Snate token(); 536941Snate } 537941Snate 5387767Sache /* flip months and days for european timing 539941Snate */ 540941Snate if (sep == DOT) { 541941Snate int x = mday; 542941Snate mday = mon; 543941Snate mon = x; 544941Snate } 5457767Sache } 5467767Sache else if (tlen == 6 || tlen == 8) { 547941Snate if (tlen == 8) { 548941Snate year = (mon % 10000) - 1900; 549941Snate mon /= 10000; 5507767Sache } 5517767Sache else { 552941Snate year = mon % 100; 553941Snate mon /= 100; 554941Snate } 555941Snate mday = mon % 100; 556941Snate mon /= 100; 5577767Sache } 5587767Sache else 559941Snate panic("garbled time"); 560941Snate 561941Snate mon--; 562941Snate if (mon < 0 || mon > 11 || mday < 1 || mday > 31) 563941Snate panic("garbled time"); 564941Snate 565941Snate assign_date(tm, mday, mon, year); 566941Snate break; 567941Snate } /* case */ 568941Snate} /* month */ 569941Snate 570941Snate 571941Snate/* Global functions */ 572941Snate 573941Snatetime_t 5747767Sacheparsetime(int argc, char **argv) 575941Snate{ 5767767Sache/* Do the argument parsing, die if necessary, and return the time the job 577941Snate * should be run. 578941Snate */ 579941Snate time_t nowtimer, runtimer; 580941Snate struct tm nowtime, runtime; 581941Snate int hr = 0; 582941Snate /* this MUST be initialized to zero for midnight/noon/teatime */ 583941Snate 584941Snate nowtimer = time(NULL); 585941Snate nowtime = *localtime(&nowtimer); 586941Snate 587941Snate runtime = nowtime; 588941Snate runtime.tm_sec = 0; 589941Snate runtime.tm_isdst = 0; 590941Snate 591941Snate if (argc <= optind) 592941Snate usage(); 593941Snate 594941Snate init_scanner(argc-optind, argv+optind); 595941Snate 596941Snate switch (token()) { 597941Snate case NOW: /* now is optional prefix for PLUS tree */ 598941Snate expect(PLUS); 599941Snate case PLUS: 600941Snate plus(&runtime); 601941Snate break; 602941Snate 603941Snate case NUMBER: 604941Snate tod(&runtime); 605941Snate month(&runtime); 606941Snate break; 607941Snate 6087767Sache /* evil coding for TEATIME|NOON|MIDNIGHT - we've initialised 609941Snate * hr to zero up above, then fall into this case in such a 610941Snate * way so we add +12 +4 hours to it for teatime, +12 hours 611941Snate * to it for noon, and nothing at all for midnight, then 612941Snate * set our runtime to that hour before leaping into the 613941Snate * month scanner 614941Snate */ 615941Snate case TEATIME: 616941Snate hr += 4; 617941Snate case NOON: 618941Snate hr += 12; 619941Snate case MIDNIGHT: 6207767Sache if (runtime.tm_hour >= hr) { 621941Snate runtime.tm_mday++; 6227767Sache runtime.tm_wday++; 6237767Sache } 624941Snate runtime.tm_hour = hr; 625941Snate runtime.tm_min = 0; 626941Snate token(); 627941Snate /* fall through to month setting */ 628941Snate default: 629941Snate month(&runtime); 630941Snate break; 631941Snate } /* ugly case statement */ 632941Snate expect(EOF); 633941Snate 6347767Sache /* adjust for daylight savings time 635941Snate */ 636941Snate runtime.tm_isdst = -1; 637941Snate runtimer = mktime(&runtime); 638941Snate if (runtime.tm_isdst > 0) { 639941Snate runtimer -= 3600; 640941Snate runtimer = mktime(&runtime); 641941Snate } 642941Snate 643941Snate if (runtimer < 0) 644941Snate panic("garbled time"); 645941Snate 646941Snate if (nowtimer > runtimer) 647941Snate panic("Trying to travel back in time"); 648941Snate 649941Snate return runtimer; 650941Snate} /* parsetime */ 651