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