xref: /freebsd/usr.bin/at/parsetime.c (revision 1d386b48a555f61cb7325543adbbb5c3f3407a66)
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>
40d78e98d2SNate Williams /* System Headers */
41d78e98d2SNate Williams 
42d78e98d2SNate Williams #include <sys/types.h>
4381c8c7a4SPhilippe Charnier #include <ctype.h>
44e5e5da15SPhilippe Charnier #include <err.h>
45d78e98d2SNate Williams #include <errno.h>
46d78e98d2SNate Williams #include <stdio.h>
47d78e98d2SNate Williams #include <stdlib.h>
48d78e98d2SNate Williams #include <string.h>
49d78e98d2SNate Williams #include <time.h>
50d78e98d2SNate Williams #include <unistd.h>
51b89321a5SAndrey A. Chernov #ifndef __FreeBSD__
52b89321a5SAndrey A. Chernov #include <getopt.h>
53b89321a5SAndrey A. Chernov #endif
54d78e98d2SNate Williams 
55d78e98d2SNate Williams /* Local headers */
56d78e98d2SNate Williams 
57d78e98d2SNate Williams #include "at.h"
58d78e98d2SNate Williams #include "panic.h"
593ce6c357SMark Murray #include "parsetime.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,
6893a2fe45SMartin Cracauer     NUMBER, PLUS, MINUS, 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  */
76f64efe8bSEd Schouten static const struct {
773ce6c357SMark Murray     const 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
parse_token(char * arg)160ddcf8022SAndrey A. Chernov parse_token(char *arg)
161d78e98d2SNate Williams {
1623ce6c357SMark Murray     size_t 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
init_scanner(int argc,char ** argv)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
token(void)1963ce6c357SMark Murray token(void)
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;
25093a2fe45SMartin Cracauer 	else if (sc_token[0] == '-')
25193a2fe45SMartin Cracauer 	    return sc_tokid = MINUS;
252b89321a5SAndrey A. Chernov 	else if (sc_token[0] == '/')
253d78e98d2SNate Williams 	    return sc_tokid = SLASH;
254d78e98d2SNate Williams 	else
255d78e98d2SNate Williams 	    return sc_tokid = JUNK;
256d78e98d2SNate Williams     } /* while (1) */
257d78e98d2SNate Williams } /* token */
258d78e98d2SNate Williams 
259d78e98d2SNate Williams 
260d78e98d2SNate Williams /*
261d78e98d2SNate Williams  * plonk() gives an appropriate error message if a token is incorrect
262d78e98d2SNate Williams  */
263d78e98d2SNate Williams static void
plonk(int tok)264b89321a5SAndrey A. Chernov plonk(int tok)
265d78e98d2SNate Williams {
266d78e98d2SNate Williams     panic((tok == EOF) ? "incomplete time"
267d78e98d2SNate Williams 		       : "garbled time");
268d78e98d2SNate Williams } /* plonk */
269d78e98d2SNate Williams 
270d78e98d2SNate Williams 
271d78e98d2SNate Williams /*
272d78e98d2SNate Williams  * expect() gets a token and dies most horribly if it's not the token we want
273d78e98d2SNate Williams  */
274d78e98d2SNate Williams static void
expect(int desired)275ddcf8022SAndrey A. Chernov expect(int desired)
276d78e98d2SNate Williams {
277d78e98d2SNate Williams     if (token() != desired)
278d78e98d2SNate Williams 	plonk(sc_tokid);	/* and we die here... */
279d78e98d2SNate Williams } /* expect */
280d78e98d2SNate Williams 
281d78e98d2SNate Williams 
282d78e98d2SNate Williams /*
28393a2fe45SMartin Cracauer  * plus_or_minus() holds functionality common to plus() and minus()
284d78e98d2SNate Williams  */
285d78e98d2SNate Williams static void
plus_or_minus(struct tm * tm,int delay)28693a2fe45SMartin Cracauer plus_or_minus(struct tm *tm, int delay)
287d78e98d2SNate Williams {
288b89321a5SAndrey A. Chernov     int expectplur;
289d78e98d2SNate Williams 
29093a2fe45SMartin Cracauer     expectplur = (delay != 1 && delay != -1) ? 1 : 0;
291d78e98d2SNate Williams 
292d78e98d2SNate Williams     switch (token()) {
293b6c989ffSNick Sayer     case YEARS:
294b6c989ffSNick Sayer 	    tm->tm_year += delay;
295b6c989ffSNick Sayer 	    break;
296b6c989ffSNick Sayer     case MONTHS:
297b6c989ffSNick Sayer 	    tm->tm_mon += delay;
298b6c989ffSNick Sayer 	    break;
299d78e98d2SNate Williams     case WEEKS:
300d78e98d2SNate Williams 	    delay *= 7;
301d78e98d2SNate Williams     case DAYS:
302b6c989ffSNick Sayer 	    tm->tm_mday += delay;
303b6c989ffSNick Sayer 	    break;
304d78e98d2SNate Williams     case HOURS:
305b6c989ffSNick Sayer 	    tm->tm_hour += delay;
306b6c989ffSNick Sayer 	    break;
307d78e98d2SNate Williams     case MINUTES:
308b6c989ffSNick Sayer 	    tm->tm_min += delay;
309b6c989ffSNick Sayer 	    break;
310b6c989ffSNick Sayer     default:
311b6c989ffSNick Sayer     	    plonk(sc_tokid);
312b6c989ffSNick Sayer 	    break;
313b6c989ffSNick Sayer     }
314b6c989ffSNick Sayer 
315b89321a5SAndrey A. Chernov     if (expectplur != sc_tokplur)
31612d20ef9SPhilippe Charnier 	warnx("pluralization is wrong");
317b6c989ffSNick Sayer 
318b6c989ffSNick Sayer     tm->tm_isdst = -1;
319b6c989ffSNick Sayer     if (mktime(tm) < 0)
320d78e98d2SNate Williams 	plonk(sc_tokid);
32193a2fe45SMartin Cracauer } /* plus_or_minus */
322b6c989ffSNick Sayer 
32393a2fe45SMartin Cracauer 
32493a2fe45SMartin Cracauer /*
32593a2fe45SMartin Cracauer  * plus() parses a now + time
32693a2fe45SMartin Cracauer  *
32793a2fe45SMartin Cracauer  *  at [NOW] PLUS NUMBER [MINUTES|HOURS|DAYS|WEEKS|MONTHS|YEARS]
32893a2fe45SMartin Cracauer  *
32993a2fe45SMartin Cracauer  */
33093a2fe45SMartin Cracauer static void
plus(struct tm * tm)33193a2fe45SMartin Cracauer plus(struct tm *tm)
33293a2fe45SMartin Cracauer {
33393a2fe45SMartin Cracauer     int delay;
33493a2fe45SMartin Cracauer 
33593a2fe45SMartin Cracauer     expect(NUMBER);
33693a2fe45SMartin Cracauer 
33793a2fe45SMartin Cracauer     delay = atoi(sc_token);
33893a2fe45SMartin Cracauer     plus_or_minus(tm, delay);
339d78e98d2SNate Williams } /* plus */
340d78e98d2SNate Williams 
341d78e98d2SNate Williams 
342d78e98d2SNate Williams /*
34393a2fe45SMartin Cracauer  * minus() is like plus but can not be used with NOW
34493a2fe45SMartin Cracauer  */
34593a2fe45SMartin Cracauer static void
minus(struct tm * tm)34693a2fe45SMartin Cracauer minus(struct tm *tm)
34793a2fe45SMartin Cracauer {
34893a2fe45SMartin Cracauer     int delay;
34993a2fe45SMartin Cracauer 
35093a2fe45SMartin Cracauer     expect(NUMBER);
35193a2fe45SMartin Cracauer 
35293a2fe45SMartin Cracauer     delay = -atoi(sc_token);
35393a2fe45SMartin Cracauer     plus_or_minus(tm, delay);
35493a2fe45SMartin Cracauer } /* minus */
35593a2fe45SMartin Cracauer 
35693a2fe45SMartin Cracauer 
35793a2fe45SMartin Cracauer /*
358d78e98d2SNate Williams  * tod() computes the time of day
359d78e98d2SNate Williams  *     [NUMBER [DOT NUMBER] [AM|PM]]
360d78e98d2SNate Williams  */
361d78e98d2SNate Williams static void
tod(struct tm * tm)362b89321a5SAndrey A. Chernov tod(struct tm *tm)
363d78e98d2SNate Williams {
364d78e98d2SNate Williams     int hour, minute = 0;
365d78e98d2SNate Williams     int tlen;
366d78e98d2SNate Williams 
367d78e98d2SNate Williams     hour = atoi(sc_token);
368d78e98d2SNate Williams     tlen = strlen(sc_token);
369d78e98d2SNate Williams 
370b89321a5SAndrey A. Chernov     /* first pick out the time of day - if it's 4 digits, we assume
371d78e98d2SNate Williams      * a HHMM time, otherwise it's HH DOT MM time
372d78e98d2SNate Williams      */
373d78e98d2SNate Williams     if (token() == DOT) {
374d78e98d2SNate Williams 	expect(NUMBER);
375d78e98d2SNate Williams 	minute = atoi(sc_token);
376d78e98d2SNate Williams 	if (minute > 59)
377d78e98d2SNate Williams 	    panic("garbled time");
378d78e98d2SNate Williams 	token();
379b89321a5SAndrey A. Chernov     }
380b89321a5SAndrey A. Chernov     else if (tlen == 4) {
381d78e98d2SNate Williams 	minute = hour%100;
382d78e98d2SNate Williams 	if (minute > 59)
3833297b869SSteve Price 	    panic("garbled time");
384d78e98d2SNate Williams 	hour = hour/100;
385d78e98d2SNate Williams     }
386d78e98d2SNate Williams 
387b89321a5SAndrey A. Chernov     /* check if an AM or PM specifier was given
388d78e98d2SNate Williams      */
389d78e98d2SNate Williams     if (sc_tokid == AM || sc_tokid == PM) {
390d78e98d2SNate Williams 	if (hour > 12)
391d78e98d2SNate Williams 	    panic("garbled time");
392d78e98d2SNate Williams 
3939b2ea11cSJohn Polstra 	if (sc_tokid == PM) {
3949b2ea11cSJohn Polstra 	    if (hour != 12)	/* 12:xx PM is 12:xx, not 24:xx */
395d78e98d2SNate Williams 			hour += 12;
3969b2ea11cSJohn Polstra 	} else {
3979b2ea11cSJohn Polstra 	    if (hour == 12)	/* 12:xx AM is 00:xx, not 12:xx */
39812d20ef9SPhilippe Charnier 			hour = 0;
3999b2ea11cSJohn Polstra 	}
400d78e98d2SNate Williams 	token();
401b89321a5SAndrey A. Chernov     }
402b89321a5SAndrey A. Chernov     else if (hour > 23)
403d78e98d2SNate Williams 	panic("garbled time");
404d78e98d2SNate Williams 
405b89321a5SAndrey A. Chernov     /* if we specify an absolute time, we don't want to bump the day even
406d78e98d2SNate Williams      * if we've gone past that time - but if we're specifying a time plus
407d78e98d2SNate Williams      * a relative offset, it's okay to bump things
408d78e98d2SNate Williams      */
40993a2fe45SMartin Cracauer     if ((sc_tokid == EOF || sc_tokid == PLUS || sc_tokid == MINUS) &&
41093a2fe45SMartin Cracauer 	tm->tm_hour > hour) {
411d78e98d2SNate Williams 	tm->tm_mday++;
412b89321a5SAndrey A. Chernov 	tm->tm_wday++;
413b89321a5SAndrey A. Chernov     }
414d78e98d2SNate Williams 
415d78e98d2SNate Williams     tm->tm_hour = hour;
416d78e98d2SNate Williams     tm->tm_min = minute;
417d78e98d2SNate Williams     if (tm->tm_hour == 24) {
418d78e98d2SNate Williams 	tm->tm_hour = 0;
419d78e98d2SNate Williams 	tm->tm_mday++;
420d78e98d2SNate Williams     }
421d78e98d2SNate Williams } /* tod */
422d78e98d2SNate Williams 
423d78e98d2SNate Williams 
424d78e98d2SNate Williams /*
425d78e98d2SNate Williams  * assign_date() assigns a date, wrapping to next year if needed
426d78e98d2SNate Williams  */
427d78e98d2SNate Williams static void
assign_date(struct tm * tm,long mday,long mon,long year)428b89321a5SAndrey A. Chernov assign_date(struct tm *tm, long mday, long mon, long year)
429d78e98d2SNate Williams {
430f4510294SSheldon Hearn 
431f4510294SSheldon Hearn    /*
432f4510294SSheldon Hearn     * Convert year into tm_year format (year - 1900).
433f4510294SSheldon Hearn     * We may be given the year in 2 digit, 4 digit, or tm_year format.
434f4510294SSheldon Hearn     */
435f4510294SSheldon Hearn     if (year != -1) {
436f4510294SSheldon Hearn 	if (year >= 1900)
437f4510294SSheldon Hearn 		year -= 1900;   /* convert from 4 digit year */
438f4510294SSheldon Hearn 	else if (year < 100) {
439f4510294SSheldon Hearn 		/* convert from 2 digit year */
4401dbfc421SAlexander Langer 		struct tm *lt;
4411dbfc421SAlexander Langer 		time_t now;
4421dbfc421SAlexander Langer 
4431dbfc421SAlexander Langer 		time(&now);
4441dbfc421SAlexander Langer 		lt = localtime(&now);
4451dbfc421SAlexander Langer 
446f4510294SSheldon Hearn 		/* Convert to tm_year assuming current century */
447f4510294SSheldon Hearn 		year += (lt->tm_year / 100) * 100;
448f4510294SSheldon Hearn 
449f4510294SSheldon Hearn 		if (year == lt->tm_year - 1) year++;
450f4510294SSheldon Hearn 		else if (year < lt->tm_year)
451f4510294SSheldon Hearn 			year += 100;    /* must be in next century */
452f4510294SSheldon Hearn 	}
453d78e98d2SNate Williams     }
454d78e98d2SNate Williams 
455d78e98d2SNate Williams     if (year < 0 &&
456d78e98d2SNate Williams 	(tm->tm_mon > mon ||(tm->tm_mon == mon && tm->tm_mday > mday)))
457d78e98d2SNate Williams 	year = tm->tm_year + 1;
458d78e98d2SNate Williams 
459d78e98d2SNate Williams     tm->tm_mday = mday;
460d78e98d2SNate Williams     tm->tm_mon = mon;
461d78e98d2SNate Williams 
462d78e98d2SNate Williams     if (year >= 0)
463d78e98d2SNate Williams 	tm->tm_year = year;
464d78e98d2SNate Williams } /* assign_date */
465d78e98d2SNate Williams 
466d78e98d2SNate Williams 
467d78e98d2SNate Williams /*
468d78e98d2SNate Williams  * month() picks apart a month specification
469d78e98d2SNate Williams  *
470d78e98d2SNate Williams  *  /[<month> NUMBER [NUMBER]]           \
471d78e98d2SNate Williams  *  |[TOMORROW]                          |
472b89321a5SAndrey A. Chernov  *  |[DAY OF WEEK]                       |
473d78e98d2SNate Williams  *  |NUMBER [SLASH NUMBER [SLASH NUMBER]]|
474d78e98d2SNate Williams  *  \PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS/
475d78e98d2SNate Williams  */
476d78e98d2SNate Williams static void
month(struct tm * tm)477b89321a5SAndrey A. Chernov month(struct tm *tm)
478d78e98d2SNate Williams {
479d78e98d2SNate Williams     long year= (-1);
4805f618f92SArchie Cobbs     long mday = 0, wday, mon;
481d78e98d2SNate Williams     int tlen;
482d78e98d2SNate Williams 
483d78e98d2SNate Williams     switch (sc_tokid) {
484d78e98d2SNate Williams     case PLUS:
485d78e98d2SNate Williams 	    plus(tm);
486d78e98d2SNate Williams 	    break;
48793a2fe45SMartin Cracauer     case MINUS:
48893a2fe45SMartin Cracauer 	    minus(tm);
48993a2fe45SMartin Cracauer 	    break;
490d78e98d2SNate Williams 
491d78e98d2SNate Williams     case TOMORROW:
492d78e98d2SNate Williams 	    /* do something tomorrow */
493d78e98d2SNate Williams 	    tm->tm_mday ++;
494b89321a5SAndrey A. Chernov 	    tm->tm_wday ++;
495d78e98d2SNate Williams     case TODAY:	/* force ourselves to stay in today - no further processing */
496d78e98d2SNate Williams 	    token();
497d78e98d2SNate Williams 	    break;
498d78e98d2SNate Williams 
499d78e98d2SNate Williams     case JAN: case FEB: case MAR: case APR: case MAY: case JUN:
500d78e98d2SNate Williams     case JUL: case AUG: case SEP: case OCT: case NOV: case DEC:
501b89321a5SAndrey A. Chernov 	    /* do month mday [year]
502d78e98d2SNate Williams 	     */
503d78e98d2SNate Williams 	    mon = (sc_tokid-JAN);
504d78e98d2SNate Williams 	    expect(NUMBER);
50568abb9f9SBruce Evans 	    mday = atol(sc_token);
506d78e98d2SNate Williams 	    if (token() == NUMBER) {
507d78e98d2SNate Williams 		year = atol(sc_token);
508d78e98d2SNate Williams 		token();
509d78e98d2SNate Williams 	    }
510d78e98d2SNate Williams 	    assign_date(tm, mday, mon, year);
511d78e98d2SNate Williams 	    break;
512d78e98d2SNate Williams 
513b89321a5SAndrey A. Chernov     case SUN: case MON: case TUE:
514b89321a5SAndrey A. Chernov     case WED: case THU: case FRI:
515b89321a5SAndrey A. Chernov     case SAT:
516b89321a5SAndrey A. Chernov 	    /* do a particular day of the week
517b89321a5SAndrey A. Chernov 	     */
518b89321a5SAndrey A. Chernov 	    wday = (sc_tokid-SUN);
519b89321a5SAndrey A. Chernov 
520b89321a5SAndrey A. Chernov 	    mday = tm->tm_mday;
521b89321a5SAndrey A. Chernov 
522b89321a5SAndrey A. Chernov 	    /* if this day is < today, then roll to next week
523b89321a5SAndrey A. Chernov 	     */
524b89321a5SAndrey A. Chernov 	    if (wday < tm->tm_wday)
525b89321a5SAndrey A. Chernov 		mday += 7 - (tm->tm_wday - wday);
526b89321a5SAndrey A. Chernov 	    else
527b89321a5SAndrey A. Chernov 		mday += (wday - tm->tm_wday);
528b89321a5SAndrey A. Chernov 
529b89321a5SAndrey A. Chernov 	    tm->tm_wday = wday;
530b89321a5SAndrey A. Chernov 
531b89321a5SAndrey A. Chernov 	    assign_date(tm, mday, tm->tm_mon, tm->tm_year);
532b89321a5SAndrey A. Chernov 	    break;
533b89321a5SAndrey A. Chernov 
534d78e98d2SNate Williams     case NUMBER:
535b89321a5SAndrey A. Chernov 	    /* get numeric MMDDYY, mm/dd/yy, or dd.mm.yy
536d78e98d2SNate Williams 	     */
537d78e98d2SNate Williams 	    tlen = strlen(sc_token);
538d78e98d2SNate Williams 	    mon = atol(sc_token);
539d78e98d2SNate Williams 	    token();
540d78e98d2SNate Williams 
541d78e98d2SNate Williams 	    if (sc_tokid == SLASH || sc_tokid == DOT) {
542ddcf8022SAndrey A. Chernov 		int sep;
543d78e98d2SNate Williams 
544d78e98d2SNate Williams 		sep = sc_tokid;
545d78e98d2SNate Williams 		expect(NUMBER);
546d78e98d2SNate Williams 		mday = atol(sc_token);
547d78e98d2SNate Williams 		if (token() == sep) {
548d78e98d2SNate Williams 		    expect(NUMBER);
549d78e98d2SNate Williams 		    year = atol(sc_token);
550d78e98d2SNate Williams 		    token();
551d78e98d2SNate Williams 		}
552d78e98d2SNate Williams 
55381c8c7a4SPhilippe Charnier 		/* flip months and days for European timing
554d78e98d2SNate Williams 		 */
555d78e98d2SNate Williams 		if (sep == DOT) {
556d78e98d2SNate Williams 		    int x = mday;
557d78e98d2SNate Williams 		    mday = mon;
558d78e98d2SNate Williams 		    mon = x;
559d78e98d2SNate Williams 		}
560b89321a5SAndrey A. Chernov 	    }
561b89321a5SAndrey A. Chernov 	    else if (tlen == 6 || tlen == 8) {
562d78e98d2SNate Williams 		if (tlen == 8) {
563d78e98d2SNate Williams 		    year = (mon % 10000) - 1900;
564d78e98d2SNate Williams 		    mon /= 10000;
565b89321a5SAndrey A. Chernov 		}
566b89321a5SAndrey A. Chernov 		else {
567d78e98d2SNate Williams 		    year = mon % 100;
568d78e98d2SNate Williams 		    mon /= 100;
569d78e98d2SNate Williams 		}
570d78e98d2SNate Williams 		mday = mon % 100;
571d78e98d2SNate Williams 		mon /= 100;
572b89321a5SAndrey A. Chernov 	    }
573b89321a5SAndrey A. Chernov 	    else
574d78e98d2SNate Williams 		panic("garbled time");
575d78e98d2SNate Williams 
576d78e98d2SNate Williams 	    mon--;
577d78e98d2SNate Williams 	    if (mon < 0 || mon > 11 || mday < 1 || mday > 31)
578d78e98d2SNate Williams 		panic("garbled time");
579d78e98d2SNate Williams 
580d78e98d2SNate Williams 	    assign_date(tm, mday, mon, year);
581d78e98d2SNate Williams 	    break;
582d78e98d2SNate Williams     } /* case */
583d78e98d2SNate Williams } /* month */
584d78e98d2SNate Williams 
585d78e98d2SNate Williams 
586d78e98d2SNate Williams /* Global functions */
587d78e98d2SNate Williams 
588d78e98d2SNate Williams time_t
parsetime(int argc,char ** argv)589b89321a5SAndrey A. Chernov parsetime(int argc, char **argv)
590d78e98d2SNate Williams {
591b89321a5SAndrey A. Chernov /* Do the argument parsing, die if necessary, and return the time the job
592d78e98d2SNate Williams  * should be run.
593d78e98d2SNate Williams  */
594d78e98d2SNate Williams     time_t nowtimer, runtimer;
595d78e98d2SNate Williams     struct tm nowtime, runtime;
596d78e98d2SNate Williams     int hr = 0;
597d78e98d2SNate Williams     /* this MUST be initialized to zero for midnight/noon/teatime */
598d78e98d2SNate Williams 
599d78e98d2SNate Williams     nowtimer = time(NULL);
600d78e98d2SNate Williams     nowtime = *localtime(&nowtimer);
601d78e98d2SNate Williams 
602d78e98d2SNate Williams     runtime = nowtime;
603d78e98d2SNate Williams     runtime.tm_sec = 0;
604d78e98d2SNate Williams     runtime.tm_isdst = 0;
605d78e98d2SNate Williams 
606d78e98d2SNate Williams     if (argc <= optind)
607d78e98d2SNate Williams 	usage();
608d78e98d2SNate Williams 
609d78e98d2SNate Williams     init_scanner(argc-optind, argv+optind);
610d78e98d2SNate Williams 
611d78e98d2SNate Williams     switch (token()) {
612bafdc304SBrian Somers     case NOW:
613bafdc304SBrian Somers 	    if (scc < 1) {
614bafdc304SBrian Somers 		return nowtimer;
615bafdc304SBrian Somers 	    }
616bafdc304SBrian Somers 	    /* now is optional prefix for PLUS tree */
617d78e98d2SNate Williams 	    expect(PLUS);
61890de1ffbSAlan Somers 	    /* FALLTHROUGH */
619d78e98d2SNate Williams     case PLUS:
620d78e98d2SNate Williams 	    plus(&runtime);
621d78e98d2SNate Williams 	    break;
622d78e98d2SNate Williams 
62393a2fe45SMartin Cracauer 	    /* MINUS is different from PLUS in that NOW is not
62493a2fe45SMartin Cracauer 	     * an optional prefix for it
62593a2fe45SMartin Cracauer 	     */
62693a2fe45SMartin Cracauer     case MINUS:
62793a2fe45SMartin Cracauer 	    minus(&runtime);
62893a2fe45SMartin Cracauer 	    break;
629d78e98d2SNate Williams     case NUMBER:
630d78e98d2SNate Williams 	    tod(&runtime);
631d78e98d2SNate Williams 	    month(&runtime);
632d78e98d2SNate Williams 	    break;
633d78e98d2SNate Williams 
634b89321a5SAndrey A. Chernov 	    /* evil coding for TEATIME|NOON|MIDNIGHT - we've initialised
635d78e98d2SNate Williams 	     * hr to zero up above, then fall into this case in such a
636d78e98d2SNate Williams 	     * way so we add +12 +4 hours to it for teatime, +12 hours
637d78e98d2SNate Williams 	     * to it for noon, and nothing at all for midnight, then
638d78e98d2SNate Williams 	     * set our runtime to that hour before leaping into the
639d78e98d2SNate Williams 	     * month scanner
640d78e98d2SNate Williams 	     */
641d78e98d2SNate Williams     case TEATIME:
642d78e98d2SNate Williams 	    hr += 4;
64390de1ffbSAlan Somers 	    /* FALLTHROUGH */
644d78e98d2SNate Williams     case NOON:
645d78e98d2SNate Williams 	    hr += 12;
64690de1ffbSAlan Somers 	    /* FALLTHROUGH */
647d78e98d2SNate Williams     case MIDNIGHT:
648b89321a5SAndrey A. Chernov 	    if (runtime.tm_hour >= hr) {
649d78e98d2SNate Williams 		runtime.tm_mday++;
650b89321a5SAndrey A. Chernov 		runtime.tm_wday++;
651b89321a5SAndrey A. Chernov 	    }
652d78e98d2SNate Williams 	    runtime.tm_hour = hr;
653d78e98d2SNate Williams 	    runtime.tm_min = 0;
654d78e98d2SNate Williams 	    token();
65581c8c7a4SPhilippe Charnier 	    /* FALLTHROUGH to month setting */
656d78e98d2SNate Williams     default:
657d78e98d2SNate Williams 	    month(&runtime);
658d78e98d2SNate Williams 	    break;
659d78e98d2SNate Williams     } /* ugly case statement */
660d78e98d2SNate Williams     expect(EOF);
661d78e98d2SNate Williams 
6624ed70180SStefan Farfeleder     /* convert back to time_t
663d78e98d2SNate Williams      */
664d78e98d2SNate Williams     runtime.tm_isdst = -1;
665d78e98d2SNate Williams     runtimer = mktime(&runtime);
666d78e98d2SNate Williams 
667d78e98d2SNate Williams     if (runtimer < 0)
668d78e98d2SNate Williams 	panic("garbled time");
669d78e98d2SNate Williams 
670d78e98d2SNate Williams     if (nowtimer > runtimer)
67181c8c7a4SPhilippe Charnier 	panic("trying to travel back in time");
672d78e98d2SNate Williams 
673d78e98d2SNate Williams     return runtimer;
674d78e98d2SNate Williams } /* parsetime */
675