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