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