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