parsetime.c revision 35729
119370Spst/*
2130803Smarcel *  parsetime.c - parse time for at(1)
398944Sobrien *  Copyright (C) 1993, 1994  Thomas Koenig
4130803Smarcel *
546283Sdfr *  modifications for english-language times
619370Spst *  Copyright (C) 1993  David Parsons
798944Sobrien *
819370Spst * Redistribution and use in source and binary forms, with or without
998944Sobrien * modification, are permitted provided that the following conditions
1098944Sobrien * are met:
1198944Sobrien * 1. Redistributions of source code must retain the above copyright
1298944Sobrien *    notice, this list of conditions and the following disclaimer.
1319370Spst * 2. The name of the author(s) may not be used to endorse or promote
1498944Sobrien *    products derived from this software without specific prior written
1598944Sobrien *    permission.
1698944Sobrien *
1798944Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
1819370Spst * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1998944Sobrien * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2098944Sobrien * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
2198944Sobrien * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2298944Sobrien * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2319370Spst * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2419370Spst * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2519370Spst * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2619370Spst * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2798944Sobrien *
28130803Smarcel *  at [NOW] PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS
2998944Sobrien *     /NUMBER [DOT NUMBER] [AM|PM]\ /[MONTH NUMBER [NUMBER]]             \
30130803Smarcel *     |NOON                       | |[TOMORROW]                          |
31130803Smarcel *     |MIDNIGHT                   | |[DAY OF WEEK]                       |
32130803Smarcel *     \TEATIME                    / |NUMBER [SLASH NUMBER [SLASH NUMBER]]|
33130803Smarcel *                                   \PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS/
34130803Smarcel */
35130803Smarcel
3619370Spst/* System Headers */
37130803Smarcel
38130803Smarcel
39130803Smarcel#include <sys/types.h>
40130803Smarcel#include <err.h>
41130803Smarcel#include <errno.h>
4219370Spst#include <stdio.h>
43130803Smarcel#include <stdlib.h>
44130803Smarcel#include <string.h>
45130803Smarcel#include <time.h>
46130803Smarcel#include <unistd.h>
47130803Smarcel#include <ctype.h>
48130803Smarcel#ifndef __FreeBSD__
49130803Smarcel#include <getopt.h>
50130803Smarcel#endif
51130803Smarcel
52130803Smarcel/* Local headers */
5319370Spst
54130803Smarcel#include "at.h"
55130803Smarcel#include "panic.h"
56130803Smarcel
57130803Smarcel
58130803Smarcel/* Structures and unions */
59130803Smarcel
60130803Smarcelenum {	/* symbols */
61130803Smarcel    MIDNIGHT, NOON, TEATIME,
62130803Smarcel    PM, AM, TOMORROW, TODAY, NOW,
63130803Smarcel    MINUTES, HOURS, DAYS, WEEKS,
64130803Smarcel    NUMBER, PLUS, DOT, SLASH, ID, JUNK,
65130803Smarcel    JAN, FEB, MAR, APR, MAY, JUN,
66130803Smarcel    JUL, AUG, SEP, OCT, NOV, DEC,
67130803Smarcel    SUN, MON, TUE, WED, THU, FRI, SAT
68130803Smarcel    };
69130803Smarcel
70130803Smarcel/* parse translation table - table driven parsers can be your FRIEND!
71130803Smarcel */
72130803Smarcelstruct {
73130803Smarcel    char *name;	/* token name */
74130803Smarcel    int value;	/* token id */
75130803Smarcel    int plural;	/* is this plural? */
76130803Smarcel} Specials[] = {
77130803Smarcel    { "midnight", MIDNIGHT,0 },	/* 00:00:00 of today or tomorrow */
78130803Smarcel    { "noon", NOON,0 },		/* 12:00:00 of today or tomorrow */
7919370Spst    { "teatime", TEATIME,0 },	/* 16:00:00 of today or tomorrow */
8098944Sobrien    { "am", AM,0 },		/* morning times for 0-12 clock */
81130803Smarcel    { "pm", PM,0 },		/* evening times for 0-12 clock */
82130803Smarcel    { "tomorrow", TOMORROW,0 },	/* execute 24 hours from time */
83130803Smarcel    { "today", TODAY, 0 },	/* execute today - don't advance time */
84130803Smarcel    { "now", NOW,0 },		/* opt prefix for PLUS */
85130803Smarcel
86130803Smarcel    { "minute", MINUTES,0 },	/* minutes multiplier */
8746283Sdfr    { "minutes", MINUTES,1 },	/* (pluralized) */
88130803Smarcel    { "hour", HOURS,0 },	/* hours ... */
89130803Smarcel    { "hours", HOURS,1 },	/* (pluralized) */
9098944Sobrien    { "day", DAYS,0 },		/* days ... */
91130803Smarcel    { "days", DAYS,1 },		/* (pluralized) */
92130803Smarcel    { "week", WEEKS,0 },	/* week ... */
93130803Smarcel    { "weeks", WEEKS,1 },	/* (pluralized) */
94130803Smarcel    { "jan", JAN,0 },
95130803Smarcel    { "feb", FEB,0 },
96130803Smarcel    { "mar", MAR,0 },
9798944Sobrien    { "apr", APR,0 },
98130803Smarcel    { "may", MAY,0 },
99130803Smarcel    { "jun", JUN,0 },
100130803Smarcel    { "jul", JUL,0 },
101130803Smarcel    { "aug", AUG,0 },
102130803Smarcel    { "sep", SEP,0 },
10398944Sobrien    { "oct", OCT,0 },
104130803Smarcel    { "nov", NOV,0 },
105130803Smarcel    { "dec", DEC,0 },
106130803Smarcel    { "sunday", SUN, 0 },
10798944Sobrien    { "sun", SUN, 0 },
108130803Smarcel    { "monday", MON, 0 },
109130803Smarcel    { "mon", MON, 0 },
110130803Smarcel    { "tuesday", TUE, 0 },
11198944Sobrien    { "tue", TUE, 0 },
112130803Smarcel    { "wednesday", WED, 0 },
113130803Smarcel    { "wed", WED, 0 },
114130803Smarcel    { "thursday", THU, 0 },
115130803Smarcel    { "thu", THU, 0 },
116130803Smarcel    { "friday", FRI, 0 },
117130803Smarcel    { "fri", FRI, 0 },
11898944Sobrien    { "saturday", SAT, 0 },
119130803Smarcel    { "sat", SAT, 0 },
120130803Smarcel} ;
121130803Smarcel
122130803Smarcel/* File scope variables */
123130803Smarcel
124130803Smarcelstatic char **scp;	/* scanner - pointer at arglist */
125130803Smarcelstatic char scc;	/* scanner - count of remaining arguments */
126130803Smarcelstatic char *sct;	/* scanner - next char pointer in current argument */
127130803Smarcelstatic int need;	/* scanner - need to advance to next argument */
128130803Smarcel
129130803Smarcelstatic char *sc_token;	/* scanner - token buffer */
130130803Smarcelstatic size_t sc_len;   /* scanner - lenght of token buffer */
13146283Sdfrstatic int sc_tokid;	/* scanner - token id */
13298944Sobrienstatic int sc_tokplur;	/* scanner - is token plural? */
13398944Sobrien
13498944Sobrienstatic char rcsid[] = "$Id: parsetime.c,v 1.11 1997/06/24 06:26:32 charnier Exp $";
13519370Spst
13619370Spst/* Local functions */
13719370Spst
13819370Spst/*
13919370Spst * parse a token, checking if it's something special to us
14019370Spst */
141130803Smarcelstatic int
14219370Spstparse_token(char *arg)
14398944Sobrien{
144130803Smarcel    int i;
145130803Smarcel
146130803Smarcel    for (i=0; i<(sizeof Specials/sizeof Specials[0]); i++)
147130803Smarcel	if (strcasecmp(Specials[i].name, arg) == 0) {
148130803Smarcel	    sc_tokplur = Specials[i].plural;
14998944Sobrien	    return sc_tokid = Specials[i].value;
15098944Sobrien	}
15198944Sobrien
15298944Sobrien    /* not special - must be some random id */
15398944Sobrien    return ID;
15498944Sobrien} /* parse_token */
15598944Sobrien
15698944Sobrien
15798944Sobrien/*
15898944Sobrien * init_scanner() sets up the scanner to eat arguments
15919370Spst */
160130803Smarcelstatic void
16119370Spstinit_scanner(int argc, char **argv)
16219370Spst{
16319370Spst    scp = argv;
164130803Smarcel    scc = argc;
16546283Sdfr    need = 1;
16646283Sdfr    sc_len = 1;
167130803Smarcel    while (argc-- > 0)
16819370Spst	sc_len += strlen(*argv++);
16919370Spst
17019370Spst    sc_token = (char *) mymalloc(sc_len);
17146283Sdfr} /* init_scanner */
17246283Sdfr
17398944Sobrien/*
174130803Smarcel * token() fetches a token from the input stream
175130803Smarcel */
176130803Smarcelstatic int
177130803Smarceltoken()
178130803Smarcel{
179130803Smarcel    int idx;
180130803Smarcel
181130803Smarcel    while (1) {
182130803Smarcel	memset(sc_token, 0, sc_len);
18319370Spst	sc_tokid = EOF;
18419370Spst	sc_tokplur = 0;
18546283Sdfr	idx = 0;
18619370Spst
187130803Smarcel	/* if we need to read another argument, walk along the argument list;
18819370Spst	 * when we fall off the arglist, we'll just return EOF forever
189130803Smarcel	 */
190130803Smarcel	if (need) {
191130803Smarcel	    if (scc < 1)
192130803Smarcel		return sc_tokid;
193130803Smarcel	    sct = *scp;
194130803Smarcel	    scp++;
195130803Smarcel	    scc--;
196130803Smarcel	    need = 0;
19746283Sdfr	}
19846283Sdfr	/* eat whitespace now - if we walk off the end of the argument,
199130803Smarcel	 * we'll continue, which puts us up at the top of the while loop
20046283Sdfr	 * to fetch the next argument in
201130803Smarcel	 */
202130803Smarcel	while (isspace(*sct))
203130803Smarcel	    ++sct;
204130803Smarcel	if (!*sct) {
20546283Sdfr	    need = 1;
20646283Sdfr	    continue;
207130803Smarcel	}
20898944Sobrien
20946283Sdfr	/* preserve the first character of the new token
210130803Smarcel	 */
211130803Smarcel	sc_token[0] = *sct++;
212130803Smarcel
21319370Spst	/* then see what it is
21498944Sobrien	 */
21598944Sobrien	if (isdigit(sc_token[0])) {
21619370Spst	    while (isdigit(*sct))
21719370Spst		sc_token[++idx] = *sct++;
21819370Spst	    sc_token[++idx] = 0;
21919370Spst	    return sc_tokid = NUMBER;
220130803Smarcel	}
22119370Spst	else if (isalpha(sc_token[0])) {
22219370Spst	    while (isalpha(*sct))
22319370Spst		sc_token[++idx] = *sct++;
22419370Spst	    sc_token[++idx] = 0;
22519370Spst	    return parse_token(sc_token);
22619370Spst	}
22746283Sdfr	else if (sc_token[0] == ':' || sc_token[0] == '.')
22846283Sdfr	    return sc_tokid = DOT;
22946283Sdfr	else if (sc_token[0] == '+')
23019370Spst	    return sc_tokid = PLUS;
231130803Smarcel	else if (sc_token[0] == '/')
23219370Spst	    return sc_tokid = SLASH;
233130803Smarcel	else
234130803Smarcel	    return sc_tokid = JUNK;
235130803Smarcel    } /* while (1) */
236130803Smarcel} /* token */
237130803Smarcel
238130803Smarcel
239130803Smarcel/*
240130803Smarcel * plonk() gives an appropriate error message if a token is incorrect
241130803Smarcel */
24219370Spststatic void
24319370Spstplonk(int tok)
24419370Spst{
24519370Spst    panic((tok == EOF) ? "incomplete time"
24619370Spst		       : "garbled time");
24719370Spst} /* plonk */
24819370Spst
249130803Smarcel
250130803Smarcel/*
251130803Smarcel * expect() gets a token and dies most horribly if it's not the token we want
252130803Smarcel */
253130803Smarcelstatic void
254130803Smarcelexpect(int desired)
255130803Smarcel{
256130803Smarcel    if (token() != desired)
257130803Smarcel	plonk(sc_tokid);	/* and we die here... */
25819370Spst} /* expect */
259130803Smarcel
260130803Smarcel
26119370Spst/*
262130803Smarcel * dateadd() adds a number of minutes to a date.  It is extraordinarily
263130803Smarcel * stupid regarding day-of-month overflow, and will most likely not
264130803Smarcel * work properly
26519370Spst */
26619370Spststatic void
26719370Spstdateadd(int minutes, struct tm *tm)
268130803Smarcel{
269130803Smarcel    /* increment days */
270130803Smarcel
271130803Smarcel    while (minutes > 24*60) {
272130803Smarcel	minutes -= 24*60;
27319370Spst	tm->tm_mday++;
27419370Spst    }
27519370Spst
27619370Spst    /* increment hours */
27719370Spst    while (minutes > 60) {
278130803Smarcel	minutes -= 60;
279130803Smarcel	tm->tm_hour++;
280130803Smarcel	if (tm->tm_hour > 23) {
281130803Smarcel	    tm->tm_mday++;
282130803Smarcel	    tm->tm_hour = 0;
28319370Spst	}
284130803Smarcel    }
28519370Spst
28619370Spst    /* increment minutes */
28719370Spst    tm->tm_min += minutes;
28819370Spst
28998944Sobrien    if (tm->tm_min > 59) {
29019370Spst	tm->tm_hour++;
29119370Spst	tm->tm_min -= 60;
29219370Spst
29319370Spst	if (tm->tm_hour > 23) {
29419370Spst	    tm->tm_mday++;
29519370Spst	    tm->tm_hour = 0;
29619370Spst	}
29719370Spst    }
29898944Sobrien} /* dateadd */
29919370Spst
30098944Sobrien
30198944Sobrien/*
30298944Sobrien * plus() parses a now + time
30319370Spst *
304130803Smarcel *  at [NOW] PLUS NUMBER [MINUTES|HOURS|DAYS|WEEKS]
305130803Smarcel *
306130803Smarcel */
307130803Smarcelstatic void
308130803Smarcelplus(struct tm *tm)
30998944Sobrien{
31019370Spst    int delay;
31198944Sobrien    int expectplur;
31298944Sobrien
31398944Sobrien    expect(NUMBER);
31498944Sobrien
31519370Spst    delay = atoi(sc_token);
31698944Sobrien    expectplur = (delay != 1) ? 1 : 0;
31798944Sobrien
31898944Sobrien    switch (token()) {
31998944Sobrien    case WEEKS:
32019370Spst	    delay *= 7;
32198944Sobrien    case DAYS:
32298944Sobrien	    delay *= 24;
32319370Spst    case HOURS:
32498944Sobrien	    delay *= 60;
32519370Spst    case MINUTES:
326130803Smarcel	    if (expectplur != sc_tokplur)
327130803Smarcel		warnx("pluralization is wrong");
32819370Spst	    dateadd(delay, tm);
32998944Sobrien	    return;
33019370Spst    }
33198944Sobrien    plonk(sc_tokid);
33298944Sobrien} /* plus */
33319370Spst
33498944Sobrien
33598944Sobrien/*
33619370Spst * tod() computes the time of day
33798944Sobrien *     [NUMBER [DOT NUMBER] [AM|PM]]
33819370Spst */
33998944Sobrienstatic void
34019370Spsttod(struct tm *tm)
34198944Sobrien{
34298944Sobrien    int hour, minute = 0;
34319370Spst    int tlen;
34498944Sobrien
34519370Spst    hour = atoi(sc_token);
34698944Sobrien    tlen = strlen(sc_token);
34719370Spst
34898944Sobrien    /* first pick out the time of day - if it's 4 digits, we assume
34998944Sobrien     * a HHMM time, otherwise it's HH DOT MM time
35019370Spst     */
35198944Sobrien    if (token() == DOT) {
35298944Sobrien	expect(NUMBER);
35319370Spst	minute = atoi(sc_token);
35498944Sobrien	if (minute > 59)
35519370Spst	    panic("garbled time");
35698944Sobrien	token();
35798944Sobrien    }
35819370Spst    else if (tlen == 4) {
35998944Sobrien	minute = hour%100;
36019370Spst	if (minute > 59)
36198944Sobrien	    panic("garbeld time");
36219370Spst	hour = hour/100;
36398944Sobrien    }
36419370Spst
36598944Sobrien    /* check if an AM or PM specifier was given
36619370Spst     */
36798944Sobrien    if (sc_tokid == AM || sc_tokid == PM) {
36819370Spst	if (hour > 12)
36998944Sobrien	    panic("garbled time");
37019370Spst
37198944Sobrien	if (sc_tokid == PM) {
37219370Spst	    if (hour != 12)	/* 12:xx PM is 12:xx, not 24:xx */
37398944Sobrien			hour += 12;
37419370Spst	} else {
37598944Sobrien	    if (hour == 12)	/* 12:xx AM is 00:xx, not 12:xx */
37646283Sdfr			hour = 0;
37798944Sobrien	}
37898944Sobrien	token();
37998944Sobrien    }
38098944Sobrien    else if (hour > 23)
38146283Sdfr	panic("garbled time");
382130803Smarcel
383130803Smarcel    /* if we specify an absolute time, we don't want to bump the day even
384130803Smarcel     * if we've gone past that time - but if we're specifying a time plus
38519370Spst     * a relative offset, it's okay to bump things
38698944Sobrien     */
38719370Spst    if ((sc_tokid == EOF || sc_tokid == PLUS) && tm->tm_hour > hour) {
38898944Sobrien	tm->tm_mday++;
389130803Smarcel	tm->tm_wday++;
39098944Sobrien    }
39146283Sdfr
39298944Sobrien    tm->tm_hour = hour;
39398944Sobrien    tm->tm_min = minute;
39498944Sobrien    if (tm->tm_hour == 24) {
39598944Sobrien	tm->tm_hour = 0;
39698944Sobrien	tm->tm_mday++;
39746283Sdfr    }
39898944Sobrien} /* tod */
39946283Sdfr
40098944Sobrien
40198944Sobrien/*
40298944Sobrien * assign_date() assigns a date, wrapping to next year if needed
40319370Spst */
40419370Spststatic void
40598944Sobrienassign_date(struct tm *tm, long mday, long mon, long year)
40698944Sobrien{
40719370Spst    if (year > 99) {
40898944Sobrien	if (year > 1899)
40998944Sobrien	    year -= 1900;
41019370Spst	else
41198944Sobrien	    panic("garbled time");
41219370Spst    } else {
41398944Sobrien	struct tm *lt;
41419370Spst	time_t now;
41598944Sobrien
41619370Spst	time(&now);
41798944Sobrien	lt = localtime(&now);
41819370Spst
419130803Smarcel	/*
420130803Smarcel	 * check if the specified year is in the next century.
42119370Spst	 * allow for one year of user error as many people will
42298944Sobrien	 * enter n - 1 at the start of year n.
42319370Spst	 */
42498944Sobrien	if (year < (lt->tm_year % 100) - 1)
42519370Spst	    year += 100;
426130803Smarcel	/* adjust for the year 2000 and beyond */
42719370Spst	year += lt->tm_year - (lt->tm_year % 100);
42898944Sobrien    }
42919370Spst
43098944Sobrien    if (year < 0 &&
43119370Spst	(tm->tm_mon > mon ||(tm->tm_mon == mon && tm->tm_mday > mday)))
43298944Sobrien	year = tm->tm_year + 1;
43398944Sobrien
43419370Spst    tm->tm_mday = mday;
43598944Sobrien    tm->tm_mon = mon;
43619370Spst
43798944Sobrien    if (year >= 0)
43819370Spst	tm->tm_year = year;
43998944Sobrien} /* assign_date */
44019370Spst
44198944Sobrien
44219370Spst/*
44398944Sobrien * month() picks apart a month specification
44419370Spst *
44598944Sobrien *  /[<month> NUMBER [NUMBER]]           \
44619370Spst *  |[TOMORROW]                          |
44798944Sobrien *  |[DAY OF WEEK]                       |
44819370Spst *  |NUMBER [SLASH NUMBER [SLASH NUMBER]]|
44998944Sobrien *  \PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS/
45019370Spst */
45198944Sobrienstatic void
45219370Spstmonth(struct tm *tm)
45398944Sobrien{
45498944Sobrien    long year= (-1);
45598944Sobrien    long mday, wday, mon;
45698944Sobrien    int tlen;
45719370Spst
45898944Sobrien    switch (sc_tokid) {
45919370Spst    case PLUS:
46098944Sobrien	    plus(tm);
46119370Spst	    break;
46298944Sobrien
46319370Spst    case TOMORROW:
46498944Sobrien	    /* do something tomorrow */
46519370Spst	    tm->tm_mday ++;
46698944Sobrien	    tm->tm_wday ++;
46719370Spst    case TODAY:	/* force ourselves to stay in today - no further processing */
46898944Sobrien	    token();
46919370Spst	    break;
47098944Sobrien
47198944Sobrien    case JAN: case FEB: case MAR: case APR: case MAY: case JUN:
47298944Sobrien    case JUL: case AUG: case SEP: case OCT: case NOV: case DEC:
47398944Sobrien	    /* do month mday [year]
47419370Spst	     */
47598944Sobrien	    mon = (sc_tokid-JAN);
47698944Sobrien	    expect(NUMBER);
47719370Spst	    mday = atol(sc_token);
47898944Sobrien	    if (token() == NUMBER) {
47998944Sobrien		year = atol(sc_token);
48019370Spst		token();
48198944Sobrien	    }
48298944Sobrien	    assign_date(tm, mday, mon, year);
48319370Spst	    break;
48498944Sobrien
48519370Spst    case SUN: case MON: case TUE:
48698944Sobrien    case WED: case THU: case FRI:
48719370Spst    case SAT:
48898944Sobrien	    /* do a particular day of the week
48919370Spst	     */
49098944Sobrien	    wday = (sc_tokid-SUN);
49119370Spst
49298944Sobrien	    mday = tm->tm_mday;
49319370Spst
49498944Sobrien	    /* if this day is < today, then roll to next week
49519370Spst	     */
496130803Smarcel	    if (wday < tm->tm_wday)
497130803Smarcel		mday += 7 - (tm->tm_wday - wday);
49819370Spst	    else
49998944Sobrien		mday += (wday - tm->tm_wday);
50098944Sobrien
50146283Sdfr	    tm->tm_wday = wday;
50298944Sobrien
50398944Sobrien	    assign_date(tm, mday, tm->tm_mon, tm->tm_year);
50419370Spst	    break;
50598944Sobrien
50698944Sobrien    case NUMBER:
50719370Spst	    /* get numeric MMDDYY, mm/dd/yy, or dd.mm.yy
50898944Sobrien	     */
50998944Sobrien	    tlen = strlen(sc_token);
51019370Spst	    mon = atol(sc_token);
51198944Sobrien	    token();
51298944Sobrien
51319370Spst	    if (sc_tokid == SLASH || sc_tokid == DOT) {
51498944Sobrien		int sep;
51598944Sobrien
51698944Sobrien		sep = sc_tokid;
51719370Spst		expect(NUMBER);
51898944Sobrien		mday = atol(sc_token);
51919370Spst		if (token() == sep) {
52098944Sobrien		    expect(NUMBER);
52198944Sobrien		    year = atol(sc_token);
52298944Sobrien		    token();
52398944Sobrien		}
52498944Sobrien
52519370Spst		/* flip months and days for european timing
526242936Semaste		 */
527242936Semaste		if (sep == DOT) {
528242936Semaste		    int x = mday;
529242936Semaste		    mday = mon;
530242936Semaste		    mon = x;
53198944Sobrien		}
53219370Spst	    }
53398944Sobrien	    else if (tlen == 6 || tlen == 8) {
53498944Sobrien		if (tlen == 8) {
53598944Sobrien		    year = (mon % 10000) - 1900;
53619370Spst		    mon /= 10000;
53798944Sobrien		}
53819370Spst		else {
53998944Sobrien		    year = mon % 100;
54098944Sobrien		    mon /= 100;
54119370Spst		}
54298944Sobrien		mday = mon % 100;
54319370Spst		mon /= 100;
54498944Sobrien	    }
54519370Spst	    else
54698944Sobrien		panic("garbled time");
54719370Spst
54898944Sobrien	    mon--;
54919370Spst	    if (mon < 0 || mon > 11 || mday < 1 || mday > 31)
55098944Sobrien		panic("garbled time");
55119370Spst
55298944Sobrien	    assign_date(tm, mday, mon, year);
55319370Spst	    break;
55498944Sobrien    } /* case */
55519370Spst} /* month */
55698944Sobrien
55719370Spst
55898944Sobrien/* Global functions */
55998944Sobrien
56019370Spsttime_t
56198944Sobrienparsetime(int argc, char **argv)
56298944Sobrien{
56319370Spst/* Do the argument parsing, die if necessary, and return the time the job
564130803Smarcel * should be run.
56519370Spst */
56698944Sobrien    time_t nowtimer, runtimer;
56719370Spst    struct tm nowtime, runtime;
568130803Smarcel    int hr = 0;
569130803Smarcel    /* this MUST be initialized to zero for midnight/noon/teatime */
570130803Smarcel
57146283Sdfr    nowtimer = time(NULL);
572130803Smarcel    nowtime = *localtime(&nowtimer);
573130803Smarcel
57498944Sobrien    runtime = nowtime;
575    runtime.tm_sec = 0;
576    runtime.tm_isdst = 0;
577
578    if (argc <= optind)
579	usage();
580
581    init_scanner(argc-optind, argv+optind);
582
583    switch (token()) {
584    case NOW:	/* now is optional prefix for PLUS tree */
585	    expect(PLUS);
586    case PLUS:
587	    plus(&runtime);
588	    break;
589
590    case NUMBER:
591	    tod(&runtime);
592	    month(&runtime);
593	    break;
594
595	    /* evil coding for TEATIME|NOON|MIDNIGHT - we've initialised
596	     * hr to zero up above, then fall into this case in such a
597	     * way so we add +12 +4 hours to it for teatime, +12 hours
598	     * to it for noon, and nothing at all for midnight, then
599	     * set our runtime to that hour before leaping into the
600	     * month scanner
601	     */
602    case TEATIME:
603	    hr += 4;
604    case NOON:
605	    hr += 12;
606    case MIDNIGHT:
607	    if (runtime.tm_hour >= hr) {
608		runtime.tm_mday++;
609		runtime.tm_wday++;
610	    }
611	    runtime.tm_hour = hr;
612	    runtime.tm_min = 0;
613	    token();
614	    /* fall through to month setting */
615    default:
616	    month(&runtime);
617	    break;
618    } /* ugly case statement */
619    expect(EOF);
620
621    /* adjust for daylight savings time
622     */
623    runtime.tm_isdst = -1;
624    runtimer = mktime(&runtime);
625    if (runtime.tm_isdst > 0) {
626	runtimer -= 3600;
627	runtimer = mktime(&runtime);
628    }
629
630    if (runtimer < 0)
631	panic("garbled time");
632
633    if (nowtimer > runtimer)
634	panic("Trying to travel back in time");
635
636    return runtimer;
637} /* parsetime */
638