xref: /freebsd/usr.bin/at/parsetime.c (revision 12d20ef97d054015becec04cf3a089d49eaac101)
1d78e98d2SNate Williams /*
2d78e98d2SNate Williams  *  parsetime.c - parse time for at(1)
3b89321a5SAndrey A. Chernov  *  Copyright (C) 1993, 1994  Thomas Koenig
4d78e98d2SNate Williams  *
5d78e98d2SNate Williams  *  modifications for english-language times
6d78e98d2SNate Williams  *  Copyright (C) 1993  David Parsons
7d78e98d2SNate Williams  *
8d78e98d2SNate Williams  * Redistribution and use in source and binary forms, with or without
9d78e98d2SNate Williams  * modification, are permitted provided that the following conditions
10d78e98d2SNate Williams  * are met:
11d78e98d2SNate Williams  * 1. Redistributions of source code must retain the above copyright
12d78e98d2SNate Williams  *    notice, this list of conditions and the following disclaimer.
13d78e98d2SNate Williams  * 2. The name of the author(s) may not be used to endorse or promote
14d78e98d2SNate Williams  *    products derived from this software without specific prior written
15d78e98d2SNate Williams  *    permission.
16d78e98d2SNate Williams  *
17d78e98d2SNate Williams  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
18d78e98d2SNate Williams  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19d78e98d2SNate Williams  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20ddcf8022SAndrey A. Chernov  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
21d78e98d2SNate Williams  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22d78e98d2SNate Williams  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23d78e98d2SNate Williams  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24d78e98d2SNate Williams  * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25d78e98d2SNate Williams  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26d78e98d2SNate Williams  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27d78e98d2SNate Williams  *
28d78e98d2SNate Williams  *  at [NOW] PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS
29d78e98d2SNate Williams  *     /NUMBER [DOT NUMBER] [AM|PM]\ /[MONTH NUMBER [NUMBER]]             \
30d78e98d2SNate Williams  *     |NOON                       | |[TOMORROW]                          |
31b89321a5SAndrey A. Chernov  *     |MIDNIGHT                   | |[DAY OF WEEK]                       |
32b89321a5SAndrey A. Chernov  *     \TEATIME                    / |NUMBER [SLASH NUMBER [SLASH NUMBER]]|
33b89321a5SAndrey A. Chernov  *                                   \PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS/
3412d20ef9SPhilippe Charnier  *
3512d20ef9SPhilippe Charnier  *	$Id$
36d78e98d2SNate Williams  */
37d78e98d2SNate Williams 
38d78e98d2SNate Williams /* System Headers */
39d78e98d2SNate Williams 
40b89321a5SAndrey A. Chernov 
41d78e98d2SNate Williams #include <sys/types.h>
42d78e98d2SNate Williams #include <errno.h>
43d78e98d2SNate Williams #include <stdio.h>
44d78e98d2SNate Williams #include <stdlib.h>
45d78e98d2SNate Williams #include <string.h>
46d78e98d2SNate Williams #include <time.h>
47d78e98d2SNate Williams #include <unistd.h>
48d78e98d2SNate Williams #include <ctype.h>
49b89321a5SAndrey A. Chernov #ifndef __FreeBSD__
50b89321a5SAndrey A. Chernov #include <getopt.h>
51b89321a5SAndrey A. Chernov #endif
5212d20ef9SPhilippe Charnier #include <err.h>
53d78e98d2SNate Williams 
54d78e98d2SNate Williams /* Local headers */
55d78e98d2SNate Williams 
56d78e98d2SNate Williams #include "at.h"
57d78e98d2SNate Williams #include "panic.h"
58d78e98d2SNate Williams 
59d78e98d2SNate Williams 
60d78e98d2SNate Williams /* Structures and unions */
61d78e98d2SNate Williams 
62ddcf8022SAndrey A. Chernov enum {	/* symbols */
63d78e98d2SNate Williams     MIDNIGHT, NOON, TEATIME,
64d78e98d2SNate Williams     PM, AM, TOMORROW, TODAY, NOW,
65d78e98d2SNate Williams     MINUTES, HOURS, DAYS, WEEKS,
66d78e98d2SNate Williams     NUMBER, PLUS, DOT, SLASH, ID, JUNK,
67d78e98d2SNate Williams     JAN, FEB, MAR, APR, MAY, JUN,
68b89321a5SAndrey A. Chernov     JUL, AUG, SEP, OCT, NOV, DEC,
69b89321a5SAndrey A. Chernov     SUN, MON, TUE, WED, THU, FRI, SAT
70d78e98d2SNate Williams     };
71d78e98d2SNate Williams 
72b89321a5SAndrey A. Chernov /* parse translation table - table driven parsers can be your FRIEND!
73d78e98d2SNate Williams  */
74d78e98d2SNate Williams struct {
75ddcf8022SAndrey A. Chernov     char *name;	/* token name */
76ddcf8022SAndrey A. Chernov     int value;	/* token id */
77b89321a5SAndrey A. Chernov     int plural;	/* is this plural? */
78d78e98d2SNate Williams } Specials[] = {
79b89321a5SAndrey A. Chernov     { "midnight", MIDNIGHT,0 },	/* 00:00:00 of today or tomorrow */
80b89321a5SAndrey A. Chernov     { "noon", NOON,0 },		/* 12:00:00 of today or tomorrow */
81b89321a5SAndrey A. Chernov     { "teatime", TEATIME,0 },	/* 16:00:00 of today or tomorrow */
82b89321a5SAndrey A. Chernov     { "am", AM,0 },		/* morning times for 0-12 clock */
83b89321a5SAndrey A. Chernov     { "pm", PM,0 },		/* evening times for 0-12 clock */
84b89321a5SAndrey A. Chernov     { "tomorrow", TOMORROW,0 },	/* execute 24 hours from time */
85b89321a5SAndrey A. Chernov     { "today", TODAY, 0 },	/* execute today - don't advance time */
86b89321a5SAndrey A. Chernov     { "now", NOW,0 },		/* opt prefix for PLUS */
87d78e98d2SNate Williams 
88b89321a5SAndrey A. Chernov     { "minute", MINUTES,0 },	/* minutes multiplier */
89b89321a5SAndrey A. Chernov     { "minutes", MINUTES,1 },	/* (pluralized) */
90b89321a5SAndrey A. Chernov     { "hour", HOURS,0 },	/* hours ... */
91b89321a5SAndrey A. Chernov     { "hours", HOURS,1 },	/* (pluralized) */
92b89321a5SAndrey A. Chernov     { "day", DAYS,0 },		/* days ... */
93b89321a5SAndrey A. Chernov     { "days", DAYS,1 },		/* (pluralized) */
94b89321a5SAndrey A. Chernov     { "week", WEEKS,0 },	/* week ... */
95b89321a5SAndrey A. Chernov     { "weeks", WEEKS,1 },	/* (pluralized) */
96b89321a5SAndrey A. Chernov     { "jan", JAN,0 },
97b89321a5SAndrey A. Chernov     { "feb", FEB,0 },
98b89321a5SAndrey A. Chernov     { "mar", MAR,0 },
99b89321a5SAndrey A. Chernov     { "apr", APR,0 },
100b89321a5SAndrey A. Chernov     { "may", MAY,0 },
101b89321a5SAndrey A. Chernov     { "jun", JUN,0 },
102b89321a5SAndrey A. Chernov     { "jul", JUL,0 },
103b89321a5SAndrey A. Chernov     { "aug", AUG,0 },
104b89321a5SAndrey A. Chernov     { "sep", SEP,0 },
105b89321a5SAndrey A. Chernov     { "oct", OCT,0 },
106b89321a5SAndrey A. Chernov     { "nov", NOV,0 },
107b89321a5SAndrey A. Chernov     { "dec", DEC,0 },
108b89321a5SAndrey A. Chernov     { "sunday", SUN, 0 },
109b89321a5SAndrey A. Chernov     { "sun", SUN, 0 },
110b89321a5SAndrey A. Chernov     { "monday", MON, 0 },
111b89321a5SAndrey A. Chernov     { "mon", MON, 0 },
112b89321a5SAndrey A. Chernov     { "tuesday", TUE, 0 },
113b89321a5SAndrey A. Chernov     { "tue", TUE, 0 },
114b89321a5SAndrey A. Chernov     { "wednesday", WED, 0 },
115b89321a5SAndrey A. Chernov     { "wed", WED, 0 },
116b89321a5SAndrey A. Chernov     { "thursday", THU, 0 },
117b89321a5SAndrey A. Chernov     { "thu", THU, 0 },
118b89321a5SAndrey A. Chernov     { "friday", FRI, 0 },
119b89321a5SAndrey A. Chernov     { "fri", FRI, 0 },
120b89321a5SAndrey A. Chernov     { "saturday", SAT, 0 },
121b89321a5SAndrey A. Chernov     { "sat", SAT, 0 },
122d78e98d2SNate Williams } ;
123d78e98d2SNate Williams 
124d78e98d2SNate Williams /* File scope variables */
125d78e98d2SNate Williams 
126d78e98d2SNate Williams static char **scp;	/* scanner - pointer at arglist */
127d78e98d2SNate Williams static char scc;	/* scanner - count of remaining arguments */
128d78e98d2SNate Williams static char *sct;	/* scanner - next char pointer in current argument */
129d78e98d2SNate Williams static int need;	/* scanner - need to advance to next argument */
130d78e98d2SNate Williams 
131d78e98d2SNate Williams static char *sc_token;	/* scanner - token buffer */
132d78e98d2SNate Williams static size_t sc_len;   /* scanner - lenght of token buffer */
133ddcf8022SAndrey A. Chernov static int sc_tokid;	/* scanner - token id */
134b89321a5SAndrey A. Chernov static int sc_tokplur;	/* scanner - is token plural? */
135d78e98d2SNate Williams 
13612d20ef9SPhilippe Charnier static char rcsid[] = "$Id: parsetime.c,v 1.9 1997/02/22 19:54:07 peter Exp $";
137d78e98d2SNate Williams 
138d78e98d2SNate Williams /* Local functions */
139d78e98d2SNate Williams 
140d78e98d2SNate Williams /*
141d78e98d2SNate Williams  * parse a token, checking if it's something special to us
142d78e98d2SNate Williams  */
143ddcf8022SAndrey A. Chernov static int
144ddcf8022SAndrey A. Chernov parse_token(char *arg)
145d78e98d2SNate Williams {
146d78e98d2SNate Williams     int i;
147d78e98d2SNate Williams 
148d78e98d2SNate Williams     for (i=0; i<(sizeof Specials/sizeof Specials[0]); i++)
149d78e98d2SNate Williams 	if (strcasecmp(Specials[i].name, arg) == 0) {
150b89321a5SAndrey A. Chernov 	    sc_tokplur = Specials[i].plural;
151d78e98d2SNate Williams 	    return sc_tokid = Specials[i].value;
152d78e98d2SNate Williams 	}
153d78e98d2SNate Williams 
154d78e98d2SNate Williams     /* not special - must be some random id */
155d78e98d2SNate Williams     return ID;
156d78e98d2SNate Williams } /* parse_token */
157d78e98d2SNate Williams 
158d78e98d2SNate Williams 
159d78e98d2SNate Williams /*
160d78e98d2SNate Williams  * init_scanner() sets up the scanner to eat arguments
161d78e98d2SNate Williams  */
162d78e98d2SNate Williams static void
163b89321a5SAndrey A. Chernov init_scanner(int argc, char **argv)
164d78e98d2SNate Williams {
165d78e98d2SNate Williams     scp = argv;
166d78e98d2SNate Williams     scc = argc;
167d78e98d2SNate Williams     need = 1;
168d78e98d2SNate Williams     sc_len = 1;
169ddcf8022SAndrey A. Chernov     while (argc-- > 0)
170ddcf8022SAndrey A. Chernov 	sc_len += strlen(*argv++);
171d78e98d2SNate Williams 
172b89321a5SAndrey A. Chernov     sc_token = (char *) mymalloc(sc_len);
173d78e98d2SNate Williams } /* init_scanner */
174d78e98d2SNate Williams 
175d78e98d2SNate Williams /*
176d78e98d2SNate Williams  * token() fetches a token from the input stream
177d78e98d2SNate Williams  */
178ddcf8022SAndrey A. Chernov static int
179d78e98d2SNate Williams token()
180d78e98d2SNate Williams {
181d78e98d2SNate Williams     int idx;
182d78e98d2SNate Williams 
183d78e98d2SNate Williams     while (1) {
184d78e98d2SNate Williams 	memset(sc_token, 0, sc_len);
185d78e98d2SNate Williams 	sc_tokid = EOF;
186b89321a5SAndrey A. Chernov 	sc_tokplur = 0;
187d78e98d2SNate Williams 	idx = 0;
188d78e98d2SNate Williams 
189b89321a5SAndrey A. Chernov 	/* if we need to read another argument, walk along the argument list;
190d78e98d2SNate Williams 	 * when we fall off the arglist, we'll just return EOF forever
191d78e98d2SNate Williams 	 */
192d78e98d2SNate Williams 	if (need) {
193d78e98d2SNate Williams 	    if (scc < 1)
194d78e98d2SNate Williams 		return sc_tokid;
195d78e98d2SNate Williams 	    sct = *scp;
196d78e98d2SNate Williams 	    scp++;
197d78e98d2SNate Williams 	    scc--;
198d78e98d2SNate Williams 	    need = 0;
199d78e98d2SNate Williams 	}
200b89321a5SAndrey A. Chernov 	/* eat whitespace now - if we walk off the end of the argument,
201d78e98d2SNate Williams 	 * we'll continue, which puts us up at the top of the while loop
202d78e98d2SNate Williams 	 * to fetch the next argument in
203d78e98d2SNate Williams 	 */
204d78e98d2SNate Williams 	while (isspace(*sct))
205d78e98d2SNate Williams 	    ++sct;
206d78e98d2SNate Williams 	if (!*sct) {
207d78e98d2SNate Williams 	    need = 1;
208d78e98d2SNate Williams 	    continue;
209d78e98d2SNate Williams 	}
210d78e98d2SNate Williams 
211b89321a5SAndrey A. Chernov 	/* preserve the first character of the new token
212d78e98d2SNate Williams 	 */
213d78e98d2SNate Williams 	sc_token[0] = *sct++;
214d78e98d2SNate Williams 
215b89321a5SAndrey A. Chernov 	/* then see what it is
216d78e98d2SNate Williams 	 */
217d78e98d2SNate Williams 	if (isdigit(sc_token[0])) {
218d78e98d2SNate Williams 	    while (isdigit(*sct))
219d78e98d2SNate Williams 		sc_token[++idx] = *sct++;
220d78e98d2SNate Williams 	    sc_token[++idx] = 0;
221d78e98d2SNate Williams 	    return sc_tokid = NUMBER;
222b89321a5SAndrey A. Chernov 	}
223b89321a5SAndrey A. Chernov 	else if (isalpha(sc_token[0])) {
224d78e98d2SNate Williams 	    while (isalpha(*sct))
225d78e98d2SNate Williams 		sc_token[++idx] = *sct++;
226d78e98d2SNate Williams 	    sc_token[++idx] = 0;
227d78e98d2SNate Williams 	    return parse_token(sc_token);
228d78e98d2SNate Williams 	}
229d78e98d2SNate Williams 	else if (sc_token[0] == ':' || sc_token[0] == '.')
230d78e98d2SNate Williams 	    return sc_tokid = DOT;
231d78e98d2SNate Williams 	else if (sc_token[0] == '+')
232d78e98d2SNate Williams 	    return sc_tokid = PLUS;
233b89321a5SAndrey A. Chernov 	else if (sc_token[0] == '/')
234d78e98d2SNate Williams 	    return sc_tokid = SLASH;
235d78e98d2SNate Williams 	else
236d78e98d2SNate Williams 	    return sc_tokid = JUNK;
237d78e98d2SNate Williams     } /* while (1) */
238d78e98d2SNate Williams } /* token */
239d78e98d2SNate Williams 
240d78e98d2SNate Williams 
241d78e98d2SNate Williams /*
242d78e98d2SNate Williams  * plonk() gives an appropriate error message if a token is incorrect
243d78e98d2SNate Williams  */
244d78e98d2SNate Williams static void
245b89321a5SAndrey A. Chernov plonk(int tok)
246d78e98d2SNate Williams {
247d78e98d2SNate Williams     panic((tok == EOF) ? "incomplete time"
248d78e98d2SNate Williams 		       : "garbled time");
249d78e98d2SNate Williams } /* plonk */
250d78e98d2SNate Williams 
251d78e98d2SNate Williams 
252d78e98d2SNate Williams /*
253d78e98d2SNate Williams  * expect() gets a token and dies most horribly if it's not the token we want
254d78e98d2SNate Williams  */
255d78e98d2SNate Williams static void
256ddcf8022SAndrey A. Chernov expect(int desired)
257d78e98d2SNate Williams {
258d78e98d2SNate Williams     if (token() != desired)
259d78e98d2SNate Williams 	plonk(sc_tokid);	/* and we die here... */
260d78e98d2SNate Williams } /* expect */
261d78e98d2SNate Williams 
262d78e98d2SNate Williams 
263d78e98d2SNate Williams /*
264d78e98d2SNate Williams  * dateadd() adds a number of minutes to a date.  It is extraordinarily
265d78e98d2SNate Williams  * stupid regarding day-of-month overflow, and will most likely not
266d78e98d2SNate Williams  * work properly
267d78e98d2SNate Williams  */
268d78e98d2SNate Williams static void
269b89321a5SAndrey A. Chernov dateadd(int minutes, struct tm *tm)
270d78e98d2SNate Williams {
271d78e98d2SNate Williams     /* increment days */
272d78e98d2SNate Williams 
273d78e98d2SNate Williams     while (minutes > 24*60) {
274d78e98d2SNate Williams 	minutes -= 24*60;
275d78e98d2SNate Williams 	tm->tm_mday++;
276d78e98d2SNate Williams     }
277d78e98d2SNate Williams 
278d78e98d2SNate Williams     /* increment hours */
279d78e98d2SNate Williams     while (minutes > 60) {
280d78e98d2SNate Williams 	minutes -= 60;
281d78e98d2SNate Williams 	tm->tm_hour++;
282d78e98d2SNate Williams 	if (tm->tm_hour > 23) {
283d78e98d2SNate Williams 	    tm->tm_mday++;
284d78e98d2SNate Williams 	    tm->tm_hour = 0;
285d78e98d2SNate Williams 	}
286d78e98d2SNate Williams     }
287d78e98d2SNate Williams 
288d78e98d2SNate Williams     /* increment minutes */
289d78e98d2SNate Williams     tm->tm_min += minutes;
290d78e98d2SNate Williams 
291d78e98d2SNate Williams     if (tm->tm_min > 59) {
292d78e98d2SNate Williams 	tm->tm_hour++;
293d78e98d2SNate Williams 	tm->tm_min -= 60;
294d78e98d2SNate Williams 
295d78e98d2SNate Williams 	if (tm->tm_hour > 23) {
296d78e98d2SNate Williams 	    tm->tm_mday++;
297d78e98d2SNate Williams 	    tm->tm_hour = 0;
298d78e98d2SNate Williams 	}
299d78e98d2SNate Williams     }
300d78e98d2SNate Williams } /* dateadd */
301d78e98d2SNate Williams 
302d78e98d2SNate Williams 
303d78e98d2SNate Williams /*
304d78e98d2SNate Williams  * plus() parses a now + time
305d78e98d2SNate Williams  *
306d78e98d2SNate Williams  *  at [NOW] PLUS NUMBER [MINUTES|HOURS|DAYS|WEEKS]
307d78e98d2SNate Williams  *
308d78e98d2SNate Williams  */
309d78e98d2SNate Williams static void
310b89321a5SAndrey A. Chernov plus(struct tm *tm)
311d78e98d2SNate Williams {
312d78e98d2SNate Williams     int delay;
313b89321a5SAndrey A. Chernov     int expectplur;
314d78e98d2SNate Williams 
315d78e98d2SNate Williams     expect(NUMBER);
316d78e98d2SNate Williams 
317d78e98d2SNate Williams     delay = atoi(sc_token);
318b89321a5SAndrey A. Chernov     expectplur = (delay != 1) ? 1 : 0;
319d78e98d2SNate Williams 
320d78e98d2SNate Williams     switch (token()) {
321d78e98d2SNate Williams     case WEEKS:
322d78e98d2SNate Williams 	    delay *= 7;
323d78e98d2SNate Williams     case DAYS:
324d78e98d2SNate Williams 	    delay *= 24;
325d78e98d2SNate Williams     case HOURS:
326d78e98d2SNate Williams 	    delay *= 60;
327d78e98d2SNate Williams     case MINUTES:
328b89321a5SAndrey A. Chernov 	    if (expectplur != sc_tokplur)
32912d20ef9SPhilippe Charnier 		warnx("pluralization is wrong");
330d78e98d2SNate Williams 	    dateadd(delay, tm);
331d78e98d2SNate Williams 	    return;
332d78e98d2SNate Williams     }
333d78e98d2SNate Williams     plonk(sc_tokid);
334d78e98d2SNate Williams } /* plus */
335d78e98d2SNate Williams 
336d78e98d2SNate Williams 
337d78e98d2SNate Williams /*
338d78e98d2SNate Williams  * tod() computes the time of day
339d78e98d2SNate Williams  *     [NUMBER [DOT NUMBER] [AM|PM]]
340d78e98d2SNate Williams  */
341d78e98d2SNate Williams static void
342b89321a5SAndrey A. Chernov tod(struct tm *tm)
343d78e98d2SNate Williams {
344d78e98d2SNate Williams     int hour, minute = 0;
345d78e98d2SNate Williams     int tlen;
346d78e98d2SNate Williams 
347d78e98d2SNate Williams     hour = atoi(sc_token);
348d78e98d2SNate Williams     tlen = strlen(sc_token);
349d78e98d2SNate Williams 
350b89321a5SAndrey A. Chernov     /* first pick out the time of day - if it's 4 digits, we assume
351d78e98d2SNate Williams      * a HHMM time, otherwise it's HH DOT MM time
352d78e98d2SNate Williams      */
353d78e98d2SNate Williams     if (token() == DOT) {
354d78e98d2SNate Williams 	expect(NUMBER);
355d78e98d2SNate Williams 	minute = atoi(sc_token);
356d78e98d2SNate Williams 	if (minute > 59)
357d78e98d2SNate Williams 	    panic("garbled time");
358d78e98d2SNate Williams 	token();
359b89321a5SAndrey A. Chernov     }
360b89321a5SAndrey A. Chernov     else if (tlen == 4) {
361d78e98d2SNate Williams 	minute = hour%100;
362d78e98d2SNate Williams 	if (minute > 59)
363d78e98d2SNate Williams 	    panic("garbeld time");
364d78e98d2SNate Williams 	hour = hour/100;
365d78e98d2SNate Williams     }
366d78e98d2SNate Williams 
367b89321a5SAndrey A. Chernov     /* check if an AM or PM specifier was given
368d78e98d2SNate Williams      */
369d78e98d2SNate Williams     if (sc_tokid == AM || sc_tokid == PM) {
370d78e98d2SNate Williams 	if (hour > 12)
371d78e98d2SNate Williams 	    panic("garbled time");
372d78e98d2SNate Williams 
3739b2ea11cSJohn Polstra 	if (sc_tokid == PM) {
3749b2ea11cSJohn Polstra 	    if (hour != 12)	/* 12:xx PM is 12:xx, not 24:xx */
375d78e98d2SNate Williams 			hour += 12;
3769b2ea11cSJohn Polstra 	} else {
3779b2ea11cSJohn Polstra 	    if (hour == 12)	/* 12:xx AM is 00:xx, not 12:xx */
37812d20ef9SPhilippe Charnier 			hour = 0;
3799b2ea11cSJohn Polstra 	}
380d78e98d2SNate Williams 	token();
381b89321a5SAndrey A. Chernov     }
382b89321a5SAndrey A. Chernov     else if (hour > 23)
383d78e98d2SNate Williams 	panic("garbled time");
384d78e98d2SNate Williams 
385b89321a5SAndrey A. Chernov     /* if we specify an absolute time, we don't want to bump the day even
386d78e98d2SNate Williams      * if we've gone past that time - but if we're specifying a time plus
387d78e98d2SNate Williams      * a relative offset, it's okay to bump things
388d78e98d2SNate Williams      */
389b89321a5SAndrey A. Chernov     if ((sc_tokid == EOF || sc_tokid == PLUS) && tm->tm_hour > hour) {
390d78e98d2SNate Williams 	tm->tm_mday++;
391b89321a5SAndrey A. Chernov 	tm->tm_wday++;
392b89321a5SAndrey A. Chernov     }
393d78e98d2SNate Williams 
394d78e98d2SNate Williams     tm->tm_hour = hour;
395d78e98d2SNate Williams     tm->tm_min = minute;
396d78e98d2SNate Williams     if (tm->tm_hour == 24) {
397d78e98d2SNate Williams 	tm->tm_hour = 0;
398d78e98d2SNate Williams 	tm->tm_mday++;
399d78e98d2SNate Williams     }
400d78e98d2SNate Williams } /* tod */
401d78e98d2SNate Williams 
402d78e98d2SNate Williams 
403d78e98d2SNate Williams /*
404d78e98d2SNate Williams  * assign_date() assigns a date, wrapping to next year if needed
405d78e98d2SNate Williams  */
406d78e98d2SNate Williams static void
407b89321a5SAndrey A. Chernov assign_date(struct tm *tm, long mday, long mon, long year)
408d78e98d2SNate Williams {
409d78e98d2SNate Williams     if (year > 99) {
410d78e98d2SNate Williams 	if (year > 1899)
411d78e98d2SNate Williams 	    year -= 1900;
412d78e98d2SNate Williams 	else
413d78e98d2SNate Williams 	    panic("garbled time");
414d78e98d2SNate Williams     }
415d78e98d2SNate Williams 
416d78e98d2SNate Williams     if (year < 0 &&
417d78e98d2SNate Williams 	(tm->tm_mon > mon ||(tm->tm_mon == mon && tm->tm_mday > mday)))
418d78e98d2SNate Williams 	year = tm->tm_year + 1;
419d78e98d2SNate Williams 
420d78e98d2SNate Williams     tm->tm_mday = mday;
421d78e98d2SNate Williams     tm->tm_mon = mon;
422d78e98d2SNate Williams 
423d78e98d2SNate Williams     if (year >= 0)
424d78e98d2SNate Williams 	tm->tm_year = year;
425d78e98d2SNate Williams } /* assign_date */
426d78e98d2SNate Williams 
427d78e98d2SNate Williams 
428d78e98d2SNate Williams /*
429d78e98d2SNate Williams  * month() picks apart a month specification
430d78e98d2SNate Williams  *
431d78e98d2SNate Williams  *  /[<month> NUMBER [NUMBER]]           \
432d78e98d2SNate Williams  *  |[TOMORROW]                          |
433b89321a5SAndrey A. Chernov  *  |[DAY OF WEEK]                       |
434d78e98d2SNate Williams  *  |NUMBER [SLASH NUMBER [SLASH NUMBER]]|
435d78e98d2SNate Williams  *  \PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS/
436d78e98d2SNate Williams  */
437d78e98d2SNate Williams static void
438b89321a5SAndrey A. Chernov month(struct tm *tm)
439d78e98d2SNate Williams {
440d78e98d2SNate Williams     long year= (-1);
441b89321a5SAndrey A. Chernov     long mday, wday, mon;
442d78e98d2SNate Williams     int tlen;
443d78e98d2SNate Williams 
444d78e98d2SNate Williams     switch (sc_tokid) {
445d78e98d2SNate Williams     case PLUS:
446d78e98d2SNate Williams 	    plus(tm);
447d78e98d2SNate Williams 	    break;
448d78e98d2SNate Williams 
449d78e98d2SNate Williams     case TOMORROW:
450d78e98d2SNate Williams 	    /* do something tomorrow */
451d78e98d2SNate Williams 	    tm->tm_mday ++;
452b89321a5SAndrey A. Chernov 	    tm->tm_wday ++;
453d78e98d2SNate Williams     case TODAY:	/* force ourselves to stay in today - no further processing */
454d78e98d2SNate Williams 	    token();
455d78e98d2SNate Williams 	    break;
456d78e98d2SNate Williams 
457d78e98d2SNate Williams     case JAN: case FEB: case MAR: case APR: case MAY: case JUN:
458d78e98d2SNate Williams     case JUL: case AUG: case SEP: case OCT: case NOV: case DEC:
459b89321a5SAndrey A. Chernov 	    /* do month mday [year]
460d78e98d2SNate Williams 	     */
461d78e98d2SNate Williams 	    mon = (sc_tokid-JAN);
462d78e98d2SNate Williams 	    expect(NUMBER);
46368abb9f9SBruce Evans 	    mday = atol(sc_token);
464d78e98d2SNate Williams 	    if (token() == NUMBER) {
465d78e98d2SNate Williams 		year = atol(sc_token);
466d78e98d2SNate Williams 		token();
467d78e98d2SNate Williams 	    }
468d78e98d2SNate Williams 	    assign_date(tm, mday, mon, year);
469d78e98d2SNate Williams 	    break;
470d78e98d2SNate Williams 
471b89321a5SAndrey A. Chernov     case SUN: case MON: case TUE:
472b89321a5SAndrey A. Chernov     case WED: case THU: case FRI:
473b89321a5SAndrey A. Chernov     case SAT:
474b89321a5SAndrey A. Chernov 	    /* do a particular day of the week
475b89321a5SAndrey A. Chernov 	     */
476b89321a5SAndrey A. Chernov 	    wday = (sc_tokid-SUN);
477b89321a5SAndrey A. Chernov 
478b89321a5SAndrey A. Chernov 	    mday = tm->tm_mday;
479b89321a5SAndrey A. Chernov 
480b89321a5SAndrey A. Chernov 	    /* if this day is < today, then roll to next week
481b89321a5SAndrey A. Chernov 	     */
482b89321a5SAndrey A. Chernov 	    if (wday < tm->tm_wday)
483b89321a5SAndrey A. Chernov 		mday += 7 - (tm->tm_wday - wday);
484b89321a5SAndrey A. Chernov 	    else
485b89321a5SAndrey A. Chernov 		mday += (wday - tm->tm_wday);
486b89321a5SAndrey A. Chernov 
487b89321a5SAndrey A. Chernov 	    tm->tm_wday = wday;
488b89321a5SAndrey A. Chernov 
489b89321a5SAndrey A. Chernov 	    assign_date(tm, mday, tm->tm_mon, tm->tm_year);
490b89321a5SAndrey A. Chernov 	    break;
491b89321a5SAndrey A. Chernov 
492d78e98d2SNate Williams     case NUMBER:
493b89321a5SAndrey A. Chernov 	    /* get numeric MMDDYY, mm/dd/yy, or dd.mm.yy
494d78e98d2SNate Williams 	     */
495d78e98d2SNate Williams 	    tlen = strlen(sc_token);
496d78e98d2SNate Williams 	    mon = atol(sc_token);
497d78e98d2SNate Williams 	    token();
498d78e98d2SNate Williams 
499d78e98d2SNate Williams 	    if (sc_tokid == SLASH || sc_tokid == DOT) {
500ddcf8022SAndrey A. Chernov 		int sep;
501d78e98d2SNate Williams 
502d78e98d2SNate Williams 		sep = sc_tokid;
503d78e98d2SNate Williams 		expect(NUMBER);
504d78e98d2SNate Williams 		mday = atol(sc_token);
505d78e98d2SNate Williams 		if (token() == sep) {
506d78e98d2SNate Williams 		    expect(NUMBER);
507d78e98d2SNate Williams 		    year = atol(sc_token);
508d78e98d2SNate Williams 		    token();
509d78e98d2SNate Williams 		}
510d78e98d2SNate Williams 
511b89321a5SAndrey A. Chernov 		/* flip months and days for european timing
512d78e98d2SNate Williams 		 */
513d78e98d2SNate Williams 		if (sep == DOT) {
514d78e98d2SNate Williams 		    int x = mday;
515d78e98d2SNate Williams 		    mday = mon;
516d78e98d2SNate Williams 		    mon = x;
517d78e98d2SNate Williams 		}
518b89321a5SAndrey A. Chernov 	    }
519b89321a5SAndrey A. Chernov 	    else if (tlen == 6 || tlen == 8) {
520d78e98d2SNate Williams 		if (tlen == 8) {
521d78e98d2SNate Williams 		    year = (mon % 10000) - 1900;
522d78e98d2SNate Williams 		    mon /= 10000;
523b89321a5SAndrey A. Chernov 		}
524b89321a5SAndrey A. Chernov 		else {
525d78e98d2SNate Williams 		    year = mon % 100;
526d78e98d2SNate Williams 		    mon /= 100;
527d78e98d2SNate Williams 		}
528d78e98d2SNate Williams 		mday = mon % 100;
529d78e98d2SNate Williams 		mon /= 100;
530b89321a5SAndrey A. Chernov 	    }
531b89321a5SAndrey A. Chernov 	    else
532d78e98d2SNate Williams 		panic("garbled time");
533d78e98d2SNate Williams 
534d78e98d2SNate Williams 	    mon--;
535d78e98d2SNate Williams 	    if (mon < 0 || mon > 11 || mday < 1 || mday > 31)
536d78e98d2SNate Williams 		panic("garbled time");
537d78e98d2SNate Williams 
538d78e98d2SNate Williams 	    assign_date(tm, mday, mon, year);
539d78e98d2SNate Williams 	    break;
540d78e98d2SNate Williams     } /* case */
541d78e98d2SNate Williams } /* month */
542d78e98d2SNate Williams 
543d78e98d2SNate Williams 
544d78e98d2SNate Williams /* Global functions */
545d78e98d2SNate Williams 
546d78e98d2SNate Williams time_t
547b89321a5SAndrey A. Chernov parsetime(int argc, char **argv)
548d78e98d2SNate Williams {
549b89321a5SAndrey A. Chernov /* Do the argument parsing, die if necessary, and return the time the job
550d78e98d2SNate Williams  * should be run.
551d78e98d2SNate Williams  */
552d78e98d2SNate Williams     time_t nowtimer, runtimer;
553d78e98d2SNate Williams     struct tm nowtime, runtime;
554d78e98d2SNate Williams     int hr = 0;
555d78e98d2SNate Williams     /* this MUST be initialized to zero for midnight/noon/teatime */
556d78e98d2SNate Williams 
557d78e98d2SNate Williams     nowtimer = time(NULL);
558d78e98d2SNate Williams     nowtime = *localtime(&nowtimer);
559d78e98d2SNate Williams 
560d78e98d2SNate Williams     runtime = nowtime;
561d78e98d2SNate Williams     runtime.tm_sec = 0;
562d78e98d2SNate Williams     runtime.tm_isdst = 0;
563d78e98d2SNate Williams 
564d78e98d2SNate Williams     if (argc <= optind)
565d78e98d2SNate Williams 	usage();
566d78e98d2SNate Williams 
567d78e98d2SNate Williams     init_scanner(argc-optind, argv+optind);
568d78e98d2SNate Williams 
569d78e98d2SNate Williams     switch (token()) {
570d78e98d2SNate Williams     case NOW:	/* now is optional prefix for PLUS tree */
571d78e98d2SNate Williams 	    expect(PLUS);
572d78e98d2SNate Williams     case PLUS:
573d78e98d2SNate Williams 	    plus(&runtime);
574d78e98d2SNate Williams 	    break;
575d78e98d2SNate Williams 
576d78e98d2SNate Williams     case NUMBER:
577d78e98d2SNate Williams 	    tod(&runtime);
578d78e98d2SNate Williams 	    month(&runtime);
579d78e98d2SNate Williams 	    break;
580d78e98d2SNate Williams 
581b89321a5SAndrey A. Chernov 	    /* evil coding for TEATIME|NOON|MIDNIGHT - we've initialised
582d78e98d2SNate Williams 	     * hr to zero up above, then fall into this case in such a
583d78e98d2SNate Williams 	     * way so we add +12 +4 hours to it for teatime, +12 hours
584d78e98d2SNate Williams 	     * to it for noon, and nothing at all for midnight, then
585d78e98d2SNate Williams 	     * set our runtime to that hour before leaping into the
586d78e98d2SNate Williams 	     * month scanner
587d78e98d2SNate Williams 	     */
588d78e98d2SNate Williams     case TEATIME:
589d78e98d2SNate Williams 	    hr += 4;
590d78e98d2SNate Williams     case NOON:
591d78e98d2SNate Williams 	    hr += 12;
592d78e98d2SNate Williams     case MIDNIGHT:
593b89321a5SAndrey A. Chernov 	    if (runtime.tm_hour >= hr) {
594d78e98d2SNate Williams 		runtime.tm_mday++;
595b89321a5SAndrey A. Chernov 		runtime.tm_wday++;
596b89321a5SAndrey A. Chernov 	    }
597d78e98d2SNate Williams 	    runtime.tm_hour = hr;
598d78e98d2SNate Williams 	    runtime.tm_min = 0;
599d78e98d2SNate Williams 	    token();
600d78e98d2SNate Williams 	    /* fall through to month setting */
601d78e98d2SNate Williams     default:
602d78e98d2SNate Williams 	    month(&runtime);
603d78e98d2SNate Williams 	    break;
604d78e98d2SNate Williams     } /* ugly case statement */
605d78e98d2SNate Williams     expect(EOF);
606d78e98d2SNate Williams 
607b89321a5SAndrey A. Chernov     /* adjust for daylight savings time
608d78e98d2SNate Williams      */
609d78e98d2SNate Williams     runtime.tm_isdst = -1;
610d78e98d2SNate Williams     runtimer = mktime(&runtime);
611d78e98d2SNate Williams     if (runtime.tm_isdst > 0) {
612d78e98d2SNate Williams 	runtimer -= 3600;
613d78e98d2SNate Williams 	runtimer = mktime(&runtime);
614d78e98d2SNate Williams     }
615d78e98d2SNate Williams 
616d78e98d2SNate Williams     if (runtimer < 0)
617d78e98d2SNate Williams 	panic("garbled time");
618d78e98d2SNate Williams 
619d78e98d2SNate Williams     if (nowtimer > runtimer)
620d78e98d2SNate Williams 	panic("Trying to travel back in time");
621d78e98d2SNate Williams 
622d78e98d2SNate Williams     return runtimer;
623d78e98d2SNate Williams } /* parsetime */
624