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