parsetime.c revision 86848
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 3654158Scharnier#ifndef lint 3754158Scharnierstatic const char rcsid[] = 3854158Scharnier "$FreeBSD: head/usr.bin/at/parsetime.c 86848 2001-11-24 10:43:53Z brian $"; 3954158Scharnier#endif /* not lint */ 4054158Scharnier 41941Snate/* System Headers */ 42941Snate 43941Snate#include <sys/types.h> 4454158Scharnier#include <ctype.h> 4526872Scharnier#include <err.h> 46941Snate#include <errno.h> 47941Snate#include <stdio.h> 48941Snate#include <stdlib.h> 49941Snate#include <string.h> 50941Snate#include <time.h> 51941Snate#include <unistd.h> 527767Sache#ifndef __FreeBSD__ 537767Sache#include <getopt.h> 547767Sache#endif 55941Snate 56941Snate/* Local headers */ 57941Snate 58941Snate#include "at.h" 59941Snate#include "panic.h" 60941Snate 61941Snate 62941Snate/* Structures and unions */ 63941Snate 6410154Sacheenum { /* symbols */ 65941Snate MIDNIGHT, NOON, TEATIME, 66941Snate PM, AM, TOMORROW, TODAY, NOW, 6750411Snsayer MINUTES, HOURS, DAYS, WEEKS, MONTHS, YEARS, 68941Snate NUMBER, PLUS, DOT, SLASH, ID, JUNK, 69941Snate JAN, FEB, MAR, APR, MAY, JUN, 707767Sache JUL, AUG, SEP, OCT, NOV, DEC, 717767Sache SUN, MON, TUE, WED, THU, FRI, SAT 727767Sache }; 73941Snate 747767Sache/* parse translation table - table driven parsers can be your FRIEND! 75941Snate */ 76941Snatestruct { 7710154Sache char *name; /* token name */ 7810154Sache int value; /* token id */ 797767Sache int plural; /* is this plural? */ 80941Snate} Specials[] = { 817767Sache { "midnight", MIDNIGHT,0 }, /* 00:00:00 of today or tomorrow */ 827767Sache { "noon", NOON,0 }, /* 12:00:00 of today or tomorrow */ 837767Sache { "teatime", TEATIME,0 }, /* 16:00:00 of today or tomorrow */ 847767Sache { "am", AM,0 }, /* morning times for 0-12 clock */ 857767Sache { "pm", PM,0 }, /* evening times for 0-12 clock */ 867767Sache { "tomorrow", TOMORROW,0 }, /* execute 24 hours from time */ 877767Sache { "today", TODAY, 0 }, /* execute today - don't advance time */ 887767Sache { "now", NOW,0 }, /* opt prefix for PLUS */ 89941Snate 907767Sache { "minute", MINUTES,0 }, /* minutes multiplier */ 917767Sache { "minutes", MINUTES,1 }, /* (pluralized) */ 927767Sache { "hour", HOURS,0 }, /* hours ... */ 937767Sache { "hours", HOURS,1 }, /* (pluralized) */ 947767Sache { "day", DAYS,0 }, /* days ... */ 957767Sache { "days", DAYS,1 }, /* (pluralized) */ 967767Sache { "week", WEEKS,0 }, /* week ... */ 977767Sache { "weeks", WEEKS,1 }, /* (pluralized) */ 9850411Snsayer { "month", MONTHS,0 }, /* month ... */ 9950411Snsayer { "months", MONTHS,1 }, /* (pluralized) */ 10050411Snsayer { "year", YEARS,0 }, /* year ... */ 10150411Snsayer { "years", YEARS,1 }, /* (pluralized) */ 1027767Sache { "jan", JAN,0 }, 1037767Sache { "feb", FEB,0 }, 1047767Sache { "mar", MAR,0 }, 1057767Sache { "apr", APR,0 }, 1067767Sache { "may", MAY,0 }, 1077767Sache { "jun", JUN,0 }, 1087767Sache { "jul", JUL,0 }, 1097767Sache { "aug", AUG,0 }, 1107767Sache { "sep", SEP,0 }, 1117767Sache { "oct", OCT,0 }, 1127767Sache { "nov", NOV,0 }, 1137767Sache { "dec", DEC,0 }, 11437538Sdes { "january", JAN,0 }, 11537538Sdes { "february", FEB,0 }, 11637538Sdes { "march", MAR,0 }, 11737538Sdes { "april", APR,0 }, 11837538Sdes { "may", MAY,0 }, 11937538Sdes { "june", JUN,0 }, 12037538Sdes { "july", JUL,0 }, 12137538Sdes { "august", AUG,0 }, 12237538Sdes { "september", SEP,0 }, 12337538Sdes { "october", OCT,0 }, 12437538Sdes { "november", NOV,0 }, 12537538Sdes { "december", DEC,0 }, 1267767Sache { "sunday", SUN, 0 }, 1277767Sache { "sun", SUN, 0 }, 1287767Sache { "monday", MON, 0 }, 1297767Sache { "mon", MON, 0 }, 1307767Sache { "tuesday", TUE, 0 }, 1317767Sache { "tue", TUE, 0 }, 1327767Sache { "wednesday", WED, 0 }, 1337767Sache { "wed", WED, 0 }, 1347767Sache { "thursday", THU, 0 }, 1357767Sache { "thu", THU, 0 }, 1367767Sache { "friday", FRI, 0 }, 1377767Sache { "fri", FRI, 0 }, 1387767Sache { "saturday", SAT, 0 }, 1397767Sache { "sat", SAT, 0 }, 140941Snate} ; 141941Snate 142941Snate/* File scope variables */ 143941Snate 144941Snatestatic char **scp; /* scanner - pointer at arglist */ 145941Snatestatic char scc; /* scanner - count of remaining arguments */ 146941Snatestatic char *sct; /* scanner - next char pointer in current argument */ 147941Snatestatic int need; /* scanner - need to advance to next argument */ 148941Snate 149941Snatestatic char *sc_token; /* scanner - token buffer */ 15054158Scharnierstatic size_t sc_len; /* scanner - length of token buffer */ 15110154Sachestatic int sc_tokid; /* scanner - token id */ 1527767Sachestatic int sc_tokplur; /* scanner - is token plural? */ 153941Snate 154941Snate/* Local functions */ 155941Snate 156941Snate/* 157941Snate * parse a token, checking if it's something special to us 158941Snate */ 15910154Sachestatic int 16010154Sacheparse_token(char *arg) 161941Snate{ 162941Snate int i; 163941Snate 164941Snate for (i=0; i<(sizeof Specials/sizeof Specials[0]); i++) 165941Snate if (strcasecmp(Specials[i].name, arg) == 0) { 1667767Sache sc_tokplur = Specials[i].plural; 167941Snate return sc_tokid = Specials[i].value; 168941Snate } 169941Snate 170941Snate /* not special - must be some random id */ 171941Snate return ID; 172941Snate} /* parse_token */ 173941Snate 174941Snate 175941Snate/* 176941Snate * init_scanner() sets up the scanner to eat arguments 177941Snate */ 178941Snatestatic void 1797767Sacheinit_scanner(int argc, char **argv) 180941Snate{ 181941Snate scp = argv; 182941Snate scc = argc; 183941Snate need = 1; 184941Snate sc_len = 1; 18510154Sache while (argc-- > 0) 18610154Sache sc_len += strlen(*argv++); 187941Snate 18880294Sobrien if ((sc_token = malloc(sc_len)) == NULL) 18980294Sobrien errx(EXIT_FAILURE, "virtual memory exhausted"); 190941Snate} /* init_scanner */ 191941Snate 192941Snate/* 193941Snate * token() fetches a token from the input stream 194941Snate */ 19510154Sachestatic int 196941Snatetoken() 197941Snate{ 198941Snate int idx; 199941Snate 200941Snate while (1) { 201941Snate memset(sc_token, 0, sc_len); 202941Snate sc_tokid = EOF; 2037767Sache sc_tokplur = 0; 204941Snate idx = 0; 205941Snate 2067767Sache /* if we need to read another argument, walk along the argument list; 207941Snate * when we fall off the arglist, we'll just return EOF forever 208941Snate */ 209941Snate if (need) { 210941Snate if (scc < 1) 211941Snate return sc_tokid; 212941Snate sct = *scp; 213941Snate scp++; 214941Snate scc--; 215941Snate need = 0; 216941Snate } 2177767Sache /* eat whitespace now - if we walk off the end of the argument, 218941Snate * we'll continue, which puts us up at the top of the while loop 219941Snate * to fetch the next argument in 220941Snate */ 221941Snate while (isspace(*sct)) 222941Snate ++sct; 223941Snate if (!*sct) { 224941Snate need = 1; 225941Snate continue; 226941Snate } 227941Snate 2287767Sache /* preserve the first character of the new token 229941Snate */ 230941Snate sc_token[0] = *sct++; 231941Snate 2327767Sache /* then see what it is 233941Snate */ 234941Snate if (isdigit(sc_token[0])) { 235941Snate while (isdigit(*sct)) 236941Snate sc_token[++idx] = *sct++; 237941Snate sc_token[++idx] = 0; 238941Snate return sc_tokid = NUMBER; 2397767Sache } 2407767Sache else if (isalpha(sc_token[0])) { 241941Snate while (isalpha(*sct)) 242941Snate sc_token[++idx] = *sct++; 243941Snate sc_token[++idx] = 0; 244941Snate return parse_token(sc_token); 245941Snate } 246941Snate else if (sc_token[0] == ':' || sc_token[0] == '.') 247941Snate return sc_tokid = DOT; 248941Snate else if (sc_token[0] == '+') 249941Snate return sc_tokid = PLUS; 2507767Sache else if (sc_token[0] == '/') 251941Snate return sc_tokid = SLASH; 252941Snate else 253941Snate return sc_tokid = JUNK; 254941Snate } /* while (1) */ 255941Snate} /* token */ 256941Snate 257941Snate 258941Snate/* 259941Snate * plonk() gives an appropriate error message if a token is incorrect 260941Snate */ 261941Snatestatic void 2627767Sacheplonk(int tok) 263941Snate{ 264941Snate panic((tok == EOF) ? "incomplete time" 265941Snate : "garbled time"); 266941Snate} /* plonk */ 267941Snate 268941Snate 26910154Sache/* 270941Snate * expect() gets a token and dies most horribly if it's not the token we want 271941Snate */ 272941Snatestatic void 27310154Sacheexpect(int desired) 274941Snate{ 275941Snate if (token() != desired) 276941Snate plonk(sc_tokid); /* and we die here... */ 277941Snate} /* expect */ 278941Snate 279941Snate 280941Snate/* 281941Snate * plus() parses a now + time 282941Snate * 28350411Snsayer * at [NOW] PLUS NUMBER [MINUTES|HOURS|DAYS|WEEKS|MONTHS|YEARS] 284941Snate * 285941Snate */ 28650411Snsayer 287941Snatestatic void 2887767Sacheplus(struct tm *tm) 289941Snate{ 290941Snate int delay; 2917767Sache int expectplur; 292941Snate 293941Snate expect(NUMBER); 294941Snate 295941Snate delay = atoi(sc_token); 2967767Sache expectplur = (delay != 1) ? 1 : 0; 297941Snate 298941Snate switch (token()) { 29950411Snsayer case YEARS: 30050411Snsayer tm->tm_year += delay; 30150411Snsayer break; 30250411Snsayer case MONTHS: 30350411Snsayer tm->tm_mon += delay; 30450411Snsayer break; 305941Snate case WEEKS: 306941Snate delay *= 7; 307941Snate case DAYS: 30850411Snsayer tm->tm_mday += delay; 30950411Snsayer break; 310941Snate case HOURS: 31150411Snsayer tm->tm_hour += delay; 31250411Snsayer break; 313941Snate case MINUTES: 31450411Snsayer tm->tm_min += delay; 31550411Snsayer break; 31650411Snsayer default: 31750411Snsayer plonk(sc_tokid); 31850411Snsayer break; 319941Snate } 32050411Snsayer 32150411Snsayer if (expectplur != sc_tokplur) 32250411Snsayer warnx("pluralization is wrong"); 32350411Snsayer 32450411Snsayer tm->tm_isdst = -1; 32550411Snsayer if (mktime(tm) < 0) 32650411Snsayer plonk(sc_tokid); 32750411Snsayer 328941Snate} /* plus */ 329941Snate 330941Snate 331941Snate/* 332941Snate * tod() computes the time of day 333941Snate * [NUMBER [DOT NUMBER] [AM|PM]] 334941Snate */ 335941Snatestatic void 3367767Sachetod(struct tm *tm) 337941Snate{ 338941Snate int hour, minute = 0; 339941Snate int tlen; 340941Snate 341941Snate hour = atoi(sc_token); 342941Snate tlen = strlen(sc_token); 343941Snate 3447767Sache /* first pick out the time of day - if it's 4 digits, we assume 345941Snate * a HHMM time, otherwise it's HH DOT MM time 346941Snate */ 347941Snate if (token() == DOT) { 348941Snate expect(NUMBER); 349941Snate minute = atoi(sc_token); 350941Snate if (minute > 59) 351941Snate panic("garbled time"); 352941Snate token(); 3537767Sache } 3547767Sache else if (tlen == 4) { 355941Snate minute = hour%100; 356941Snate if (minute > 59) 35738646Ssteve panic("garbled time"); 358941Snate hour = hour/100; 359941Snate } 360941Snate 3617767Sache /* check if an AM or PM specifier was given 362941Snate */ 363941Snate if (sc_tokid == AM || sc_tokid == PM) { 364941Snate if (hour > 12) 365941Snate panic("garbled time"); 366941Snate 36717221Sjdp if (sc_tokid == PM) { 36817221Sjdp if (hour != 12) /* 12:xx PM is 12:xx, not 24:xx */ 36926835Scharnier hour += 12; 37017221Sjdp } else { 37117221Sjdp if (hour == 12) /* 12:xx AM is 00:xx, not 12:xx */ 37226835Scharnier hour = 0; 37317221Sjdp } 374941Snate token(); 3757767Sache } 3767767Sache else if (hour > 23) 377941Snate panic("garbled time"); 378941Snate 3797767Sache /* if we specify an absolute time, we don't want to bump the day even 380941Snate * if we've gone past that time - but if we're specifying a time plus 381941Snate * a relative offset, it's okay to bump things 382941Snate */ 3837767Sache if ((sc_tokid == EOF || sc_tokid == PLUS) && tm->tm_hour > hour) { 384941Snate tm->tm_mday++; 3857767Sache tm->tm_wday++; 3867767Sache } 387941Snate 388941Snate tm->tm_hour = hour; 389941Snate tm->tm_min = minute; 390941Snate if (tm->tm_hour == 24) { 391941Snate tm->tm_hour = 0; 392941Snate tm->tm_mday++; 393941Snate } 394941Snate} /* tod */ 395941Snate 396941Snate 397941Snate/* 398941Snate * assign_date() assigns a date, wrapping to next year if needed 399941Snate */ 400941Snatestatic void 4017767Sacheassign_date(struct tm *tm, long mday, long mon, long year) 402941Snate{ 40335729Salex 40458660Ssheldonh /* 40558660Ssheldonh * Convert year into tm_year format (year - 1900). 40658660Ssheldonh * We may be given the year in 2 digit, 4 digit, or tm_year format. 40758660Ssheldonh */ 40858660Ssheldonh if (year != -1) { 40958660Ssheldonh if (year >= 1900) 41058660Ssheldonh year -= 1900; /* convert from 4 digit year */ 41158660Ssheldonh else if (year < 100) { 41258660Ssheldonh /* convert from 2 digit year */ 41358660Ssheldonh struct tm *lt; 41458660Ssheldonh time_t now; 41535729Salex 41658660Ssheldonh time(&now); 41758660Ssheldonh lt = localtime(&now); 41858660Ssheldonh 41958660Ssheldonh /* Convert to tm_year assuming current century */ 42058660Ssheldonh year += (lt->tm_year / 100) * 100; 42158660Ssheldonh 42258660Ssheldonh if (year == lt->tm_year - 1) year++; 42358660Ssheldonh else if (year < lt->tm_year) 42458660Ssheldonh year += 100; /* must be in next century */ 42558660Ssheldonh } 426941Snate } 427941Snate 428941Snate if (year < 0 && 429941Snate (tm->tm_mon > mon ||(tm->tm_mon == mon && tm->tm_mday > mday))) 430941Snate year = tm->tm_year + 1; 431941Snate 432941Snate tm->tm_mday = mday; 433941Snate tm->tm_mon = mon; 434941Snate 435941Snate if (year >= 0) 436941Snate tm->tm_year = year; 437941Snate} /* assign_date */ 438941Snate 439941Snate 44010154Sache/* 441941Snate * month() picks apart a month specification 442941Snate * 443941Snate * /[<month> NUMBER [NUMBER]] \ 444941Snate * |[TOMORROW] | 4457767Sache * |[DAY OF WEEK] | 446941Snate * |NUMBER [SLASH NUMBER [SLASH NUMBER]]| 447941Snate * \PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS/ 448941Snate */ 449941Snatestatic void 4507767Sachemonth(struct tm *tm) 451941Snate{ 452941Snate long year= (-1); 45341556Sarchie long mday = 0, wday, mon; 454941Snate int tlen; 455941Snate 456941Snate switch (sc_tokid) { 457941Snate case PLUS: 458941Snate plus(tm); 459941Snate break; 460941Snate 461941Snate case TOMORROW: 462941Snate /* do something tomorrow */ 463941Snate tm->tm_mday ++; 4647767Sache tm->tm_wday ++; 465941Snate case TODAY: /* force ourselves to stay in today - no further processing */ 466941Snate token(); 467941Snate break; 468941Snate 469941Snate case JAN: case FEB: case MAR: case APR: case MAY: case JUN: 470941Snate case JUL: case AUG: case SEP: case OCT: case NOV: case DEC: 4717767Sache /* do month mday [year] 472941Snate */ 473941Snate mon = (sc_tokid-JAN); 474941Snate expect(NUMBER); 4756079Sbde mday = atol(sc_token); 476941Snate if (token() == NUMBER) { 477941Snate year = atol(sc_token); 478941Snate token(); 479941Snate } 480941Snate assign_date(tm, mday, mon, year); 481941Snate break; 482941Snate 4837767Sache case SUN: case MON: case TUE: 4847767Sache case WED: case THU: case FRI: 4857767Sache case SAT: 4867767Sache /* do a particular day of the week 4877767Sache */ 4887767Sache wday = (sc_tokid-SUN); 4897767Sache 4907767Sache mday = tm->tm_mday; 4917767Sache 4927767Sache /* if this day is < today, then roll to next week 4937767Sache */ 4947767Sache if (wday < tm->tm_wday) 4957767Sache mday += 7 - (tm->tm_wday - wday); 4967767Sache else 4977767Sache mday += (wday - tm->tm_wday); 4987767Sache 4997767Sache tm->tm_wday = wday; 5007767Sache 5017767Sache assign_date(tm, mday, tm->tm_mon, tm->tm_year); 5027767Sache break; 5037767Sache 504941Snate case NUMBER: 5057767Sache /* get numeric MMDDYY, mm/dd/yy, or dd.mm.yy 506941Snate */ 507941Snate tlen = strlen(sc_token); 508941Snate mon = atol(sc_token); 509941Snate token(); 510941Snate 511941Snate if (sc_tokid == SLASH || sc_tokid == DOT) { 51210154Sache int sep; 513941Snate 514941Snate sep = sc_tokid; 515941Snate expect(NUMBER); 516941Snate mday = atol(sc_token); 517941Snate if (token() == sep) { 518941Snate expect(NUMBER); 519941Snate year = atol(sc_token); 520941Snate token(); 521941Snate } 522941Snate 52354158Scharnier /* flip months and days for European timing 524941Snate */ 525941Snate if (sep == DOT) { 526941Snate int x = mday; 527941Snate mday = mon; 528941Snate mon = x; 529941Snate } 5307767Sache } 5317767Sache else if (tlen == 6 || tlen == 8) { 532941Snate if (tlen == 8) { 533941Snate year = (mon % 10000) - 1900; 534941Snate mon /= 10000; 5357767Sache } 5367767Sache else { 537941Snate year = mon % 100; 538941Snate mon /= 100; 539941Snate } 540941Snate mday = mon % 100; 541941Snate mon /= 100; 5427767Sache } 5437767Sache else 544941Snate panic("garbled time"); 545941Snate 546941Snate mon--; 547941Snate if (mon < 0 || mon > 11 || mday < 1 || mday > 31) 548941Snate panic("garbled time"); 549941Snate 550941Snate assign_date(tm, mday, mon, year); 551941Snate break; 552941Snate } /* case */ 553941Snate} /* month */ 554941Snate 555941Snate 556941Snate/* Global functions */ 557941Snate 558941Snatetime_t 5597767Sacheparsetime(int argc, char **argv) 560941Snate{ 5617767Sache/* Do the argument parsing, die if necessary, and return the time the job 562941Snate * should be run. 563941Snate */ 564941Snate time_t nowtimer, runtimer; 565941Snate struct tm nowtime, runtime; 566941Snate int hr = 0; 567941Snate /* this MUST be initialized to zero for midnight/noon/teatime */ 568941Snate 569941Snate nowtimer = time(NULL); 570941Snate nowtime = *localtime(&nowtimer); 571941Snate 572941Snate runtime = nowtime; 573941Snate runtime.tm_sec = 0; 574941Snate runtime.tm_isdst = 0; 575941Snate 576941Snate if (argc <= optind) 577941Snate usage(); 578941Snate 579941Snate init_scanner(argc-optind, argv+optind); 580941Snate 581941Snate switch (token()) { 58286848Sbrian case NOW: 58386848Sbrian if (scc < 1) { 58486848Sbrian return nowtimer; 58586848Sbrian } 58686848Sbrian /* now is optional prefix for PLUS tree */ 587941Snate expect(PLUS); 588941Snate case PLUS: 589941Snate plus(&runtime); 590941Snate break; 591941Snate 592941Snate case NUMBER: 593941Snate tod(&runtime); 594941Snate month(&runtime); 595941Snate break; 596941Snate 5977767Sache /* evil coding for TEATIME|NOON|MIDNIGHT - we've initialised 598941Snate * hr to zero up above, then fall into this case in such a 599941Snate * way so we add +12 +4 hours to it for teatime, +12 hours 600941Snate * to it for noon, and nothing at all for midnight, then 601941Snate * set our runtime to that hour before leaping into the 602941Snate * month scanner 603941Snate */ 604941Snate case TEATIME: 605941Snate hr += 4; 606941Snate case NOON: 607941Snate hr += 12; 608941Snate case MIDNIGHT: 6097767Sache if (runtime.tm_hour >= hr) { 610941Snate runtime.tm_mday++; 6117767Sache runtime.tm_wday++; 6127767Sache } 613941Snate runtime.tm_hour = hr; 614941Snate runtime.tm_min = 0; 615941Snate token(); 61654158Scharnier /* FALLTHROUGH to month setting */ 617941Snate default: 618941Snate month(&runtime); 619941Snate break; 620941Snate } /* ugly case statement */ 621941Snate expect(EOF); 622941Snate 6237767Sache /* adjust for daylight savings time 624941Snate */ 625941Snate runtime.tm_isdst = -1; 626941Snate runtimer = mktime(&runtime); 627941Snate if (runtime.tm_isdst > 0) { 628941Snate runtimer -= 3600; 629941Snate runtimer = mktime(&runtime); 630941Snate } 631941Snate 632941Snate if (runtimer < 0) 633941Snate panic("garbled time"); 634941Snate 635941Snate if (nowtimer > runtimer) 63654158Scharnier panic("trying to travel back in time"); 637941Snate 638941Snate return runtimer; 639941Snate} /* parsetime */ 640