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