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