1 |
|
|
/* $OpenBSD: parsetime.c,v 1.26 2015/11/11 17:42:51 millert Exp $ */ |
2 |
|
|
|
3 |
|
|
/* |
4 |
|
|
* parsetime.c - parse time for at(1) |
5 |
|
|
* Copyright (C) 1993, 1994 Thomas Koenig |
6 |
|
|
* |
7 |
|
|
* modifications for english-language times |
8 |
|
|
* Copyright (C) 1993 David Parsons |
9 |
|
|
* |
10 |
|
|
* Redistribution and use in source and binary forms, with or without |
11 |
|
|
* modification, are permitted provided that the following conditions |
12 |
|
|
* are met: |
13 |
|
|
* 1. Redistributions of source code must retain the above copyright |
14 |
|
|
* notice, this list of conditions and the following disclaimer. |
15 |
|
|
* 2. The name of the author(s) may not be used to endorse or promote |
16 |
|
|
* products derived from this software without specific prior written |
17 |
|
|
* permission. |
18 |
|
|
* |
19 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR |
20 |
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
21 |
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
22 |
|
|
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, |
23 |
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
24 |
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
25 |
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
26 |
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
27 |
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
28 |
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
29 |
|
|
* |
30 |
|
|
* at [NOW] PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS|MONTHS|YEARS |
31 |
|
|
* /NUMBER [DOT NUMBER] [AM|PM]\ /[MONTH NUMBER [NUMBER]] \ |
32 |
|
|
* |NOON | |[TOMORROW] | |
33 |
|
|
* |MIDNIGHT | |[DAY OF WEEK] | |
34 |
|
|
* \TEATIME / |NUMBER [SLASH NUMBER [SLASH NUMBER]]| |
35 |
|
|
* \PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS|MONTHS|YEARS/ |
36 |
|
|
*/ |
37 |
|
|
|
38 |
|
|
#include <sys/types.h> |
39 |
|
|
|
40 |
|
|
#include <err.h> |
41 |
|
|
#include <errno.h> |
42 |
|
|
#include <ctype.h> |
43 |
|
|
#include <stdio.h> |
44 |
|
|
#include <stdlib.h> |
45 |
|
|
#include <string.h> |
46 |
|
|
#include <time.h> |
47 |
|
|
#include <unistd.h> |
48 |
|
|
|
49 |
|
|
#include "globals.h" |
50 |
|
|
#include "at.h" |
51 |
|
|
|
52 |
|
|
/* Structures and unions */ |
53 |
|
|
|
54 |
|
|
enum { /* symbols */ |
55 |
|
|
MIDNIGHT, NOON, TEATIME, |
56 |
|
|
PM, AM, TOMORROW, TODAY, NOW, |
57 |
|
|
MINUTES, HOURS, DAYS, WEEKS, MONTHS, YEARS, |
58 |
|
|
NUMBER, NEXT, PLUS, DOT, SLASH, ID, JUNK, |
59 |
|
|
JAN, FEB, MAR, APR, MAY, JUN, |
60 |
|
|
JUL, AUG, SEP, OCT, NOV, DEC, |
61 |
|
|
SUN, MON, TUE, WED, THU, FRI, SAT |
62 |
|
|
}; |
63 |
|
|
|
64 |
|
|
/* |
65 |
|
|
* parse translation table - table driven parsers can be your FRIEND! |
66 |
|
|
*/ |
67 |
|
|
struct { |
68 |
|
|
char *name; /* token name */ |
69 |
|
|
int value; /* token id */ |
70 |
|
|
int plural; /* is this plural? */ |
71 |
|
|
} Specials[] = { |
72 |
|
|
{ "midnight", MIDNIGHT, 0 }, /* 00:00:00 of today or tomorrow */ |
73 |
|
|
{ "noon", NOON, 0 }, /* 12:00:00 of today or tomorrow */ |
74 |
|
|
{ "teatime", TEATIME, 0 }, /* 16:00:00 of today or tomorrow */ |
75 |
|
|
{ "am", AM, 0 }, /* morning times for 0-12 clock */ |
76 |
|
|
{ "pm", PM, 0 }, /* evening times for 0-12 clock */ |
77 |
|
|
{ "tomorrow", TOMORROW, 0 }, /* execute 24 hours from time */ |
78 |
|
|
{ "today", TODAY, 0 }, /* execute today - don't advance time */ |
79 |
|
|
{ "now", NOW, 0 }, /* opt prefix for PLUS */ |
80 |
|
|
{ "next", NEXT, 0 }, /* opt prefix for + 1 */ |
81 |
|
|
|
82 |
|
|
{ "minute", MINUTES, 0 }, /* minutes multiplier */ |
83 |
|
|
{ "min", MINUTES, 0 }, |
84 |
|
|
{ "m", MINUTES, 0 }, |
85 |
|
|
{ "minutes", MINUTES, 1 }, /* (pluralized) */ |
86 |
|
|
{ "hour", HOURS, 0 }, /* hours ... */ |
87 |
|
|
{ "hr", HOURS, 0 }, /* abbreviated */ |
88 |
|
|
{ "h", HOURS, 0 }, |
89 |
|
|
{ "hours", HOURS, 1 }, /* (pluralized) */ |
90 |
|
|
{ "day", DAYS, 0 }, /* days ... */ |
91 |
|
|
{ "d", DAYS, 0 }, |
92 |
|
|
{ "days", DAYS, 1 }, /* (pluralized) */ |
93 |
|
|
{ "week", WEEKS, 0 }, /* week ... */ |
94 |
|
|
{ "w", WEEKS, 0 }, |
95 |
|
|
{ "weeks", WEEKS, 1 }, /* (pluralized) */ |
96 |
|
|
{ "month", MONTHS, 0 }, /* month ... */ |
97 |
|
|
{ "mo", MONTHS, 0 }, |
98 |
|
|
{ "mth", MONTHS, 0 }, |
99 |
|
|
{ "months", MONTHS, 1 }, /* (pluralized) */ |
100 |
|
|
{ "year", YEARS, 0 }, /* year ... */ |
101 |
|
|
{ "y", YEARS, 0 }, |
102 |
|
|
{ "years", YEARS, 1 }, /* (pluralized) */ |
103 |
|
|
{ "jan", JAN, 0 }, |
104 |
|
|
{ "feb", FEB, 0 }, |
105 |
|
|
{ "mar", MAR, 0 }, |
106 |
|
|
{ "apr", APR, 0 }, |
107 |
|
|
{ "may", MAY, 0 }, |
108 |
|
|
{ "jun", JUN, 0 }, |
109 |
|
|
{ "jul", JUL, 0 }, |
110 |
|
|
{ "aug", AUG, 0 }, |
111 |
|
|
{ "sep", SEP, 0 }, |
112 |
|
|
{ "oct", OCT, 0 }, |
113 |
|
|
{ "nov", NOV, 0 }, |
114 |
|
|
{ "dec", DEC, 0 }, |
115 |
|
|
{ "january", JAN,0 }, |
116 |
|
|
{ "february", FEB,0 }, |
117 |
|
|
{ "march", MAR,0 }, |
118 |
|
|
{ "april", APR,0 }, |
119 |
|
|
{ "may", MAY,0 }, |
120 |
|
|
{ "june", JUN,0 }, |
121 |
|
|
{ "july", JUL,0 }, |
122 |
|
|
{ "august", AUG,0 }, |
123 |
|
|
{ "september", SEP,0 }, |
124 |
|
|
{ "october", OCT,0 }, |
125 |
|
|
{ "november", NOV,0 }, |
126 |
|
|
{ "december", DEC,0 }, |
127 |
|
|
{ "sunday", SUN, 0 }, |
128 |
|
|
{ "sun", SUN, 0 }, |
129 |
|
|
{ "monday", MON, 0 }, |
130 |
|
|
{ "mon", MON, 0 }, |
131 |
|
|
{ "tuesday", TUE, 0 }, |
132 |
|
|
{ "tue", TUE, 0 }, |
133 |
|
|
{ "wednesday", WED, 0 }, |
134 |
|
|
{ "wed", WED, 0 }, |
135 |
|
|
{ "thursday", THU, 0 }, |
136 |
|
|
{ "thu", THU, 0 }, |
137 |
|
|
{ "friday", FRI, 0 }, |
138 |
|
|
{ "fri", FRI, 0 }, |
139 |
|
|
{ "saturday", SAT, 0 }, |
140 |
|
|
{ "sat", SAT, 0 }, |
141 |
|
|
}; |
142 |
|
|
|
143 |
|
|
static char **scp; /* scanner - pointer at arglist */ |
144 |
|
|
static int scc; /* scanner - count of remaining arguments */ |
145 |
|
|
static char *sct; /* scanner - next char pointer in current argument */ |
146 |
|
|
static int need; /* scanner - need to advance to next argument */ |
147 |
|
|
static char *sc_token; /* scanner - token buffer */ |
148 |
|
|
static size_t sc_len; /* scanner - length of token buffer */ |
149 |
|
|
static int sc_tokid; /* scanner - token id */ |
150 |
|
|
static int sc_tokplur; /* scanner - is token plural? */ |
151 |
|
|
|
152 |
|
|
/* |
153 |
|
|
* parse a token, checking if it's something special to us |
154 |
|
|
*/ |
155 |
|
|
static int |
156 |
|
|
parse_token(char *arg) |
157 |
|
|
{ |
158 |
|
|
int i; |
159 |
|
|
|
160 |
|
|
for (i=0; i < sizeof(Specials) / sizeof(Specials[0]); i++) { |
161 |
|
|
if (strcasecmp(Specials[i].name, arg) == 0) { |
162 |
|
|
sc_tokplur = Specials[i].plural; |
163 |
|
|
return (sc_tokid = Specials[i].value); |
164 |
|
|
} |
165 |
|
|
} |
166 |
|
|
|
167 |
|
|
/* not special - must be some random id */ |
168 |
|
|
return (ID); |
169 |
|
|
} |
170 |
|
|
|
171 |
|
|
|
172 |
|
|
/* |
173 |
|
|
* init_scanner() sets up the scanner to eat arguments |
174 |
|
|
*/ |
175 |
|
|
static int |
176 |
|
|
init_scanner(int argc, char **argv) |
177 |
|
|
{ |
178 |
|
|
scp = argv; |
179 |
|
|
scc = argc; |
180 |
|
|
need = 1; |
181 |
|
|
sc_len = 1; |
182 |
|
|
while (argc-- > 0) |
183 |
|
|
sc_len += strlen(*argv++); |
184 |
|
|
|
185 |
|
|
if ((sc_token = malloc(sc_len)) == NULL) { |
186 |
|
|
warn(NULL); |
187 |
|
|
return (-1); |
188 |
|
|
} |
189 |
|
|
return (0); |
190 |
|
|
} |
191 |
|
|
|
192 |
|
|
/* |
193 |
|
|
* token() fetches a token from the input stream |
194 |
|
|
*/ |
195 |
|
|
static int |
196 |
|
|
token(void) |
197 |
|
|
{ |
198 |
|
|
int idx; |
199 |
|
|
|
200 |
|
|
for (;;) { |
201 |
|
|
bzero(sc_token, sc_len); |
202 |
|
|
sc_tokid = EOF; |
203 |
|
|
sc_tokplur = 0; |
204 |
|
|
idx = 0; |
205 |
|
|
|
206 |
|
|
/* |
207 |
|
|
* if we need to read another argument, walk along the |
208 |
|
|
* argument list; when we fall off the arglist, we'll |
209 |
|
|
* just return EOF forever |
210 |
|
|
*/ |
211 |
|
|
if (need) { |
212 |
|
|
if (scc < 1) |
213 |
|
|
return (sc_tokid); |
214 |
|
|
sct = *scp; |
215 |
|
|
scp++; |
216 |
|
|
scc--; |
217 |
|
|
need = 0; |
218 |
|
|
} |
219 |
|
|
/* |
220 |
|
|
* eat whitespace now - if we walk off the end of the argument, |
221 |
|
|
* we'll continue, which puts us up at the top of the while loop |
222 |
|
|
* to fetch the next argument in |
223 |
|
|
*/ |
224 |
|
|
while (isspace((unsigned char)*sct)) |
225 |
|
|
++sct; |
226 |
|
|
if (!*sct) { |
227 |
|
|
need = 1; |
228 |
|
|
continue; |
229 |
|
|
} |
230 |
|
|
|
231 |
|
|
/* |
232 |
|
|
* preserve the first character of the new token |
233 |
|
|
*/ |
234 |
|
|
sc_token[0] = *sct++; |
235 |
|
|
|
236 |
|
|
/* |
237 |
|
|
* then see what it is |
238 |
|
|
*/ |
239 |
|
|
if (isdigit((unsigned char)sc_token[0])) { |
240 |
|
|
while (isdigit((unsigned char)*sct)) |
241 |
|
|
sc_token[++idx] = *sct++; |
242 |
|
|
sc_token[++idx] = 0; |
243 |
|
|
return ((sc_tokid = NUMBER)); |
244 |
|
|
} else if (isalpha((unsigned char)sc_token[0])) { |
245 |
|
|
while (isalpha((unsigned char)*sct)) |
246 |
|
|
sc_token[++idx] = *sct++; |
247 |
|
|
sc_token[++idx] = 0; |
248 |
|
|
return (parse_token(sc_token)); |
249 |
|
|
} |
250 |
|
|
else if (sc_token[0] == ':' || sc_token[0] == '.') |
251 |
|
|
return ((sc_tokid = DOT)); |
252 |
|
|
else if (sc_token[0] == '+') |
253 |
|
|
return ((sc_tokid = PLUS)); |
254 |
|
|
else if (sc_token[0] == '/') |
255 |
|
|
return ((sc_tokid = SLASH)); |
256 |
|
|
else |
257 |
|
|
return ((sc_tokid = JUNK)); |
258 |
|
|
} |
259 |
|
|
} |
260 |
|
|
|
261 |
|
|
|
262 |
|
|
/* |
263 |
|
|
* plonk() gives an appropriate error message if a token is incorrect |
264 |
|
|
*/ |
265 |
|
|
static void |
266 |
|
|
plonk(int tok) |
267 |
|
|
{ |
268 |
|
|
warnx("%s time", (tok == EOF) ? "incomplete" : "garbled"); |
269 |
|
|
} |
270 |
|
|
|
271 |
|
|
|
272 |
|
|
/* |
273 |
|
|
* expect() gets a token and returns -1 if it's not the token we want |
274 |
|
|
*/ |
275 |
|
|
static int |
276 |
|
|
expect(int desired) |
277 |
|
|
{ |
278 |
|
|
if (token() != desired) { |
279 |
|
|
plonk(sc_tokid); |
280 |
|
|
return (-1); |
281 |
|
|
} |
282 |
|
|
return (0); |
283 |
|
|
} |
284 |
|
|
|
285 |
|
|
|
286 |
|
|
/* |
287 |
|
|
* dateadd() adds a number of minutes to a date. It is extraordinarily |
288 |
|
|
* stupid regarding day-of-month overflow, and will most likely not |
289 |
|
|
* work properly |
290 |
|
|
*/ |
291 |
|
|
static void |
292 |
|
|
dateadd(int minutes, struct tm *tm) |
293 |
|
|
{ |
294 |
|
|
/* increment days */ |
295 |
|
|
|
296 |
|
|
while (minutes > 24*60) { |
297 |
|
|
minutes -= 24*60; |
298 |
|
|
tm->tm_mday++; |
299 |
|
|
} |
300 |
|
|
|
301 |
|
|
/* increment hours */ |
302 |
|
|
while (minutes > 60) { |
303 |
|
|
minutes -= 60; |
304 |
|
|
tm->tm_hour++; |
305 |
|
|
if (tm->tm_hour > 23) { |
306 |
|
|
tm->tm_mday++; |
307 |
|
|
tm->tm_hour = 0; |
308 |
|
|
} |
309 |
|
|
} |
310 |
|
|
|
311 |
|
|
/* increment minutes */ |
312 |
|
|
tm->tm_min += minutes; |
313 |
|
|
|
314 |
|
|
if (tm->tm_min > 59) { |
315 |
|
|
tm->tm_hour++; |
316 |
|
|
tm->tm_min -= 60; |
317 |
|
|
|
318 |
|
|
if (tm->tm_hour > 23) { |
319 |
|
|
tm->tm_mday++; |
320 |
|
|
tm->tm_hour = 0; |
321 |
|
|
} |
322 |
|
|
} |
323 |
|
|
} |
324 |
|
|
|
325 |
|
|
|
326 |
|
|
/* |
327 |
|
|
* plus() parses a now + time |
328 |
|
|
* |
329 |
|
|
* at [NOW] PLUS NUMBER [MINUTES|HOURS|DAYS|WEEKS|MONTHS|YEARS] |
330 |
|
|
* |
331 |
|
|
*/ |
332 |
|
|
static int |
333 |
|
|
plus(struct tm *tm) |
334 |
|
|
{ |
335 |
|
|
int increment; |
336 |
|
|
int expectplur; |
337 |
|
|
|
338 |
|
|
if (sc_tokid == NEXT) { |
339 |
|
|
increment = 1; |
340 |
|
|
expectplur = 0; |
341 |
|
|
} else { |
342 |
|
|
if (expect(NUMBER) != 0) |
343 |
|
|
return (-1); |
344 |
|
|
increment = atoi(sc_token); |
345 |
|
|
expectplur = (increment != 1) ? 1 : 0; |
346 |
|
|
} |
347 |
|
|
|
348 |
|
|
switch (token()) { |
349 |
|
|
case YEARS: |
350 |
|
|
tm->tm_year += increment; |
351 |
|
|
return (0); |
352 |
|
|
case MONTHS: |
353 |
|
|
tm->tm_mon += increment; |
354 |
|
|
while (tm->tm_mon >= 12) { |
355 |
|
|
tm->tm_year++; |
356 |
|
|
tm->tm_mon -= 12; |
357 |
|
|
} |
358 |
|
|
return (0); |
359 |
|
|
case WEEKS: |
360 |
|
|
increment *= 7; |
361 |
|
|
/* FALLTHROUGH */ |
362 |
|
|
case DAYS: |
363 |
|
|
increment *= 24; |
364 |
|
|
/* FALLTHROUGH */ |
365 |
|
|
case HOURS: |
366 |
|
|
increment *= 60; |
367 |
|
|
/* FALLTHROUGH */ |
368 |
|
|
case MINUTES: |
369 |
|
|
if (expectplur != sc_tokplur) |
370 |
|
|
warnx("pluralization is wrong"); |
371 |
|
|
dateadd(increment, tm); |
372 |
|
|
return (0); |
373 |
|
|
} |
374 |
|
|
|
375 |
|
|
plonk(sc_tokid); |
376 |
|
|
return (-1); |
377 |
|
|
} |
378 |
|
|
|
379 |
|
|
|
380 |
|
|
/* |
381 |
|
|
* tod() computes the time of day |
382 |
|
|
* [NUMBER [DOT NUMBER] [AM|PM]] |
383 |
|
|
*/ |
384 |
|
|
static int |
385 |
|
|
tod(struct tm *tm) |
386 |
|
|
{ |
387 |
|
|
int hour, minute = 0; |
388 |
|
|
size_t tlen; |
389 |
|
|
|
390 |
|
|
hour = atoi(sc_token); |
391 |
|
|
tlen = strlen(sc_token); |
392 |
|
|
|
393 |
|
|
/* |
394 |
|
|
* first pick out the time of day - if it's 4 digits, we assume |
395 |
|
|
* a HHMM time, otherwise it's HH DOT MM time |
396 |
|
|
*/ |
397 |
|
|
if (token() == DOT) { |
398 |
|
|
if (expect(NUMBER) != 0) |
399 |
|
|
return (-1); |
400 |
|
|
minute = atoi(sc_token); |
401 |
|
|
if (minute > 59) |
402 |
|
|
goto bad; |
403 |
|
|
token(); |
404 |
|
|
} else if (tlen == 4) { |
405 |
|
|
minute = hour % 100; |
406 |
|
|
if (minute > 59) |
407 |
|
|
goto bad; |
408 |
|
|
hour = hour / 100; |
409 |
|
|
} |
410 |
|
|
|
411 |
|
|
/* |
412 |
|
|
* check if an AM or PM specifier was given |
413 |
|
|
*/ |
414 |
|
|
if (sc_tokid == AM || sc_tokid == PM) { |
415 |
|
|
if (hour > 12) |
416 |
|
|
goto bad; |
417 |
|
|
|
418 |
|
|
if (sc_tokid == PM) { |
419 |
|
|
if (hour != 12) /* 12:xx PM is 12:xx, not 24:xx */ |
420 |
|
|
hour += 12; |
421 |
|
|
} else { |
422 |
|
|
if (hour == 12) /* 12:xx AM is 00:xx, not 12:xx */ |
423 |
|
|
hour = 0; |
424 |
|
|
} |
425 |
|
|
token(); |
426 |
|
|
} else if (hour > 23) |
427 |
|
|
goto bad; |
428 |
|
|
|
429 |
|
|
/* |
430 |
|
|
* if we specify an absolute time, we don't want to bump the day even |
431 |
|
|
* if we've gone past that time - but if we're specifying a time plus |
432 |
|
|
* a relative offset, it's okay to bump things |
433 |
|
|
*/ |
434 |
|
|
if ((sc_tokid == EOF || sc_tokid == PLUS || sc_tokid == NEXT) && |
435 |
|
|
tm->tm_hour > hour) { |
436 |
|
|
tm->tm_mday++; |
437 |
|
|
tm->tm_wday++; |
438 |
|
|
} |
439 |
|
|
|
440 |
|
|
tm->tm_hour = hour; |
441 |
|
|
tm->tm_min = minute; |
442 |
|
|
if (tm->tm_hour == 24) { |
443 |
|
|
tm->tm_hour = 0; |
444 |
|
|
tm->tm_mday++; |
445 |
|
|
} |
446 |
|
|
return (0); |
447 |
|
|
bad: |
448 |
|
|
warnx("garbled time"); |
449 |
|
|
return (-1); |
450 |
|
|
} |
451 |
|
|
|
452 |
|
|
|
453 |
|
|
/* |
454 |
|
|
* assign_date() assigns a date, wrapping to next year if needed |
455 |
|
|
*/ |
456 |
|
|
static void |
457 |
|
|
assign_date(struct tm *tm, int mday, int mon, int year) |
458 |
|
|
{ |
459 |
|
|
|
460 |
|
|
/* |
461 |
|
|
* Convert year into tm_year format (year - 1900). |
462 |
|
|
* We may be given the year in 2 digit, 4 digit, or tm_year format. |
463 |
|
|
*/ |
464 |
|
|
if (year != -1) { |
465 |
|
|
if (year >= 1900) |
466 |
|
|
year -= 1900; /* convert from 4 digit year */ |
467 |
|
|
else if (year < 100) { |
468 |
|
|
/* Convert to tm_year assuming current century */ |
469 |
|
|
year += (tm->tm_year / 100) * 100; |
470 |
|
|
|
471 |
|
|
if (year == tm->tm_year - 1) |
472 |
|
|
year++; /* Common off by one error */ |
473 |
|
|
else if (year < tm->tm_year) |
474 |
|
|
year += 100; /* must be in next century */ |
475 |
|
|
} |
476 |
|
|
} |
477 |
|
|
|
478 |
|
|
if (year < 0 && |
479 |
|
|
(tm->tm_mon > mon ||(tm->tm_mon == mon && tm->tm_mday > mday))) |
480 |
|
|
year = tm->tm_year + 1; |
481 |
|
|
|
482 |
|
|
tm->tm_mday = mday; |
483 |
|
|
tm->tm_mon = mon; |
484 |
|
|
|
485 |
|
|
if (year >= 0) |
486 |
|
|
tm->tm_year = year; |
487 |
|
|
} |
488 |
|
|
|
489 |
|
|
|
490 |
|
|
/* |
491 |
|
|
* month() picks apart a month specification |
492 |
|
|
* |
493 |
|
|
* /[<month> NUMBER [NUMBER]] \ |
494 |
|
|
* |[TOMORROW] | |
495 |
|
|
* |[DAY OF WEEK] | |
496 |
|
|
* |NUMBER [SLASH NUMBER [SLASH NUMBER]]| |
497 |
|
|
* \PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS|MONTHS|YEARS/ |
498 |
|
|
*/ |
499 |
|
|
static int |
500 |
|
|
month(struct tm *tm) |
501 |
|
|
{ |
502 |
|
|
int year = (-1); |
503 |
|
|
int mday, wday, mon; |
504 |
|
|
size_t tlen; |
505 |
|
|
|
506 |
|
|
switch (sc_tokid) { |
507 |
|
|
case NEXT: |
508 |
|
|
case PLUS: |
509 |
|
|
if (plus(tm) != 0) |
510 |
|
|
return (-1); |
511 |
|
|
break; |
512 |
|
|
|
513 |
|
|
case TOMORROW: |
514 |
|
|
/* do something tomorrow */ |
515 |
|
|
tm->tm_mday++; |
516 |
|
|
tm->tm_wday++; |
517 |
|
|
case TODAY: |
518 |
|
|
/* force ourselves to stay in today - no further processing */ |
519 |
|
|
token(); |
520 |
|
|
break; |
521 |
|
|
|
522 |
|
|
case JAN: case FEB: case MAR: case APR: case MAY: case JUN: |
523 |
|
|
case JUL: case AUG: case SEP: case OCT: case NOV: case DEC: |
524 |
|
|
/* |
525 |
|
|
* do month mday [year] |
526 |
|
|
*/ |
527 |
|
|
mon = sc_tokid - JAN; |
528 |
|
|
if (expect(NUMBER) != 0) |
529 |
|
|
return (-1); |
530 |
|
|
mday = atoi(sc_token); |
531 |
|
|
if (token() == NUMBER) { |
532 |
|
|
year = atoi(sc_token); |
533 |
|
|
token(); |
534 |
|
|
} |
535 |
|
|
assign_date(tm, mday, mon, year); |
536 |
|
|
break; |
537 |
|
|
|
538 |
|
|
case SUN: case MON: case TUE: |
539 |
|
|
case WED: case THU: case FRI: |
540 |
|
|
case SAT: |
541 |
|
|
/* do a particular day of the week */ |
542 |
|
|
wday = sc_tokid - SUN; |
543 |
|
|
|
544 |
|
|
mday = tm->tm_mday; |
545 |
|
|
|
546 |
|
|
/* if this day is < today, then roll to next week */ |
547 |
|
|
if (wday < tm->tm_wday) |
548 |
|
|
mday += 7 - (tm->tm_wday - wday); |
549 |
|
|
else |
550 |
|
|
mday += (wday - tm->tm_wday); |
551 |
|
|
|
552 |
|
|
tm->tm_wday = wday; |
553 |
|
|
|
554 |
|
|
assign_date(tm, mday, tm->tm_mon, tm->tm_year); |
555 |
|
|
break; |
556 |
|
|
|
557 |
|
|
case NUMBER: |
558 |
|
|
/* |
559 |
|
|
* get numeric MMDDYY, mm/dd/yy, or dd.mm.yy |
560 |
|
|
*/ |
561 |
|
|
tlen = strlen(sc_token); |
562 |
|
|
mon = atoi(sc_token); |
563 |
|
|
token(); |
564 |
|
|
|
565 |
|
|
if (sc_tokid == SLASH || sc_tokid == DOT) { |
566 |
|
|
int sep; |
567 |
|
|
|
568 |
|
|
sep = sc_tokid; |
569 |
|
|
if (expect(NUMBER) != 0) |
570 |
|
|
return (-1); |
571 |
|
|
mday = atoi(sc_token); |
572 |
|
|
if (token() == sep) { |
573 |
|
|
if (expect(NUMBER) != 0) |
574 |
|
|
return (-1); |
575 |
|
|
year = atoi(sc_token); |
576 |
|
|
token(); |
577 |
|
|
} |
578 |
|
|
|
579 |
|
|
/* |
580 |
|
|
* flip months and days for european timing |
581 |
|
|
*/ |
582 |
|
|
if (sep == DOT) { |
583 |
|
|
int x = mday; |
584 |
|
|
mday = mon; |
585 |
|
|
mon = x; |
586 |
|
|
} |
587 |
|
|
} else if (tlen == 6 || tlen == 8) { |
588 |
|
|
if (tlen == 8) { |
589 |
|
|
year = (mon % 10000) - 1900; |
590 |
|
|
mon /= 10000; |
591 |
|
|
} else { |
592 |
|
|
year = mon % 100; |
593 |
|
|
mon /= 100; |
594 |
|
|
} |
595 |
|
|
mday = mon % 100; |
596 |
|
|
mon /= 100; |
597 |
|
|
} else |
598 |
|
|
goto bad; |
599 |
|
|
|
600 |
|
|
mon--; |
601 |
|
|
if (mon < 0 || mon > 11 || mday < 1 || mday > 31) |
602 |
|
|
goto bad; |
603 |
|
|
|
604 |
|
|
assign_date(tm, mday, mon, year); |
605 |
|
|
break; |
606 |
|
|
} |
607 |
|
|
return (0); |
608 |
|
|
bad: |
609 |
|
|
warnx("garbled time"); |
610 |
|
|
return (-1); |
611 |
|
|
} |
612 |
|
|
|
613 |
|
|
|
614 |
|
|
time_t |
615 |
|
|
parsetime(int argc, char **argv) |
616 |
|
|
{ |
617 |
|
|
/* |
618 |
|
|
* Do the argument parsing, die if necessary, and return the |
619 |
|
|
* time the job should be run. |
620 |
|
|
*/ |
621 |
|
|
time_t nowtimer, runtimer; |
622 |
|
|
struct tm nowtime, runtime; |
623 |
|
|
int hr = 0; |
624 |
|
|
/* this MUST be initialized to zero for midnight/noon/teatime */ |
625 |
|
|
|
626 |
|
|
if (argc == 0) |
627 |
|
|
return (-1); |
628 |
|
|
|
629 |
|
|
nowtimer = time(NULL); |
630 |
|
|
nowtime = *localtime(&nowtimer); |
631 |
|
|
|
632 |
|
|
runtime = nowtime; |
633 |
|
|
runtime.tm_sec = 0; |
634 |
|
|
runtime.tm_isdst = 0; |
635 |
|
|
|
636 |
|
|
if (init_scanner(argc, argv) == -1) |
637 |
|
|
return (-1); |
638 |
|
|
|
639 |
|
|
switch (token()) { |
640 |
|
|
case NOW: /* now is optional prefix for PLUS tree */ |
641 |
|
|
token(); |
642 |
|
|
if (sc_tokid == EOF) { |
643 |
|
|
runtime = nowtime; |
644 |
|
|
break; |
645 |
|
|
} |
646 |
|
|
else if (sc_tokid != PLUS && sc_tokid != NEXT) |
647 |
|
|
plonk(sc_tokid); |
648 |
|
|
case NEXT: |
649 |
|
|
case PLUS: |
650 |
|
|
if (plus(&runtime) != 0) |
651 |
|
|
return (-1); |
652 |
|
|
break; |
653 |
|
|
|
654 |
|
|
case NUMBER: |
655 |
|
|
if (tod(&runtime) != 0 || month(&runtime) != 0) |
656 |
|
|
return (-1); |
657 |
|
|
break; |
658 |
|
|
|
659 |
|
|
/* |
660 |
|
|
* evil coding for TEATIME|NOON|MIDNIGHT - we've initialised |
661 |
|
|
* hr to zero up above, then fall into this case in such a |
662 |
|
|
* way so we add +12 +4 hours to it for teatime, +12 hours |
663 |
|
|
* to it for noon, and nothing at all for midnight, then |
664 |
|
|
* set our runtime to that hour before leaping into the |
665 |
|
|
* month scanner |
666 |
|
|
*/ |
667 |
|
|
case TEATIME: |
668 |
|
|
hr += 4; |
669 |
|
|
/* FALLTHROUGH */ |
670 |
|
|
case NOON: |
671 |
|
|
hr += 12; |
672 |
|
|
/* FALLTHROUGH */ |
673 |
|
|
case MIDNIGHT: |
674 |
|
|
if (runtime.tm_hour >= hr) { |
675 |
|
|
runtime.tm_mday++; |
676 |
|
|
runtime.tm_wday++; |
677 |
|
|
} |
678 |
|
|
runtime.tm_hour = hr; |
679 |
|
|
runtime.tm_min = 0; |
680 |
|
|
token(); |
681 |
|
|
/* fall through to month setting */ |
682 |
|
|
/* FALLTHROUGH */ |
683 |
|
|
default: |
684 |
|
|
if (month(&runtime) != 0) |
685 |
|
|
return (-1); |
686 |
|
|
break; |
687 |
|
|
} /* ugly case statement */ |
688 |
|
|
if (expect(EOF) != 0) |
689 |
|
|
return (-1); |
690 |
|
|
|
691 |
|
|
/* |
692 |
|
|
* adjust for daylight savings time |
693 |
|
|
*/ |
694 |
|
|
runtime.tm_isdst = -1; |
695 |
|
|
runtimer = mktime(&runtime); |
696 |
|
|
|
697 |
|
|
if (runtimer < 0) { |
698 |
|
|
warnx("garbled time"); |
699 |
|
|
return (-1); |
700 |
|
|
} |
701 |
|
|
|
702 |
|
|
if (nowtimer > runtimer) { |
703 |
|
|
warnx("cannot schedule jobs in the past"); |
704 |
|
|
return (-1); |
705 |
|
|
} |
706 |
|
|
|
707 |
|
|
return (runtimer); |
708 |
|
|
} |