parsetime.c revision 10154
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>
40941Snate#include <errno.h>
41941Snate#include <stdio.h>
42941Snate#include <stdlib.h>
43941Snate#include <string.h>
44941Snate#include <time.h>
45941Snate#include <unistd.h>
46941Snate#include <ctype.h>
477767Sache#ifndef __FreeBSD__
487767Sache#include <getopt.h>
497767Sache#endif
50941Snate
51941Snate/* Local headers */
52941Snate
53941Snate#include "at.h"
54941Snate#include "panic.h"
55941Snate
56941Snate
57941Snate/* Structures and unions */
58941Snate
5910154Sacheenum {	/* symbols */
60941Snate    MIDNIGHT, NOON, TEATIME,
61941Snate    PM, AM, TOMORROW, TODAY, NOW,
62941Snate    MINUTES, HOURS, DAYS, WEEKS,
63941Snate    NUMBER, PLUS, DOT, SLASH, ID, JUNK,
64941Snate    JAN, FEB, MAR, APR, MAY, JUN,
657767Sache    JUL, AUG, SEP, OCT, NOV, DEC,
667767Sache    SUN, MON, TUE, WED, THU, FRI, SAT
677767Sache    };
68941Snate
697767Sache/* parse translation table - table driven parsers can be your FRIEND!
70941Snate */
71941Snatestruct {
7210154Sache    char *name;	/* token name */
7310154Sache    int value;	/* token id */
747767Sache    int plural;	/* is this plural? */
75941Snate} Specials[] = {
767767Sache    { "midnight", MIDNIGHT,0 },	/* 00:00:00 of today or tomorrow */
777767Sache    { "noon", NOON,0 },		/* 12:00:00 of today or tomorrow */
787767Sache    { "teatime", TEATIME,0 },	/* 16:00:00 of today or tomorrow */
797767Sache    { "am", AM,0 },		/* morning times for 0-12 clock */
807767Sache    { "pm", PM,0 },		/* evening times for 0-12 clock */
817767Sache    { "tomorrow", TOMORROW,0 },	/* execute 24 hours from time */
827767Sache    { "today", TODAY, 0 },	/* execute today - don't advance time */
837767Sache    { "now", NOW,0 },		/* opt prefix for PLUS */
84941Snate
857767Sache    { "minute", MINUTES,0 },	/* minutes multiplier */
867767Sache    { "minutes", MINUTES,1 },	/* (pluralized) */
877767Sache    { "hour", HOURS,0 },	/* hours ... */
887767Sache    { "hours", HOURS,1 },	/* (pluralized) */
897767Sache    { "day", DAYS,0 },		/* days ... */
907767Sache    { "days", DAYS,1 },		/* (pluralized) */
917767Sache    { "week", WEEKS,0 },	/* week ... */
927767Sache    { "weeks", WEEKS,1 },	/* (pluralized) */
937767Sache    { "jan", JAN,0 },
947767Sache    { "feb", FEB,0 },
957767Sache    { "mar", MAR,0 },
967767Sache    { "apr", APR,0 },
977767Sache    { "may", MAY,0 },
987767Sache    { "jun", JUN,0 },
997767Sache    { "jul", JUL,0 },
1007767Sache    { "aug", AUG,0 },
1017767Sache    { "sep", SEP,0 },
1027767Sache    { "oct", OCT,0 },
1037767Sache    { "nov", NOV,0 },
1047767Sache    { "dec", DEC,0 },
1057767Sache    { "sunday", SUN, 0 },
1067767Sache    { "sun", SUN, 0 },
1077767Sache    { "monday", MON, 0 },
1087767Sache    { "mon", MON, 0 },
1097767Sache    { "tuesday", TUE, 0 },
1107767Sache    { "tue", TUE, 0 },
1117767Sache    { "wednesday", WED, 0 },
1127767Sache    { "wed", WED, 0 },
1137767Sache    { "thursday", THU, 0 },
1147767Sache    { "thu", THU, 0 },
1157767Sache    { "friday", FRI, 0 },
1167767Sache    { "fri", FRI, 0 },
1177767Sache    { "saturday", SAT, 0 },
1187767Sache    { "sat", SAT, 0 },
119941Snate} ;
120941Snate
121941Snate/* File scope variables */
122941Snate
123941Snatestatic char **scp;	/* scanner - pointer at arglist */
124941Snatestatic char scc;	/* scanner - count of remaining arguments */
125941Snatestatic char *sct;	/* scanner - next char pointer in current argument */
126941Snatestatic int need;	/* scanner - need to advance to next argument */
127941Snate
128941Snatestatic char *sc_token;	/* scanner - token buffer */
129941Snatestatic size_t sc_len;   /* scanner - lenght of token buffer */
13010154Sachestatic int sc_tokid;	/* scanner - token id */
1317767Sachestatic int sc_tokplur;	/* scanner - is token plural? */
132941Snate
13310154Sachestatic char rcsid[] = "$Id: parsetime.c,v 1.1 1995/05/24 15:07:32 ig25 Exp $";
134941Snate
135941Snate/* Local functions */
136941Snate
137941Snate/*
138941Snate * parse a token, checking if it's something special to us
139941Snate */
14010154Sachestatic int
14110154Sacheparse_token(char *arg)
142941Snate{
143941Snate    int i;
144941Snate
145941Snate    for (i=0; i<(sizeof Specials/sizeof Specials[0]); i++)
146941Snate	if (strcasecmp(Specials[i].name, arg) == 0) {
1477767Sache	    sc_tokplur = Specials[i].plural;
148941Snate	    return sc_tokid = Specials[i].value;
149941Snate	}
150941Snate
151941Snate    /* not special - must be some random id */
152941Snate    return ID;
153941Snate} /* parse_token */
154941Snate
155941Snate
156941Snate/*
157941Snate * init_scanner() sets up the scanner to eat arguments
158941Snate */
159941Snatestatic void
1607767Sacheinit_scanner(int argc, char **argv)
161941Snate{
162941Snate    scp = argv;
163941Snate    scc = argc;
164941Snate    need = 1;
165941Snate    sc_len = 1;
16610154Sache    while (argc-- > 0)
16710154Sache	sc_len += strlen(*argv++);
168941Snate
1697767Sache    sc_token = (char *) mymalloc(sc_len);
170941Snate} /* init_scanner */
171941Snate
172941Snate/*
173941Snate * token() fetches a token from the input stream
174941Snate */
17510154Sachestatic int
176941Snatetoken()
177941Snate{
178941Snate    int idx;
179941Snate
180941Snate    while (1) {
181941Snate	memset(sc_token, 0, sc_len);
182941Snate	sc_tokid = EOF;
1837767Sache	sc_tokplur = 0;
184941Snate	idx = 0;
185941Snate
1867767Sache	/* if we need to read another argument, walk along the argument list;
187941Snate	 * when we fall off the arglist, we'll just return EOF forever
188941Snate	 */
189941Snate	if (need) {
190941Snate	    if (scc < 1)
191941Snate		return sc_tokid;
192941Snate	    sct = *scp;
193941Snate	    scp++;
194941Snate	    scc--;
195941Snate	    need = 0;
196941Snate	}
1977767Sache	/* eat whitespace now - if we walk off the end of the argument,
198941Snate	 * we'll continue, which puts us up at the top of the while loop
199941Snate	 * to fetch the next argument in
200941Snate	 */
201941Snate	while (isspace(*sct))
202941Snate	    ++sct;
203941Snate	if (!*sct) {
204941Snate	    need = 1;
205941Snate	    continue;
206941Snate	}
207941Snate
2087767Sache	/* preserve the first character of the new token
209941Snate	 */
210941Snate	sc_token[0] = *sct++;
211941Snate
2127767Sache	/* then see what it is
213941Snate	 */
214941Snate	if (isdigit(sc_token[0])) {
215941Snate	    while (isdigit(*sct))
216941Snate		sc_token[++idx] = *sct++;
217941Snate	    sc_token[++idx] = 0;
218941Snate	    return sc_tokid = NUMBER;
2197767Sache	}
2207767Sache	else if (isalpha(sc_token[0])) {
221941Snate	    while (isalpha(*sct))
222941Snate		sc_token[++idx] = *sct++;
223941Snate	    sc_token[++idx] = 0;
224941Snate	    return parse_token(sc_token);
225941Snate	}
226941Snate	else if (sc_token[0] == ':' || sc_token[0] == '.')
227941Snate	    return sc_tokid = DOT;
228941Snate	else if (sc_token[0] == '+')
229941Snate	    return sc_tokid = PLUS;
2307767Sache	else if (sc_token[0] == '/')
231941Snate	    return sc_tokid = SLASH;
232941Snate	else
233941Snate	    return sc_tokid = JUNK;
234941Snate    } /* while (1) */
235941Snate} /* token */
236941Snate
237941Snate
238941Snate/*
239941Snate * plonk() gives an appropriate error message if a token is incorrect
240941Snate */
241941Snatestatic void
2427767Sacheplonk(int tok)
243941Snate{
244941Snate    panic((tok == EOF) ? "incomplete time"
245941Snate		       : "garbled time");
246941Snate} /* plonk */
247941Snate
248941Snate
24910154Sache/*
250941Snate * expect() gets a token and dies most horribly if it's not the token we want
251941Snate */
252941Snatestatic void
25310154Sacheexpect(int desired)
254941Snate{
255941Snate    if (token() != desired)
256941Snate	plonk(sc_tokid);	/* and we die here... */
257941Snate} /* expect */
258941Snate
259941Snate
260941Snate/*
261941Snate * dateadd() adds a number of minutes to a date.  It is extraordinarily
262941Snate * stupid regarding day-of-month overflow, and will most likely not
263941Snate * work properly
264941Snate */
265941Snatestatic void
2667767Sachedateadd(int minutes, struct tm *tm)
267941Snate{
268941Snate    /* increment days */
269941Snate
270941Snate    while (minutes > 24*60) {
271941Snate	minutes -= 24*60;
272941Snate	tm->tm_mday++;
273941Snate    }
274941Snate
275941Snate    /* increment hours */
276941Snate    while (minutes > 60) {
277941Snate	minutes -= 60;
278941Snate	tm->tm_hour++;
279941Snate	if (tm->tm_hour > 23) {
280941Snate	    tm->tm_mday++;
281941Snate	    tm->tm_hour = 0;
282941Snate	}
283941Snate    }
284941Snate
285941Snate    /* increment minutes */
286941Snate    tm->tm_min += minutes;
287941Snate
288941Snate    if (tm->tm_min > 59) {
289941Snate	tm->tm_hour++;
290941Snate	tm->tm_min -= 60;
291941Snate
292941Snate	if (tm->tm_hour > 23) {
293941Snate	    tm->tm_mday++;
294941Snate	    tm->tm_hour = 0;
295941Snate	}
296941Snate    }
297941Snate} /* dateadd */
298941Snate
299941Snate
300941Snate/*
301941Snate * plus() parses a now + time
302941Snate *
303941Snate *  at [NOW] PLUS NUMBER [MINUTES|HOURS|DAYS|WEEKS]
304941Snate *
305941Snate */
306941Snatestatic void
3077767Sacheplus(struct tm *tm)
308941Snate{
309941Snate    int delay;
3107767Sache    int expectplur;
311941Snate
312941Snate    expect(NUMBER);
313941Snate
314941Snate    delay = atoi(sc_token);
3157767Sache    expectplur = (delay != 1) ? 1 : 0;
316941Snate
317941Snate    switch (token()) {
318941Snate    case WEEKS:
319941Snate	    delay *= 7;
320941Snate    case DAYS:
321941Snate	    delay *= 24;
322941Snate    case HOURS:
323941Snate	    delay *= 60;
324941Snate    case MINUTES:
3257767Sache	    if (expectplur != sc_tokplur)
3267767Sache		fprintf(stderr, "at: pluralization is wrong\n");
327941Snate	    dateadd(delay, tm);
328941Snate	    return;
329941Snate    }
330941Snate    plonk(sc_tokid);
331941Snate} /* plus */
332941Snate
333941Snate
334941Snate/*
335941Snate * tod() computes the time of day
336941Snate *     [NUMBER [DOT NUMBER] [AM|PM]]
337941Snate */
338941Snatestatic void
3397767Sachetod(struct tm *tm)
340941Snate{
341941Snate    int hour, minute = 0;
342941Snate    int tlen;
343941Snate
344941Snate    hour = atoi(sc_token);
345941Snate    tlen = strlen(sc_token);
346941Snate
3477767Sache    /* first pick out the time of day - if it's 4 digits, we assume
348941Snate     * a HHMM time, otherwise it's HH DOT MM time
349941Snate     */
350941Snate    if (token() == DOT) {
351941Snate	expect(NUMBER);
352941Snate	minute = atoi(sc_token);
353941Snate	if (minute > 59)
354941Snate	    panic("garbled time");
355941Snate	token();
3567767Sache    }
3577767Sache    else if (tlen == 4) {
358941Snate	minute = hour%100;
359941Snate	if (minute > 59)
360941Snate	    panic("garbeld time");
361941Snate	hour = hour/100;
362941Snate    }
363941Snate
3647767Sache    /* check if an AM or PM specifier was given
365941Snate     */
366941Snate    if (sc_tokid == AM || sc_tokid == PM) {
367941Snate	if (hour > 12)
368941Snate	    panic("garbled time");
369941Snate
370941Snate	if (sc_tokid == PM)
371941Snate	    hour += 12;
372941Snate	token();
3737767Sache    }
3747767Sache    else if (hour > 23)
375941Snate	panic("garbled time");
376941Snate
3777767Sache    /* if we specify an absolute time, we don't want to bump the day even
378941Snate     * if we've gone past that time - but if we're specifying a time plus
379941Snate     * a relative offset, it's okay to bump things
380941Snate     */
3817767Sache    if ((sc_tokid == EOF || sc_tokid == PLUS) && tm->tm_hour > hour) {
382941Snate	tm->tm_mday++;
3837767Sache	tm->tm_wday++;
3847767Sache    }
385941Snate
386941Snate    tm->tm_hour = hour;
387941Snate    tm->tm_min = minute;
388941Snate    if (tm->tm_hour == 24) {
389941Snate	tm->tm_hour = 0;
390941Snate	tm->tm_mday++;
391941Snate    }
392941Snate} /* tod */
393941Snate
394941Snate
395941Snate/*
396941Snate * assign_date() assigns a date, wrapping to next year if needed
397941Snate */
398941Snatestatic void
3997767Sacheassign_date(struct tm *tm, long mday, long mon, long year)
400941Snate{
401941Snate    if (year > 99) {
402941Snate	if (year > 1899)
403941Snate	    year -= 1900;
404941Snate	else
405941Snate	    panic("garbled time");
406941Snate    }
407941Snate
408941Snate    if (year < 0 &&
409941Snate	(tm->tm_mon > mon ||(tm->tm_mon == mon && tm->tm_mday > mday)))
410941Snate	year = tm->tm_year + 1;
411941Snate
412941Snate    tm->tm_mday = mday;
413941Snate    tm->tm_mon = mon;
414941Snate
415941Snate    if (year >= 0)
416941Snate	tm->tm_year = year;
417941Snate} /* assign_date */
418941Snate
419941Snate
42010154Sache/*
421941Snate * month() picks apart a month specification
422941Snate *
423941Snate *  /[<month> NUMBER [NUMBER]]           \
424941Snate *  |[TOMORROW]                          |
4257767Sache *  |[DAY OF WEEK]                       |
426941Snate *  |NUMBER [SLASH NUMBER [SLASH NUMBER]]|
427941Snate *  \PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS/
428941Snate */
429941Snatestatic void
4307767Sachemonth(struct tm *tm)
431941Snate{
432941Snate    long year= (-1);
4337767Sache    long mday, wday, mon;
434941Snate    int tlen;
435941Snate
436941Snate    switch (sc_tokid) {
437941Snate    case PLUS:
438941Snate	    plus(tm);
439941Snate	    break;
440941Snate
441941Snate    case TOMORROW:
442941Snate	    /* do something tomorrow */
443941Snate	    tm->tm_mday ++;
4447767Sache	    tm->tm_wday ++;
445941Snate    case TODAY:	/* force ourselves to stay in today - no further processing */
446941Snate	    token();
447941Snate	    break;
448941Snate
449941Snate    case JAN: case FEB: case MAR: case APR: case MAY: case JUN:
450941Snate    case JUL: case AUG: case SEP: case OCT: case NOV: case DEC:
4517767Sache	    /* do month mday [year]
452941Snate	     */
453941Snate	    mon = (sc_tokid-JAN);
454941Snate	    expect(NUMBER);
4556079Sbde	    mday = atol(sc_token);
456941Snate	    if (token() == NUMBER) {
457941Snate		year = atol(sc_token);
458941Snate		token();
459941Snate	    }
460941Snate	    assign_date(tm, mday, mon, year);
461941Snate	    break;
462941Snate
4637767Sache    case SUN: case MON: case TUE:
4647767Sache    case WED: case THU: case FRI:
4657767Sache    case SAT:
4667767Sache	    /* do a particular day of the week
4677767Sache	     */
4687767Sache	    wday = (sc_tokid-SUN);
4697767Sache
4707767Sache	    mday = tm->tm_mday;
4717767Sache
4727767Sache	    /* if this day is < today, then roll to next week
4737767Sache	     */
4747767Sache	    if (wday < tm->tm_wday)
4757767Sache		mday += 7 - (tm->tm_wday - wday);
4767767Sache	    else
4777767Sache		mday += (wday - tm->tm_wday);
4787767Sache
4797767Sache	    tm->tm_wday = wday;
4807767Sache
4817767Sache	    assign_date(tm, mday, tm->tm_mon, tm->tm_year);
4827767Sache	    break;
4837767Sache
484941Snate    case NUMBER:
4857767Sache	    /* get numeric MMDDYY, mm/dd/yy, or dd.mm.yy
486941Snate	     */
487941Snate	    tlen = strlen(sc_token);
488941Snate	    mon = atol(sc_token);
489941Snate	    token();
490941Snate
491941Snate	    if (sc_tokid == SLASH || sc_tokid == DOT) {
49210154Sache		int sep;
493941Snate
494941Snate		sep = sc_tokid;
495941Snate		expect(NUMBER);
496941Snate		mday = atol(sc_token);
497941Snate		if (token() == sep) {
498941Snate		    expect(NUMBER);
499941Snate		    year = atol(sc_token);
500941Snate		    token();
501941Snate		}
502941Snate
5037767Sache		/* flip months and days for european timing
504941Snate		 */
505941Snate		if (sep == DOT) {
506941Snate		    int x = mday;
507941Snate		    mday = mon;
508941Snate		    mon = x;
509941Snate		}
5107767Sache	    }
5117767Sache	    else if (tlen == 6 || tlen == 8) {
512941Snate		if (tlen == 8) {
513941Snate		    year = (mon % 10000) - 1900;
514941Snate		    mon /= 10000;
5157767Sache		}
5167767Sache		else {
517941Snate		    year = mon % 100;
518941Snate		    mon /= 100;
519941Snate		}
520941Snate		mday = mon % 100;
521941Snate		mon /= 100;
5227767Sache	    }
5237767Sache	    else
524941Snate		panic("garbled time");
525941Snate
526941Snate	    mon--;
527941Snate	    if (mon < 0 || mon > 11 || mday < 1 || mday > 31)
528941Snate		panic("garbled time");
529941Snate
530941Snate	    assign_date(tm, mday, mon, year);
531941Snate	    break;
532941Snate    } /* case */
533941Snate} /* month */
534941Snate
535941Snate
536941Snate/* Global functions */
537941Snate
538941Snatetime_t
5397767Sacheparsetime(int argc, char **argv)
540941Snate{
5417767Sache/* Do the argument parsing, die if necessary, and return the time the job
542941Snate * should be run.
543941Snate */
544941Snate    time_t nowtimer, runtimer;
545941Snate    struct tm nowtime, runtime;
546941Snate    int hr = 0;
547941Snate    /* this MUST be initialized to zero for midnight/noon/teatime */
548941Snate
549941Snate    nowtimer = time(NULL);
550941Snate    nowtime = *localtime(&nowtimer);
551941Snate
552941Snate    runtime = nowtime;
553941Snate    runtime.tm_sec = 0;
554941Snate    runtime.tm_isdst = 0;
555941Snate
556941Snate    if (argc <= optind)
557941Snate	usage();
558941Snate
559941Snate    init_scanner(argc-optind, argv+optind);
560941Snate
561941Snate    switch (token()) {
562941Snate    case NOW:	/* now is optional prefix for PLUS tree */
563941Snate	    expect(PLUS);
564941Snate    case PLUS:
565941Snate	    plus(&runtime);
566941Snate	    break;
567941Snate
568941Snate    case NUMBER:
569941Snate	    tod(&runtime);
570941Snate	    month(&runtime);
571941Snate	    break;
572941Snate
5737767Sache	    /* evil coding for TEATIME|NOON|MIDNIGHT - we've initialised
574941Snate	     * hr to zero up above, then fall into this case in such a
575941Snate	     * way so we add +12 +4 hours to it for teatime, +12 hours
576941Snate	     * to it for noon, and nothing at all for midnight, then
577941Snate	     * set our runtime to that hour before leaping into the
578941Snate	     * month scanner
579941Snate	     */
580941Snate    case TEATIME:
581941Snate	    hr += 4;
582941Snate    case NOON:
583941Snate	    hr += 12;
584941Snate    case MIDNIGHT:
5857767Sache	    if (runtime.tm_hour >= hr) {
586941Snate		runtime.tm_mday++;
5877767Sache		runtime.tm_wday++;
5887767Sache	    }
589941Snate	    runtime.tm_hour = hr;
590941Snate	    runtime.tm_min = 0;
591941Snate	    token();
592941Snate	    /* fall through to month setting */
593941Snate    default:
594941Snate	    month(&runtime);
595941Snate	    break;
596941Snate    } /* ugly case statement */
597941Snate    expect(EOF);
598941Snate
5997767Sache    /* adjust for daylight savings time
600941Snate     */
601941Snate    runtime.tm_isdst = -1;
602941Snate    runtimer = mktime(&runtime);
603941Snate    if (runtime.tm_isdst > 0) {
604941Snate	runtimer -= 3600;
605941Snate	runtimer = mktime(&runtime);
606941Snate    }
607941Snate
608941Snate    if (runtimer < 0)
609941Snate	panic("garbled time");
610941Snate
611941Snate    if (nowtimer > runtimer)
612941Snate	panic("Trying to travel back in time");
613941Snate
614941Snate    return runtimer;
615941Snate} /* parsetime */
616