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