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