1d78e98d2SNate Williams /* 2d78e98d2SNate Williams * parsetime.c - parse time for at(1) 3b89321a5SAndrey A. Chernov * Copyright (C) 1993, 1994 Thomas Koenig 4d78e98d2SNate Williams * 581c8c7a4SPhilippe Charnier * 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 3651e2220cSMark Murray #include <sys/cdefs.h> 3751e2220cSMark Murray __FBSDID("$FreeBSD$"); 3881c8c7a4SPhilippe Charnier 39d78e98d2SNate Williams /* System Headers */ 40d78e98d2SNate Williams 41d78e98d2SNate Williams #include <sys/types.h> 4281c8c7a4SPhilippe Charnier #include <ctype.h> 43e5e5da15SPhilippe Charnier #include <err.h> 44d78e98d2SNate Williams #include <errno.h> 45d78e98d2SNate Williams #include <stdio.h> 46d78e98d2SNate Williams #include <stdlib.h> 47d78e98d2SNate Williams #include <string.h> 48d78e98d2SNate Williams #include <time.h> 49d78e98d2SNate Williams #include <unistd.h> 50b89321a5SAndrey A. Chernov #ifndef __FreeBSD__ 51b89321a5SAndrey A. Chernov #include <getopt.h> 52b89321a5SAndrey A. Chernov #endif 53d78e98d2SNate Williams 54d78e98d2SNate Williams /* Local headers */ 55d78e98d2SNate Williams 56d78e98d2SNate Williams #include "at.h" 57d78e98d2SNate Williams #include "panic.h" 583ce6c357SMark Murray #include "parsetime.h" 59d78e98d2SNate Williams 60d78e98d2SNate Williams 61d78e98d2SNate Williams /* Structures and unions */ 62d78e98d2SNate Williams 63ddcf8022SAndrey A. Chernov enum { /* symbols */ 64d78e98d2SNate Williams MIDNIGHT, NOON, TEATIME, 65d78e98d2SNate Williams PM, AM, TOMORROW, TODAY, NOW, 66b6c989ffSNick Sayer MINUTES, HOURS, DAYS, WEEKS, MONTHS, YEARS, 67d78e98d2SNate Williams NUMBER, PLUS, DOT, SLASH, ID, JUNK, 68d78e98d2SNate Williams JAN, FEB, MAR, APR, MAY, JUN, 69b89321a5SAndrey A. Chernov JUL, AUG, SEP, OCT, NOV, DEC, 70b89321a5SAndrey A. Chernov SUN, MON, TUE, WED, THU, FRI, SAT 71d78e98d2SNate Williams }; 72d78e98d2SNate Williams 73b89321a5SAndrey A. Chernov /* parse translation table - table driven parsers can be your FRIEND! 74d78e98d2SNate Williams */ 75d78e98d2SNate Williams struct { 763ce6c357SMark Murray const char *name; /* token name */ 77ddcf8022SAndrey A. Chernov int value; /* token id */ 78b89321a5SAndrey A. Chernov int plural; /* is this plural? */ 79d78e98d2SNate Williams } Specials[] = { 80b89321a5SAndrey A. Chernov { "midnight", MIDNIGHT,0 }, /* 00:00:00 of today or tomorrow */ 81b89321a5SAndrey A. Chernov { "noon", NOON,0 }, /* 12:00:00 of today or tomorrow */ 82b89321a5SAndrey A. Chernov { "teatime", TEATIME,0 }, /* 16:00:00 of today or tomorrow */ 83b89321a5SAndrey A. Chernov { "am", AM,0 }, /* morning times for 0-12 clock */ 84b89321a5SAndrey A. Chernov { "pm", PM,0 }, /* evening times for 0-12 clock */ 85b89321a5SAndrey A. Chernov { "tomorrow", TOMORROW,0 }, /* execute 24 hours from time */ 86b89321a5SAndrey A. Chernov { "today", TODAY, 0 }, /* execute today - don't advance time */ 87b89321a5SAndrey A. Chernov { "now", NOW,0 }, /* opt prefix for PLUS */ 88d78e98d2SNate Williams 89b89321a5SAndrey A. Chernov { "minute", MINUTES,0 }, /* minutes multiplier */ 90b89321a5SAndrey A. Chernov { "minutes", MINUTES,1 }, /* (pluralized) */ 91b89321a5SAndrey A. Chernov { "hour", HOURS,0 }, /* hours ... */ 92b89321a5SAndrey A. Chernov { "hours", HOURS,1 }, /* (pluralized) */ 93b89321a5SAndrey A. Chernov { "day", DAYS,0 }, /* days ... */ 94b89321a5SAndrey A. Chernov { "days", DAYS,1 }, /* (pluralized) */ 95b89321a5SAndrey A. Chernov { "week", WEEKS,0 }, /* week ... */ 96b89321a5SAndrey A. Chernov { "weeks", WEEKS,1 }, /* (pluralized) */ 97b6c989ffSNick Sayer { "month", MONTHS,0 }, /* month ... */ 98b6c989ffSNick Sayer { "months", MONTHS,1 }, /* (pluralized) */ 99b6c989ffSNick Sayer { "year", YEARS,0 }, /* year ... */ 100b6c989ffSNick Sayer { "years", YEARS,1 }, /* (pluralized) */ 101b89321a5SAndrey A. Chernov { "jan", JAN,0 }, 102b89321a5SAndrey A. Chernov { "feb", FEB,0 }, 103b89321a5SAndrey A. Chernov { "mar", MAR,0 }, 104b89321a5SAndrey A. Chernov { "apr", APR,0 }, 105b89321a5SAndrey A. Chernov { "may", MAY,0 }, 106b89321a5SAndrey A. Chernov { "jun", JUN,0 }, 107b89321a5SAndrey A. Chernov { "jul", JUL,0 }, 108b89321a5SAndrey A. Chernov { "aug", AUG,0 }, 109b89321a5SAndrey A. Chernov { "sep", SEP,0 }, 110b89321a5SAndrey A. Chernov { "oct", OCT,0 }, 111b89321a5SAndrey A. Chernov { "nov", NOV,0 }, 112b89321a5SAndrey A. Chernov { "dec", DEC,0 }, 113b9dd99f2SDag-Erling Smørgrav { "january", JAN,0 }, 114b9dd99f2SDag-Erling Smørgrav { "february", FEB,0 }, 115b9dd99f2SDag-Erling Smørgrav { "march", MAR,0 }, 116b9dd99f2SDag-Erling Smørgrav { "april", APR,0 }, 117b9dd99f2SDag-Erling Smørgrav { "may", MAY,0 }, 118b9dd99f2SDag-Erling Smørgrav { "june", JUN,0 }, 119b9dd99f2SDag-Erling Smørgrav { "july", JUL,0 }, 120b9dd99f2SDag-Erling Smørgrav { "august", AUG,0 }, 121b9dd99f2SDag-Erling Smørgrav { "september", SEP,0 }, 122b9dd99f2SDag-Erling Smørgrav { "october", OCT,0 }, 123b9dd99f2SDag-Erling Smørgrav { "november", NOV,0 }, 124b9dd99f2SDag-Erling Smørgrav { "december", DEC,0 }, 125b89321a5SAndrey A. Chernov { "sunday", SUN, 0 }, 126b89321a5SAndrey A. Chernov { "sun", SUN, 0 }, 127b89321a5SAndrey A. Chernov { "monday", MON, 0 }, 128b89321a5SAndrey A. Chernov { "mon", MON, 0 }, 129b89321a5SAndrey A. Chernov { "tuesday", TUE, 0 }, 130b89321a5SAndrey A. Chernov { "tue", TUE, 0 }, 131b89321a5SAndrey A. Chernov { "wednesday", WED, 0 }, 132b89321a5SAndrey A. Chernov { "wed", WED, 0 }, 133b89321a5SAndrey A. Chernov { "thursday", THU, 0 }, 134b89321a5SAndrey A. Chernov { "thu", THU, 0 }, 135b89321a5SAndrey A. Chernov { "friday", FRI, 0 }, 136b89321a5SAndrey A. Chernov { "fri", FRI, 0 }, 137b89321a5SAndrey A. Chernov { "saturday", SAT, 0 }, 138b89321a5SAndrey A. Chernov { "sat", SAT, 0 }, 139d78e98d2SNate Williams } ; 140d78e98d2SNate Williams 141d78e98d2SNate Williams /* File scope variables */ 142d78e98d2SNate Williams 143d78e98d2SNate Williams static char **scp; /* scanner - pointer at arglist */ 144d78e98d2SNate Williams static char scc; /* scanner - count of remaining arguments */ 145d78e98d2SNate Williams static char *sct; /* scanner - next char pointer in current argument */ 146d78e98d2SNate Williams static int need; /* scanner - need to advance to next argument */ 147d78e98d2SNate Williams 148d78e98d2SNate Williams static char *sc_token; /* scanner - token buffer */ 14981c8c7a4SPhilippe Charnier static size_t sc_len; /* scanner - length of token buffer */ 150ddcf8022SAndrey A. Chernov static int sc_tokid; /* scanner - token id */ 151b89321a5SAndrey A. Chernov static int sc_tokplur; /* scanner - is token plural? */ 152d78e98d2SNate Williams 153d78e98d2SNate Williams /* Local functions */ 154d78e98d2SNate Williams 155d78e98d2SNate Williams /* 156d78e98d2SNate Williams * parse a token, checking if it's something special to us 157d78e98d2SNate Williams */ 158ddcf8022SAndrey A. Chernov static int 159ddcf8022SAndrey A. Chernov parse_token(char *arg) 160d78e98d2SNate Williams { 1613ce6c357SMark Murray size_t i; 162d78e98d2SNate Williams 163d78e98d2SNate Williams for (i=0; i<(sizeof Specials/sizeof Specials[0]); i++) 164d78e98d2SNate Williams if (strcasecmp(Specials[i].name, arg) == 0) { 165b89321a5SAndrey A. Chernov sc_tokplur = Specials[i].plural; 166d78e98d2SNate Williams return sc_tokid = Specials[i].value; 167d78e98d2SNate Williams } 168d78e98d2SNate Williams 169d78e98d2SNate Williams /* not special - must be some random id */ 170d78e98d2SNate Williams return ID; 171d78e98d2SNate Williams } /* parse_token */ 172d78e98d2SNate Williams 173d78e98d2SNate Williams 174d78e98d2SNate Williams /* 175d78e98d2SNate Williams * init_scanner() sets up the scanner to eat arguments 176d78e98d2SNate Williams */ 177d78e98d2SNate Williams static void 178b89321a5SAndrey A. Chernov init_scanner(int argc, char **argv) 179d78e98d2SNate Williams { 180d78e98d2SNate Williams scp = argv; 181d78e98d2SNate Williams scc = argc; 182d78e98d2SNate Williams need = 1; 183d78e98d2SNate Williams sc_len = 1; 184ddcf8022SAndrey A. Chernov while (argc-- > 0) 185ddcf8022SAndrey A. Chernov sc_len += strlen(*argv++); 186d78e98d2SNate Williams 187a9be9be8SDavid E. O'Brien if ((sc_token = malloc(sc_len)) == NULL) 188a9be9be8SDavid E. O'Brien errx(EXIT_FAILURE, "virtual memory exhausted"); 189d78e98d2SNate Williams } /* init_scanner */ 190d78e98d2SNate Williams 191d78e98d2SNate Williams /* 192d78e98d2SNate Williams * token() fetches a token from the input stream 193d78e98d2SNate Williams */ 194ddcf8022SAndrey A. Chernov static int 1953ce6c357SMark Murray token(void) 196d78e98d2SNate Williams { 197d78e98d2SNate Williams int idx; 198d78e98d2SNate Williams 199d78e98d2SNate Williams while (1) { 200d78e98d2SNate Williams memset(sc_token, 0, sc_len); 201d78e98d2SNate Williams sc_tokid = EOF; 202b89321a5SAndrey A. Chernov sc_tokplur = 0; 203d78e98d2SNate Williams idx = 0; 204d78e98d2SNate Williams 205b89321a5SAndrey A. Chernov /* if we need to read another argument, walk along the argument list; 206d78e98d2SNate Williams * when we fall off the arglist, we'll just return EOF forever 207d78e98d2SNate Williams */ 208d78e98d2SNate Williams if (need) { 209d78e98d2SNate Williams if (scc < 1) 210d78e98d2SNate Williams return sc_tokid; 211d78e98d2SNate Williams sct = *scp; 212d78e98d2SNate Williams scp++; 213d78e98d2SNate Williams scc--; 214d78e98d2SNate Williams need = 0; 215d78e98d2SNate Williams } 216b89321a5SAndrey A. Chernov /* eat whitespace now - if we walk off the end of the argument, 217d78e98d2SNate Williams * we'll continue, which puts us up at the top of the while loop 218d78e98d2SNate Williams * to fetch the next argument in 219d78e98d2SNate Williams */ 220d78e98d2SNate Williams while (isspace(*sct)) 221d78e98d2SNate Williams ++sct; 222d78e98d2SNate Williams if (!*sct) { 223d78e98d2SNate Williams need = 1; 224d78e98d2SNate Williams continue; 225d78e98d2SNate Williams } 226d78e98d2SNate Williams 227b89321a5SAndrey A. Chernov /* preserve the first character of the new token 228d78e98d2SNate Williams */ 229d78e98d2SNate Williams sc_token[0] = *sct++; 230d78e98d2SNate Williams 231b89321a5SAndrey A. Chernov /* then see what it is 232d78e98d2SNate Williams */ 233d78e98d2SNate Williams if (isdigit(sc_token[0])) { 234d78e98d2SNate Williams while (isdigit(*sct)) 235d78e98d2SNate Williams sc_token[++idx] = *sct++; 236d78e98d2SNate Williams sc_token[++idx] = 0; 237d78e98d2SNate Williams return sc_tokid = NUMBER; 238b89321a5SAndrey A. Chernov } 239b89321a5SAndrey A. Chernov else if (isalpha(sc_token[0])) { 240d78e98d2SNate Williams while (isalpha(*sct)) 241d78e98d2SNate Williams sc_token[++idx] = *sct++; 242d78e98d2SNate Williams sc_token[++idx] = 0; 243d78e98d2SNate Williams return parse_token(sc_token); 244d78e98d2SNate Williams } 245d78e98d2SNate Williams else if (sc_token[0] == ':' || sc_token[0] == '.') 246d78e98d2SNate Williams return sc_tokid = DOT; 247d78e98d2SNate Williams else if (sc_token[0] == '+') 248d78e98d2SNate Williams return sc_tokid = PLUS; 249b89321a5SAndrey A. Chernov else if (sc_token[0] == '/') 250d78e98d2SNate Williams return sc_tokid = SLASH; 251d78e98d2SNate Williams else 252d78e98d2SNate Williams return sc_tokid = JUNK; 253d78e98d2SNate Williams } /* while (1) */ 254d78e98d2SNate Williams } /* token */ 255d78e98d2SNate Williams 256d78e98d2SNate Williams 257d78e98d2SNate Williams /* 258d78e98d2SNate Williams * plonk() gives an appropriate error message if a token is incorrect 259d78e98d2SNate Williams */ 260d78e98d2SNate Williams static void 261b89321a5SAndrey A. Chernov plonk(int tok) 262d78e98d2SNate Williams { 263d78e98d2SNate Williams panic((tok == EOF) ? "incomplete time" 264d78e98d2SNate Williams : "garbled time"); 265d78e98d2SNate Williams } /* plonk */ 266d78e98d2SNate Williams 267d78e98d2SNate Williams 268d78e98d2SNate Williams /* 269d78e98d2SNate Williams * expect() gets a token and dies most horribly if it's not the token we want 270d78e98d2SNate Williams */ 271d78e98d2SNate Williams static void 272ddcf8022SAndrey A. Chernov expect(int desired) 273d78e98d2SNate Williams { 274d78e98d2SNate Williams if (token() != desired) 275d78e98d2SNate Williams plonk(sc_tokid); /* and we die here... */ 276d78e98d2SNate Williams } /* expect */ 277d78e98d2SNate Williams 278d78e98d2SNate Williams 279d78e98d2SNate Williams /* 280d78e98d2SNate Williams * plus() parses a now + time 281d78e98d2SNate Williams * 282b6c989ffSNick Sayer * at [NOW] PLUS NUMBER [MINUTES|HOURS|DAYS|WEEKS|MONTHS|YEARS] 283d78e98d2SNate Williams * 284d78e98d2SNate Williams */ 285b6c989ffSNick Sayer 286d78e98d2SNate Williams static void 287b89321a5SAndrey A. Chernov plus(struct tm *tm) 288d78e98d2SNate Williams { 289d78e98d2SNate Williams int delay; 290b89321a5SAndrey A. Chernov int expectplur; 291d78e98d2SNate Williams 292d78e98d2SNate Williams expect(NUMBER); 293d78e98d2SNate Williams 294d78e98d2SNate Williams delay = atoi(sc_token); 295b89321a5SAndrey A. Chernov expectplur = (delay != 1) ? 1 : 0; 296d78e98d2SNate Williams 297d78e98d2SNate Williams switch (token()) { 298b6c989ffSNick Sayer case YEARS: 299b6c989ffSNick Sayer tm->tm_year += delay; 300b6c989ffSNick Sayer break; 301b6c989ffSNick Sayer case MONTHS: 302b6c989ffSNick Sayer tm->tm_mon += delay; 303b6c989ffSNick Sayer break; 304d78e98d2SNate Williams case WEEKS: 305d78e98d2SNate Williams delay *= 7; 306d78e98d2SNate Williams case DAYS: 307b6c989ffSNick Sayer tm->tm_mday += delay; 308b6c989ffSNick Sayer break; 309d78e98d2SNate Williams case HOURS: 310b6c989ffSNick Sayer tm->tm_hour += delay; 311b6c989ffSNick Sayer break; 312d78e98d2SNate Williams case MINUTES: 313b6c989ffSNick Sayer tm->tm_min += delay; 314b6c989ffSNick Sayer break; 315b6c989ffSNick Sayer default: 316b6c989ffSNick Sayer plonk(sc_tokid); 317b6c989ffSNick Sayer break; 318b6c989ffSNick Sayer } 319b6c989ffSNick Sayer 320b89321a5SAndrey A. Chernov if (expectplur != sc_tokplur) 32112d20ef9SPhilippe Charnier warnx("pluralization is wrong"); 322b6c989ffSNick Sayer 323b6c989ffSNick Sayer tm->tm_isdst = -1; 324b6c989ffSNick Sayer if (mktime(tm) < 0) 325d78e98d2SNate Williams plonk(sc_tokid); 326b6c989ffSNick Sayer 327d78e98d2SNate Williams } /* plus */ 328d78e98d2SNate Williams 329d78e98d2SNate Williams 330d78e98d2SNate Williams /* 331d78e98d2SNate Williams * tod() computes the time of day 332d78e98d2SNate Williams * [NUMBER [DOT NUMBER] [AM|PM]] 333d78e98d2SNate Williams */ 334d78e98d2SNate Williams static void 335b89321a5SAndrey A. Chernov tod(struct tm *tm) 336d78e98d2SNate Williams { 337d78e98d2SNate Williams int hour, minute = 0; 338d78e98d2SNate Williams int tlen; 339d78e98d2SNate Williams 340d78e98d2SNate Williams hour = atoi(sc_token); 341d78e98d2SNate Williams tlen = strlen(sc_token); 342d78e98d2SNate Williams 343b89321a5SAndrey A. Chernov /* first pick out the time of day - if it's 4 digits, we assume 344d78e98d2SNate Williams * a HHMM time, otherwise it's HH DOT MM time 345d78e98d2SNate Williams */ 346d78e98d2SNate Williams if (token() == DOT) { 347d78e98d2SNate Williams expect(NUMBER); 348d78e98d2SNate Williams minute = atoi(sc_token); 349d78e98d2SNate Williams if (minute > 59) 350d78e98d2SNate Williams panic("garbled time"); 351d78e98d2SNate Williams token(); 352b89321a5SAndrey A. Chernov } 353b89321a5SAndrey A. Chernov else if (tlen == 4) { 354d78e98d2SNate Williams minute = hour%100; 355d78e98d2SNate Williams if (minute > 59) 3563297b869SSteve Price panic("garbled time"); 357d78e98d2SNate Williams hour = hour/100; 358d78e98d2SNate Williams } 359d78e98d2SNate Williams 360b89321a5SAndrey A. Chernov /* check if an AM or PM specifier was given 361d78e98d2SNate Williams */ 362d78e98d2SNate Williams if (sc_tokid == AM || sc_tokid == PM) { 363d78e98d2SNate Williams if (hour > 12) 364d78e98d2SNate Williams panic("garbled time"); 365d78e98d2SNate Williams 3669b2ea11cSJohn Polstra if (sc_tokid == PM) { 3679b2ea11cSJohn Polstra if (hour != 12) /* 12:xx PM is 12:xx, not 24:xx */ 368d78e98d2SNate Williams hour += 12; 3699b2ea11cSJohn Polstra } else { 3709b2ea11cSJohn Polstra if (hour == 12) /* 12:xx AM is 00:xx, not 12:xx */ 37112d20ef9SPhilippe Charnier hour = 0; 3729b2ea11cSJohn Polstra } 373d78e98d2SNate Williams token(); 374b89321a5SAndrey A. Chernov } 375b89321a5SAndrey A. Chernov else if (hour > 23) 376d78e98d2SNate Williams panic("garbled time"); 377d78e98d2SNate Williams 378b89321a5SAndrey A. Chernov /* if we specify an absolute time, we don't want to bump the day even 379d78e98d2SNate Williams * if we've gone past that time - but if we're specifying a time plus 380d78e98d2SNate Williams * a relative offset, it's okay to bump things 381d78e98d2SNate Williams */ 382b89321a5SAndrey A. Chernov if ((sc_tokid == EOF || sc_tokid == PLUS) && tm->tm_hour > hour) { 383d78e98d2SNate Williams tm->tm_mday++; 384b89321a5SAndrey A. Chernov tm->tm_wday++; 385b89321a5SAndrey A. Chernov } 386d78e98d2SNate Williams 387d78e98d2SNate Williams tm->tm_hour = hour; 388d78e98d2SNate Williams tm->tm_min = minute; 389d78e98d2SNate Williams if (tm->tm_hour == 24) { 390d78e98d2SNate Williams tm->tm_hour = 0; 391d78e98d2SNate Williams tm->tm_mday++; 392d78e98d2SNate Williams } 393d78e98d2SNate Williams } /* tod */ 394d78e98d2SNate Williams 395d78e98d2SNate Williams 396d78e98d2SNate Williams /* 397d78e98d2SNate Williams * assign_date() assigns a date, wrapping to next year if needed 398d78e98d2SNate Williams */ 399d78e98d2SNate Williams static void 400b89321a5SAndrey A. Chernov assign_date(struct tm *tm, long mday, long mon, long year) 401d78e98d2SNate Williams { 402f4510294SSheldon Hearn 403f4510294SSheldon Hearn /* 404f4510294SSheldon Hearn * Convert year into tm_year format (year - 1900). 405f4510294SSheldon Hearn * We may be given the year in 2 digit, 4 digit, or tm_year format. 406f4510294SSheldon Hearn */ 407f4510294SSheldon Hearn if (year != -1) { 408f4510294SSheldon Hearn if (year >= 1900) 409f4510294SSheldon Hearn year -= 1900; /* convert from 4 digit year */ 410f4510294SSheldon Hearn else if (year < 100) { 411f4510294SSheldon Hearn /* convert from 2 digit year */ 4121dbfc421SAlexander Langer struct tm *lt; 4131dbfc421SAlexander Langer time_t now; 4141dbfc421SAlexander Langer 4151dbfc421SAlexander Langer time(&now); 4161dbfc421SAlexander Langer lt = localtime(&now); 4171dbfc421SAlexander Langer 418f4510294SSheldon Hearn /* Convert to tm_year assuming current century */ 419f4510294SSheldon Hearn year += (lt->tm_year / 100) * 100; 420f4510294SSheldon Hearn 421f4510294SSheldon Hearn if (year == lt->tm_year - 1) year++; 422f4510294SSheldon Hearn else if (year < lt->tm_year) 423f4510294SSheldon Hearn year += 100; /* must be in next century */ 424f4510294SSheldon Hearn } 425d78e98d2SNate Williams } 426d78e98d2SNate Williams 427d78e98d2SNate Williams if (year < 0 && 428d78e98d2SNate Williams (tm->tm_mon > mon ||(tm->tm_mon == mon && tm->tm_mday > mday))) 429d78e98d2SNate Williams year = tm->tm_year + 1; 430d78e98d2SNate Williams 431d78e98d2SNate Williams tm->tm_mday = mday; 432d78e98d2SNate Williams tm->tm_mon = mon; 433d78e98d2SNate Williams 434d78e98d2SNate Williams if (year >= 0) 435d78e98d2SNate Williams tm->tm_year = year; 436d78e98d2SNate Williams } /* assign_date */ 437d78e98d2SNate Williams 438d78e98d2SNate Williams 439d78e98d2SNate Williams /* 440d78e98d2SNate Williams * month() picks apart a month specification 441d78e98d2SNate Williams * 442d78e98d2SNate Williams * /[<month> NUMBER [NUMBER]] \ 443d78e98d2SNate Williams * |[TOMORROW] | 444b89321a5SAndrey A. Chernov * |[DAY OF WEEK] | 445d78e98d2SNate Williams * |NUMBER [SLASH NUMBER [SLASH NUMBER]]| 446d78e98d2SNate Williams * \PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS/ 447d78e98d2SNate Williams */ 448d78e98d2SNate Williams static void 449b89321a5SAndrey A. Chernov month(struct tm *tm) 450d78e98d2SNate Williams { 451d78e98d2SNate Williams long year= (-1); 4525f618f92SArchie Cobbs long mday = 0, wday, mon; 453d78e98d2SNate Williams int tlen; 454d78e98d2SNate Williams 455d78e98d2SNate Williams switch (sc_tokid) { 456d78e98d2SNate Williams case PLUS: 457d78e98d2SNate Williams plus(tm); 458d78e98d2SNate Williams break; 459d78e98d2SNate Williams 460d78e98d2SNate Williams case TOMORROW: 461d78e98d2SNate Williams /* do something tomorrow */ 462d78e98d2SNate Williams tm->tm_mday ++; 463b89321a5SAndrey A. Chernov tm->tm_wday ++; 464d78e98d2SNate Williams case TODAY: /* force ourselves to stay in today - no further processing */ 465d78e98d2SNate Williams token(); 466d78e98d2SNate Williams break; 467d78e98d2SNate Williams 468d78e98d2SNate Williams case JAN: case FEB: case MAR: case APR: case MAY: case JUN: 469d78e98d2SNate Williams case JUL: case AUG: case SEP: case OCT: case NOV: case DEC: 470b89321a5SAndrey A. Chernov /* do month mday [year] 471d78e98d2SNate Williams */ 472d78e98d2SNate Williams mon = (sc_tokid-JAN); 473d78e98d2SNate Williams expect(NUMBER); 47468abb9f9SBruce Evans mday = atol(sc_token); 475d78e98d2SNate Williams if (token() == NUMBER) { 476d78e98d2SNate Williams year = atol(sc_token); 477d78e98d2SNate Williams token(); 478d78e98d2SNate Williams } 479d78e98d2SNate Williams assign_date(tm, mday, mon, year); 480d78e98d2SNate Williams break; 481d78e98d2SNate Williams 482b89321a5SAndrey A. Chernov case SUN: case MON: case TUE: 483b89321a5SAndrey A. Chernov case WED: case THU: case FRI: 484b89321a5SAndrey A. Chernov case SAT: 485b89321a5SAndrey A. Chernov /* do a particular day of the week 486b89321a5SAndrey A. Chernov */ 487b89321a5SAndrey A. Chernov wday = (sc_tokid-SUN); 488b89321a5SAndrey A. Chernov 489b89321a5SAndrey A. Chernov mday = tm->tm_mday; 490b89321a5SAndrey A. Chernov 491b89321a5SAndrey A. Chernov /* if this day is < today, then roll to next week 492b89321a5SAndrey A. Chernov */ 493b89321a5SAndrey A. Chernov if (wday < tm->tm_wday) 494b89321a5SAndrey A. Chernov mday += 7 - (tm->tm_wday - wday); 495b89321a5SAndrey A. Chernov else 496b89321a5SAndrey A. Chernov mday += (wday - tm->tm_wday); 497b89321a5SAndrey A. Chernov 498b89321a5SAndrey A. Chernov tm->tm_wday = wday; 499b89321a5SAndrey A. Chernov 500b89321a5SAndrey A. Chernov assign_date(tm, mday, tm->tm_mon, tm->tm_year); 501b89321a5SAndrey A. Chernov break; 502b89321a5SAndrey A. Chernov 503d78e98d2SNate Williams case NUMBER: 504b89321a5SAndrey A. Chernov /* get numeric MMDDYY, mm/dd/yy, or dd.mm.yy 505d78e98d2SNate Williams */ 506d78e98d2SNate Williams tlen = strlen(sc_token); 507d78e98d2SNate Williams mon = atol(sc_token); 508d78e98d2SNate Williams token(); 509d78e98d2SNate Williams 510d78e98d2SNate Williams if (sc_tokid == SLASH || sc_tokid == DOT) { 511ddcf8022SAndrey A. Chernov int sep; 512d78e98d2SNate Williams 513d78e98d2SNate Williams sep = sc_tokid; 514d78e98d2SNate Williams expect(NUMBER); 515d78e98d2SNate Williams mday = atol(sc_token); 516d78e98d2SNate Williams if (token() == sep) { 517d78e98d2SNate Williams expect(NUMBER); 518d78e98d2SNate Williams year = atol(sc_token); 519d78e98d2SNate Williams token(); 520d78e98d2SNate Williams } 521d78e98d2SNate Williams 52281c8c7a4SPhilippe Charnier /* flip months and days for European timing 523d78e98d2SNate Williams */ 524d78e98d2SNate Williams if (sep == DOT) { 525d78e98d2SNate Williams int x = mday; 526d78e98d2SNate Williams mday = mon; 527d78e98d2SNate Williams mon = x; 528d78e98d2SNate Williams } 529b89321a5SAndrey A. Chernov } 530b89321a5SAndrey A. Chernov else if (tlen == 6 || tlen == 8) { 531d78e98d2SNate Williams if (tlen == 8) { 532d78e98d2SNate Williams year = (mon % 10000) - 1900; 533d78e98d2SNate Williams mon /= 10000; 534b89321a5SAndrey A. Chernov } 535b89321a5SAndrey A. Chernov else { 536d78e98d2SNate Williams year = mon % 100; 537d78e98d2SNate Williams mon /= 100; 538d78e98d2SNate Williams } 539d78e98d2SNate Williams mday = mon % 100; 540d78e98d2SNate Williams mon /= 100; 541b89321a5SAndrey A. Chernov } 542b89321a5SAndrey A. Chernov else 543d78e98d2SNate Williams panic("garbled time"); 544d78e98d2SNate Williams 545d78e98d2SNate Williams mon--; 546d78e98d2SNate Williams if (mon < 0 || mon > 11 || mday < 1 || mday > 31) 547d78e98d2SNate Williams panic("garbled time"); 548d78e98d2SNate Williams 549d78e98d2SNate Williams assign_date(tm, mday, mon, year); 550d78e98d2SNate Williams break; 551d78e98d2SNate Williams } /* case */ 552d78e98d2SNate Williams } /* month */ 553d78e98d2SNate Williams 554d78e98d2SNate Williams 555d78e98d2SNate Williams /* Global functions */ 556d78e98d2SNate Williams 557d78e98d2SNate Williams time_t 558b89321a5SAndrey A. Chernov parsetime(int argc, char **argv) 559d78e98d2SNate Williams { 560b89321a5SAndrey A. Chernov /* Do the argument parsing, die if necessary, and return the time the job 561d78e98d2SNate Williams * should be run. 562d78e98d2SNate Williams */ 563d78e98d2SNate Williams time_t nowtimer, runtimer; 564d78e98d2SNate Williams struct tm nowtime, runtime; 565d78e98d2SNate Williams int hr = 0; 566d78e98d2SNate Williams /* this MUST be initialized to zero for midnight/noon/teatime */ 567d78e98d2SNate Williams 568d78e98d2SNate Williams nowtimer = time(NULL); 569d78e98d2SNate Williams nowtime = *localtime(&nowtimer); 570d78e98d2SNate Williams 571d78e98d2SNate Williams runtime = nowtime; 572d78e98d2SNate Williams runtime.tm_sec = 0; 573d78e98d2SNate Williams runtime.tm_isdst = 0; 574d78e98d2SNate Williams 575d78e98d2SNate Williams if (argc <= optind) 576d78e98d2SNate Williams usage(); 577d78e98d2SNate Williams 578d78e98d2SNate Williams init_scanner(argc-optind, argv+optind); 579d78e98d2SNate Williams 580d78e98d2SNate Williams switch (token()) { 581bafdc304SBrian Somers case NOW: 582bafdc304SBrian Somers if (scc < 1) { 583bafdc304SBrian Somers return nowtimer; 584bafdc304SBrian Somers } 585bafdc304SBrian Somers /* now is optional prefix for PLUS tree */ 586d78e98d2SNate Williams expect(PLUS); 587d78e98d2SNate Williams case PLUS: 588d78e98d2SNate Williams plus(&runtime); 589d78e98d2SNate Williams break; 590d78e98d2SNate Williams 591d78e98d2SNate Williams case NUMBER: 592d78e98d2SNate Williams tod(&runtime); 593d78e98d2SNate Williams month(&runtime); 594d78e98d2SNate Williams break; 595d78e98d2SNate Williams 596b89321a5SAndrey A. Chernov /* evil coding for TEATIME|NOON|MIDNIGHT - we've initialised 597d78e98d2SNate Williams * hr to zero up above, then fall into this case in such a 598d78e98d2SNate Williams * way so we add +12 +4 hours to it for teatime, +12 hours 599d78e98d2SNate Williams * to it for noon, and nothing at all for midnight, then 600d78e98d2SNate Williams * set our runtime to that hour before leaping into the 601d78e98d2SNate Williams * month scanner 602d78e98d2SNate Williams */ 603d78e98d2SNate Williams case TEATIME: 604d78e98d2SNate Williams hr += 4; 605d78e98d2SNate Williams case NOON: 606d78e98d2SNate Williams hr += 12; 607d78e98d2SNate Williams case MIDNIGHT: 608b89321a5SAndrey A. Chernov if (runtime.tm_hour >= hr) { 609d78e98d2SNate Williams runtime.tm_mday++; 610b89321a5SAndrey A. Chernov runtime.tm_wday++; 611b89321a5SAndrey A. Chernov } 612d78e98d2SNate Williams runtime.tm_hour = hr; 613d78e98d2SNate Williams runtime.tm_min = 0; 614d78e98d2SNate Williams token(); 61581c8c7a4SPhilippe Charnier /* FALLTHROUGH to month setting */ 616d78e98d2SNate Williams default: 617d78e98d2SNate Williams month(&runtime); 618d78e98d2SNate Williams break; 619d78e98d2SNate Williams } /* ugly case statement */ 620d78e98d2SNate Williams expect(EOF); 621d78e98d2SNate Williams 6224ed70180SStefan Farfeleder /* convert back to time_t 623d78e98d2SNate Williams */ 624d78e98d2SNate Williams runtime.tm_isdst = -1; 625d78e98d2SNate Williams runtimer = mktime(&runtime); 626d78e98d2SNate Williams 627d78e98d2SNate Williams if (runtimer < 0) 628d78e98d2SNate Williams panic("garbled time"); 629d78e98d2SNate Williams 630d78e98d2SNate Williams if (nowtimer > runtimer) 63181c8c7a4SPhilippe Charnier panic("trying to travel back in time"); 632d78e98d2SNate Williams 633d78e98d2SNate Williams return runtimer; 634d78e98d2SNate Williams } /* parsetime */ 635