xref: /freebsd/usr.bin/at/parsetime.c (revision b9dd99f2f5645cff49601a09834112809adf6c60)
1d78e98d2SNate Williams /*
2d78e98d2SNate Williams  *  parsetime.c - parse time for at(1)
3b89321a5SAndrey A. Chernov  *  Copyright (C) 1993, 1994  Thomas Koenig
4d78e98d2SNate Williams  *
5d78e98d2SNate Williams  *  modifications for english-language times
6d78e98d2SNate Williams  *  Copyright (C) 1993  David Parsons
7d78e98d2SNate Williams  *
8d78e98d2SNate Williams  * Redistribution and use in source and binary forms, with or without
9d78e98d2SNate Williams  * modification, are permitted provided that the following conditions
10d78e98d2SNate Williams  * are met:
11d78e98d2SNate Williams  * 1. Redistributions of source code must retain the above copyright
12d78e98d2SNate Williams  *    notice, this list of conditions and the following disclaimer.
13d78e98d2SNate Williams  * 2. The name of the author(s) may not be used to endorse or promote
14d78e98d2SNate Williams  *    products derived from this software without specific prior written
15d78e98d2SNate Williams  *    permission.
16d78e98d2SNate Williams  *
17d78e98d2SNate Williams  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
18d78e98d2SNate Williams  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19d78e98d2SNate Williams  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20ddcf8022SAndrey A. Chernov  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
21d78e98d2SNate Williams  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22d78e98d2SNate Williams  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23d78e98d2SNate Williams  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24d78e98d2SNate Williams  * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25d78e98d2SNate Williams  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26d78e98d2SNate Williams  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27d78e98d2SNate Williams  *
28d78e98d2SNate Williams  *  at [NOW] PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS
29d78e98d2SNate Williams  *     /NUMBER [DOT NUMBER] [AM|PM]\ /[MONTH NUMBER [NUMBER]]             \
30d78e98d2SNate Williams  *     |NOON                       | |[TOMORROW]                          |
31b89321a5SAndrey A. Chernov  *     |MIDNIGHT                   | |[DAY OF WEEK]                       |
32b89321a5SAndrey A. Chernov  *     \TEATIME                    / |NUMBER [SLASH NUMBER [SLASH NUMBER]]|
33b89321a5SAndrey A. Chernov  *                                   \PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS/
34d78e98d2SNate Williams  */
35d78e98d2SNate Williams 
36d78e98d2SNate Williams /* System Headers */
37d78e98d2SNate Williams 
38b89321a5SAndrey A. Chernov 
39d78e98d2SNate Williams #include <sys/types.h>
40e5e5da15SPhilippe Charnier #include <err.h>
41d78e98d2SNate Williams #include <errno.h>
42d78e98d2SNate Williams #include <stdio.h>
43d78e98d2SNate Williams #include <stdlib.h>
44d78e98d2SNate Williams #include <string.h>
45d78e98d2SNate Williams #include <time.h>
46d78e98d2SNate Williams #include <unistd.h>
47d78e98d2SNate Williams #include <ctype.h>
48b89321a5SAndrey A. Chernov #ifndef __FreeBSD__
49b89321a5SAndrey A. Chernov #include <getopt.h>
50b89321a5SAndrey A. Chernov #endif
51d78e98d2SNate Williams 
52d78e98d2SNate Williams /* Local headers */
53d78e98d2SNate Williams 
54d78e98d2SNate Williams #include "at.h"
55d78e98d2SNate Williams #include "panic.h"
56d78e98d2SNate Williams 
57d78e98d2SNate Williams 
58d78e98d2SNate Williams /* Structures and unions */
59d78e98d2SNate Williams 
60ddcf8022SAndrey A. Chernov enum {	/* symbols */
61d78e98d2SNate Williams     MIDNIGHT, NOON, TEATIME,
62d78e98d2SNate Williams     PM, AM, TOMORROW, TODAY, NOW,
63d78e98d2SNate Williams     MINUTES, HOURS, DAYS, WEEKS,
64d78e98d2SNate Williams     NUMBER, PLUS, DOT, SLASH, ID, JUNK,
65d78e98d2SNate Williams     JAN, FEB, MAR, APR, MAY, JUN,
66b89321a5SAndrey A. Chernov     JUL, AUG, SEP, OCT, NOV, DEC,
67b89321a5SAndrey A. Chernov     SUN, MON, TUE, WED, THU, FRI, SAT
68d78e98d2SNate Williams     };
69d78e98d2SNate Williams 
70b89321a5SAndrey A. Chernov /* parse translation table - table driven parsers can be your FRIEND!
71d78e98d2SNate Williams  */
72d78e98d2SNate Williams struct {
73ddcf8022SAndrey A. Chernov     char *name;	/* token name */
74ddcf8022SAndrey A. Chernov     int value;	/* token id */
75b89321a5SAndrey A. Chernov     int plural;	/* is this plural? */
76d78e98d2SNate Williams } Specials[] = {
77b89321a5SAndrey A. Chernov     { "midnight", MIDNIGHT,0 },	/* 00:00:00 of today or tomorrow */
78b89321a5SAndrey A. Chernov     { "noon", NOON,0 },		/* 12:00:00 of today or tomorrow */
79b89321a5SAndrey A. Chernov     { "teatime", TEATIME,0 },	/* 16:00:00 of today or tomorrow */
80b89321a5SAndrey A. Chernov     { "am", AM,0 },		/* morning times for 0-12 clock */
81b89321a5SAndrey A. Chernov     { "pm", PM,0 },		/* evening times for 0-12 clock */
82b89321a5SAndrey A. Chernov     { "tomorrow", TOMORROW,0 },	/* execute 24 hours from time */
83b89321a5SAndrey A. Chernov     { "today", TODAY, 0 },	/* execute today - don't advance time */
84b89321a5SAndrey A. Chernov     { "now", NOW,0 },		/* opt prefix for PLUS */
85d78e98d2SNate Williams 
86b89321a5SAndrey A. Chernov     { "minute", MINUTES,0 },	/* minutes multiplier */
87b89321a5SAndrey A. Chernov     { "minutes", MINUTES,1 },	/* (pluralized) */
88b89321a5SAndrey A. Chernov     { "hour", HOURS,0 },	/* hours ... */
89b89321a5SAndrey A. Chernov     { "hours", HOURS,1 },	/* (pluralized) */
90b89321a5SAndrey A. Chernov     { "day", DAYS,0 },		/* days ... */
91b89321a5SAndrey A. Chernov     { "days", DAYS,1 },		/* (pluralized) */
92b89321a5SAndrey A. Chernov     { "week", WEEKS,0 },	/* week ... */
93b89321a5SAndrey A. Chernov     { "weeks", WEEKS,1 },	/* (pluralized) */
94b89321a5SAndrey A. Chernov     { "jan", JAN,0 },
95b89321a5SAndrey A. Chernov     { "feb", FEB,0 },
96b89321a5SAndrey A. Chernov     { "mar", MAR,0 },
97b89321a5SAndrey A. Chernov     { "apr", APR,0 },
98b89321a5SAndrey A. Chernov     { "may", MAY,0 },
99b89321a5SAndrey A. Chernov     { "jun", JUN,0 },
100b89321a5SAndrey A. Chernov     { "jul", JUL,0 },
101b89321a5SAndrey A. Chernov     { "aug", AUG,0 },
102b89321a5SAndrey A. Chernov     { "sep", SEP,0 },
103b89321a5SAndrey A. Chernov     { "oct", OCT,0 },
104b89321a5SAndrey A. Chernov     { "nov", NOV,0 },
105b89321a5SAndrey A. Chernov     { "dec", DEC,0 },
106b9dd99f2SDag-Erling Smørgrav     { "january", JAN,0 },
107b9dd99f2SDag-Erling Smørgrav     { "february", FEB,0 },
108b9dd99f2SDag-Erling Smørgrav     { "march", MAR,0 },
109b9dd99f2SDag-Erling Smørgrav     { "april", APR,0 },
110b9dd99f2SDag-Erling Smørgrav     { "may", MAY,0 },
111b9dd99f2SDag-Erling Smørgrav     { "june", JUN,0 },
112b9dd99f2SDag-Erling Smørgrav     { "july", JUL,0 },
113b9dd99f2SDag-Erling Smørgrav     { "august", AUG,0 },
114b9dd99f2SDag-Erling Smørgrav     { "september", SEP,0 },
115b9dd99f2SDag-Erling Smørgrav     { "october", OCT,0 },
116b9dd99f2SDag-Erling Smørgrav     { "november", NOV,0 },
117b9dd99f2SDag-Erling Smørgrav     { "december", DEC,0 },
118b89321a5SAndrey A. Chernov     { "sunday", SUN, 0 },
119b89321a5SAndrey A. Chernov     { "sun", SUN, 0 },
120b89321a5SAndrey A. Chernov     { "monday", MON, 0 },
121b89321a5SAndrey A. Chernov     { "mon", MON, 0 },
122b89321a5SAndrey A. Chernov     { "tuesday", TUE, 0 },
123b89321a5SAndrey A. Chernov     { "tue", TUE, 0 },
124b89321a5SAndrey A. Chernov     { "wednesday", WED, 0 },
125b89321a5SAndrey A. Chernov     { "wed", WED, 0 },
126b89321a5SAndrey A. Chernov     { "thursday", THU, 0 },
127b89321a5SAndrey A. Chernov     { "thu", THU, 0 },
128b89321a5SAndrey A. Chernov     { "friday", FRI, 0 },
129b89321a5SAndrey A. Chernov     { "fri", FRI, 0 },
130b89321a5SAndrey A. Chernov     { "saturday", SAT, 0 },
131b89321a5SAndrey A. Chernov     { "sat", SAT, 0 },
132d78e98d2SNate Williams } ;
133d78e98d2SNate Williams 
134d78e98d2SNate Williams /* File scope variables */
135d78e98d2SNate Williams 
136d78e98d2SNate Williams static char **scp;	/* scanner - pointer at arglist */
137d78e98d2SNate Williams static char scc;	/* scanner - count of remaining arguments */
138d78e98d2SNate Williams static char *sct;	/* scanner - next char pointer in current argument */
139d78e98d2SNate Williams static int need;	/* scanner - need to advance to next argument */
140d78e98d2SNate Williams 
141d78e98d2SNate Williams static char *sc_token;	/* scanner - token buffer */
142d78e98d2SNate Williams static size_t sc_len;   /* scanner - lenght of token buffer */
143ddcf8022SAndrey A. Chernov static int sc_tokid;	/* scanner - token id */
144b89321a5SAndrey A. Chernov static int sc_tokplur;	/* scanner - is token plural? */
145d78e98d2SNate Williams 
146b9dd99f2SDag-Erling Smørgrav static char rcsid[] = "$Id: parsetime.c,v 1.12 1998/05/05 01:53:15 alex Exp $";
147d78e98d2SNate Williams 
148d78e98d2SNate Williams /* Local functions */
149d78e98d2SNate Williams 
150d78e98d2SNate Williams /*
151d78e98d2SNate Williams  * parse a token, checking if it's something special to us
152d78e98d2SNate Williams  */
153ddcf8022SAndrey A. Chernov static int
154ddcf8022SAndrey A. Chernov parse_token(char *arg)
155d78e98d2SNate Williams {
156d78e98d2SNate Williams     int i;
157d78e98d2SNate Williams 
158d78e98d2SNate Williams     for (i=0; i<(sizeof Specials/sizeof Specials[0]); i++)
159d78e98d2SNate Williams 	if (strcasecmp(Specials[i].name, arg) == 0) {
160b89321a5SAndrey A. Chernov 	    sc_tokplur = Specials[i].plural;
161d78e98d2SNate Williams 	    return sc_tokid = Specials[i].value;
162d78e98d2SNate Williams 	}
163d78e98d2SNate Williams 
164d78e98d2SNate Williams     /* not special - must be some random id */
165d78e98d2SNate Williams     return ID;
166d78e98d2SNate Williams } /* parse_token */
167d78e98d2SNate Williams 
168d78e98d2SNate Williams 
169d78e98d2SNate Williams /*
170d78e98d2SNate Williams  * init_scanner() sets up the scanner to eat arguments
171d78e98d2SNate Williams  */
172d78e98d2SNate Williams static void
173b89321a5SAndrey A. Chernov init_scanner(int argc, char **argv)
174d78e98d2SNate Williams {
175d78e98d2SNate Williams     scp = argv;
176d78e98d2SNate Williams     scc = argc;
177d78e98d2SNate Williams     need = 1;
178d78e98d2SNate Williams     sc_len = 1;
179ddcf8022SAndrey A. Chernov     while (argc-- > 0)
180ddcf8022SAndrey A. Chernov 	sc_len += strlen(*argv++);
181d78e98d2SNate Williams 
182b89321a5SAndrey A. Chernov     sc_token = (char *) mymalloc(sc_len);
183d78e98d2SNate Williams } /* init_scanner */
184d78e98d2SNate Williams 
185d78e98d2SNate Williams /*
186d78e98d2SNate Williams  * token() fetches a token from the input stream
187d78e98d2SNate Williams  */
188ddcf8022SAndrey A. Chernov static int
189d78e98d2SNate Williams token()
190d78e98d2SNate Williams {
191d78e98d2SNate Williams     int idx;
192d78e98d2SNate Williams 
193d78e98d2SNate Williams     while (1) {
194d78e98d2SNate Williams 	memset(sc_token, 0, sc_len);
195d78e98d2SNate Williams 	sc_tokid = EOF;
196b89321a5SAndrey A. Chernov 	sc_tokplur = 0;
197d78e98d2SNate Williams 	idx = 0;
198d78e98d2SNate Williams 
199b89321a5SAndrey A. Chernov 	/* if we need to read another argument, walk along the argument list;
200d78e98d2SNate Williams 	 * when we fall off the arglist, we'll just return EOF forever
201d78e98d2SNate Williams 	 */
202d78e98d2SNate Williams 	if (need) {
203d78e98d2SNate Williams 	    if (scc < 1)
204d78e98d2SNate Williams 		return sc_tokid;
205d78e98d2SNate Williams 	    sct = *scp;
206d78e98d2SNate Williams 	    scp++;
207d78e98d2SNate Williams 	    scc--;
208d78e98d2SNate Williams 	    need = 0;
209d78e98d2SNate Williams 	}
210b89321a5SAndrey A. Chernov 	/* eat whitespace now - if we walk off the end of the argument,
211d78e98d2SNate Williams 	 * we'll continue, which puts us up at the top of the while loop
212d78e98d2SNate Williams 	 * to fetch the next argument in
213d78e98d2SNate Williams 	 */
214d78e98d2SNate Williams 	while (isspace(*sct))
215d78e98d2SNate Williams 	    ++sct;
216d78e98d2SNate Williams 	if (!*sct) {
217d78e98d2SNate Williams 	    need = 1;
218d78e98d2SNate Williams 	    continue;
219d78e98d2SNate Williams 	}
220d78e98d2SNate Williams 
221b89321a5SAndrey A. Chernov 	/* preserve the first character of the new token
222d78e98d2SNate Williams 	 */
223d78e98d2SNate Williams 	sc_token[0] = *sct++;
224d78e98d2SNate Williams 
225b89321a5SAndrey A. Chernov 	/* then see what it is
226d78e98d2SNate Williams 	 */
227d78e98d2SNate Williams 	if (isdigit(sc_token[0])) {
228d78e98d2SNate Williams 	    while (isdigit(*sct))
229d78e98d2SNate Williams 		sc_token[++idx] = *sct++;
230d78e98d2SNate Williams 	    sc_token[++idx] = 0;
231d78e98d2SNate Williams 	    return sc_tokid = NUMBER;
232b89321a5SAndrey A. Chernov 	}
233b89321a5SAndrey A. Chernov 	else if (isalpha(sc_token[0])) {
234d78e98d2SNate Williams 	    while (isalpha(*sct))
235d78e98d2SNate Williams 		sc_token[++idx] = *sct++;
236d78e98d2SNate Williams 	    sc_token[++idx] = 0;
237d78e98d2SNate Williams 	    return parse_token(sc_token);
238d78e98d2SNate Williams 	}
239d78e98d2SNate Williams 	else if (sc_token[0] == ':' || sc_token[0] == '.')
240d78e98d2SNate Williams 	    return sc_tokid = DOT;
241d78e98d2SNate Williams 	else if (sc_token[0] == '+')
242d78e98d2SNate Williams 	    return sc_tokid = PLUS;
243b89321a5SAndrey A. Chernov 	else if (sc_token[0] == '/')
244d78e98d2SNate Williams 	    return sc_tokid = SLASH;
245d78e98d2SNate Williams 	else
246d78e98d2SNate Williams 	    return sc_tokid = JUNK;
247d78e98d2SNate Williams     } /* while (1) */
248d78e98d2SNate Williams } /* token */
249d78e98d2SNate Williams 
250d78e98d2SNate Williams 
251d78e98d2SNate Williams /*
252d78e98d2SNate Williams  * plonk() gives an appropriate error message if a token is incorrect
253d78e98d2SNate Williams  */
254d78e98d2SNate Williams static void
255b89321a5SAndrey A. Chernov plonk(int tok)
256d78e98d2SNate Williams {
257d78e98d2SNate Williams     panic((tok == EOF) ? "incomplete time"
258d78e98d2SNate Williams 		       : "garbled time");
259d78e98d2SNate Williams } /* plonk */
260d78e98d2SNate Williams 
261d78e98d2SNate Williams 
262d78e98d2SNate Williams /*
263d78e98d2SNate Williams  * expect() gets a token and dies most horribly if it's not the token we want
264d78e98d2SNate Williams  */
265d78e98d2SNate Williams static void
266ddcf8022SAndrey A. Chernov expect(int desired)
267d78e98d2SNate Williams {
268d78e98d2SNate Williams     if (token() != desired)
269d78e98d2SNate Williams 	plonk(sc_tokid);	/* and we die here... */
270d78e98d2SNate Williams } /* expect */
271d78e98d2SNate Williams 
272d78e98d2SNate Williams 
273d78e98d2SNate Williams /*
274d78e98d2SNate Williams  * dateadd() adds a number of minutes to a date.  It is extraordinarily
275d78e98d2SNate Williams  * stupid regarding day-of-month overflow, and will most likely not
276d78e98d2SNate Williams  * work properly
277d78e98d2SNate Williams  */
278d78e98d2SNate Williams static void
279b89321a5SAndrey A. Chernov dateadd(int minutes, struct tm *tm)
280d78e98d2SNate Williams {
281d78e98d2SNate Williams     /* increment days */
282d78e98d2SNate Williams 
283d78e98d2SNate Williams     while (minutes > 24*60) {
284d78e98d2SNate Williams 	minutes -= 24*60;
285d78e98d2SNate Williams 	tm->tm_mday++;
286d78e98d2SNate Williams     }
287d78e98d2SNate Williams 
288d78e98d2SNate Williams     /* increment hours */
289d78e98d2SNate Williams     while (minutes > 60) {
290d78e98d2SNate Williams 	minutes -= 60;
291d78e98d2SNate Williams 	tm->tm_hour++;
292d78e98d2SNate Williams 	if (tm->tm_hour > 23) {
293d78e98d2SNate Williams 	    tm->tm_mday++;
294d78e98d2SNate Williams 	    tm->tm_hour = 0;
295d78e98d2SNate Williams 	}
296d78e98d2SNate Williams     }
297d78e98d2SNate Williams 
298d78e98d2SNate Williams     /* increment minutes */
299d78e98d2SNate Williams     tm->tm_min += minutes;
300d78e98d2SNate Williams 
301d78e98d2SNate Williams     if (tm->tm_min > 59) {
302d78e98d2SNate Williams 	tm->tm_hour++;
303d78e98d2SNate Williams 	tm->tm_min -= 60;
304d78e98d2SNate Williams 
305d78e98d2SNate Williams 	if (tm->tm_hour > 23) {
306d78e98d2SNate Williams 	    tm->tm_mday++;
307d78e98d2SNate Williams 	    tm->tm_hour = 0;
308d78e98d2SNate Williams 	}
309d78e98d2SNate Williams     }
310d78e98d2SNate Williams } /* dateadd */
311d78e98d2SNate Williams 
312d78e98d2SNate Williams 
313d78e98d2SNate Williams /*
314d78e98d2SNate Williams  * plus() parses a now + time
315d78e98d2SNate Williams  *
316d78e98d2SNate Williams  *  at [NOW] PLUS NUMBER [MINUTES|HOURS|DAYS|WEEKS]
317d78e98d2SNate Williams  *
318d78e98d2SNate Williams  */
319d78e98d2SNate Williams static void
320b89321a5SAndrey A. Chernov plus(struct tm *tm)
321d78e98d2SNate Williams {
322d78e98d2SNate Williams     int delay;
323b89321a5SAndrey A. Chernov     int expectplur;
324d78e98d2SNate Williams 
325d78e98d2SNate Williams     expect(NUMBER);
326d78e98d2SNate Williams 
327d78e98d2SNate Williams     delay = atoi(sc_token);
328b89321a5SAndrey A. Chernov     expectplur = (delay != 1) ? 1 : 0;
329d78e98d2SNate Williams 
330d78e98d2SNate Williams     switch (token()) {
331d78e98d2SNate Williams     case WEEKS:
332d78e98d2SNate Williams 	    delay *= 7;
333d78e98d2SNate Williams     case DAYS:
334d78e98d2SNate Williams 	    delay *= 24;
335d78e98d2SNate Williams     case HOURS:
336d78e98d2SNate Williams 	    delay *= 60;
337d78e98d2SNate Williams     case MINUTES:
338b89321a5SAndrey A. Chernov 	    if (expectplur != sc_tokplur)
33912d20ef9SPhilippe Charnier 		warnx("pluralization is wrong");
340d78e98d2SNate Williams 	    dateadd(delay, tm);
341d78e98d2SNate Williams 	    return;
342d78e98d2SNate Williams     }
343d78e98d2SNate Williams     plonk(sc_tokid);
344d78e98d2SNate Williams } /* plus */
345d78e98d2SNate Williams 
346d78e98d2SNate Williams 
347d78e98d2SNate Williams /*
348d78e98d2SNate Williams  * tod() computes the time of day
349d78e98d2SNate Williams  *     [NUMBER [DOT NUMBER] [AM|PM]]
350d78e98d2SNate Williams  */
351d78e98d2SNate Williams static void
352b89321a5SAndrey A. Chernov tod(struct tm *tm)
353d78e98d2SNate Williams {
354d78e98d2SNate Williams     int hour, minute = 0;
355d78e98d2SNate Williams     int tlen;
356d78e98d2SNate Williams 
357d78e98d2SNate Williams     hour = atoi(sc_token);
358d78e98d2SNate Williams     tlen = strlen(sc_token);
359d78e98d2SNate Williams 
360b89321a5SAndrey A. Chernov     /* first pick out the time of day - if it's 4 digits, we assume
361d78e98d2SNate Williams      * a HHMM time, otherwise it's HH DOT MM time
362d78e98d2SNate Williams      */
363d78e98d2SNate Williams     if (token() == DOT) {
364d78e98d2SNate Williams 	expect(NUMBER);
365d78e98d2SNate Williams 	minute = atoi(sc_token);
366d78e98d2SNate Williams 	if (minute > 59)
367d78e98d2SNate Williams 	    panic("garbled time");
368d78e98d2SNate Williams 	token();
369b89321a5SAndrey A. Chernov     }
370b89321a5SAndrey A. Chernov     else if (tlen == 4) {
371d78e98d2SNate Williams 	minute = hour%100;
372d78e98d2SNate Williams 	if (minute > 59)
373d78e98d2SNate Williams 	    panic("garbeld time");
374d78e98d2SNate Williams 	hour = hour/100;
375d78e98d2SNate Williams     }
376d78e98d2SNate Williams 
377b89321a5SAndrey A. Chernov     /* check if an AM or PM specifier was given
378d78e98d2SNate Williams      */
379d78e98d2SNate Williams     if (sc_tokid == AM || sc_tokid == PM) {
380d78e98d2SNate Williams 	if (hour > 12)
381d78e98d2SNate Williams 	    panic("garbled time");
382d78e98d2SNate Williams 
3839b2ea11cSJohn Polstra 	if (sc_tokid == PM) {
3849b2ea11cSJohn Polstra 	    if (hour != 12)	/* 12:xx PM is 12:xx, not 24:xx */
385d78e98d2SNate Williams 			hour += 12;
3869b2ea11cSJohn Polstra 	} else {
3879b2ea11cSJohn Polstra 	    if (hour == 12)	/* 12:xx AM is 00:xx, not 12:xx */
38812d20ef9SPhilippe Charnier 			hour = 0;
3899b2ea11cSJohn Polstra 	}
390d78e98d2SNate Williams 	token();
391b89321a5SAndrey A. Chernov     }
392b89321a5SAndrey A. Chernov     else if (hour > 23)
393d78e98d2SNate Williams 	panic("garbled time");
394d78e98d2SNate Williams 
395b89321a5SAndrey A. Chernov     /* if we specify an absolute time, we don't want to bump the day even
396d78e98d2SNate Williams      * if we've gone past that time - but if we're specifying a time plus
397d78e98d2SNate Williams      * a relative offset, it's okay to bump things
398d78e98d2SNate Williams      */
399b89321a5SAndrey A. Chernov     if ((sc_tokid == EOF || sc_tokid == PLUS) && tm->tm_hour > hour) {
400d78e98d2SNate Williams 	tm->tm_mday++;
401b89321a5SAndrey A. Chernov 	tm->tm_wday++;
402b89321a5SAndrey A. Chernov     }
403d78e98d2SNate Williams 
404d78e98d2SNate Williams     tm->tm_hour = hour;
405d78e98d2SNate Williams     tm->tm_min = minute;
406d78e98d2SNate Williams     if (tm->tm_hour == 24) {
407d78e98d2SNate Williams 	tm->tm_hour = 0;
408d78e98d2SNate Williams 	tm->tm_mday++;
409d78e98d2SNate Williams     }
410d78e98d2SNate Williams } /* tod */
411d78e98d2SNate Williams 
412d78e98d2SNate Williams 
413d78e98d2SNate Williams /*
414d78e98d2SNate Williams  * assign_date() assigns a date, wrapping to next year if needed
415d78e98d2SNate Williams  */
416d78e98d2SNate Williams static void
417b89321a5SAndrey A. Chernov assign_date(struct tm *tm, long mday, long mon, long year)
418d78e98d2SNate Williams {
419d78e98d2SNate Williams     if (year > 99) {
420d78e98d2SNate Williams 	if (year > 1899)
421d78e98d2SNate Williams 	    year -= 1900;
422d78e98d2SNate Williams 	else
423d78e98d2SNate Williams 	    panic("garbled time");
4241dbfc421SAlexander Langer     } else {
4251dbfc421SAlexander Langer 	struct tm *lt;
4261dbfc421SAlexander Langer 	time_t now;
4271dbfc421SAlexander Langer 
4281dbfc421SAlexander Langer 	time(&now);
4291dbfc421SAlexander Langer 	lt = localtime(&now);
4301dbfc421SAlexander Langer 
4311dbfc421SAlexander Langer 	/*
4321dbfc421SAlexander Langer 	 * check if the specified year is in the next century.
4331dbfc421SAlexander Langer 	 * allow for one year of user error as many people will
4341dbfc421SAlexander Langer 	 * enter n - 1 at the start of year n.
4351dbfc421SAlexander Langer 	 */
4361dbfc421SAlexander Langer 	if (year < (lt->tm_year % 100) - 1)
4371dbfc421SAlexander Langer 	    year += 100;
4381dbfc421SAlexander Langer 	/* adjust for the year 2000 and beyond */
4391dbfc421SAlexander Langer 	year += lt->tm_year - (lt->tm_year % 100);
440d78e98d2SNate Williams     }
441d78e98d2SNate Williams 
442d78e98d2SNate Williams     if (year < 0 &&
443d78e98d2SNate Williams 	(tm->tm_mon > mon ||(tm->tm_mon == mon && tm->tm_mday > mday)))
444d78e98d2SNate Williams 	year = tm->tm_year + 1;
445d78e98d2SNate Williams 
446d78e98d2SNate Williams     tm->tm_mday = mday;
447d78e98d2SNate Williams     tm->tm_mon = mon;
448d78e98d2SNate Williams 
449d78e98d2SNate Williams     if (year >= 0)
450d78e98d2SNate Williams 	tm->tm_year = year;
451d78e98d2SNate Williams } /* assign_date */
452d78e98d2SNate Williams 
453d78e98d2SNate Williams 
454d78e98d2SNate Williams /*
455d78e98d2SNate Williams  * month() picks apart a month specification
456d78e98d2SNate Williams  *
457d78e98d2SNate Williams  *  /[<month> NUMBER [NUMBER]]           \
458d78e98d2SNate Williams  *  |[TOMORROW]                          |
459b89321a5SAndrey A. Chernov  *  |[DAY OF WEEK]                       |
460d78e98d2SNate Williams  *  |NUMBER [SLASH NUMBER [SLASH NUMBER]]|
461d78e98d2SNate Williams  *  \PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS/
462d78e98d2SNate Williams  */
463d78e98d2SNate Williams static void
464b89321a5SAndrey A. Chernov month(struct tm *tm)
465d78e98d2SNate Williams {
466d78e98d2SNate Williams     long year= (-1);
467b89321a5SAndrey A. Chernov     long mday, wday, mon;
468d78e98d2SNate Williams     int tlen;
469d78e98d2SNate Williams 
470d78e98d2SNate Williams     switch (sc_tokid) {
471d78e98d2SNate Williams     case PLUS:
472d78e98d2SNate Williams 	    plus(tm);
473d78e98d2SNate Williams 	    break;
474d78e98d2SNate Williams 
475d78e98d2SNate Williams     case TOMORROW:
476d78e98d2SNate Williams 	    /* do something tomorrow */
477d78e98d2SNate Williams 	    tm->tm_mday ++;
478b89321a5SAndrey A. Chernov 	    tm->tm_wday ++;
479d78e98d2SNate Williams     case TODAY:	/* force ourselves to stay in today - no further processing */
480d78e98d2SNate Williams 	    token();
481d78e98d2SNate Williams 	    break;
482d78e98d2SNate Williams 
483d78e98d2SNate Williams     case JAN: case FEB: case MAR: case APR: case MAY: case JUN:
484d78e98d2SNate Williams     case JUL: case AUG: case SEP: case OCT: case NOV: case DEC:
485b89321a5SAndrey A. Chernov 	    /* do month mday [year]
486d78e98d2SNate Williams 	     */
487d78e98d2SNate Williams 	    mon = (sc_tokid-JAN);
488d78e98d2SNate Williams 	    expect(NUMBER);
48968abb9f9SBruce Evans 	    mday = atol(sc_token);
490d78e98d2SNate Williams 	    if (token() == NUMBER) {
491d78e98d2SNate Williams 		year = atol(sc_token);
492d78e98d2SNate Williams 		token();
493d78e98d2SNate Williams 	    }
494d78e98d2SNate Williams 	    assign_date(tm, mday, mon, year);
495d78e98d2SNate Williams 	    break;
496d78e98d2SNate Williams 
497b89321a5SAndrey A. Chernov     case SUN: case MON: case TUE:
498b89321a5SAndrey A. Chernov     case WED: case THU: case FRI:
499b89321a5SAndrey A. Chernov     case SAT:
500b89321a5SAndrey A. Chernov 	    /* do a particular day of the week
501b89321a5SAndrey A. Chernov 	     */
502b89321a5SAndrey A. Chernov 	    wday = (sc_tokid-SUN);
503b89321a5SAndrey A. Chernov 
504b89321a5SAndrey A. Chernov 	    mday = tm->tm_mday;
505b89321a5SAndrey A. Chernov 
506b89321a5SAndrey A. Chernov 	    /* if this day is < today, then roll to next week
507b89321a5SAndrey A. Chernov 	     */
508b89321a5SAndrey A. Chernov 	    if (wday < tm->tm_wday)
509b89321a5SAndrey A. Chernov 		mday += 7 - (tm->tm_wday - wday);
510b89321a5SAndrey A. Chernov 	    else
511b89321a5SAndrey A. Chernov 		mday += (wday - tm->tm_wday);
512b89321a5SAndrey A. Chernov 
513b89321a5SAndrey A. Chernov 	    tm->tm_wday = wday;
514b89321a5SAndrey A. Chernov 
515b89321a5SAndrey A. Chernov 	    assign_date(tm, mday, tm->tm_mon, tm->tm_year);
516b89321a5SAndrey A. Chernov 	    break;
517b89321a5SAndrey A. Chernov 
518d78e98d2SNate Williams     case NUMBER:
519b89321a5SAndrey A. Chernov 	    /* get numeric MMDDYY, mm/dd/yy, or dd.mm.yy
520d78e98d2SNate Williams 	     */
521d78e98d2SNate Williams 	    tlen = strlen(sc_token);
522d78e98d2SNate Williams 	    mon = atol(sc_token);
523d78e98d2SNate Williams 	    token();
524d78e98d2SNate Williams 
525d78e98d2SNate Williams 	    if (sc_tokid == SLASH || sc_tokid == DOT) {
526ddcf8022SAndrey A. Chernov 		int sep;
527d78e98d2SNate Williams 
528d78e98d2SNate Williams 		sep = sc_tokid;
529d78e98d2SNate Williams 		expect(NUMBER);
530d78e98d2SNate Williams 		mday = atol(sc_token);
531d78e98d2SNate Williams 		if (token() == sep) {
532d78e98d2SNate Williams 		    expect(NUMBER);
533d78e98d2SNate Williams 		    year = atol(sc_token);
534d78e98d2SNate Williams 		    token();
535d78e98d2SNate Williams 		}
536d78e98d2SNate Williams 
537b89321a5SAndrey A. Chernov 		/* flip months and days for european timing
538d78e98d2SNate Williams 		 */
539d78e98d2SNate Williams 		if (sep == DOT) {
540d78e98d2SNate Williams 		    int x = mday;
541d78e98d2SNate Williams 		    mday = mon;
542d78e98d2SNate Williams 		    mon = x;
543d78e98d2SNate Williams 		}
544b89321a5SAndrey A. Chernov 	    }
545b89321a5SAndrey A. Chernov 	    else if (tlen == 6 || tlen == 8) {
546d78e98d2SNate Williams 		if (tlen == 8) {
547d78e98d2SNate Williams 		    year = (mon % 10000) - 1900;
548d78e98d2SNate Williams 		    mon /= 10000;
549b89321a5SAndrey A. Chernov 		}
550b89321a5SAndrey A. Chernov 		else {
551d78e98d2SNate Williams 		    year = mon % 100;
552d78e98d2SNate Williams 		    mon /= 100;
553d78e98d2SNate Williams 		}
554d78e98d2SNate Williams 		mday = mon % 100;
555d78e98d2SNate Williams 		mon /= 100;
556b89321a5SAndrey A. Chernov 	    }
557b89321a5SAndrey A. Chernov 	    else
558d78e98d2SNate Williams 		panic("garbled time");
559d78e98d2SNate Williams 
560d78e98d2SNate Williams 	    mon--;
561d78e98d2SNate Williams 	    if (mon < 0 || mon > 11 || mday < 1 || mday > 31)
562d78e98d2SNate Williams 		panic("garbled time");
563d78e98d2SNate Williams 
564d78e98d2SNate Williams 	    assign_date(tm, mday, mon, year);
565d78e98d2SNate Williams 	    break;
566d78e98d2SNate Williams     } /* case */
567d78e98d2SNate Williams } /* month */
568d78e98d2SNate Williams 
569d78e98d2SNate Williams 
570d78e98d2SNate Williams /* Global functions */
571d78e98d2SNate Williams 
572d78e98d2SNate Williams time_t
573b89321a5SAndrey A. Chernov parsetime(int argc, char **argv)
574d78e98d2SNate Williams {
575b89321a5SAndrey A. Chernov /* Do the argument parsing, die if necessary, and return the time the job
576d78e98d2SNate Williams  * should be run.
577d78e98d2SNate Williams  */
578d78e98d2SNate Williams     time_t nowtimer, runtimer;
579d78e98d2SNate Williams     struct tm nowtime, runtime;
580d78e98d2SNate Williams     int hr = 0;
581d78e98d2SNate Williams     /* this MUST be initialized to zero for midnight/noon/teatime */
582d78e98d2SNate Williams 
583d78e98d2SNate Williams     nowtimer = time(NULL);
584d78e98d2SNate Williams     nowtime = *localtime(&nowtimer);
585d78e98d2SNate Williams 
586d78e98d2SNate Williams     runtime = nowtime;
587d78e98d2SNate Williams     runtime.tm_sec = 0;
588d78e98d2SNate Williams     runtime.tm_isdst = 0;
589d78e98d2SNate Williams 
590d78e98d2SNate Williams     if (argc <= optind)
591d78e98d2SNate Williams 	usage();
592d78e98d2SNate Williams 
593d78e98d2SNate Williams     init_scanner(argc-optind, argv+optind);
594d78e98d2SNate Williams 
595d78e98d2SNate Williams     switch (token()) {
596d78e98d2SNate Williams     case NOW:	/* now is optional prefix for PLUS tree */
597d78e98d2SNate Williams 	    expect(PLUS);
598d78e98d2SNate Williams     case PLUS:
599d78e98d2SNate Williams 	    plus(&runtime);
600d78e98d2SNate Williams 	    break;
601d78e98d2SNate Williams 
602d78e98d2SNate Williams     case NUMBER:
603d78e98d2SNate Williams 	    tod(&runtime);
604d78e98d2SNate Williams 	    month(&runtime);
605d78e98d2SNate Williams 	    break;
606d78e98d2SNate Williams 
607b89321a5SAndrey A. Chernov 	    /* evil coding for TEATIME|NOON|MIDNIGHT - we've initialised
608d78e98d2SNate Williams 	     * hr to zero up above, then fall into this case in such a
609d78e98d2SNate Williams 	     * way so we add +12 +4 hours to it for teatime, +12 hours
610d78e98d2SNate Williams 	     * to it for noon, and nothing at all for midnight, then
611d78e98d2SNate Williams 	     * set our runtime to that hour before leaping into the
612d78e98d2SNate Williams 	     * month scanner
613d78e98d2SNate Williams 	     */
614d78e98d2SNate Williams     case TEATIME:
615d78e98d2SNate Williams 	    hr += 4;
616d78e98d2SNate Williams     case NOON:
617d78e98d2SNate Williams 	    hr += 12;
618d78e98d2SNate Williams     case MIDNIGHT:
619b89321a5SAndrey A. Chernov 	    if (runtime.tm_hour >= hr) {
620d78e98d2SNate Williams 		runtime.tm_mday++;
621b89321a5SAndrey A. Chernov 		runtime.tm_wday++;
622b89321a5SAndrey A. Chernov 	    }
623d78e98d2SNate Williams 	    runtime.tm_hour = hr;
624d78e98d2SNate Williams 	    runtime.tm_min = 0;
625d78e98d2SNate Williams 	    token();
626d78e98d2SNate Williams 	    /* fall through to month setting */
627d78e98d2SNate Williams     default:
628d78e98d2SNate Williams 	    month(&runtime);
629d78e98d2SNate Williams 	    break;
630d78e98d2SNate Williams     } /* ugly case statement */
631d78e98d2SNate Williams     expect(EOF);
632d78e98d2SNate Williams 
633b89321a5SAndrey A. Chernov     /* adjust for daylight savings time
634d78e98d2SNate Williams      */
635d78e98d2SNate Williams     runtime.tm_isdst = -1;
636d78e98d2SNate Williams     runtimer = mktime(&runtime);
637d78e98d2SNate Williams     if (runtime.tm_isdst > 0) {
638d78e98d2SNate Williams 	runtimer -= 3600;
639d78e98d2SNate Williams 	runtimer = mktime(&runtime);
640d78e98d2SNate Williams     }
641d78e98d2SNate Williams 
642d78e98d2SNate Williams     if (runtimer < 0)
643d78e98d2SNate Williams 	panic("garbled time");
644d78e98d2SNate Williams 
645d78e98d2SNate Williams     if (nowtimer > runtimer)
646d78e98d2SNate Williams 	panic("Trying to travel back in time");
647d78e98d2SNate Williams 
648d78e98d2SNate Williams     return runtimer;
649d78e98d2SNate Williams } /* parsetime */
650