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