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