parsetime.c revision 54158
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 54158 1999-12-05 19:57:14Z charnier $"; 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 1887767Sache sc_token = (char *) mymalloc(sc_len); 189941Snate} /* init_scanner */ 190941Snate 191941Snate/* 192941Snate * token() fetches a token from the input stream 193941Snate */ 19410154Sachestatic int 195941Snatetoken() 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; 2497767Sache else if (sc_token[0] == '/') 250941Snate return sc_tokid = SLASH; 251941Snate else 252941Snate return sc_tokid = JUNK; 253941Snate } /* while (1) */ 254941Snate} /* token */ 255941Snate 256941Snate 257941Snate/* 258941Snate * plonk() gives an appropriate error message if a token is incorrect 259941Snate */ 260941Snatestatic void 2617767Sacheplonk(int tok) 262941Snate{ 263941Snate panic((tok == EOF) ? "incomplete time" 264941Snate : "garbled time"); 265941Snate} /* plonk */ 266941Snate 267941Snate 26810154Sache/* 269941Snate * expect() gets a token and dies most horribly if it's not the token we want 270941Snate */ 271941Snatestatic void 27210154Sacheexpect(int desired) 273941Snate{ 274941Snate if (token() != desired) 275941Snate plonk(sc_tokid); /* and we die here... */ 276941Snate} /* expect */ 277941Snate 278941Snate 279941Snate/* 280941Snate * plus() parses a now + time 281941Snate * 28250411Snsayer * at [NOW] PLUS NUMBER [MINUTES|HOURS|DAYS|WEEKS|MONTHS|YEARS] 283941Snate * 284941Snate */ 28550411Snsayer 286941Snatestatic void 2877767Sacheplus(struct tm *tm) 288941Snate{ 289941Snate int delay; 2907767Sache int expectplur; 291941Snate 292941Snate expect(NUMBER); 293941Snate 294941Snate delay = atoi(sc_token); 2957767Sache expectplur = (delay != 1) ? 1 : 0; 296941Snate 297941Snate switch (token()) { 29850411Snsayer case YEARS: 29950411Snsayer tm->tm_year += delay; 30050411Snsayer break; 30150411Snsayer case MONTHS: 30250411Snsayer tm->tm_mon += delay; 30350411Snsayer break; 304941Snate case WEEKS: 305941Snate delay *= 7; 306941Snate case DAYS: 30750411Snsayer tm->tm_mday += delay; 30850411Snsayer break; 309941Snate case HOURS: 31050411Snsayer tm->tm_hour += delay; 31150411Snsayer break; 312941Snate case MINUTES: 31350411Snsayer tm->tm_min += delay; 31450411Snsayer break; 31550411Snsayer default: 31650411Snsayer plonk(sc_tokid); 31750411Snsayer break; 318941Snate } 31950411Snsayer 32050411Snsayer if (expectplur != sc_tokplur) 32150411Snsayer warnx("pluralization is wrong"); 32250411Snsayer 32350411Snsayer tm->tm_isdst = -1; 32450411Snsayer if (mktime(tm) < 0) 32550411Snsayer plonk(sc_tokid); 32650411Snsayer 327941Snate} /* plus */ 328941Snate 329941Snate 330941Snate/* 331941Snate * tod() computes the time of day 332941Snate * [NUMBER [DOT NUMBER] [AM|PM]] 333941Snate */ 334941Snatestatic void 3357767Sachetod(struct tm *tm) 336941Snate{ 337941Snate int hour, minute = 0; 338941Snate int tlen; 339941Snate 340941Snate hour = atoi(sc_token); 341941Snate tlen = strlen(sc_token); 342941Snate 3437767Sache /* first pick out the time of day - if it's 4 digits, we assume 344941Snate * a HHMM time, otherwise it's HH DOT MM time 345941Snate */ 346941Snate if (token() == DOT) { 347941Snate expect(NUMBER); 348941Snate minute = atoi(sc_token); 349941Snate if (minute > 59) 350941Snate panic("garbled time"); 351941Snate token(); 3527767Sache } 3537767Sache else if (tlen == 4) { 354941Snate minute = hour%100; 355941Snate if (minute > 59) 35638646Ssteve panic("garbled time"); 357941Snate hour = hour/100; 358941Snate } 359941Snate 3607767Sache /* check if an AM or PM specifier was given 361941Snate */ 362941Snate if (sc_tokid == AM || sc_tokid == PM) { 363941Snate if (hour > 12) 364941Snate panic("garbled time"); 365941Snate 36617221Sjdp if (sc_tokid == PM) { 36717221Sjdp if (hour != 12) /* 12:xx PM is 12:xx, not 24:xx */ 36826835Scharnier hour += 12; 36917221Sjdp } else { 37017221Sjdp if (hour == 12) /* 12:xx AM is 00:xx, not 12:xx */ 37126835Scharnier hour = 0; 37217221Sjdp } 373941Snate token(); 3747767Sache } 3757767Sache else if (hour > 23) 376941Snate panic("garbled time"); 377941Snate 3787767Sache /* if we specify an absolute time, we don't want to bump the day even 379941Snate * if we've gone past that time - but if we're specifying a time plus 380941Snate * a relative offset, it's okay to bump things 381941Snate */ 3827767Sache if ((sc_tokid == EOF || sc_tokid == PLUS) && tm->tm_hour > hour) { 383941Snate tm->tm_mday++; 3847767Sache tm->tm_wday++; 3857767Sache } 386941Snate 387941Snate tm->tm_hour = hour; 388941Snate tm->tm_min = minute; 389941Snate if (tm->tm_hour == 24) { 390941Snate tm->tm_hour = 0; 391941Snate tm->tm_mday++; 392941Snate } 393941Snate} /* tod */ 394941Snate 395941Snate 396941Snate/* 397941Snate * assign_date() assigns a date, wrapping to next year if needed 398941Snate */ 399941Snatestatic void 4007767Sacheassign_date(struct tm *tm, long mday, long mon, long year) 401941Snate{ 402941Snate if (year > 99) { 403941Snate if (year > 1899) 404941Snate year -= 1900; 405941Snate else 406941Snate panic("garbled time"); 40738188Salex } else if (year != -1) { 40835729Salex struct tm *lt; 40935729Salex time_t now; 41035729Salex 41135729Salex time(&now); 41235729Salex lt = localtime(&now); 41335729Salex 41435729Salex /* 41535729Salex * check if the specified year is in the next century. 41635729Salex * allow for one year of user error as many people will 41735729Salex * enter n - 1 at the start of year n. 41835729Salex */ 41935729Salex if (year < (lt->tm_year % 100) - 1) 42035729Salex year += 100; 42135729Salex /* adjust for the year 2000 and beyond */ 42235729Salex year += lt->tm_year - (lt->tm_year % 100); 423941Snate } 424941Snate 425941Snate if (year < 0 && 426941Snate (tm->tm_mon > mon ||(tm->tm_mon == mon && tm->tm_mday > mday))) 427941Snate year = tm->tm_year + 1; 428941Snate 429941Snate tm->tm_mday = mday; 430941Snate tm->tm_mon = mon; 431941Snate 432941Snate if (year >= 0) 433941Snate tm->tm_year = year; 434941Snate} /* assign_date */ 435941Snate 436941Snate 43710154Sache/* 438941Snate * month() picks apart a month specification 439941Snate * 440941Snate * /[<month> NUMBER [NUMBER]] \ 441941Snate * |[TOMORROW] | 4427767Sache * |[DAY OF WEEK] | 443941Snate * |NUMBER [SLASH NUMBER [SLASH NUMBER]]| 444941Snate * \PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS/ 445941Snate */ 446941Snatestatic void 4477767Sachemonth(struct tm *tm) 448941Snate{ 449941Snate long year= (-1); 45041556Sarchie long mday = 0, wday, mon; 451941Snate int tlen; 452941Snate 453941Snate switch (sc_tokid) { 454941Snate case PLUS: 455941Snate plus(tm); 456941Snate break; 457941Snate 458941Snate case TOMORROW: 459941Snate /* do something tomorrow */ 460941Snate tm->tm_mday ++; 4617767Sache tm->tm_wday ++; 462941Snate case TODAY: /* force ourselves to stay in today - no further processing */ 463941Snate token(); 464941Snate break; 465941Snate 466941Snate case JAN: case FEB: case MAR: case APR: case MAY: case JUN: 467941Snate case JUL: case AUG: case SEP: case OCT: case NOV: case DEC: 4687767Sache /* do month mday [year] 469941Snate */ 470941Snate mon = (sc_tokid-JAN); 471941Snate expect(NUMBER); 4726079Sbde mday = atol(sc_token); 473941Snate if (token() == NUMBER) { 474941Snate year = atol(sc_token); 475941Snate token(); 476941Snate } 477941Snate assign_date(tm, mday, mon, year); 478941Snate break; 479941Snate 4807767Sache case SUN: case MON: case TUE: 4817767Sache case WED: case THU: case FRI: 4827767Sache case SAT: 4837767Sache /* do a particular day of the week 4847767Sache */ 4857767Sache wday = (sc_tokid-SUN); 4867767Sache 4877767Sache mday = tm->tm_mday; 4887767Sache 4897767Sache /* if this day is < today, then roll to next week 4907767Sache */ 4917767Sache if (wday < tm->tm_wday) 4927767Sache mday += 7 - (tm->tm_wday - wday); 4937767Sache else 4947767Sache mday += (wday - tm->tm_wday); 4957767Sache 4967767Sache tm->tm_wday = wday; 4977767Sache 4987767Sache assign_date(tm, mday, tm->tm_mon, tm->tm_year); 4997767Sache break; 5007767Sache 501941Snate case NUMBER: 5027767Sache /* get numeric MMDDYY, mm/dd/yy, or dd.mm.yy 503941Snate */ 504941Snate tlen = strlen(sc_token); 505941Snate mon = atol(sc_token); 506941Snate token(); 507941Snate 508941Snate if (sc_tokid == SLASH || sc_tokid == DOT) { 50910154Sache int sep; 510941Snate 511941Snate sep = sc_tokid; 512941Snate expect(NUMBER); 513941Snate mday = atol(sc_token); 514941Snate if (token() == sep) { 515941Snate expect(NUMBER); 516941Snate year = atol(sc_token); 517941Snate token(); 518941Snate } 519941Snate 52054158Scharnier /* flip months and days for European timing 521941Snate */ 522941Snate if (sep == DOT) { 523941Snate int x = mday; 524941Snate mday = mon; 525941Snate mon = x; 526941Snate } 5277767Sache } 5287767Sache else if (tlen == 6 || tlen == 8) { 529941Snate if (tlen == 8) { 530941Snate year = (mon % 10000) - 1900; 531941Snate mon /= 10000; 5327767Sache } 5337767Sache else { 534941Snate year = mon % 100; 535941Snate mon /= 100; 536941Snate } 537941Snate mday = mon % 100; 538941Snate mon /= 100; 5397767Sache } 5407767Sache else 541941Snate panic("garbled time"); 542941Snate 543941Snate mon--; 544941Snate if (mon < 0 || mon > 11 || mday < 1 || mday > 31) 545941Snate panic("garbled time"); 546941Snate 547941Snate assign_date(tm, mday, mon, year); 548941Snate break; 549941Snate } /* case */ 550941Snate} /* month */ 551941Snate 552941Snate 553941Snate/* Global functions */ 554941Snate 555941Snatetime_t 5567767Sacheparsetime(int argc, char **argv) 557941Snate{ 5587767Sache/* Do the argument parsing, die if necessary, and return the time the job 559941Snate * should be run. 560941Snate */ 561941Snate time_t nowtimer, runtimer; 562941Snate struct tm nowtime, runtime; 563941Snate int hr = 0; 564941Snate /* this MUST be initialized to zero for midnight/noon/teatime */ 565941Snate 566941Snate nowtimer = time(NULL); 567941Snate nowtime = *localtime(&nowtimer); 568941Snate 569941Snate runtime = nowtime; 570941Snate runtime.tm_sec = 0; 571941Snate runtime.tm_isdst = 0; 572941Snate 573941Snate if (argc <= optind) 574941Snate usage(); 575941Snate 576941Snate init_scanner(argc-optind, argv+optind); 577941Snate 578941Snate switch (token()) { 579941Snate case NOW: /* now is optional prefix for PLUS tree */ 580941Snate expect(PLUS); 581941Snate case PLUS: 582941Snate plus(&runtime); 583941Snate break; 584941Snate 585941Snate case NUMBER: 586941Snate tod(&runtime); 587941Snate month(&runtime); 588941Snate break; 589941Snate 5907767Sache /* evil coding for TEATIME|NOON|MIDNIGHT - we've initialised 591941Snate * hr to zero up above, then fall into this case in such a 592941Snate * way so we add +12 +4 hours to it for teatime, +12 hours 593941Snate * to it for noon, and nothing at all for midnight, then 594941Snate * set our runtime to that hour before leaping into the 595941Snate * month scanner 596941Snate */ 597941Snate case TEATIME: 598941Snate hr += 4; 599941Snate case NOON: 600941Snate hr += 12; 601941Snate case MIDNIGHT: 6027767Sache if (runtime.tm_hour >= hr) { 603941Snate runtime.tm_mday++; 6047767Sache runtime.tm_wday++; 6057767Sache } 606941Snate runtime.tm_hour = hr; 607941Snate runtime.tm_min = 0; 608941Snate token(); 60954158Scharnier /* FALLTHROUGH to month setting */ 610941Snate default: 611941Snate month(&runtime); 612941Snate break; 613941Snate } /* ugly case statement */ 614941Snate expect(EOF); 615941Snate 6167767Sache /* adjust for daylight savings time 617941Snate */ 618941Snate runtime.tm_isdst = -1; 619941Snate runtimer = mktime(&runtime); 620941Snate if (runtime.tm_isdst > 0) { 621941Snate runtimer -= 3600; 622941Snate runtimer = mktime(&runtime); 623941Snate } 624941Snate 625941Snate if (runtimer < 0) 626941Snate panic("garbled time"); 627941Snate 628941Snate if (nowtimer > runtimer) 62954158Scharnier panic("trying to travel back in time"); 630941Snate 631941Snate return runtimer; 632941Snate} /* parsetime */ 633