xref: /freebsd/usr.bin/at/parsetime.c (revision 4d846d260e2b9a3d4d0a701462568268cbfe7a5b)
11de7b4b8SPedro F. Giffuni /*-
2d78e98d2SNate Williams  *  parsetime.c - parse time for at(1)
31de7b4b8SPedro F. Giffuni  *
4*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
51de7b4b8SPedro F. Giffuni  *
6b89321a5SAndrey A. Chernov  *  Copyright (C) 1993, 1994  Thomas Koenig
7d78e98d2SNate Williams  *
881c8c7a4SPhilippe Charnier  *  modifications for English-language times
9d78e98d2SNate Williams  *  Copyright (C) 1993  David Parsons
10d78e98d2SNate Williams  *
11d78e98d2SNate Williams  * Redistribution and use in source and binary forms, with or without
12d78e98d2SNate Williams  * modification, are permitted provided that the following conditions
13d78e98d2SNate Williams  * are met:
14d78e98d2SNate Williams  * 1. Redistributions of source code must retain the above copyright
15d78e98d2SNate Williams  *    notice, this list of conditions and the following disclaimer.
16d78e98d2SNate Williams  * 2. The name of the author(s) may not be used to endorse or promote
17d78e98d2SNate Williams  *    products derived from this software without specific prior written
18d78e98d2SNate Williams  *    permission.
19d78e98d2SNate Williams  *
20d78e98d2SNate Williams  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
21d78e98d2SNate Williams  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22d78e98d2SNate Williams  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23ddcf8022SAndrey A. Chernov  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
24d78e98d2SNate Williams  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25d78e98d2SNate Williams  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26d78e98d2SNate Williams  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27d78e98d2SNate Williams  * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28d78e98d2SNate Williams  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29d78e98d2SNate Williams  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30d78e98d2SNate Williams  *
31d78e98d2SNate Williams  *  at [NOW] PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS
32d78e98d2SNate Williams  *     /NUMBER [DOT NUMBER] [AM|PM]\ /[MONTH NUMBER [NUMBER]]             \
33d78e98d2SNate Williams  *     |NOON                       | |[TOMORROW]                          |
34b89321a5SAndrey A. Chernov  *     |MIDNIGHT                   | |[DAY OF WEEK]                       |
35b89321a5SAndrey A. Chernov  *     \TEATIME                    / |NUMBER [SLASH NUMBER [SLASH NUMBER]]|
36b89321a5SAndrey A. Chernov  *                                   \PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS/
37d78e98d2SNate Williams  */
38d78e98d2SNate Williams 
3951e2220cSMark Murray #include <sys/cdefs.h>
4051e2220cSMark Murray __FBSDID("$FreeBSD$");
4181c8c7a4SPhilippe Charnier 
42d78e98d2SNate Williams /* System Headers */
43d78e98d2SNate Williams 
44d78e98d2SNate Williams #include <sys/types.h>
4581c8c7a4SPhilippe Charnier #include <ctype.h>
46e5e5da15SPhilippe Charnier #include <err.h>
47d78e98d2SNate Williams #include <errno.h>
48d78e98d2SNate Williams #include <stdio.h>
49d78e98d2SNate Williams #include <stdlib.h>
50d78e98d2SNate Williams #include <string.h>
51d78e98d2SNate Williams #include <time.h>
52d78e98d2SNate Williams #include <unistd.h>
53b89321a5SAndrey A. Chernov #ifndef __FreeBSD__
54b89321a5SAndrey A. Chernov #include <getopt.h>
55b89321a5SAndrey A. Chernov #endif
56d78e98d2SNate Williams 
57d78e98d2SNate Williams /* Local headers */
58d78e98d2SNate Williams 
59d78e98d2SNate Williams #include "at.h"
60d78e98d2SNate Williams #include "panic.h"
613ce6c357SMark Murray #include "parsetime.h"
62d78e98d2SNate Williams 
63d78e98d2SNate Williams 
64d78e98d2SNate Williams /* Structures and unions */
65d78e98d2SNate Williams 
66ddcf8022SAndrey A. Chernov enum {	/* symbols */
67d78e98d2SNate Williams     MIDNIGHT, NOON, TEATIME,
68d78e98d2SNate Williams     PM, AM, TOMORROW, TODAY, NOW,
69b6c989ffSNick Sayer     MINUTES, HOURS, DAYS, WEEKS, MONTHS, YEARS,
7093a2fe45SMartin Cracauer     NUMBER, PLUS, MINUS, DOT, SLASH, ID, JUNK,
71d78e98d2SNate Williams     JAN, FEB, MAR, APR, MAY, JUN,
72b89321a5SAndrey A. Chernov     JUL, AUG, SEP, OCT, NOV, DEC,
73b89321a5SAndrey A. Chernov     SUN, MON, TUE, WED, THU, FRI, SAT
74d78e98d2SNate Williams     };
75d78e98d2SNate Williams 
76b89321a5SAndrey A. Chernov /* parse translation table - table driven parsers can be your FRIEND!
77d78e98d2SNate Williams  */
78f64efe8bSEd Schouten static const struct {
793ce6c357SMark Murray     const char *name;	/* token name */
80ddcf8022SAndrey A. Chernov     int value;	/* token id */
81b89321a5SAndrey A. Chernov     int plural;	/* is this plural? */
82d78e98d2SNate Williams } Specials[] = {
83b89321a5SAndrey A. Chernov     { "midnight", MIDNIGHT,0 },	/* 00:00:00 of today or tomorrow */
84b89321a5SAndrey A. Chernov     { "noon", NOON,0 },		/* 12:00:00 of today or tomorrow */
85b89321a5SAndrey A. Chernov     { "teatime", TEATIME,0 },	/* 16:00:00 of today or tomorrow */
86b89321a5SAndrey A. Chernov     { "am", AM,0 },		/* morning times for 0-12 clock */
87b89321a5SAndrey A. Chernov     { "pm", PM,0 },		/* evening times for 0-12 clock */
88b89321a5SAndrey A. Chernov     { "tomorrow", TOMORROW,0 },	/* execute 24 hours from time */
89b89321a5SAndrey A. Chernov     { "today", TODAY, 0 },	/* execute today - don't advance time */
90b89321a5SAndrey A. Chernov     { "now", NOW,0 },		/* opt prefix for PLUS */
91d78e98d2SNate Williams 
92b89321a5SAndrey A. Chernov     { "minute", MINUTES,0 },	/* minutes multiplier */
93b89321a5SAndrey A. Chernov     { "minutes", MINUTES,1 },	/* (pluralized) */
94b89321a5SAndrey A. Chernov     { "hour", HOURS,0 },	/* hours ... */
95b89321a5SAndrey A. Chernov     { "hours", HOURS,1 },	/* (pluralized) */
96b89321a5SAndrey A. Chernov     { "day", DAYS,0 },		/* days ... */
97b89321a5SAndrey A. Chernov     { "days", DAYS,1 },		/* (pluralized) */
98b89321a5SAndrey A. Chernov     { "week", WEEKS,0 },	/* week ... */
99b89321a5SAndrey A. Chernov     { "weeks", WEEKS,1 },	/* (pluralized) */
100b6c989ffSNick Sayer     { "month", MONTHS,0 },	/* month ... */
101b6c989ffSNick Sayer     { "months", MONTHS,1 },	/* (pluralized) */
102b6c989ffSNick Sayer     { "year", YEARS,0 },	/* year ... */
103b6c989ffSNick Sayer     { "years", YEARS,1 },	/* (pluralized) */
104b89321a5SAndrey A. Chernov     { "jan", JAN,0 },
105b89321a5SAndrey A. Chernov     { "feb", FEB,0 },
106b89321a5SAndrey A. Chernov     { "mar", MAR,0 },
107b89321a5SAndrey A. Chernov     { "apr", APR,0 },
108b89321a5SAndrey A. Chernov     { "may", MAY,0 },
109b89321a5SAndrey A. Chernov     { "jun", JUN,0 },
110b89321a5SAndrey A. Chernov     { "jul", JUL,0 },
111b89321a5SAndrey A. Chernov     { "aug", AUG,0 },
112b89321a5SAndrey A. Chernov     { "sep", SEP,0 },
113b89321a5SAndrey A. Chernov     { "oct", OCT,0 },
114b89321a5SAndrey A. Chernov     { "nov", NOV,0 },
115b89321a5SAndrey A. Chernov     { "dec", DEC,0 },
116b9dd99f2SDag-Erling Smørgrav     { "january", JAN,0 },
117b9dd99f2SDag-Erling Smørgrav     { "february", FEB,0 },
118b9dd99f2SDag-Erling Smørgrav     { "march", MAR,0 },
119b9dd99f2SDag-Erling Smørgrav     { "april", APR,0 },
120b9dd99f2SDag-Erling Smørgrav     { "may", MAY,0 },
121b9dd99f2SDag-Erling Smørgrav     { "june", JUN,0 },
122b9dd99f2SDag-Erling Smørgrav     { "july", JUL,0 },
123b9dd99f2SDag-Erling Smørgrav     { "august", AUG,0 },
124b9dd99f2SDag-Erling Smørgrav     { "september", SEP,0 },
125b9dd99f2SDag-Erling Smørgrav     { "october", OCT,0 },
126b9dd99f2SDag-Erling Smørgrav     { "november", NOV,0 },
127b9dd99f2SDag-Erling Smørgrav     { "december", DEC,0 },
128b89321a5SAndrey A. Chernov     { "sunday", SUN, 0 },
129b89321a5SAndrey A. Chernov     { "sun", SUN, 0 },
130b89321a5SAndrey A. Chernov     { "monday", MON, 0 },
131b89321a5SAndrey A. Chernov     { "mon", MON, 0 },
132b89321a5SAndrey A. Chernov     { "tuesday", TUE, 0 },
133b89321a5SAndrey A. Chernov     { "tue", TUE, 0 },
134b89321a5SAndrey A. Chernov     { "wednesday", WED, 0 },
135b89321a5SAndrey A. Chernov     { "wed", WED, 0 },
136b89321a5SAndrey A. Chernov     { "thursday", THU, 0 },
137b89321a5SAndrey A. Chernov     { "thu", THU, 0 },
138b89321a5SAndrey A. Chernov     { "friday", FRI, 0 },
139b89321a5SAndrey A. Chernov     { "fri", FRI, 0 },
140b89321a5SAndrey A. Chernov     { "saturday", SAT, 0 },
141b89321a5SAndrey A. Chernov     { "sat", SAT, 0 },
142d78e98d2SNate Williams } ;
143d78e98d2SNate Williams 
144d78e98d2SNate Williams /* File scope variables */
145d78e98d2SNate Williams 
146d78e98d2SNate Williams static char **scp;	/* scanner - pointer at arglist */
147d78e98d2SNate Williams static char scc;	/* scanner - count of remaining arguments */
148d78e98d2SNate Williams static char *sct;	/* scanner - next char pointer in current argument */
149d78e98d2SNate Williams static int need;	/* scanner - need to advance to next argument */
150d78e98d2SNate Williams 
151d78e98d2SNate Williams static char *sc_token;	/* scanner - token buffer */
15281c8c7a4SPhilippe Charnier static size_t sc_len;   /* scanner - length of token buffer */
153ddcf8022SAndrey A. Chernov static int sc_tokid;	/* scanner - token id */
154b89321a5SAndrey A. Chernov static int sc_tokplur;	/* scanner - is token plural? */
155d78e98d2SNate Williams 
156d78e98d2SNate Williams /* Local functions */
157d78e98d2SNate Williams 
158d78e98d2SNate Williams /*
159d78e98d2SNate Williams  * parse a token, checking if it's something special to us
160d78e98d2SNate Williams  */
161ddcf8022SAndrey A. Chernov static int
162ddcf8022SAndrey A. Chernov parse_token(char *arg)
163d78e98d2SNate Williams {
1643ce6c357SMark Murray     size_t i;
165d78e98d2SNate Williams 
166d78e98d2SNate Williams     for (i=0; i<(sizeof Specials/sizeof Specials[0]); i++)
167d78e98d2SNate Williams 	if (strcasecmp(Specials[i].name, arg) == 0) {
168b89321a5SAndrey A. Chernov 	    sc_tokplur = Specials[i].plural;
169d78e98d2SNate Williams 	    return sc_tokid = Specials[i].value;
170d78e98d2SNate Williams 	}
171d78e98d2SNate Williams 
172d78e98d2SNate Williams     /* not special - must be some random id */
173d78e98d2SNate Williams     return ID;
174d78e98d2SNate Williams } /* parse_token */
175d78e98d2SNate Williams 
176d78e98d2SNate Williams 
177d78e98d2SNate Williams /*
178d78e98d2SNate Williams  * init_scanner() sets up the scanner to eat arguments
179d78e98d2SNate Williams  */
180d78e98d2SNate Williams static void
181b89321a5SAndrey A. Chernov init_scanner(int argc, char **argv)
182d78e98d2SNate Williams {
183d78e98d2SNate Williams     scp = argv;
184d78e98d2SNate Williams     scc = argc;
185d78e98d2SNate Williams     need = 1;
186d78e98d2SNate Williams     sc_len = 1;
187ddcf8022SAndrey A. Chernov     while (argc-- > 0)
188ddcf8022SAndrey A. Chernov 	sc_len += strlen(*argv++);
189d78e98d2SNate Williams 
190a9be9be8SDavid E. O'Brien     if ((sc_token = malloc(sc_len)) == NULL)
191a9be9be8SDavid E. O'Brien 	errx(EXIT_FAILURE, "virtual memory exhausted");
192d78e98d2SNate Williams } /* init_scanner */
193d78e98d2SNate Williams 
194d78e98d2SNate Williams /*
195d78e98d2SNate Williams  * token() fetches a token from the input stream
196d78e98d2SNate Williams  */
197ddcf8022SAndrey A. Chernov static int
1983ce6c357SMark Murray token(void)
199d78e98d2SNate Williams {
200d78e98d2SNate Williams     int idx;
201d78e98d2SNate Williams 
202d78e98d2SNate Williams     while (1) {
203d78e98d2SNate Williams 	memset(sc_token, 0, sc_len);
204d78e98d2SNate Williams 	sc_tokid = EOF;
205b89321a5SAndrey A. Chernov 	sc_tokplur = 0;
206d78e98d2SNate Williams 	idx = 0;
207d78e98d2SNate Williams 
208b89321a5SAndrey A. Chernov 	/* if we need to read another argument, walk along the argument list;
209d78e98d2SNate Williams 	 * when we fall off the arglist, we'll just return EOF forever
210d78e98d2SNate Williams 	 */
211d78e98d2SNate Williams 	if (need) {
212d78e98d2SNate Williams 	    if (scc < 1)
213d78e98d2SNate Williams 		return sc_tokid;
214d78e98d2SNate Williams 	    sct = *scp;
215d78e98d2SNate Williams 	    scp++;
216d78e98d2SNate Williams 	    scc--;
217d78e98d2SNate Williams 	    need = 0;
218d78e98d2SNate Williams 	}
219b89321a5SAndrey A. Chernov 	/* eat whitespace now - if we walk off the end of the argument,
220d78e98d2SNate Williams 	 * we'll continue, which puts us up at the top of the while loop
221d78e98d2SNate Williams 	 * to fetch the next argument in
222d78e98d2SNate Williams 	 */
223d78e98d2SNate Williams 	while (isspace(*sct))
224d78e98d2SNate Williams 	    ++sct;
225d78e98d2SNate Williams 	if (!*sct) {
226d78e98d2SNate Williams 	    need = 1;
227d78e98d2SNate Williams 	    continue;
228d78e98d2SNate Williams 	}
229d78e98d2SNate Williams 
230b89321a5SAndrey A. Chernov 	/* preserve the first character of the new token
231d78e98d2SNate Williams 	 */
232d78e98d2SNate Williams 	sc_token[0] = *sct++;
233d78e98d2SNate Williams 
234b89321a5SAndrey A. Chernov 	/* then see what it is
235d78e98d2SNate Williams 	 */
236d78e98d2SNate Williams 	if (isdigit(sc_token[0])) {
237d78e98d2SNate Williams 	    while (isdigit(*sct))
238d78e98d2SNate Williams 		sc_token[++idx] = *sct++;
239d78e98d2SNate Williams 	    sc_token[++idx] = 0;
240d78e98d2SNate Williams 	    return sc_tokid = NUMBER;
241b89321a5SAndrey A. Chernov 	}
242b89321a5SAndrey A. Chernov 	else if (isalpha(sc_token[0])) {
243d78e98d2SNate Williams 	    while (isalpha(*sct))
244d78e98d2SNate Williams 		sc_token[++idx] = *sct++;
245d78e98d2SNate Williams 	    sc_token[++idx] = 0;
246d78e98d2SNate Williams 	    return parse_token(sc_token);
247d78e98d2SNate Williams 	}
248d78e98d2SNate Williams 	else if (sc_token[0] == ':' || sc_token[0] == '.')
249d78e98d2SNate Williams 	    return sc_tokid = DOT;
250d78e98d2SNate Williams 	else if (sc_token[0] == '+')
251d78e98d2SNate Williams 	    return sc_tokid = PLUS;
25293a2fe45SMartin Cracauer 	else if (sc_token[0] == '-')
25393a2fe45SMartin Cracauer 	    return sc_tokid = MINUS;
254b89321a5SAndrey A. Chernov 	else if (sc_token[0] == '/')
255d78e98d2SNate Williams 	    return sc_tokid = SLASH;
256d78e98d2SNate Williams 	else
257d78e98d2SNate Williams 	    return sc_tokid = JUNK;
258d78e98d2SNate Williams     } /* while (1) */
259d78e98d2SNate Williams } /* token */
260d78e98d2SNate Williams 
261d78e98d2SNate Williams 
262d78e98d2SNate Williams /*
263d78e98d2SNate Williams  * plonk() gives an appropriate error message if a token is incorrect
264d78e98d2SNate Williams  */
265d78e98d2SNate Williams static void
266b89321a5SAndrey A. Chernov plonk(int tok)
267d78e98d2SNate Williams {
268d78e98d2SNate Williams     panic((tok == EOF) ? "incomplete time"
269d78e98d2SNate Williams 		       : "garbled time");
270d78e98d2SNate Williams } /* plonk */
271d78e98d2SNate Williams 
272d78e98d2SNate Williams 
273d78e98d2SNate Williams /*
274d78e98d2SNate Williams  * expect() gets a token and dies most horribly if it's not the token we want
275d78e98d2SNate Williams  */
276d78e98d2SNate Williams static void
277ddcf8022SAndrey A. Chernov expect(int desired)
278d78e98d2SNate Williams {
279d78e98d2SNate Williams     if (token() != desired)
280d78e98d2SNate Williams 	plonk(sc_tokid);	/* and we die here... */
281d78e98d2SNate Williams } /* expect */
282d78e98d2SNate Williams 
283d78e98d2SNate Williams 
284d78e98d2SNate Williams /*
28593a2fe45SMartin Cracauer  * plus_or_minus() holds functionality common to plus() and minus()
286d78e98d2SNate Williams  */
287d78e98d2SNate Williams static void
28893a2fe45SMartin Cracauer plus_or_minus(struct tm *tm, int delay)
289d78e98d2SNate Williams {
290b89321a5SAndrey A. Chernov     int expectplur;
291d78e98d2SNate Williams 
29293a2fe45SMartin Cracauer     expectplur = (delay != 1 && delay != -1) ? 1 : 0;
293d78e98d2SNate Williams 
294d78e98d2SNate Williams     switch (token()) {
295b6c989ffSNick Sayer     case YEARS:
296b6c989ffSNick Sayer 	    tm->tm_year += delay;
297b6c989ffSNick Sayer 	    break;
298b6c989ffSNick Sayer     case MONTHS:
299b6c989ffSNick Sayer 	    tm->tm_mon += delay;
300b6c989ffSNick Sayer 	    break;
301d78e98d2SNate Williams     case WEEKS:
302d78e98d2SNate Williams 	    delay *= 7;
303d78e98d2SNate Williams     case DAYS:
304b6c989ffSNick Sayer 	    tm->tm_mday += delay;
305b6c989ffSNick Sayer 	    break;
306d78e98d2SNate Williams     case HOURS:
307b6c989ffSNick Sayer 	    tm->tm_hour += delay;
308b6c989ffSNick Sayer 	    break;
309d78e98d2SNate Williams     case MINUTES:
310b6c989ffSNick Sayer 	    tm->tm_min += delay;
311b6c989ffSNick Sayer 	    break;
312b6c989ffSNick Sayer     default:
313b6c989ffSNick Sayer     	    plonk(sc_tokid);
314b6c989ffSNick Sayer 	    break;
315b6c989ffSNick Sayer     }
316b6c989ffSNick Sayer 
317b89321a5SAndrey A. Chernov     if (expectplur != sc_tokplur)
31812d20ef9SPhilippe Charnier 	warnx("pluralization is wrong");
319b6c989ffSNick Sayer 
320b6c989ffSNick Sayer     tm->tm_isdst = -1;
321b6c989ffSNick Sayer     if (mktime(tm) < 0)
322d78e98d2SNate Williams 	plonk(sc_tokid);
32393a2fe45SMartin Cracauer } /* plus_or_minus */
324b6c989ffSNick Sayer 
32593a2fe45SMartin Cracauer 
32693a2fe45SMartin Cracauer /*
32793a2fe45SMartin Cracauer  * plus() parses a now + time
32893a2fe45SMartin Cracauer  *
32993a2fe45SMartin Cracauer  *  at [NOW] PLUS NUMBER [MINUTES|HOURS|DAYS|WEEKS|MONTHS|YEARS]
33093a2fe45SMartin Cracauer  *
33193a2fe45SMartin Cracauer  */
33293a2fe45SMartin Cracauer static void
33393a2fe45SMartin Cracauer plus(struct tm *tm)
33493a2fe45SMartin Cracauer {
33593a2fe45SMartin Cracauer     int delay;
33693a2fe45SMartin Cracauer 
33793a2fe45SMartin Cracauer     expect(NUMBER);
33893a2fe45SMartin Cracauer 
33993a2fe45SMartin Cracauer     delay = atoi(sc_token);
34093a2fe45SMartin Cracauer     plus_or_minus(tm, delay);
341d78e98d2SNate Williams } /* plus */
342d78e98d2SNate Williams 
343d78e98d2SNate Williams 
344d78e98d2SNate Williams /*
34593a2fe45SMartin Cracauer  * minus() is like plus but can not be used with NOW
34693a2fe45SMartin Cracauer  */
34793a2fe45SMartin Cracauer static void
34893a2fe45SMartin Cracauer minus(struct tm *tm)
34993a2fe45SMartin Cracauer {
35093a2fe45SMartin Cracauer     int delay;
35193a2fe45SMartin Cracauer 
35293a2fe45SMartin Cracauer     expect(NUMBER);
35393a2fe45SMartin Cracauer 
35493a2fe45SMartin Cracauer     delay = -atoi(sc_token);
35593a2fe45SMartin Cracauer     plus_or_minus(tm, delay);
35693a2fe45SMartin Cracauer } /* minus */
35793a2fe45SMartin Cracauer 
35893a2fe45SMartin Cracauer 
35993a2fe45SMartin Cracauer /*
360d78e98d2SNate Williams  * tod() computes the time of day
361d78e98d2SNate Williams  *     [NUMBER [DOT NUMBER] [AM|PM]]
362d78e98d2SNate Williams  */
363d78e98d2SNate Williams static void
364b89321a5SAndrey A. Chernov tod(struct tm *tm)
365d78e98d2SNate Williams {
366d78e98d2SNate Williams     int hour, minute = 0;
367d78e98d2SNate Williams     int tlen;
368d78e98d2SNate Williams 
369d78e98d2SNate Williams     hour = atoi(sc_token);
370d78e98d2SNate Williams     tlen = strlen(sc_token);
371d78e98d2SNate Williams 
372b89321a5SAndrey A. Chernov     /* first pick out the time of day - if it's 4 digits, we assume
373d78e98d2SNate Williams      * a HHMM time, otherwise it's HH DOT MM time
374d78e98d2SNate Williams      */
375d78e98d2SNate Williams     if (token() == DOT) {
376d78e98d2SNate Williams 	expect(NUMBER);
377d78e98d2SNate Williams 	minute = atoi(sc_token);
378d78e98d2SNate Williams 	if (minute > 59)
379d78e98d2SNate Williams 	    panic("garbled time");
380d78e98d2SNate Williams 	token();
381b89321a5SAndrey A. Chernov     }
382b89321a5SAndrey A. Chernov     else if (tlen == 4) {
383d78e98d2SNate Williams 	minute = hour%100;
384d78e98d2SNate Williams 	if (minute > 59)
3853297b869SSteve Price 	    panic("garbled time");
386d78e98d2SNate Williams 	hour = hour/100;
387d78e98d2SNate Williams     }
388d78e98d2SNate Williams 
389b89321a5SAndrey A. Chernov     /* check if an AM or PM specifier was given
390d78e98d2SNate Williams      */
391d78e98d2SNate Williams     if (sc_tokid == AM || sc_tokid == PM) {
392d78e98d2SNate Williams 	if (hour > 12)
393d78e98d2SNate Williams 	    panic("garbled time");
394d78e98d2SNate Williams 
3959b2ea11cSJohn Polstra 	if (sc_tokid == PM) {
3969b2ea11cSJohn Polstra 	    if (hour != 12)	/* 12:xx PM is 12:xx, not 24:xx */
397d78e98d2SNate Williams 			hour += 12;
3989b2ea11cSJohn Polstra 	} else {
3999b2ea11cSJohn Polstra 	    if (hour == 12)	/* 12:xx AM is 00:xx, not 12:xx */
40012d20ef9SPhilippe Charnier 			hour = 0;
4019b2ea11cSJohn Polstra 	}
402d78e98d2SNate Williams 	token();
403b89321a5SAndrey A. Chernov     }
404b89321a5SAndrey A. Chernov     else if (hour > 23)
405d78e98d2SNate Williams 	panic("garbled time");
406d78e98d2SNate Williams 
407b89321a5SAndrey A. Chernov     /* if we specify an absolute time, we don't want to bump the day even
408d78e98d2SNate Williams      * if we've gone past that time - but if we're specifying a time plus
409d78e98d2SNate Williams      * a relative offset, it's okay to bump things
410d78e98d2SNate Williams      */
41193a2fe45SMartin Cracauer     if ((sc_tokid == EOF || sc_tokid == PLUS || sc_tokid == MINUS) &&
41293a2fe45SMartin Cracauer 	tm->tm_hour > hour) {
413d78e98d2SNate Williams 	tm->tm_mday++;
414b89321a5SAndrey A. Chernov 	tm->tm_wday++;
415b89321a5SAndrey A. Chernov     }
416d78e98d2SNate Williams 
417d78e98d2SNate Williams     tm->tm_hour = hour;
418d78e98d2SNate Williams     tm->tm_min = minute;
419d78e98d2SNate Williams     if (tm->tm_hour == 24) {
420d78e98d2SNate Williams 	tm->tm_hour = 0;
421d78e98d2SNate Williams 	tm->tm_mday++;
422d78e98d2SNate Williams     }
423d78e98d2SNate Williams } /* tod */
424d78e98d2SNate Williams 
425d78e98d2SNate Williams 
426d78e98d2SNate Williams /*
427d78e98d2SNate Williams  * assign_date() assigns a date, wrapping to next year if needed
428d78e98d2SNate Williams  */
429d78e98d2SNate Williams static void
430b89321a5SAndrey A. Chernov assign_date(struct tm *tm, long mday, long mon, long year)
431d78e98d2SNate Williams {
432f4510294SSheldon Hearn 
433f4510294SSheldon Hearn    /*
434f4510294SSheldon Hearn     * Convert year into tm_year format (year - 1900).
435f4510294SSheldon Hearn     * We may be given the year in 2 digit, 4 digit, or tm_year format.
436f4510294SSheldon Hearn     */
437f4510294SSheldon Hearn     if (year != -1) {
438f4510294SSheldon Hearn 	if (year >= 1900)
439f4510294SSheldon Hearn 		year -= 1900;   /* convert from 4 digit year */
440f4510294SSheldon Hearn 	else if (year < 100) {
441f4510294SSheldon Hearn 		/* convert from 2 digit year */
4421dbfc421SAlexander Langer 		struct tm *lt;
4431dbfc421SAlexander Langer 		time_t now;
4441dbfc421SAlexander Langer 
4451dbfc421SAlexander Langer 		time(&now);
4461dbfc421SAlexander Langer 		lt = localtime(&now);
4471dbfc421SAlexander Langer 
448f4510294SSheldon Hearn 		/* Convert to tm_year assuming current century */
449f4510294SSheldon Hearn 		year += (lt->tm_year / 100) * 100;
450f4510294SSheldon Hearn 
451f4510294SSheldon Hearn 		if (year == lt->tm_year - 1) year++;
452f4510294SSheldon Hearn 		else if (year < lt->tm_year)
453f4510294SSheldon Hearn 			year += 100;    /* must be in next century */
454f4510294SSheldon Hearn 	}
455d78e98d2SNate Williams     }
456d78e98d2SNate Williams 
457d78e98d2SNate Williams     if (year < 0 &&
458d78e98d2SNate Williams 	(tm->tm_mon > mon ||(tm->tm_mon == mon && tm->tm_mday > mday)))
459d78e98d2SNate Williams 	year = tm->tm_year + 1;
460d78e98d2SNate Williams 
461d78e98d2SNate Williams     tm->tm_mday = mday;
462d78e98d2SNate Williams     tm->tm_mon = mon;
463d78e98d2SNate Williams 
464d78e98d2SNate Williams     if (year >= 0)
465d78e98d2SNate Williams 	tm->tm_year = year;
466d78e98d2SNate Williams } /* assign_date */
467d78e98d2SNate Williams 
468d78e98d2SNate Williams 
469d78e98d2SNate Williams /*
470d78e98d2SNate Williams  * month() picks apart a month specification
471d78e98d2SNate Williams  *
472d78e98d2SNate Williams  *  /[<month> NUMBER [NUMBER]]           \
473d78e98d2SNate Williams  *  |[TOMORROW]                          |
474b89321a5SAndrey A. Chernov  *  |[DAY OF WEEK]                       |
475d78e98d2SNate Williams  *  |NUMBER [SLASH NUMBER [SLASH NUMBER]]|
476d78e98d2SNate Williams  *  \PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS/
477d78e98d2SNate Williams  */
478d78e98d2SNate Williams static void
479b89321a5SAndrey A. Chernov month(struct tm *tm)
480d78e98d2SNate Williams {
481d78e98d2SNate Williams     long year= (-1);
4825f618f92SArchie Cobbs     long mday = 0, wday, mon;
483d78e98d2SNate Williams     int tlen;
484d78e98d2SNate Williams 
485d78e98d2SNate Williams     switch (sc_tokid) {
486d78e98d2SNate Williams     case PLUS:
487d78e98d2SNate Williams 	    plus(tm);
488d78e98d2SNate Williams 	    break;
48993a2fe45SMartin Cracauer     case MINUS:
49093a2fe45SMartin Cracauer 	    minus(tm);
49193a2fe45SMartin Cracauer 	    break;
492d78e98d2SNate Williams 
493d78e98d2SNate Williams     case TOMORROW:
494d78e98d2SNate Williams 	    /* do something tomorrow */
495d78e98d2SNate Williams 	    tm->tm_mday ++;
496b89321a5SAndrey A. Chernov 	    tm->tm_wday ++;
497d78e98d2SNate Williams     case TODAY:	/* force ourselves to stay in today - no further processing */
498d78e98d2SNate Williams 	    token();
499d78e98d2SNate Williams 	    break;
500d78e98d2SNate Williams 
501d78e98d2SNate Williams     case JAN: case FEB: case MAR: case APR: case MAY: case JUN:
502d78e98d2SNate Williams     case JUL: case AUG: case SEP: case OCT: case NOV: case DEC:
503b89321a5SAndrey A. Chernov 	    /* do month mday [year]
504d78e98d2SNate Williams 	     */
505d78e98d2SNate Williams 	    mon = (sc_tokid-JAN);
506d78e98d2SNate Williams 	    expect(NUMBER);
50768abb9f9SBruce Evans 	    mday = atol(sc_token);
508d78e98d2SNate Williams 	    if (token() == NUMBER) {
509d78e98d2SNate Williams 		year = atol(sc_token);
510d78e98d2SNate Williams 		token();
511d78e98d2SNate Williams 	    }
512d78e98d2SNate Williams 	    assign_date(tm, mday, mon, year);
513d78e98d2SNate Williams 	    break;
514d78e98d2SNate Williams 
515b89321a5SAndrey A. Chernov     case SUN: case MON: case TUE:
516b89321a5SAndrey A. Chernov     case WED: case THU: case FRI:
517b89321a5SAndrey A. Chernov     case SAT:
518b89321a5SAndrey A. Chernov 	    /* do a particular day of the week
519b89321a5SAndrey A. Chernov 	     */
520b89321a5SAndrey A. Chernov 	    wday = (sc_tokid-SUN);
521b89321a5SAndrey A. Chernov 
522b89321a5SAndrey A. Chernov 	    mday = tm->tm_mday;
523b89321a5SAndrey A. Chernov 
524b89321a5SAndrey A. Chernov 	    /* if this day is < today, then roll to next week
525b89321a5SAndrey A. Chernov 	     */
526b89321a5SAndrey A. Chernov 	    if (wday < tm->tm_wday)
527b89321a5SAndrey A. Chernov 		mday += 7 - (tm->tm_wday - wday);
528b89321a5SAndrey A. Chernov 	    else
529b89321a5SAndrey A. Chernov 		mday += (wday - tm->tm_wday);
530b89321a5SAndrey A. Chernov 
531b89321a5SAndrey A. Chernov 	    tm->tm_wday = wday;
532b89321a5SAndrey A. Chernov 
533b89321a5SAndrey A. Chernov 	    assign_date(tm, mday, tm->tm_mon, tm->tm_year);
534b89321a5SAndrey A. Chernov 	    break;
535b89321a5SAndrey A. Chernov 
536d78e98d2SNate Williams     case NUMBER:
537b89321a5SAndrey A. Chernov 	    /* get numeric MMDDYY, mm/dd/yy, or dd.mm.yy
538d78e98d2SNate Williams 	     */
539d78e98d2SNate Williams 	    tlen = strlen(sc_token);
540d78e98d2SNate Williams 	    mon = atol(sc_token);
541d78e98d2SNate Williams 	    token();
542d78e98d2SNate Williams 
543d78e98d2SNate Williams 	    if (sc_tokid == SLASH || sc_tokid == DOT) {
544ddcf8022SAndrey A. Chernov 		int sep;
545d78e98d2SNate Williams 
546d78e98d2SNate Williams 		sep = sc_tokid;
547d78e98d2SNate Williams 		expect(NUMBER);
548d78e98d2SNate Williams 		mday = atol(sc_token);
549d78e98d2SNate Williams 		if (token() == sep) {
550d78e98d2SNate Williams 		    expect(NUMBER);
551d78e98d2SNate Williams 		    year = atol(sc_token);
552d78e98d2SNate Williams 		    token();
553d78e98d2SNate Williams 		}
554d78e98d2SNate Williams 
55581c8c7a4SPhilippe Charnier 		/* flip months and days for European timing
556d78e98d2SNate Williams 		 */
557d78e98d2SNate Williams 		if (sep == DOT) {
558d78e98d2SNate Williams 		    int x = mday;
559d78e98d2SNate Williams 		    mday = mon;
560d78e98d2SNate Williams 		    mon = x;
561d78e98d2SNate Williams 		}
562b89321a5SAndrey A. Chernov 	    }
563b89321a5SAndrey A. Chernov 	    else if (tlen == 6 || tlen == 8) {
564d78e98d2SNate Williams 		if (tlen == 8) {
565d78e98d2SNate Williams 		    year = (mon % 10000) - 1900;
566d78e98d2SNate Williams 		    mon /= 10000;
567b89321a5SAndrey A. Chernov 		}
568b89321a5SAndrey A. Chernov 		else {
569d78e98d2SNate Williams 		    year = mon % 100;
570d78e98d2SNate Williams 		    mon /= 100;
571d78e98d2SNate Williams 		}
572d78e98d2SNate Williams 		mday = mon % 100;
573d78e98d2SNate Williams 		mon /= 100;
574b89321a5SAndrey A. Chernov 	    }
575b89321a5SAndrey A. Chernov 	    else
576d78e98d2SNate Williams 		panic("garbled time");
577d78e98d2SNate Williams 
578d78e98d2SNate Williams 	    mon--;
579d78e98d2SNate Williams 	    if (mon < 0 || mon > 11 || mday < 1 || mday > 31)
580d78e98d2SNate Williams 		panic("garbled time");
581d78e98d2SNate Williams 
582d78e98d2SNate Williams 	    assign_date(tm, mday, mon, year);
583d78e98d2SNate Williams 	    break;
584d78e98d2SNate Williams     } /* case */
585d78e98d2SNate Williams } /* month */
586d78e98d2SNate Williams 
587d78e98d2SNate Williams 
588d78e98d2SNate Williams /* Global functions */
589d78e98d2SNate Williams 
590d78e98d2SNate Williams time_t
591b89321a5SAndrey A. Chernov parsetime(int argc, char **argv)
592d78e98d2SNate Williams {
593b89321a5SAndrey A. Chernov /* Do the argument parsing, die if necessary, and return the time the job
594d78e98d2SNate Williams  * should be run.
595d78e98d2SNate Williams  */
596d78e98d2SNate Williams     time_t nowtimer, runtimer;
597d78e98d2SNate Williams     struct tm nowtime, runtime;
598d78e98d2SNate Williams     int hr = 0;
599d78e98d2SNate Williams     /* this MUST be initialized to zero for midnight/noon/teatime */
600d78e98d2SNate Williams 
601d78e98d2SNate Williams     nowtimer = time(NULL);
602d78e98d2SNate Williams     nowtime = *localtime(&nowtimer);
603d78e98d2SNate Williams 
604d78e98d2SNate Williams     runtime = nowtime;
605d78e98d2SNate Williams     runtime.tm_sec = 0;
606d78e98d2SNate Williams     runtime.tm_isdst = 0;
607d78e98d2SNate Williams 
608d78e98d2SNate Williams     if (argc <= optind)
609d78e98d2SNate Williams 	usage();
610d78e98d2SNate Williams 
611d78e98d2SNate Williams     init_scanner(argc-optind, argv+optind);
612d78e98d2SNate Williams 
613d78e98d2SNate Williams     switch (token()) {
614bafdc304SBrian Somers     case NOW:
615bafdc304SBrian Somers 	    if (scc < 1) {
616bafdc304SBrian Somers 		return nowtimer;
617bafdc304SBrian Somers 	    }
618bafdc304SBrian Somers 	    /* now is optional prefix for PLUS tree */
619d78e98d2SNate Williams 	    expect(PLUS);
62090de1ffbSAlan Somers 	    /* FALLTHROUGH */
621d78e98d2SNate Williams     case PLUS:
622d78e98d2SNate Williams 	    plus(&runtime);
623d78e98d2SNate Williams 	    break;
624d78e98d2SNate Williams 
62593a2fe45SMartin Cracauer 	    /* MINUS is different from PLUS in that NOW is not
62693a2fe45SMartin Cracauer 	     * an optional prefix for it
62793a2fe45SMartin Cracauer 	     */
62893a2fe45SMartin Cracauer     case MINUS:
62993a2fe45SMartin Cracauer 	    minus(&runtime);
63093a2fe45SMartin Cracauer 	    break;
631d78e98d2SNate Williams     case NUMBER:
632d78e98d2SNate Williams 	    tod(&runtime);
633d78e98d2SNate Williams 	    month(&runtime);
634d78e98d2SNate Williams 	    break;
635d78e98d2SNate Williams 
636b89321a5SAndrey A. Chernov 	    /* evil coding for TEATIME|NOON|MIDNIGHT - we've initialised
637d78e98d2SNate Williams 	     * hr to zero up above, then fall into this case in such a
638d78e98d2SNate Williams 	     * way so we add +12 +4 hours to it for teatime, +12 hours
639d78e98d2SNate Williams 	     * to it for noon, and nothing at all for midnight, then
640d78e98d2SNate Williams 	     * set our runtime to that hour before leaping into the
641d78e98d2SNate Williams 	     * month scanner
642d78e98d2SNate Williams 	     */
643d78e98d2SNate Williams     case TEATIME:
644d78e98d2SNate Williams 	    hr += 4;
64590de1ffbSAlan Somers 	    /* FALLTHROUGH */
646d78e98d2SNate Williams     case NOON:
647d78e98d2SNate Williams 	    hr += 12;
64890de1ffbSAlan Somers 	    /* FALLTHROUGH */
649d78e98d2SNate Williams     case MIDNIGHT:
650b89321a5SAndrey A. Chernov 	    if (runtime.tm_hour >= hr) {
651d78e98d2SNate Williams 		runtime.tm_mday++;
652b89321a5SAndrey A. Chernov 		runtime.tm_wday++;
653b89321a5SAndrey A. Chernov 	    }
654d78e98d2SNate Williams 	    runtime.tm_hour = hr;
655d78e98d2SNate Williams 	    runtime.tm_min = 0;
656d78e98d2SNate Williams 	    token();
65781c8c7a4SPhilippe Charnier 	    /* FALLTHROUGH to month setting */
658d78e98d2SNate Williams     default:
659d78e98d2SNate Williams 	    month(&runtime);
660d78e98d2SNate Williams 	    break;
661d78e98d2SNate Williams     } /* ugly case statement */
662d78e98d2SNate Williams     expect(EOF);
663d78e98d2SNate Williams 
6644ed70180SStefan Farfeleder     /* convert back to time_t
665d78e98d2SNate Williams      */
666d78e98d2SNate Williams     runtime.tm_isdst = -1;
667d78e98d2SNate Williams     runtimer = mktime(&runtime);
668d78e98d2SNate Williams 
669d78e98d2SNate Williams     if (runtimer < 0)
670d78e98d2SNate Williams 	panic("garbled time");
671d78e98d2SNate Williams 
672d78e98d2SNate Williams     if (nowtimer > runtimer)
67381c8c7a4SPhilippe Charnier 	panic("trying to travel back in time");
674d78e98d2SNate Williams 
675d78e98d2SNate Williams     return runtimer;
676d78e98d2SNate Williams } /* parsetime */
677