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