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
3687230Smarkm#include <sys/cdefs.h>
3787230Smarkm__FBSDID("$FreeBSD$");
3887230Smarkm
39941Snate/* System Headers */
40941Snate
41941Snate#include <sys/types.h>
4254158Scharnier#include <ctype.h>
4326872Scharnier#include <err.h>
44941Snate#include <errno.h>
45941Snate#include <stdio.h>
46941Snate#include <stdlib.h>
47941Snate#include <string.h>
48941Snate#include <time.h>
49941Snate#include <unistd.h>
507767Sache#ifndef __FreeBSD__
517767Sache#include <getopt.h>
527767Sache#endif
53941Snate
54941Snate/* Local headers */
55941Snate
56941Snate#include "at.h"
57941Snate#include "panic.h"
5887208Smarkm#include "parsetime.h"
59941Snate
60941Snate
61941Snate/* Structures and unions */
62941Snate
6310154Sacheenum {	/* symbols */
64941Snate    MIDNIGHT, NOON, TEATIME,
65941Snate    PM, AM, TOMORROW, TODAY, NOW,
6650411Snsayer    MINUTES, HOURS, DAYS, WEEKS, MONTHS, YEARS,
67241230Scracauer    NUMBER, PLUS, MINUS, DOT, SLASH, ID, JUNK,
68941Snate    JAN, FEB, MAR, APR, MAY, JUN,
697767Sache    JUL, AUG, SEP, OCT, NOV, DEC,
707767Sache    SUN, MON, TUE, WED, THU, FRI, SAT
717767Sache    };
72941Snate
737767Sache/* parse translation table - table driven parsers can be your FRIEND!
74941Snate */
75227233Sedstatic const struct {
7687208Smarkm    const char *name;	/* token name */
7710154Sache    int value;	/* token id */
787767Sache    int plural;	/* is this plural? */
79941Snate} Specials[] = {
807767Sache    { "midnight", MIDNIGHT,0 },	/* 00:00:00 of today or tomorrow */
817767Sache    { "noon", NOON,0 },		/* 12:00:00 of today or tomorrow */
827767Sache    { "teatime", TEATIME,0 },	/* 16:00:00 of today or tomorrow */
837767Sache    { "am", AM,0 },		/* morning times for 0-12 clock */
847767Sache    { "pm", PM,0 },		/* evening times for 0-12 clock */
857767Sache    { "tomorrow", TOMORROW,0 },	/* execute 24 hours from time */
867767Sache    { "today", TODAY, 0 },	/* execute today - don't advance time */
877767Sache    { "now", NOW,0 },		/* opt prefix for PLUS */
88941Snate
897767Sache    { "minute", MINUTES,0 },	/* minutes multiplier */
907767Sache    { "minutes", MINUTES,1 },	/* (pluralized) */
917767Sache    { "hour", HOURS,0 },	/* hours ... */
927767Sache    { "hours", HOURS,1 },	/* (pluralized) */
937767Sache    { "day", DAYS,0 },		/* days ... */
947767Sache    { "days", DAYS,1 },		/* (pluralized) */
957767Sache    { "week", WEEKS,0 },	/* week ... */
967767Sache    { "weeks", WEEKS,1 },	/* (pluralized) */
9750411Snsayer    { "month", MONTHS,0 },	/* month ... */
9850411Snsayer    { "months", MONTHS,1 },	/* (pluralized) */
9950411Snsayer    { "year", YEARS,0 },	/* year ... */
10050411Snsayer    { "years", YEARS,1 },	/* (pluralized) */
1017767Sache    { "jan", JAN,0 },
1027767Sache    { "feb", FEB,0 },
1037767Sache    { "mar", MAR,0 },
1047767Sache    { "apr", APR,0 },
1057767Sache    { "may", MAY,0 },
1067767Sache    { "jun", JUN,0 },
1077767Sache    { "jul", JUL,0 },
1087767Sache    { "aug", AUG,0 },
1097767Sache    { "sep", SEP,0 },
1107767Sache    { "oct", OCT,0 },
1117767Sache    { "nov", NOV,0 },
1127767Sache    { "dec", DEC,0 },
11337538Sdes    { "january", JAN,0 },
11437538Sdes    { "february", FEB,0 },
11537538Sdes    { "march", MAR,0 },
11637538Sdes    { "april", APR,0 },
11737538Sdes    { "may", MAY,0 },
11837538Sdes    { "june", JUN,0 },
11937538Sdes    { "july", JUL,0 },
12037538Sdes    { "august", AUG,0 },
12137538Sdes    { "september", SEP,0 },
12237538Sdes    { "october", OCT,0 },
12337538Sdes    { "november", NOV,0 },
12437538Sdes    { "december", DEC,0 },
1257767Sache    { "sunday", SUN, 0 },
1267767Sache    { "sun", SUN, 0 },
1277767Sache    { "monday", MON, 0 },
1287767Sache    { "mon", MON, 0 },
1297767Sache    { "tuesday", TUE, 0 },
1307767Sache    { "tue", TUE, 0 },
1317767Sache    { "wednesday", WED, 0 },
1327767Sache    { "wed", WED, 0 },
1337767Sache    { "thursday", THU, 0 },
1347767Sache    { "thu", THU, 0 },
1357767Sache    { "friday", FRI, 0 },
1367767Sache    { "fri", FRI, 0 },
1377767Sache    { "saturday", SAT, 0 },
1387767Sache    { "sat", SAT, 0 },
139941Snate} ;
140941Snate
141941Snate/* File scope variables */
142941Snate
143941Snatestatic char **scp;	/* scanner - pointer at arglist */
144941Snatestatic char scc;	/* scanner - count of remaining arguments */
145941Snatestatic char *sct;	/* scanner - next char pointer in current argument */
146941Snatestatic int need;	/* scanner - need to advance to next argument */
147941Snate
148941Snatestatic char *sc_token;	/* scanner - token buffer */
14954158Scharnierstatic size_t sc_len;   /* scanner - length of token buffer */
15010154Sachestatic int sc_tokid;	/* scanner - token id */
1517767Sachestatic int sc_tokplur;	/* scanner - is token plural? */
152941Snate
153941Snate/* Local functions */
154941Snate
155941Snate/*
156941Snate * parse a token, checking if it's something special to us
157941Snate */
15810154Sachestatic int
15910154Sacheparse_token(char *arg)
160941Snate{
16187208Smarkm    size_t i;
162941Snate
163941Snate    for (i=0; i<(sizeof Specials/sizeof Specials[0]); i++)
164941Snate	if (strcasecmp(Specials[i].name, arg) == 0) {
1657767Sache	    sc_tokplur = Specials[i].plural;
166941Snate	    return sc_tokid = Specials[i].value;
167941Snate	}
168941Snate
169941Snate    /* not special - must be some random id */
170941Snate    return ID;
171941Snate} /* parse_token */
172941Snate
173941Snate
174941Snate/*
175941Snate * init_scanner() sets up the scanner to eat arguments
176941Snate */
177941Snatestatic void
1787767Sacheinit_scanner(int argc, char **argv)
179941Snate{
180941Snate    scp = argv;
181941Snate    scc = argc;
182941Snate    need = 1;
183941Snate    sc_len = 1;
18410154Sache    while (argc-- > 0)
18510154Sache	sc_len += strlen(*argv++);
186941Snate
18780294Sobrien    if ((sc_token = malloc(sc_len)) == NULL)
18880294Sobrien	errx(EXIT_FAILURE, "virtual memory exhausted");
189941Snate} /* init_scanner */
190941Snate
191941Snate/*
192941Snate * token() fetches a token from the input stream
193941Snate */
19410154Sachestatic int
19587208Smarkmtoken(void)
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;
249241230Scracauer	else if (sc_token[0] == '-')
250241230Scracauer	    return sc_tokid = MINUS;
2517767Sache	else if (sc_token[0] == '/')
252941Snate	    return sc_tokid = SLASH;
253941Snate	else
254941Snate	    return sc_tokid = JUNK;
255941Snate    } /* while (1) */
256941Snate} /* token */
257941Snate
258941Snate
259941Snate/*
260941Snate * plonk() gives an appropriate error message if a token is incorrect
261941Snate */
262941Snatestatic void
2637767Sacheplonk(int tok)
264941Snate{
265941Snate    panic((tok == EOF) ? "incomplete time"
266941Snate		       : "garbled time");
267941Snate} /* plonk */
268941Snate
269941Snate
27010154Sache/*
271941Snate * expect() gets a token and dies most horribly if it's not the token we want
272941Snate */
273941Snatestatic void
27410154Sacheexpect(int desired)
275941Snate{
276941Snate    if (token() != desired)
277941Snate	plonk(sc_tokid);	/* and we die here... */
278941Snate} /* expect */
279941Snate
280941Snate
281941Snate/*
282241230Scracauer * plus_or_minus() holds functionality common to plus() and minus()
283941Snate */
284941Snatestatic void
285241230Scracauerplus_or_minus(struct tm *tm, int delay)
286941Snate{
2877767Sache    int expectplur;
288941Snate
289241230Scracauer    expectplur = (delay != 1 && delay != -1) ? 1 : 0;
290941Snate
291941Snate    switch (token()) {
29250411Snsayer    case YEARS:
29350411Snsayer	    tm->tm_year += delay;
29450411Snsayer	    break;
29550411Snsayer    case MONTHS:
29650411Snsayer	    tm->tm_mon += delay;
29750411Snsayer	    break;
298941Snate    case WEEKS:
299941Snate	    delay *= 7;
300941Snate    case DAYS:
30150411Snsayer	    tm->tm_mday += delay;
30250411Snsayer	    break;
303941Snate    case HOURS:
30450411Snsayer	    tm->tm_hour += delay;
30550411Snsayer	    break;
306941Snate    case MINUTES:
30750411Snsayer	    tm->tm_min += delay;
30850411Snsayer	    break;
30950411Snsayer    default:
31050411Snsayer    	    plonk(sc_tokid);
31150411Snsayer	    break;
312941Snate    }
31350411Snsayer
31450411Snsayer    if (expectplur != sc_tokplur)
31550411Snsayer	warnx("pluralization is wrong");
31650411Snsayer
31750411Snsayer    tm->tm_isdst = -1;
31850411Snsayer    if (mktime(tm) < 0)
31950411Snsayer	plonk(sc_tokid);
320241230Scracauer} /* plus_or_minus */
32150411Snsayer
322241230Scracauer
323241230Scracauer/*
324241230Scracauer * plus() parses a now + time
325241230Scracauer *
326241230Scracauer *  at [NOW] PLUS NUMBER [MINUTES|HOURS|DAYS|WEEKS|MONTHS|YEARS]
327241230Scracauer *
328241230Scracauer */
329241230Scracauerstatic void
330241230Scracauerplus(struct tm *tm)
331241230Scracauer{
332241230Scracauer    int delay;
333241230Scracauer
334241230Scracauer    expect(NUMBER);
335241230Scracauer
336241230Scracauer    delay = atoi(sc_token);
337241230Scracauer    plus_or_minus(tm, delay);
338941Snate} /* plus */
339941Snate
340941Snate
341941Snate/*
342241230Scracauer * minus() is like plus but can not be used with NOW
343241230Scracauer */
344241230Scracauerstatic void
345241230Scracauerminus(struct tm *tm)
346241230Scracauer{
347241230Scracauer    int delay;
348241230Scracauer
349241230Scracauer    expect(NUMBER);
350241230Scracauer
351241230Scracauer    delay = -atoi(sc_token);
352241230Scracauer    plus_or_minus(tm, delay);
353241230Scracauer} /* minus */
354241230Scracauer
355241230Scracauer
356241230Scracauer/*
357941Snate * tod() computes the time of day
358941Snate *     [NUMBER [DOT NUMBER] [AM|PM]]
359941Snate */
360941Snatestatic void
3617767Sachetod(struct tm *tm)
362941Snate{
363941Snate    int hour, minute = 0;
364941Snate    int tlen;
365941Snate
366941Snate    hour = atoi(sc_token);
367941Snate    tlen = strlen(sc_token);
368941Snate
3697767Sache    /* first pick out the time of day - if it's 4 digits, we assume
370941Snate     * a HHMM time, otherwise it's HH DOT MM time
371941Snate     */
372941Snate    if (token() == DOT) {
373941Snate	expect(NUMBER);
374941Snate	minute = atoi(sc_token);
375941Snate	if (minute > 59)
376941Snate	    panic("garbled time");
377941Snate	token();
3787767Sache    }
3797767Sache    else if (tlen == 4) {
380941Snate	minute = hour%100;
381941Snate	if (minute > 59)
38238646Ssteve	    panic("garbled time");
383941Snate	hour = hour/100;
384941Snate    }
385941Snate
3867767Sache    /* check if an AM or PM specifier was given
387941Snate     */
388941Snate    if (sc_tokid == AM || sc_tokid == PM) {
389941Snate	if (hour > 12)
390941Snate	    panic("garbled time");
391941Snate
39217221Sjdp	if (sc_tokid == PM) {
39317221Sjdp	    if (hour != 12)	/* 12:xx PM is 12:xx, not 24:xx */
39426835Scharnier			hour += 12;
39517221Sjdp	} else {
39617221Sjdp	    if (hour == 12)	/* 12:xx AM is 00:xx, not 12:xx */
39726835Scharnier			hour = 0;
39817221Sjdp	}
399941Snate	token();
4007767Sache    }
4017767Sache    else if (hour > 23)
402941Snate	panic("garbled time");
403941Snate
4047767Sache    /* if we specify an absolute time, we don't want to bump the day even
405941Snate     * if we've gone past that time - but if we're specifying a time plus
406941Snate     * a relative offset, it's okay to bump things
407941Snate     */
408241230Scracauer    if ((sc_tokid == EOF || sc_tokid == PLUS || sc_tokid == MINUS) &&
409241230Scracauer	tm->tm_hour > hour) {
410941Snate	tm->tm_mday++;
4117767Sache	tm->tm_wday++;
4127767Sache    }
413941Snate
414941Snate    tm->tm_hour = hour;
415941Snate    tm->tm_min = minute;
416941Snate    if (tm->tm_hour == 24) {
417941Snate	tm->tm_hour = 0;
418941Snate	tm->tm_mday++;
419941Snate    }
420941Snate} /* tod */
421941Snate
422941Snate
423941Snate/*
424941Snate * assign_date() assigns a date, wrapping to next year if needed
425941Snate */
426941Snatestatic void
4277767Sacheassign_date(struct tm *tm, long mday, long mon, long year)
428941Snate{
42935729Salex
43058660Ssheldonh   /*
43158660Ssheldonh    * Convert year into tm_year format (year - 1900).
43258660Ssheldonh    * We may be given the year in 2 digit, 4 digit, or tm_year format.
43358660Ssheldonh    */
43458660Ssheldonh    if (year != -1) {
43558660Ssheldonh	if (year >= 1900)
43658660Ssheldonh		year -= 1900;   /* convert from 4 digit year */
43758660Ssheldonh	else if (year < 100) {
43858660Ssheldonh		/* convert from 2 digit year */
43958660Ssheldonh		struct tm *lt;
44058660Ssheldonh		time_t now;
44135729Salex
44258660Ssheldonh		time(&now);
44358660Ssheldonh		lt = localtime(&now);
44458660Ssheldonh
44558660Ssheldonh		/* Convert to tm_year assuming current century */
44658660Ssheldonh		year += (lt->tm_year / 100) * 100;
44758660Ssheldonh
44858660Ssheldonh		if (year == lt->tm_year - 1) year++;
44958660Ssheldonh		else if (year < lt->tm_year)
45058660Ssheldonh			year += 100;    /* must be in next century */
45158660Ssheldonh	}
452941Snate    }
453941Snate
454941Snate    if (year < 0 &&
455941Snate	(tm->tm_mon > mon ||(tm->tm_mon == mon && tm->tm_mday > mday)))
456941Snate	year = tm->tm_year + 1;
457941Snate
458941Snate    tm->tm_mday = mday;
459941Snate    tm->tm_mon = mon;
460941Snate
461941Snate    if (year >= 0)
462941Snate	tm->tm_year = year;
463941Snate} /* assign_date */
464941Snate
465941Snate
46610154Sache/*
467941Snate * month() picks apart a month specification
468941Snate *
469941Snate *  /[<month> NUMBER [NUMBER]]           \
470941Snate *  |[TOMORROW]                          |
4717767Sache *  |[DAY OF WEEK]                       |
472941Snate *  |NUMBER [SLASH NUMBER [SLASH NUMBER]]|
473941Snate *  \PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS/
474941Snate */
475941Snatestatic void
4767767Sachemonth(struct tm *tm)
477941Snate{
478941Snate    long year= (-1);
47941556Sarchie    long mday = 0, wday, mon;
480941Snate    int tlen;
481941Snate
482941Snate    switch (sc_tokid) {
483941Snate    case PLUS:
484941Snate	    plus(tm);
485941Snate	    break;
486241230Scracauer    case MINUS:
487241230Scracauer	    minus(tm);
488241230Scracauer	    break;
489941Snate
490941Snate    case TOMORROW:
491941Snate	    /* do something tomorrow */
492941Snate	    tm->tm_mday ++;
4937767Sache	    tm->tm_wday ++;
494941Snate    case TODAY:	/* force ourselves to stay in today - no further processing */
495941Snate	    token();
496941Snate	    break;
497941Snate
498941Snate    case JAN: case FEB: case MAR: case APR: case MAY: case JUN:
499941Snate    case JUL: case AUG: case SEP: case OCT: case NOV: case DEC:
5007767Sache	    /* do month mday [year]
501941Snate	     */
502941Snate	    mon = (sc_tokid-JAN);
503941Snate	    expect(NUMBER);
5046079Sbde	    mday = atol(sc_token);
505941Snate	    if (token() == NUMBER) {
506941Snate		year = atol(sc_token);
507941Snate		token();
508941Snate	    }
509941Snate	    assign_date(tm, mday, mon, year);
510941Snate	    break;
511941Snate
5127767Sache    case SUN: case MON: case TUE:
5137767Sache    case WED: case THU: case FRI:
5147767Sache    case SAT:
5157767Sache	    /* do a particular day of the week
5167767Sache	     */
5177767Sache	    wday = (sc_tokid-SUN);
5187767Sache
5197767Sache	    mday = tm->tm_mday;
5207767Sache
5217767Sache	    /* if this day is < today, then roll to next week
5227767Sache	     */
5237767Sache	    if (wday < tm->tm_wday)
5247767Sache		mday += 7 - (tm->tm_wday - wday);
5257767Sache	    else
5267767Sache		mday += (wday - tm->tm_wday);
5277767Sache
5287767Sache	    tm->tm_wday = wday;
5297767Sache
5307767Sache	    assign_date(tm, mday, tm->tm_mon, tm->tm_year);
5317767Sache	    break;
5327767Sache
533941Snate    case NUMBER:
5347767Sache	    /* get numeric MMDDYY, mm/dd/yy, or dd.mm.yy
535941Snate	     */
536941Snate	    tlen = strlen(sc_token);
537941Snate	    mon = atol(sc_token);
538941Snate	    token();
539941Snate
540941Snate	    if (sc_tokid == SLASH || sc_tokid == DOT) {
54110154Sache		int sep;
542941Snate
543941Snate		sep = sc_tokid;
544941Snate		expect(NUMBER);
545941Snate		mday = atol(sc_token);
546941Snate		if (token() == sep) {
547941Snate		    expect(NUMBER);
548941Snate		    year = atol(sc_token);
549941Snate		    token();
550941Snate		}
551941Snate
55254158Scharnier		/* flip months and days for European timing
553941Snate		 */
554941Snate		if (sep == DOT) {
555941Snate		    int x = mday;
556941Snate		    mday = mon;
557941Snate		    mon = x;
558941Snate		}
5597767Sache	    }
5607767Sache	    else if (tlen == 6 || tlen == 8) {
561941Snate		if (tlen == 8) {
562941Snate		    year = (mon % 10000) - 1900;
563941Snate		    mon /= 10000;
5647767Sache		}
5657767Sache		else {
566941Snate		    year = mon % 100;
567941Snate		    mon /= 100;
568941Snate		}
569941Snate		mday = mon % 100;
570941Snate		mon /= 100;
5717767Sache	    }
5727767Sache	    else
573941Snate		panic("garbled time");
574941Snate
575941Snate	    mon--;
576941Snate	    if (mon < 0 || mon > 11 || mday < 1 || mday > 31)
577941Snate		panic("garbled time");
578941Snate
579941Snate	    assign_date(tm, mday, mon, year);
580941Snate	    break;
581941Snate    } /* case */
582941Snate} /* month */
583941Snate
584941Snate
585941Snate/* Global functions */
586941Snate
587941Snatetime_t
5887767Sacheparsetime(int argc, char **argv)
589941Snate{
5907767Sache/* Do the argument parsing, die if necessary, and return the time the job
591941Snate * should be run.
592941Snate */
593941Snate    time_t nowtimer, runtimer;
594941Snate    struct tm nowtime, runtime;
595941Snate    int hr = 0;
596941Snate    /* this MUST be initialized to zero for midnight/noon/teatime */
597941Snate
598941Snate    nowtimer = time(NULL);
599941Snate    nowtime = *localtime(&nowtimer);
600941Snate
601941Snate    runtime = nowtime;
602941Snate    runtime.tm_sec = 0;
603941Snate    runtime.tm_isdst = 0;
604941Snate
605941Snate    if (argc <= optind)
606941Snate	usage();
607941Snate
608941Snate    init_scanner(argc-optind, argv+optind);
609941Snate
610941Snate    switch (token()) {
61186848Sbrian    case NOW:
61286848Sbrian	    if (scc < 1) {
61386848Sbrian		return nowtimer;
61486848Sbrian	    }
61586848Sbrian	    /* now is optional prefix for PLUS tree */
616941Snate	    expect(PLUS);
617941Snate    case PLUS:
618941Snate	    plus(&runtime);
619941Snate	    break;
620941Snate
621241230Scracauer	    /* MINUS is different from PLUS in that NOW is not
622241230Scracauer	     * an optional prefix for it
623241230Scracauer	     */
624241230Scracauer    case MINUS:
625241230Scracauer	    minus(&runtime);
626241230Scracauer	    break;
627941Snate    case NUMBER:
628941Snate	    tod(&runtime);
629941Snate	    month(&runtime);
630941Snate	    break;
631941Snate
6327767Sache	    /* evil coding for TEATIME|NOON|MIDNIGHT - we've initialised
633941Snate	     * hr to zero up above, then fall into this case in such a
634941Snate	     * way so we add +12 +4 hours to it for teatime, +12 hours
635941Snate	     * to it for noon, and nothing at all for midnight, then
636941Snate	     * set our runtime to that hour before leaping into the
637941Snate	     * month scanner
638941Snate	     */
639941Snate    case TEATIME:
640941Snate	    hr += 4;
641941Snate    case NOON:
642941Snate	    hr += 12;
643941Snate    case MIDNIGHT:
6447767Sache	    if (runtime.tm_hour >= hr) {
645941Snate		runtime.tm_mday++;
6467767Sache		runtime.tm_wday++;
6477767Sache	    }
648941Snate	    runtime.tm_hour = hr;
649941Snate	    runtime.tm_min = 0;
650941Snate	    token();
65154158Scharnier	    /* FALLTHROUGH to month setting */
652941Snate    default:
653941Snate	    month(&runtime);
654941Snate	    break;
655941Snate    } /* ugly case statement */
656941Snate    expect(EOF);
657941Snate
658149215Sstefanf    /* convert back to time_t
659941Snate     */
660941Snate    runtime.tm_isdst = -1;
661941Snate    runtimer = mktime(&runtime);
662941Snate
663941Snate    if (runtimer < 0)
664941Snate	panic("garbled time");
665941Snate
666941Snate    if (nowtimer > runtimer)
66754158Scharnier	panic("trying to travel back in time");
668941Snate
669941Snate    return runtimer;
670941Snate} /* parsetime */
671