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