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