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