parsetime.c revision 87628
1295373Sdteske/*
2295373Sdteske *  parsetime.c - parse time for at(1)
3295373Sdteske *  Copyright (C) 1993, 1994  Thomas Koenig
4295373Sdteske *
5295373Sdteske *  modifications for English-language times
6295373Sdteske *  Copyright (C) 1993  David Parsons
7295373Sdteske *
8295373Sdteske * Redistribution and use in source and binary forms, with or without
9295373Sdteske * modification, are permitted provided that the following conditions
10295373Sdteske * are met:
11295373Sdteske * 1. Redistributions of source code must retain the above copyright
12295373Sdteske *    notice, this list of conditions and the following disclaimer.
13295373Sdteske * 2. The name of the author(s) may not be used to endorse or promote
14295373Sdteske *    products derived from this software without specific prior written
15295373Sdteske *    permission.
16295373Sdteske *
17295373Sdteske * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
18295373Sdteske * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19295373Sdteske * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20295373Sdteske * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
21295373Sdteske * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22295373Sdteske * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23295373Sdteske * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24295373Sdteske * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25295373Sdteske * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26295373Sdteske * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27295373Sdteske *
28295373Sdteske *  at [NOW] PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS
29295373Sdteske *     /NUMBER [DOT NUMBER] [AM|PM]\ /[MONTH NUMBER [NUMBER]]             \
30295373Sdteske *     |NOON                       | |[TOMORROW]                          |
31295373Sdteske *     |MIDNIGHT                   | |[DAY OF WEEK]                       |
32295373Sdteske *     \TEATIME                    / |NUMBER [SLASH NUMBER [SLASH NUMBER]]|
33295373Sdteske *                                   \PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS/
34295373Sdteske */
35295373Sdteske
36295400Sdteske#include <sys/cdefs.h>
37295400Sdteske__FBSDID("$FreeBSD: head/usr.bin/at/parsetime.c 87628 2001-12-10 21:13:08Z dwmalone $");
38295400Sdteske
39295373Sdteske/* System Headers */
40295400Sdteske
41295400Sdteske#include <sys/types.h>
42295373Sdteske#include <ctype.h>
43295373Sdteske#include <err.h>
44295373Sdteske#include <errno.h>
45295373Sdteske#include <stdio.h>
46295373Sdteske#include <stdlib.h>
47295373Sdteske#include <string.h>
48295373Sdteske#include <time.h>
49295373Sdteske#include <unistd.h>
50295373Sdteske#ifndef __FreeBSD__
51295373Sdteske#include <getopt.h>
52295373Sdteske#endif
53295373Sdteske
54295373Sdteske/* Local headers */
55295373Sdteske
56295373Sdteske#include "at.h"
57295373Sdteske#include "panic.h"
58295373Sdteske#include "parsetime.h"
59295373Sdteske
60295373Sdteske
61295373Sdteske/* Structures and unions */
62295373Sdteske
63295373Sdteskeenum {	/* symbols */
64295373Sdteske    MIDNIGHT, NOON, TEATIME,
65295373Sdteske    PM, AM, TOMORROW, TODAY, NOW,
66295373Sdteske    MINUTES, HOURS, DAYS, WEEKS, MONTHS, YEARS,
67295373Sdteske    NUMBER, PLUS, DOT, SLASH, ID, JUNK,
68295373Sdteske    JAN, FEB, MAR, APR, MAY, JUN,
69295373Sdteske    JUL, AUG, SEP, OCT, NOV, DEC,
70295373Sdteske    SUN, MON, TUE, WED, THU, FRI, SAT
71295373Sdteske    };
72295373Sdteske
73295373Sdteske/* parse translation table - table driven parsers can be your FRIEND!
74295373Sdteske */
75295373Sdteskestruct {
76295373Sdteske    const char *name;	/* token name */
77295373Sdteske    int value;	/* token id */
78295373Sdteske    int plural;	/* is this plural? */
79295373Sdteske} Specials[] = {
80295373Sdteske    { "midnight", MIDNIGHT,0 },	/* 00:00:00 of today or tomorrow */
81295373Sdteske    { "noon", NOON,0 },		/* 12:00:00 of today or tomorrow */
82295373Sdteske    { "teatime", TEATIME,0 },	/* 16:00:00 of today or tomorrow */
83295373Sdteske    { "am", AM,0 },		/* morning times for 0-12 clock */
84295373Sdteske    { "pm", PM,0 },		/* evening times for 0-12 clock */
85295373Sdteske    { "tomorrow", TOMORROW,0 },	/* execute 24 hours from time */
86295373Sdteske    { "today", TODAY, 0 },	/* execute today - don't advance time */
87295373Sdteske    { "now", NOW,0 },		/* opt prefix for PLUS */
88295373Sdteske
89295373Sdteske    { "minute", MINUTES,0 },	/* minutes multiplier */
90295373Sdteske    { "minutes", MINUTES,1 },	/* (pluralized) */
91295373Sdteske    { "hour", HOURS,0 },	/* hours ... */
92295373Sdteske    { "hours", HOURS,1 },	/* (pluralized) */
93295373Sdteske    { "day", DAYS,0 },		/* days ... */
94295373Sdteske    { "days", DAYS,1 },		/* (pluralized) */
95295373Sdteske    { "week", WEEKS,0 },	/* week ... */
96295373Sdteske    { "weeks", WEEKS,1 },	/* (pluralized) */
97295373Sdteske    { "month", MONTHS,0 },	/* month ... */
98295373Sdteske    { "months", MONTHS,1 },	/* (pluralized) */
99295373Sdteske    { "year", YEARS,0 },	/* year ... */
100295373Sdteske    { "years", YEARS,1 },	/* (pluralized) */
101295373Sdteske    { "jan", JAN,0 },
102295373Sdteske    { "feb", FEB,0 },
103295373Sdteske    { "mar", MAR,0 },
104295373Sdteske    { "apr", APR,0 },
105295373Sdteske    { "may", MAY,0 },
106295373Sdteske    { "jun", JUN,0 },
107295373Sdteske    { "jul", JUL,0 },
108295373Sdteske    { "aug", AUG,0 },
109295373Sdteske    { "sep", SEP,0 },
110295373Sdteske    { "oct", OCT,0 },
111295373Sdteske    { "nov", NOV,0 },
112295373Sdteske    { "dec", DEC,0 },
113295373Sdteske    { "january", JAN,0 },
114295373Sdteske    { "february", FEB,0 },
115295373Sdteske    { "march", MAR,0 },
116295373Sdteske    { "april", APR,0 },
117295373Sdteske    { "may", MAY,0 },
118295373Sdteske    { "june", JUN,0 },
119295373Sdteske    { "july", JUL,0 },
120295373Sdteske    { "august", AUG,0 },
121295373Sdteske    { "september", SEP,0 },
122295373Sdteske    { "october", OCT,0 },
123295373Sdteske    { "november", NOV,0 },
124295373Sdteske    { "december", DEC,0 },
125295441Sdteske    { "sunday", SUN, 0 },
126295373Sdteske    { "sun", SUN, 0 },
127295373Sdteske    { "monday", MON, 0 },
128295373Sdteske    { "mon", MON, 0 },
129295373Sdteske    { "tuesday", TUE, 0 },
130295373Sdteske    { "tue", TUE, 0 },
131295373Sdteske    { "wednesday", WED, 0 },
132295373Sdteske    { "wed", WED, 0 },
133295373Sdteske    { "thursday", THU, 0 },
134295373Sdteske    { "thu", THU, 0 },
135295373Sdteske    { "friday", FRI, 0 },
136295373Sdteske    { "fri", FRI, 0 },
137295373Sdteske    { "saturday", SAT, 0 },
138295373Sdteske    { "sat", SAT, 0 },
139295373Sdteske} ;
140295373Sdteske
141295373Sdteske/* File scope variables */
142295373Sdteske
143295373Sdteskestatic char **scp;	/* scanner - pointer at arglist */
144295373Sdteskestatic char scc;	/* scanner - count of remaining arguments */
145295373Sdteskestatic char *sct;	/* scanner - next char pointer in current argument */
146295373Sdteskestatic int need;	/* scanner - need to advance to next argument */
147295373Sdteske
148295373Sdteskestatic char *sc_token;	/* scanner - token buffer */
149295373Sdteskestatic size_t sc_len;   /* scanner - length of token buffer */
150295373Sdteskestatic int sc_tokid;	/* scanner - token id */
151295373Sdteskestatic int sc_tokplur;	/* scanner - is token plural? */
152295373Sdteske
153295373Sdteske/* Local functions */
154295373Sdteske
155295373Sdteske/*
156295373Sdteske * parse a token, checking if it's something special to us
157295460Sdteske */
158295373Sdteskestatic int
159295373Sdteskeparse_token(char *arg)
160295373Sdteske{
161295373Sdteske    size_t i;
162295373Sdteske
163295373Sdteske    for (i=0; i<(sizeof Specials/sizeof Specials[0]); i++)
164295373Sdteske	if (strcasecmp(Specials[i].name, arg) == 0) {
165295373Sdteske	    sc_tokplur = Specials[i].plural;
166295373Sdteske	    return sc_tokid = Specials[i].value;
167295373Sdteske	}
168295373Sdteske
169295587Sdteske    /* not special - must be some random id */
170295373Sdteske    return ID;
171295373Sdteske} /* parse_token */
172295587Sdteske
173295587Sdteske
174295373Sdteske/*
175295373Sdteske * init_scanner() sets up the scanner to eat arguments
176295373Sdteske */
177295546Sdteskestatic void
178295546Sdteskeinit_scanner(int argc, char **argv)
179295546Sdteske{
180295546Sdteske    scp = argv;
181295546Sdteske    scc = argc;
182295546Sdteske    need = 1;
183295546Sdteske    sc_len = 1;
184295546Sdteske    while (argc-- > 0)
185295546Sdteske	sc_len += strlen(*argv++);
186295546Sdteske
187295546Sdteske    if ((sc_token = malloc(sc_len)) == NULL)
188295546Sdteske	errx(EXIT_FAILURE, "virtual memory exhausted");
189295546Sdteske} /* init_scanner */
190295546Sdteske
191295546Sdteske/*
192295546Sdteske * token() fetches a token from the input stream
193295546Sdteske */
194295546Sdteskestatic int
195295546Sdtesketoken(void)
196295546Sdteske{
197295546Sdteske    int idx;
198295546Sdteske
199295546Sdteske    while (1) {
200295546Sdteske	memset(sc_token, 0, sc_len);
201295546Sdteske	sc_tokid = EOF;
202295546Sdteske	sc_tokplur = 0;
203295546Sdteske	idx = 0;
204295546Sdteske
205295546Sdteske	/* if we need to read another argument, walk along the argument list;
206295546Sdteske	 * when we fall off the arglist, we'll just return EOF forever
207295546Sdteske	 */
208295546Sdteske	if (need) {
209295546Sdteske	    if (scc < 1)
210295546Sdteske		return sc_tokid;
211295546Sdteske	    sct = *scp;
212295546Sdteske	    scp++;
213295546Sdteske	    scc--;
214295546Sdteske	    need = 0;
215295546Sdteske	}
216295546Sdteske	/* eat whitespace now - if we walk off the end of the argument,
217295546Sdteske	 * we'll continue, which puts us up at the top of the while loop
218295546Sdteske	 * to fetch the next argument in
219295546Sdteske	 */
220295546Sdteske	while (isspace(*sct))
221295553Sdteske	    ++sct;
222295553Sdteske	if (!*sct) {
223295553Sdteske	    need = 1;
224295546Sdteske	    continue;
225295553Sdteske	}
226295553Sdteske
227295553Sdteske	/* preserve the first character of the new token
228295546Sdteske	 */
229295546Sdteske	sc_token[0] = *sct++;
230295546Sdteske
231295546Sdteske	/* then see what it is
232295546Sdteske	 */
233295553Sdteske	if (isdigit(sc_token[0])) {
234295546Sdteske	    while (isdigit(*sct))
235295546Sdteske		sc_token[++idx] = *sct++;
236295546Sdteske	    sc_token[++idx] = 0;
237295546Sdteske	    return sc_tokid = NUMBER;
238295546Sdteske	}
239295546Sdteske	else if (isalpha(sc_token[0])) {
240295546Sdteske	    while (isalpha(*sct))
241295546Sdteske		sc_token[++idx] = *sct++;
242295546Sdteske	    sc_token[++idx] = 0;
243295546Sdteske	    return parse_token(sc_token);
244295546Sdteske	}
245295546Sdteske	else if (sc_token[0] == ':' || sc_token[0] == '.')
246295546Sdteske	    return sc_tokid = DOT;
247295546Sdteske	else if (sc_token[0] == '+')
248295546Sdteske	    return sc_tokid = PLUS;
249295546Sdteske	else if (sc_token[0] == '/')
250295546Sdteske	    return sc_tokid = SLASH;
251295546Sdteske	else
252295546Sdteske	    return sc_tokid = JUNK;
253295546Sdteske    } /* while (1) */
254295546Sdteske} /* token */
255295546Sdteske
256295373Sdteske
257295373Sdteske/*
258295373Sdteske * plonk() gives an appropriate error message if a token is incorrect
259295373Sdteske */
260295373Sdteskestatic void
261295373Sdteskeplonk(int tok)
262295373Sdteske{
263295373Sdteske    panic((tok == EOF) ? "incomplete time"
264295554Sdteske		       : "garbled time");
265295373Sdteske} /* plonk */
266295373Sdteske
267295373Sdteske
268295373Sdteske/*
269295373Sdteske * expect() gets a token and dies most horribly if it's not the token we want
270295373Sdteske */
271295373Sdteskestatic void
272295373Sdteskeexpect(int desired)
273295373Sdteske{
274295373Sdteske    if (token() != desired)
275295373Sdteske	plonk(sc_tokid);	/* and we die here... */
276295373Sdteske} /* expect */
277295373Sdteske
278295373Sdteske
279295373Sdteske/*
280295373Sdteske * plus() parses a now + time
281295373Sdteske *
282295373Sdteske *  at [NOW] PLUS NUMBER [MINUTES|HOURS|DAYS|WEEKS|MONTHS|YEARS]
283295373Sdteske *
284295373Sdteske */
285295548Sdteske
286295554Sdteskestatic void
287295373Sdteskeplus(struct tm *tm)
288295373Sdteske{
289295548Sdteske    int delay;
290295554Sdteske    int expectplur;
291295548Sdteske
292295548Sdteske    expect(NUMBER);
293295554Sdteske
294295548Sdteske    delay = atoi(sc_token);
295295548Sdteske    expectplur = (delay != 1) ? 1 : 0;
296295556Sdteske
297295373Sdteske    switch (token()) {
298295556Sdteske    case YEARS:
299295556Sdteske	    tm->tm_year += delay;
300295556Sdteske	    break;
301295556Sdteske    case MONTHS:
302295373Sdteske	    tm->tm_mon += delay;
303295556Sdteske	    break;
304295373Sdteske    case WEEKS:
305295373Sdteske	    delay *= 7;
306295556Sdteske    case DAYS:
307295373Sdteske	    tm->tm_mday += delay;
308295373Sdteske	    break;
309295373Sdteske    case HOURS:
310295556Sdteske	    tm->tm_hour += delay;
311295373Sdteske	    break;
312295373Sdteske    case MINUTES:
313295373Sdteske	    tm->tm_min += delay;
314295373Sdteske	    break;
315295373Sdteske    default:
316295373Sdteske    	    plonk(sc_tokid);
317295373Sdteske	    break;
318295556Sdteske    }
319295373Sdteske
320295373Sdteske    if (expectplur != sc_tokplur)
321295373Sdteske	warnx("pluralization is wrong");
322295373Sdteske
323295373Sdteske    tm->tm_isdst = -1;
324295373Sdteske    if (mktime(tm) < 0)
325295373Sdteske	plonk(sc_tokid);
326295373Sdteske
327295373Sdteske} /* plus */
328295373Sdteske
329295373Sdteske
330295373Sdteske/*
331295373Sdteske * tod() computes the time of day
332295373Sdteske *     [NUMBER [DOT NUMBER] [AM|PM]]
333295373Sdteske */
334295556Sdteskestatic void
335295373Sdtesketod(struct tm *tm)
336295373Sdteske{
337295373Sdteske    int hour, minute = 0;
338295373Sdteske    int tlen;
339295373Sdteske
340295373Sdteske    hour = atoi(sc_token);
341295373Sdteske    tlen = strlen(sc_token);
342295556Sdteske
343295373Sdteske    /* first pick out the time of day - if it's 4 digits, we assume
344295373Sdteske     * a HHMM time, otherwise it's HH DOT MM time
345295373Sdteske     */
346295373Sdteske    if (token() == DOT) {
347295373Sdteske	expect(NUMBER);
348295373Sdteske	minute = atoi(sc_token);
349295373Sdteske	if (minute > 59)
350295443Sdteske	    panic("garbled time");
351295373Sdteske	token();
352295373Sdteske    }
353295556Sdteske    else if (tlen == 4) {
354295373Sdteske	minute = hour%100;
355295373Sdteske	if (minute > 59)
356295554Sdteske	    panic("garbled time");
357295548Sdteske	hour = hour/100;
358295554Sdteske    }
359295554Sdteske
360295554Sdteske    /* check if an AM or PM specifier was given
361295548Sdteske     */
362295548Sdteske    if (sc_tokid == AM || sc_tokid == PM) {
363295554Sdteske	if (hour > 12)
364295554Sdteske	    panic("garbled time");
365295373Sdteske
366295556Sdteske	if (sc_tokid == PM) {
367295373Sdteske	    if (hour != 12)	/* 12:xx PM is 12:xx, not 24:xx */
368295373Sdteske			hour += 12;
369295373Sdteske	} else {
370295373Sdteske	    if (hour == 12)	/* 12:xx AM is 00:xx, not 12:xx */
371295373Sdteske			hour = 0;
372295373Sdteske	}
373295373Sdteske	token();
374295373Sdteske    }
375295373Sdteske    else if (hour > 23)
376295373Sdteske	panic("garbled time");
377295373Sdteske
378295373Sdteske    /* if we specify an absolute time, we don't want to bump the day even
379295373Sdteske     * if we've gone past that time - but if we're specifying a time plus
380295373Sdteske     * a relative offset, it's okay to bump things
381295373Sdteske     */
382295373Sdteske    if ((sc_tokid == EOF || sc_tokid == PLUS) && tm->tm_hour > hour) {
383295373Sdteske	tm->tm_mday++;
384295373Sdteske	tm->tm_wday++;
385295373Sdteske    }
386295373Sdteske
387295373Sdteske    tm->tm_hour = hour;
388295373Sdteske    tm->tm_min = minute;
389295373Sdteske    if (tm->tm_hour == 24) {
390295373Sdteske	tm->tm_hour = 0;
391295373Sdteske	tm->tm_mday++;
392295373Sdteske    }
393295373Sdteske} /* tod */
394295373Sdteske
395295373Sdteske
396295373Sdteske/*
397295373Sdteske * assign_date() assigns a date, wrapping to next year if needed
398295373Sdteske */
399295373Sdteskestatic void
400295373Sdteskeassign_date(struct tm *tm, long mday, long mon, long year)
401295373Sdteske{
402295373Sdteske
403295373Sdteske   /*
404295373Sdteske    * Convert year into tm_year format (year - 1900).
405295373Sdteske    * We may be given the year in 2 digit, 4 digit, or tm_year format.
406295373Sdteske    */
407295373Sdteske    if (year != -1) {
408295373Sdteske	if (year >= 1900)
409295373Sdteske		year -= 1900;   /* convert from 4 digit year */
410295373Sdteske	else if (year < 100) {
411295373Sdteske		/* convert from 2 digit year */
412295373Sdteske		struct tm *lt;
413295373Sdteske		time_t now;
414295373Sdteske
415295373Sdteske		time(&now);
416295373Sdteske		lt = localtime(&now);
417295373Sdteske
418295373Sdteske		/* Convert to tm_year assuming current century */
419295373Sdteske		year += (lt->tm_year / 100) * 100;
420295373Sdteske
421295373Sdteske		if (year == lt->tm_year - 1) year++;
422295373Sdteske		else if (year < lt->tm_year)
423295373Sdteske			year += 100;    /* must be in next century */
424295373Sdteske	}
425295373Sdteske    }
426295373Sdteske
427295373Sdteske    if (year < 0 &&
428295373Sdteske	(tm->tm_mon > mon ||(tm->tm_mon == mon && tm->tm_mday > mday)))
429295373Sdteske	year = tm->tm_year + 1;
430295373Sdteske
431295373Sdteske    tm->tm_mday = mday;
432295373Sdteske    tm->tm_mon = mon;
433295373Sdteske
434295373Sdteske    if (year >= 0)
435295373Sdteske	tm->tm_year = year;
436295373Sdteske} /* assign_date */
437295373Sdteske
438295373Sdteske
439295373Sdteske/*
440295373Sdteske * month() picks apart a month specification
441295373Sdteske *
442295373Sdteske *  /[<month> NUMBER [NUMBER]]           \
443295373Sdteske *  |[TOMORROW]                          |
444295373Sdteske *  |[DAY OF WEEK]                       |
445295373Sdteske *  |NUMBER [SLASH NUMBER [SLASH NUMBER]]|
446295373Sdteske *  \PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS/
447295373Sdteske */
448295373Sdteskestatic void
449295373Sdteskemonth(struct tm *tm)
450295460Sdteske{
451295460Sdteske    long year= (-1);
452295460Sdteske    long mday = 0, wday, mon;
453295460Sdteske    int tlen;
454295460Sdteske
455295460Sdteske    switch (sc_tokid) {
456295460Sdteske    case PLUS:
457295460Sdteske	    plus(tm);
458295460Sdteske	    break;
459295460Sdteske
460295460Sdteske    case TOMORROW:
461295460Sdteske	    /* do something tomorrow */
462295460Sdteske	    tm->tm_mday ++;
463295460Sdteske	    tm->tm_wday ++;
464295460Sdteske    case TODAY:	/* force ourselves to stay in today - no further processing */
465295460Sdteske	    token();
466295460Sdteske	    break;
467295460Sdteske
468295460Sdteske    case JAN: case FEB: case MAR: case APR: case MAY: case JUN:
469295460Sdteske    case JUL: case AUG: case SEP: case OCT: case NOV: case DEC:
470295460Sdteske	    /* do month mday [year]
471295460Sdteske	     */
472295460Sdteske	    mon = (sc_tokid-JAN);
473295460Sdteske	    expect(NUMBER);
474295460Sdteske	    mday = atol(sc_token);
475295460Sdteske	    if (token() == NUMBER) {
476295460Sdteske		year = atol(sc_token);
477295460Sdteske		token();
478295460Sdteske	    }
479295460Sdteske	    assign_date(tm, mday, mon, year);
480295460Sdteske	    break;
481295460Sdteske
482295460Sdteske    case SUN: case MON: case TUE:
483295373Sdteske    case WED: case THU: case FRI:
484295373Sdteske    case SAT:
485295373Sdteske	    /* do a particular day of the week
486295373Sdteske	     */
487295373Sdteske	    wday = (sc_tokid-SUN);
488295373Sdteske
489295373Sdteske	    mday = tm->tm_mday;
490295373Sdteske
491295373Sdteske	    /* if this day is < today, then roll to next week
492295373Sdteske	     */
493295373Sdteske	    if (wday < tm->tm_wday)
494295373Sdteske		mday += 7 - (tm->tm_wday - wday);
495295373Sdteske	    else
496295373Sdteske		mday += (wday - tm->tm_wday);
497295373Sdteske
498295373Sdteske	    tm->tm_wday = wday;
499295373Sdteske
500295373Sdteske	    assign_date(tm, mday, tm->tm_mon, tm->tm_year);
501295373Sdteske	    break;
502295373Sdteske
503295373Sdteske    case NUMBER:
504295373Sdteske	    /* get numeric MMDDYY, mm/dd/yy, or dd.mm.yy
505295373Sdteske	     */
506295373Sdteske	    tlen = strlen(sc_token);
507295373Sdteske	    mon = atol(sc_token);
508295373Sdteske	    token();
509295373Sdteske
510	    if (sc_tokid == SLASH || sc_tokid == DOT) {
511		int sep;
512
513		sep = sc_tokid;
514		expect(NUMBER);
515		mday = atol(sc_token);
516		if (token() == sep) {
517		    expect(NUMBER);
518		    year = atol(sc_token);
519		    token();
520		}
521
522		/* flip months and days for European timing
523		 */
524		if (sep == DOT) {
525		    int x = mday;
526		    mday = mon;
527		    mon = x;
528		}
529	    }
530	    else if (tlen == 6 || tlen == 8) {
531		if (tlen == 8) {
532		    year = (mon % 10000) - 1900;
533		    mon /= 10000;
534		}
535		else {
536		    year = mon % 100;
537		    mon /= 100;
538		}
539		mday = mon % 100;
540		mon /= 100;
541	    }
542	    else
543		panic("garbled time");
544
545	    mon--;
546	    if (mon < 0 || mon > 11 || mday < 1 || mday > 31)
547		panic("garbled time");
548
549	    assign_date(tm, mday, mon, year);
550	    break;
551    } /* case */
552} /* month */
553
554
555/* Global functions */
556
557time_t
558parsetime(int argc, char **argv)
559{
560/* Do the argument parsing, die if necessary, and return the time the job
561 * should be run.
562 */
563    time_t nowtimer, runtimer;
564    struct tm nowtime, runtime;
565    int hr = 0;
566    /* this MUST be initialized to zero for midnight/noon/teatime */
567
568    nowtimer = time(NULL);
569    nowtime = *localtime(&nowtimer);
570
571    runtime = nowtime;
572    runtime.tm_sec = 0;
573    runtime.tm_isdst = 0;
574
575    if (argc <= optind)
576	usage();
577
578    init_scanner(argc-optind, argv+optind);
579
580    switch (token()) {
581    case NOW:
582	    if (scc < 1) {
583		return nowtimer;
584	    }
585	    /* now is optional prefix for PLUS tree */
586	    expect(PLUS);
587    case PLUS:
588	    plus(&runtime);
589	    break;
590
591    case NUMBER:
592	    tod(&runtime);
593	    month(&runtime);
594	    break;
595
596	    /* evil coding for TEATIME|NOON|MIDNIGHT - we've initialised
597	     * hr to zero up above, then fall into this case in such a
598	     * way so we add +12 +4 hours to it for teatime, +12 hours
599	     * to it for noon, and nothing at all for midnight, then
600	     * set our runtime to that hour before leaping into the
601	     * month scanner
602	     */
603    case TEATIME:
604	    hr += 4;
605    case NOON:
606	    hr += 12;
607    case MIDNIGHT:
608	    if (runtime.tm_hour >= hr) {
609		runtime.tm_mday++;
610		runtime.tm_wday++;
611	    }
612	    runtime.tm_hour = hr;
613	    runtime.tm_min = 0;
614	    token();
615	    /* FALLTHROUGH to month setting */
616    default:
617	    month(&runtime);
618	    break;
619    } /* ugly case statement */
620    expect(EOF);
621
622    /* adjust for daylight savings time
623     */
624    runtime.tm_isdst = -1;
625    runtimer = mktime(&runtime);
626    if (runtime.tm_isdst > 0) {
627	runtimer -= 3600;
628	runtimer = mktime(&runtime);
629    }
630
631    if (runtimer < 0)
632	panic("garbled time");
633
634    if (nowtimer > runtimer)
635	panic("trying to travel back in time");
636
637    return runtimer;
638} /* parsetime */
639