parsetime.c revision 1.12 1 1.12 dsl /* $NetBSD: parsetime.c,v 1.12 2004/10/30 16:55:06 dsl 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.10 mjl #include <ctype.h>
42 1.1 cgd #include <errno.h>
43 1.1 cgd #include <stdio.h>
44 1.1 cgd #include <stdlib.h>
45 1.1 cgd #include <string.h>
46 1.1 cgd #include <time.h>
47 1.6 christos #include <tzfile.h>
48 1.1 cgd #include <unistd.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.10 mjl static size_t sc_len; /* scanner - length 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.12 dsl __RCSID("$NetBSD: parsetime.c,v 1.12 2004/10/30 16:55:06 dsl Exp $");
145 1.7 christos #endif
146 1.3 glass #endif
147 1.1 cgd
148 1.1 cgd /* Local functions */
149 1.11 mjl static void assign_date (struct tm *, long, long, long);
150 1.11 mjl static void dateadd (int, struct tm *);
151 1.11 mjl static void expect (int);
152 1.11 mjl static void init_scanner (int, char **);
153 1.11 mjl static void month (struct tm *);
154 1.11 mjl static int parse_token (char *);
155 1.11 mjl static void plonk (int);
156 1.11 mjl static void plus (struct tm *);
157 1.11 mjl static void tod (struct tm *);
158 1.11 mjl static int token (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.11 mjl parse_token(char *arg)
165 1.1 cgd {
166 1.7 christos int i;
167 1.1 cgd
168 1.7 christos for (i=0; i < sizeof(Specials) / sizeof(Specials[0]); i++) {
169 1.7 christos if (strcasecmp(Specials[i].name, arg) == 0) {
170 1.7 christos sc_tokplur = Specials[i].plural;
171 1.7 christos return (sc_tokid = Specials[i].value);
172 1.7 christos }
173 1.1 cgd }
174 1.1 cgd
175 1.7 christos /* not special - must be some random id */
176 1.7 christos return (ID);
177 1.1 cgd } /* parse_token */
178 1.1 cgd
179 1.1 cgd
180 1.1 cgd /*
181 1.1 cgd * init_scanner() sets up the scanner to eat arguments
182 1.1 cgd */
183 1.1 cgd static void
184 1.11 mjl init_scanner(int argc, char **argv)
185 1.1 cgd {
186 1.7 christos scp = argv;
187 1.7 christos scc = argc;
188 1.7 christos need = 1;
189 1.7 christos sc_len = 1;
190 1.7 christos while (argc-- > 0)
191 1.7 christos sc_len += strlen(*argv++);
192 1.7 christos
193 1.7 christos if ((sc_token = (char *) malloc(sc_len)) == NULL)
194 1.7 christos panic("Insufficient virtual memory");
195 1.1 cgd } /* init_scanner */
196 1.1 cgd
197 1.1 cgd /*
198 1.1 cgd * token() fetches a token from the input stream
199 1.1 cgd */
200 1.1 cgd static int
201 1.11 mjl token(void)
202 1.1 cgd {
203 1.7 christos int idx;
204 1.1 cgd
205 1.7 christos while (1) {
206 1.7 christos (void)memset(sc_token, 0, sc_len);
207 1.7 christos sc_tokid = EOF;
208 1.7 christos sc_tokplur = 0;
209 1.7 christos idx = 0;
210 1.1 cgd
211 1.7 christos /*
212 1.7 christos * if we need to read another argument, walk along the
213 1.7 christos * argument list; when we fall off the arglist, we'll
214 1.7 christos * just return EOF forever
215 1.7 christos */
216 1.7 christos if (need) {
217 1.7 christos if (scc < 1)
218 1.7 christos return (sc_tokid);
219 1.7 christos sct = *scp;
220 1.7 christos scp++;
221 1.7 christos scc--;
222 1.7 christos need = 0;
223 1.7 christos }
224 1.7 christos /*
225 1.7 christos * eat whitespace now - if we walk off the end of the argument,
226 1.7 christos * we'll continue, which puts us up at the top of the while loop
227 1.7 christos * to fetch the next argument in
228 1.7 christos */
229 1.12 dsl while (isspace((unsigned char)*sct))
230 1.7 christos ++sct;
231 1.7 christos if (!*sct) {
232 1.7 christos need = 1;
233 1.7 christos continue;
234 1.7 christos }
235 1.1 cgd
236 1.7 christos /*
237 1.7 christos * preserve the first character of the new token
238 1.7 christos */
239 1.7 christos sc_token[0] = *sct++;
240 1.1 cgd
241 1.7 christos /*
242 1.7 christos * then see what it is
243 1.7 christos */
244 1.12 dsl if (isdigit((unsigned char)sc_token[0])) {
245 1.12 dsl while (isdigit((unsigned char)*sct))
246 1.7 christos sc_token[++idx] = *sct++;
247 1.7 christos sc_token[++idx] = 0;
248 1.7 christos return ((sc_tokid = NUMBER));
249 1.12 dsl } else if (isalpha((unsigned char)sc_token[0])) {
250 1.12 dsl while (isalpha((unsigned char)*sct))
251 1.7 christos sc_token[++idx] = *sct++;
252 1.7 christos sc_token[++idx] = 0;
253 1.7 christos return (parse_token(sc_token));
254 1.7 christos }
255 1.7 christos else if (sc_token[0] == ':' || sc_token[0] == '.')
256 1.7 christos return ((sc_tokid = DOT));
257 1.7 christos else if (sc_token[0] == '+')
258 1.7 christos return ((sc_tokid = PLUS));
259 1.7 christos else if (sc_token[0] == '/')
260 1.7 christos return ((sc_tokid = SLASH));
261 1.7 christos else
262 1.7 christos return ((sc_tokid = JUNK));
263 1.7 christos } /* while (1) */
264 1.1 cgd } /* token */
265 1.1 cgd
266 1.1 cgd
267 1.1 cgd /*
268 1.1 cgd * plonk() gives an appropriate error message if a token is incorrect
269 1.1 cgd */
270 1.1 cgd static void
271 1.11 mjl plonk(int tok)
272 1.1 cgd {
273 1.7 christos panic((tok == EOF) ? "incomplete time" : "garbled time");
274 1.1 cgd } /* plonk */
275 1.1 cgd
276 1.1 cgd
277 1.1 cgd /*
278 1.1 cgd * expect() gets a token and dies most horribly if it's not the token we want
279 1.1 cgd */
280 1.1 cgd static void
281 1.11 mjl expect(int desired)
282 1.1 cgd {
283 1.7 christos if (token() != desired)
284 1.7 christos plonk(sc_tokid); /* and we die here... */
285 1.1 cgd } /* expect */
286 1.1 cgd
287 1.1 cgd
288 1.1 cgd /*
289 1.1 cgd * dateadd() adds a number of minutes to a date. It is extraordinarily
290 1.1 cgd * stupid regarding day-of-month overflow, and will most likely not
291 1.1 cgd * work properly
292 1.1 cgd */
293 1.1 cgd static void
294 1.11 mjl dateadd(int minutes, struct tm *tm)
295 1.1 cgd {
296 1.7 christos /* increment days */
297 1.7 christos
298 1.7 christos while (minutes > 24*60) {
299 1.7 christos minutes -= 24*60;
300 1.7 christos tm->tm_mday++;
301 1.7 christos }
302 1.1 cgd
303 1.7 christos /* increment hours */
304 1.7 christos while (minutes > 60) {
305 1.7 christos minutes -= 60;
306 1.7 christos tm->tm_hour++;
307 1.7 christos if (tm->tm_hour > 23) {
308 1.7 christos tm->tm_mday++;
309 1.7 christos tm->tm_hour = 0;
310 1.7 christos }
311 1.7 christos }
312 1.7 christos
313 1.7 christos /* increment minutes */
314 1.7 christos tm->tm_min += minutes;
315 1.7 christos
316 1.7 christos if (tm->tm_min > 59) {
317 1.7 christos tm->tm_hour++;
318 1.7 christos tm->tm_min -= 60;
319 1.7 christos
320 1.7 christos if (tm->tm_hour > 23) {
321 1.7 christos tm->tm_mday++;
322 1.7 christos tm->tm_hour = 0;
323 1.7 christos }
324 1.1 cgd }
325 1.1 cgd } /* dateadd */
326 1.1 cgd
327 1.1 cgd
328 1.1 cgd /*
329 1.1 cgd * plus() parses a now + time
330 1.1 cgd *
331 1.1 cgd * at [NOW] PLUS NUMBER [MINUTES|HOURS|DAYS|WEEKS]
332 1.1 cgd *
333 1.1 cgd */
334 1.1 cgd static void
335 1.11 mjl plus(struct tm *tm)
336 1.1 cgd {
337 1.7 christos int delay;
338 1.7 christos int expectplur;
339 1.1 cgd
340 1.7 christos expect(NUMBER);
341 1.7 christos
342 1.7 christos delay = atoi(sc_token);
343 1.7 christos expectplur = (delay != 1) ? 1 : 0;
344 1.1 cgd
345 1.7 christos switch (token()) {
346 1.7 christos case WEEKS:
347 1.7 christos delay *= 7;
348 1.7 christos case DAYS:
349 1.7 christos delay *= 24;
350 1.7 christos case HOURS:
351 1.7 christos delay *= 60;
352 1.7 christos case MINUTES:
353 1.7 christos if (expectplur != sc_tokplur)
354 1.7 christos (void)fprintf(stderr, "at: pluralization is wrong\n");
355 1.7 christos dateadd(delay, tm);
356 1.7 christos return;
357 1.7 christos }
358 1.1 cgd
359 1.7 christos plonk(sc_tokid);
360 1.1 cgd } /* plus */
361 1.1 cgd
362 1.1 cgd
363 1.1 cgd /*
364 1.1 cgd * tod() computes the time of day
365 1.1 cgd * [NUMBER [DOT NUMBER] [AM|PM]]
366 1.1 cgd */
367 1.1 cgd static void
368 1.11 mjl tod(struct tm *tm)
369 1.1 cgd {
370 1.7 christos int hour, minute = 0;
371 1.7 christos size_t tlen;
372 1.1 cgd
373 1.7 christos hour = atoi(sc_token);
374 1.7 christos tlen = strlen(sc_token);
375 1.7 christos
376 1.7 christos /*
377 1.7 christos * first pick out the time of day - if it's 4 digits, we assume
378 1.7 christos * a HHMM time, otherwise it's HH DOT MM time
379 1.7 christos */
380 1.7 christos if (token() == DOT) {
381 1.7 christos expect(NUMBER);
382 1.7 christos minute = atoi(sc_token);
383 1.7 christos token();
384 1.7 christos } else if (tlen == 4) {
385 1.7 christos minute = hour % 100;
386 1.7 christos hour = hour / 100;
387 1.7 christos }
388 1.7 christos
389 1.7 christos if (minute > 59)
390 1.7 christos panic("garbled time");
391 1.1 cgd
392 1.7 christos /*
393 1.7 christos * check if an AM or PM specifier was given
394 1.7 christos */
395 1.7 christos if (sc_tokid == AM || sc_tokid == PM) {
396 1.7 christos if (hour > 12)
397 1.7 christos panic("garbled time");
398 1.7 christos
399 1.7 christos if (sc_tokid == PM) {
400 1.7 christos if (hour != 12) /* 12:xx PM is 12:xx, not 24:xx */
401 1.7 christos hour += 12;
402 1.7 christos } else {
403 1.7 christos if (hour == 12) /* 12:xx AM is 00:xx, not 12:xx */
404 1.7 christos hour = 0;
405 1.7 christos }
406 1.7 christos token();
407 1.7 christos } else if (hour > 23)
408 1.7 christos panic("garbled time");
409 1.7 christos
410 1.7 christos /*
411 1.7 christos * if we specify an absolute time, we don't want to bump the day even
412 1.7 christos * if we've gone past that time - but if we're specifying a time plus
413 1.7 christos * a relative offset, it's okay to bump things
414 1.7 christos */
415 1.7 christos if ((sc_tokid == EOF || sc_tokid == PLUS) && tm->tm_hour > hour) {
416 1.7 christos tm->tm_mday++;
417 1.7 christos tm->tm_wday++;
418 1.7 christos }
419 1.1 cgd
420 1.7 christos tm->tm_hour = hour;
421 1.7 christos tm->tm_min = minute;
422 1.1 cgd } /* tod */
423 1.1 cgd
424 1.1 cgd
425 1.1 cgd /*
426 1.1 cgd * assign_date() assigns a date, wrapping to next year if needed
427 1.1 cgd */
428 1.1 cgd static void
429 1.11 mjl assign_date(struct tm *tm, long mday, long mon, long year)
430 1.1 cgd {
431 1.7 christos if (year > 99) {
432 1.7 christos if (year >= TM_YEAR_BASE)
433 1.7 christos year -= TM_YEAR_BASE;
434 1.7 christos else
435 1.7 christos panic("garbled time");
436 1.7 christos }
437 1.7 christos
438 1.7 christos if (year >= 0) {
439 1.8 tron if (year < 70)
440 1.7 christos tm->tm_year = year + 2000 - TM_YEAR_BASE;
441 1.7 christos else
442 1.7 christos tm->tm_year = year + 1900 - TM_YEAR_BASE;
443 1.7 christos }
444 1.7 christos else { /* year < 0 */
445 1.7 christos if (tm->tm_mon > mon ||
446 1.7 christos (tm->tm_mon == mon && tm->tm_mday > mday))
447 1.7 christos tm->tm_year++;
448 1.7 christos }
449 1.1 cgd
450 1.7 christos tm->tm_mday = mday;
451 1.7 christos tm->tm_mon = mon;
452 1.1 cgd } /* assign_date */
453 1.1 cgd
454 1.1 cgd
455 1.1 cgd /*
456 1.1 cgd * month() picks apart a month specification
457 1.1 cgd *
458 1.1 cgd * /[<month> NUMBER [NUMBER]] \
459 1.1 cgd * |[TOMORROW] |
460 1.7 christos * |[DAY OF WEEK] |
461 1.1 cgd * |NUMBER [SLASH NUMBER [SLASH NUMBER]]|
462 1.1 cgd * \PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS/
463 1.1 cgd */
464 1.1 cgd static void
465 1.11 mjl month(struct tm *tm)
466 1.1 cgd {
467 1.7 christos int year = (-1);
468 1.7 christos int mday, wday, mon;
469 1.7 christos size_t tlen;
470 1.7 christos
471 1.7 christos mday = 0;
472 1.7 christos switch (sc_tokid) {
473 1.7 christos case PLUS:
474 1.7 christos plus(tm);
475 1.7 christos break;
476 1.7 christos
477 1.7 christos case TOMORROW:
478 1.7 christos /* do something tomorrow */
479 1.7 christos tm->tm_mday++;
480 1.7 christos tm->tm_wday++;
481 1.7 christos case TODAY:
482 1.7 christos /* force ourselves to stay in today - no further processing */
483 1.1 cgd token();
484 1.7 christos break;
485 1.1 cgd
486 1.7 christos case JAN: case FEB: case MAR: case APR: case MAY: case JUN:
487 1.7 christos case JUL: case AUG: case SEP: case OCT: case NOV: case DEC:
488 1.7 christos /*
489 1.7 christos * do month mday [year]
490 1.7 christos */
491 1.7 christos mon = sc_tokid - JAN;
492 1.1 cgd expect(NUMBER);
493 1.7 christos mday = atoi(sc_token);
494 1.7 christos if (token() == NUMBER) {
495 1.7 christos year = atoi(sc_token);
496 1.7 christos token();
497 1.1 cgd }
498 1.7 christos assign_date(tm, mday, mon, year);
499 1.7 christos break;
500 1.7 christos
501 1.7 christos case SUN: case MON: case TUE:
502 1.7 christos case WED: case THU: case FRI:
503 1.7 christos case SAT:
504 1.7 christos /* do a particular day of the week */
505 1.7 christos wday = sc_tokid - SUN;
506 1.7 christos
507 1.7 christos mday = tm->tm_mday;
508 1.7 christos
509 1.7 christos /* if this day is < today, then roll to next week */
510 1.7 christos if (wday < tm->tm_wday)
511 1.7 christos mday += 7 - (tm->tm_wday - wday);
512 1.7 christos else
513 1.7 christos mday += (wday - tm->tm_wday);
514 1.7 christos
515 1.7 christos tm->tm_wday = wday;
516 1.1 cgd
517 1.9 tron assign_date(tm, mday, tm->tm_mon, tm->tm_year + TM_YEAR_BASE);
518 1.7 christos break;
519 1.7 christos
520 1.7 christos case NUMBER:
521 1.1 cgd /*
522 1.7 christos * get numeric MMDDYY, mm/dd/yy, or dd.mm.yy
523 1.1 cgd */
524 1.7 christos tlen = strlen(sc_token);
525 1.7 christos mon = atoi(sc_token);
526 1.7 christos token();
527 1.1 cgd
528 1.7 christos if (sc_tokid == SLASH || sc_tokid == DOT) {
529 1.7 christos int sep;
530 1.1 cgd
531 1.7 christos sep = sc_tokid;
532 1.7 christos expect(NUMBER);
533 1.7 christos mday = atoi(sc_token);
534 1.7 christos if (token() == sep) {
535 1.7 christos expect(NUMBER);
536 1.7 christos year = atoi(sc_token);
537 1.7 christos token();
538 1.7 christos }
539 1.7 christos
540 1.7 christos /*
541 1.7 christos * flip months and days for european timing
542 1.7 christos */
543 1.7 christos if (sep == DOT) {
544 1.7 christos int x = mday;
545 1.7 christos mday = mon;
546 1.7 christos mon = x;
547 1.7 christos }
548 1.7 christos } else if (tlen == 6 || tlen == 8) {
549 1.7 christos if (tlen == 8) {
550 1.7 christos year = (mon % 10000) - 1900;
551 1.7 christos mon /= 10000;
552 1.7 christos } else {
553 1.7 christos year = mon % 100;
554 1.7 christos mon /= 100;
555 1.7 christos }
556 1.7 christos mday = mon % 100;
557 1.7 christos mon /= 100;
558 1.7 christos } else
559 1.7 christos panic("garbled time");
560 1.7 christos
561 1.7 christos mon--;
562 1.7 christos if (mon < 0 || mon > 11 || mday < 1 || mday > 31)
563 1.7 christos panic("garbled time");
564 1.7 christos
565 1.7 christos assign_date(tm, mday, mon, year);
566 1.7 christos break;
567 1.7 christos } /* case */
568 1.1 cgd } /* month */
569 1.1 cgd
570 1.1 cgd
571 1.1 cgd /* Global functions */
572 1.1 cgd
573 1.1 cgd time_t
574 1.11 mjl parsetime(int argc, char **argv)
575 1.1 cgd {
576 1.7 christos /*
577 1.7 christos * Do the argument parsing, die if necessary, and return the
578 1.7 christos * time the job should be run.
579 1.7 christos */
580 1.7 christos time_t nowtimer, runtimer;
581 1.7 christos struct tm nowtime, runtime;
582 1.7 christos int hr = 0;
583 1.7 christos /* this MUST be initialized to zero for midnight/noon/teatime */
584 1.7 christos
585 1.7 christos nowtimer = time(NULL);
586 1.7 christos nowtime = *localtime(&nowtimer);
587 1.7 christos
588 1.7 christos runtime = nowtime;
589 1.7 christos runtime.tm_sec = 0;
590 1.7 christos runtime.tm_isdst = 0;
591 1.7 christos
592 1.7 christos if (argc <= optind)
593 1.7 christos usage();
594 1.7 christos
595 1.7 christos init_scanner(argc - optind, argv + optind);
596 1.7 christos
597 1.7 christos switch (token()) {
598 1.7 christos case NOW: /* now is optional prefix for PLUS tree */
599 1.7 christos expect(PLUS);
600 1.7 christos case PLUS:
601 1.7 christos plus(&runtime);
602 1.7 christos break;
603 1.7 christos
604 1.7 christos case NUMBER:
605 1.7 christos tod(&runtime);
606 1.7 christos month(&runtime);
607 1.7 christos break;
608 1.7 christos
609 1.7 christos /*
610 1.7 christos * evil coding for TEATIME|NOON|MIDNIGHT - we've initialised
611 1.7 christos * hr to zero up above, then fall into this case in such a
612 1.7 christos * way so we add +12 +4 hours to it for teatime, +12 hours
613 1.7 christos * to it for noon, and nothing at all for midnight, then
614 1.7 christos * set our runtime to that hour before leaping into the
615 1.7 christos * month scanner
616 1.7 christos */
617 1.7 christos case TEATIME:
618 1.7 christos hr += 4;
619 1.7 christos case NOON:
620 1.7 christos hr += 12;
621 1.7 christos case MIDNIGHT:
622 1.7 christos if (runtime.tm_hour >= hr) {
623 1.7 christos runtime.tm_mday++;
624 1.7 christos runtime.tm_wday++;
625 1.7 christos }
626 1.7 christos runtime.tm_hour = hr;
627 1.7 christos runtime.tm_min = 0;
628 1.7 christos token();
629 1.7 christos /* fall through to month setting */
630 1.7 christos default:
631 1.7 christos month(&runtime);
632 1.7 christos break;
633 1.7 christos } /* ugly case statement */
634 1.7 christos expect(EOF);
635 1.7 christos
636 1.7 christos /*
637 1.7 christos * adjust for daylight savings time
638 1.7 christos */
639 1.7 christos runtime.tm_isdst = -1;
640 1.1 cgd runtimer = mktime(&runtime);
641 1.7 christos if (runtime.tm_isdst > 0) {
642 1.7 christos runtimer -= 3600;
643 1.7 christos runtimer = mktime(&runtime);
644 1.7 christos }
645 1.1 cgd
646 1.7 christos if (runtimer < 0)
647 1.7 christos panic("garbled 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.7 christos return (runtimer);
653 1.1 cgd } /* parsetime */
654