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