11de7b4b8SPedro F. Giffuni /*- 2d78e98d2SNate Williams * parsetime.c - parse time for at(1) 31de7b4b8SPedro F. Giffuni * 4*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 51de7b4b8SPedro F. Giffuni * 6b89321a5SAndrey A. Chernov * Copyright (C) 1993, 1994 Thomas Koenig 7d78e98d2SNate Williams * 881c8c7a4SPhilippe Charnier * modifications for English-language times 9d78e98d2SNate Williams * Copyright (C) 1993 David Parsons 10d78e98d2SNate Williams * 11d78e98d2SNate Williams * Redistribution and use in source and binary forms, with or without 12d78e98d2SNate Williams * modification, are permitted provided that the following conditions 13d78e98d2SNate Williams * are met: 14d78e98d2SNate Williams * 1. Redistributions of source code must retain the above copyright 15d78e98d2SNate Williams * notice, this list of conditions and the following disclaimer. 16d78e98d2SNate Williams * 2. The name of the author(s) may not be used to endorse or promote 17d78e98d2SNate Williams * products derived from this software without specific prior written 18d78e98d2SNate Williams * permission. 19d78e98d2SNate Williams * 20d78e98d2SNate Williams * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 21d78e98d2SNate Williams * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22d78e98d2SNate Williams * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23ddcf8022SAndrey A. Chernov * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 24d78e98d2SNate Williams * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 25d78e98d2SNate Williams * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26d78e98d2SNate Williams * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27d78e98d2SNate Williams * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28d78e98d2SNate Williams * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 29d78e98d2SNate Williams * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30d78e98d2SNate Williams * 31d78e98d2SNate Williams * at [NOW] PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS 32d78e98d2SNate Williams * /NUMBER [DOT NUMBER] [AM|PM]\ /[MONTH NUMBER [NUMBER]] \ 33d78e98d2SNate Williams * |NOON | |[TOMORROW] | 34b89321a5SAndrey A. Chernov * |MIDNIGHT | |[DAY OF WEEK] | 35b89321a5SAndrey A. Chernov * \TEATIME / |NUMBER [SLASH NUMBER [SLASH NUMBER]]| 36b89321a5SAndrey A. Chernov * \PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS/ 37d78e98d2SNate Williams */ 38d78e98d2SNate Williams 3951e2220cSMark Murray #include <sys/cdefs.h> 4051e2220cSMark Murray __FBSDID("$FreeBSD$"); 4181c8c7a4SPhilippe Charnier 42d78e98d2SNate Williams /* System Headers */ 43d78e98d2SNate Williams 44d78e98d2SNate Williams #include <sys/types.h> 4581c8c7a4SPhilippe Charnier #include <ctype.h> 46e5e5da15SPhilippe Charnier #include <err.h> 47d78e98d2SNate Williams #include <errno.h> 48d78e98d2SNate Williams #include <stdio.h> 49d78e98d2SNate Williams #include <stdlib.h> 50d78e98d2SNate Williams #include <string.h> 51d78e98d2SNate Williams #include <time.h> 52d78e98d2SNate Williams #include <unistd.h> 53b89321a5SAndrey A. Chernov #ifndef __FreeBSD__ 54b89321a5SAndrey A. Chernov #include <getopt.h> 55b89321a5SAndrey A. Chernov #endif 56d78e98d2SNate Williams 57d78e98d2SNate Williams /* Local headers */ 58d78e98d2SNate Williams 59d78e98d2SNate Williams #include "at.h" 60d78e98d2SNate Williams #include "panic.h" 613ce6c357SMark Murray #include "parsetime.h" 62d78e98d2SNate Williams 63d78e98d2SNate Williams 64d78e98d2SNate Williams /* Structures and unions */ 65d78e98d2SNate Williams 66ddcf8022SAndrey A. Chernov enum { /* symbols */ 67d78e98d2SNate Williams MIDNIGHT, NOON, TEATIME, 68d78e98d2SNate Williams PM, AM, TOMORROW, TODAY, NOW, 69b6c989ffSNick Sayer MINUTES, HOURS, DAYS, WEEKS, MONTHS, YEARS, 7093a2fe45SMartin Cracauer NUMBER, PLUS, MINUS, DOT, SLASH, ID, JUNK, 71d78e98d2SNate Williams JAN, FEB, MAR, APR, MAY, JUN, 72b89321a5SAndrey A. Chernov JUL, AUG, SEP, OCT, NOV, DEC, 73b89321a5SAndrey A. Chernov SUN, MON, TUE, WED, THU, FRI, SAT 74d78e98d2SNate Williams }; 75d78e98d2SNate Williams 76b89321a5SAndrey A. Chernov /* parse translation table - table driven parsers can be your FRIEND! 77d78e98d2SNate Williams */ 78f64efe8bSEd Schouten static const struct { 793ce6c357SMark Murray const char *name; /* token name */ 80ddcf8022SAndrey A. Chernov int value; /* token id */ 81b89321a5SAndrey A. Chernov int plural; /* is this plural? */ 82d78e98d2SNate Williams } Specials[] = { 83b89321a5SAndrey A. Chernov { "midnight", MIDNIGHT,0 }, /* 00:00:00 of today or tomorrow */ 84b89321a5SAndrey A. Chernov { "noon", NOON,0 }, /* 12:00:00 of today or tomorrow */ 85b89321a5SAndrey A. Chernov { "teatime", TEATIME,0 }, /* 16:00:00 of today or tomorrow */ 86b89321a5SAndrey A. Chernov { "am", AM,0 }, /* morning times for 0-12 clock */ 87b89321a5SAndrey A. Chernov { "pm", PM,0 }, /* evening times for 0-12 clock */ 88b89321a5SAndrey A. Chernov { "tomorrow", TOMORROW,0 }, /* execute 24 hours from time */ 89b89321a5SAndrey A. Chernov { "today", TODAY, 0 }, /* execute today - don't advance time */ 90b89321a5SAndrey A. Chernov { "now", NOW,0 }, /* opt prefix for PLUS */ 91d78e98d2SNate Williams 92b89321a5SAndrey A. Chernov { "minute", MINUTES,0 }, /* minutes multiplier */ 93b89321a5SAndrey A. Chernov { "minutes", MINUTES,1 }, /* (pluralized) */ 94b89321a5SAndrey A. Chernov { "hour", HOURS,0 }, /* hours ... */ 95b89321a5SAndrey A. Chernov { "hours", HOURS,1 }, /* (pluralized) */ 96b89321a5SAndrey A. Chernov { "day", DAYS,0 }, /* days ... */ 97b89321a5SAndrey A. Chernov { "days", DAYS,1 }, /* (pluralized) */ 98b89321a5SAndrey A. Chernov { "week", WEEKS,0 }, /* week ... */ 99b89321a5SAndrey A. Chernov { "weeks", WEEKS,1 }, /* (pluralized) */ 100b6c989ffSNick Sayer { "month", MONTHS,0 }, /* month ... */ 101b6c989ffSNick Sayer { "months", MONTHS,1 }, /* (pluralized) */ 102b6c989ffSNick Sayer { "year", YEARS,0 }, /* year ... */ 103b6c989ffSNick Sayer { "years", YEARS,1 }, /* (pluralized) */ 104b89321a5SAndrey A. Chernov { "jan", JAN,0 }, 105b89321a5SAndrey A. Chernov { "feb", FEB,0 }, 106b89321a5SAndrey A. Chernov { "mar", MAR,0 }, 107b89321a5SAndrey A. Chernov { "apr", APR,0 }, 108b89321a5SAndrey A. Chernov { "may", MAY,0 }, 109b89321a5SAndrey A. Chernov { "jun", JUN,0 }, 110b89321a5SAndrey A. Chernov { "jul", JUL,0 }, 111b89321a5SAndrey A. Chernov { "aug", AUG,0 }, 112b89321a5SAndrey A. Chernov { "sep", SEP,0 }, 113b89321a5SAndrey A. Chernov { "oct", OCT,0 }, 114b89321a5SAndrey A. Chernov { "nov", NOV,0 }, 115b89321a5SAndrey A. Chernov { "dec", DEC,0 }, 116b9dd99f2SDag-Erling Smørgrav { "january", JAN,0 }, 117b9dd99f2SDag-Erling Smørgrav { "february", FEB,0 }, 118b9dd99f2SDag-Erling Smørgrav { "march", MAR,0 }, 119b9dd99f2SDag-Erling Smørgrav { "april", APR,0 }, 120b9dd99f2SDag-Erling Smørgrav { "may", MAY,0 }, 121b9dd99f2SDag-Erling Smørgrav { "june", JUN,0 }, 122b9dd99f2SDag-Erling Smørgrav { "july", JUL,0 }, 123b9dd99f2SDag-Erling Smørgrav { "august", AUG,0 }, 124b9dd99f2SDag-Erling Smørgrav { "september", SEP,0 }, 125b9dd99f2SDag-Erling Smørgrav { "october", OCT,0 }, 126b9dd99f2SDag-Erling Smørgrav { "november", NOV,0 }, 127b9dd99f2SDag-Erling Smørgrav { "december", DEC,0 }, 128b89321a5SAndrey A. Chernov { "sunday", SUN, 0 }, 129b89321a5SAndrey A. Chernov { "sun", SUN, 0 }, 130b89321a5SAndrey A. Chernov { "monday", MON, 0 }, 131b89321a5SAndrey A. Chernov { "mon", MON, 0 }, 132b89321a5SAndrey A. Chernov { "tuesday", TUE, 0 }, 133b89321a5SAndrey A. Chernov { "tue", TUE, 0 }, 134b89321a5SAndrey A. Chernov { "wednesday", WED, 0 }, 135b89321a5SAndrey A. Chernov { "wed", WED, 0 }, 136b89321a5SAndrey A. Chernov { "thursday", THU, 0 }, 137b89321a5SAndrey A. Chernov { "thu", THU, 0 }, 138b89321a5SAndrey A. Chernov { "friday", FRI, 0 }, 139b89321a5SAndrey A. Chernov { "fri", FRI, 0 }, 140b89321a5SAndrey A. Chernov { "saturday", SAT, 0 }, 141b89321a5SAndrey A. Chernov { "sat", SAT, 0 }, 142d78e98d2SNate Williams } ; 143d78e98d2SNate Williams 144d78e98d2SNate Williams /* File scope variables */ 145d78e98d2SNate Williams 146d78e98d2SNate Williams static char **scp; /* scanner - pointer at arglist */ 147d78e98d2SNate Williams static char scc; /* scanner - count of remaining arguments */ 148d78e98d2SNate Williams static char *sct; /* scanner - next char pointer in current argument */ 149d78e98d2SNate Williams static int need; /* scanner - need to advance to next argument */ 150d78e98d2SNate Williams 151d78e98d2SNate Williams static char *sc_token; /* scanner - token buffer */ 15281c8c7a4SPhilippe Charnier static size_t sc_len; /* scanner - length of token buffer */ 153ddcf8022SAndrey A. Chernov static int sc_tokid; /* scanner - token id */ 154b89321a5SAndrey A. Chernov static int sc_tokplur; /* scanner - is token plural? */ 155d78e98d2SNate Williams 156d78e98d2SNate Williams /* Local functions */ 157d78e98d2SNate Williams 158d78e98d2SNate Williams /* 159d78e98d2SNate Williams * parse a token, checking if it's something special to us 160d78e98d2SNate Williams */ 161ddcf8022SAndrey A. Chernov static int 162ddcf8022SAndrey A. Chernov parse_token(char *arg) 163d78e98d2SNate Williams { 1643ce6c357SMark Murray size_t i; 165d78e98d2SNate Williams 166d78e98d2SNate Williams for (i=0; i<(sizeof Specials/sizeof Specials[0]); i++) 167d78e98d2SNate Williams if (strcasecmp(Specials[i].name, arg) == 0) { 168b89321a5SAndrey A. Chernov sc_tokplur = Specials[i].plural; 169d78e98d2SNate Williams return sc_tokid = Specials[i].value; 170d78e98d2SNate Williams } 171d78e98d2SNate Williams 172d78e98d2SNate Williams /* not special - must be some random id */ 173d78e98d2SNate Williams return ID; 174d78e98d2SNate Williams } /* parse_token */ 175d78e98d2SNate Williams 176d78e98d2SNate Williams 177d78e98d2SNate Williams /* 178d78e98d2SNate Williams * init_scanner() sets up the scanner to eat arguments 179d78e98d2SNate Williams */ 180d78e98d2SNate Williams static void 181b89321a5SAndrey A. Chernov init_scanner(int argc, char **argv) 182d78e98d2SNate Williams { 183d78e98d2SNate Williams scp = argv; 184d78e98d2SNate Williams scc = argc; 185d78e98d2SNate Williams need = 1; 186d78e98d2SNate Williams sc_len = 1; 187ddcf8022SAndrey A. Chernov while (argc-- > 0) 188ddcf8022SAndrey A. Chernov sc_len += strlen(*argv++); 189d78e98d2SNate Williams 190a9be9be8SDavid E. O'Brien if ((sc_token = malloc(sc_len)) == NULL) 191a9be9be8SDavid E. O'Brien errx(EXIT_FAILURE, "virtual memory exhausted"); 192d78e98d2SNate Williams } /* init_scanner */ 193d78e98d2SNate Williams 194d78e98d2SNate Williams /* 195d78e98d2SNate Williams * token() fetches a token from the input stream 196d78e98d2SNate Williams */ 197ddcf8022SAndrey A. Chernov static int 1983ce6c357SMark Murray token(void) 199d78e98d2SNate Williams { 200d78e98d2SNate Williams int idx; 201d78e98d2SNate Williams 202d78e98d2SNate Williams while (1) { 203d78e98d2SNate Williams memset(sc_token, 0, sc_len); 204d78e98d2SNate Williams sc_tokid = EOF; 205b89321a5SAndrey A. Chernov sc_tokplur = 0; 206d78e98d2SNate Williams idx = 0; 207d78e98d2SNate Williams 208b89321a5SAndrey A. Chernov /* if we need to read another argument, walk along the argument list; 209d78e98d2SNate Williams * when we fall off the arglist, we'll just return EOF forever 210d78e98d2SNate Williams */ 211d78e98d2SNate Williams if (need) { 212d78e98d2SNate Williams if (scc < 1) 213d78e98d2SNate Williams return sc_tokid; 214d78e98d2SNate Williams sct = *scp; 215d78e98d2SNate Williams scp++; 216d78e98d2SNate Williams scc--; 217d78e98d2SNate Williams need = 0; 218d78e98d2SNate Williams } 219b89321a5SAndrey A. Chernov /* eat whitespace now - if we walk off the end of the argument, 220d78e98d2SNate Williams * we'll continue, which puts us up at the top of the while loop 221d78e98d2SNate Williams * to fetch the next argument in 222d78e98d2SNate Williams */ 223d78e98d2SNate Williams while (isspace(*sct)) 224d78e98d2SNate Williams ++sct; 225d78e98d2SNate Williams if (!*sct) { 226d78e98d2SNate Williams need = 1; 227d78e98d2SNate Williams continue; 228d78e98d2SNate Williams } 229d78e98d2SNate Williams 230b89321a5SAndrey A. Chernov /* preserve the first character of the new token 231d78e98d2SNate Williams */ 232d78e98d2SNate Williams sc_token[0] = *sct++; 233d78e98d2SNate Williams 234b89321a5SAndrey A. Chernov /* then see what it is 235d78e98d2SNate Williams */ 236d78e98d2SNate Williams if (isdigit(sc_token[0])) { 237d78e98d2SNate Williams while (isdigit(*sct)) 238d78e98d2SNate Williams sc_token[++idx] = *sct++; 239d78e98d2SNate Williams sc_token[++idx] = 0; 240d78e98d2SNate Williams return sc_tokid = NUMBER; 241b89321a5SAndrey A. Chernov } 242b89321a5SAndrey A. Chernov else if (isalpha(sc_token[0])) { 243d78e98d2SNate Williams while (isalpha(*sct)) 244d78e98d2SNate Williams sc_token[++idx] = *sct++; 245d78e98d2SNate Williams sc_token[++idx] = 0; 246d78e98d2SNate Williams return parse_token(sc_token); 247d78e98d2SNate Williams } 248d78e98d2SNate Williams else if (sc_token[0] == ':' || sc_token[0] == '.') 249d78e98d2SNate Williams return sc_tokid = DOT; 250d78e98d2SNate Williams else if (sc_token[0] == '+') 251d78e98d2SNate Williams return sc_tokid = PLUS; 25293a2fe45SMartin Cracauer else if (sc_token[0] == '-') 25393a2fe45SMartin Cracauer return sc_tokid = MINUS; 254b89321a5SAndrey A. Chernov else if (sc_token[0] == '/') 255d78e98d2SNate Williams return sc_tokid = SLASH; 256d78e98d2SNate Williams else 257d78e98d2SNate Williams return sc_tokid = JUNK; 258d78e98d2SNate Williams } /* while (1) */ 259d78e98d2SNate Williams } /* token */ 260d78e98d2SNate Williams 261d78e98d2SNate Williams 262d78e98d2SNate Williams /* 263d78e98d2SNate Williams * plonk() gives an appropriate error message if a token is incorrect 264d78e98d2SNate Williams */ 265d78e98d2SNate Williams static void 266b89321a5SAndrey A. Chernov plonk(int tok) 267d78e98d2SNate Williams { 268d78e98d2SNate Williams panic((tok == EOF) ? "incomplete time" 269d78e98d2SNate Williams : "garbled time"); 270d78e98d2SNate Williams } /* plonk */ 271d78e98d2SNate Williams 272d78e98d2SNate Williams 273d78e98d2SNate Williams /* 274d78e98d2SNate Williams * expect() gets a token and dies most horribly if it's not the token we want 275d78e98d2SNate Williams */ 276d78e98d2SNate Williams static void 277ddcf8022SAndrey A. Chernov expect(int desired) 278d78e98d2SNate Williams { 279d78e98d2SNate Williams if (token() != desired) 280d78e98d2SNate Williams plonk(sc_tokid); /* and we die here... */ 281d78e98d2SNate Williams } /* expect */ 282d78e98d2SNate Williams 283d78e98d2SNate Williams 284d78e98d2SNate Williams /* 28593a2fe45SMartin Cracauer * plus_or_minus() holds functionality common to plus() and minus() 286d78e98d2SNate Williams */ 287d78e98d2SNate Williams static void 28893a2fe45SMartin Cracauer plus_or_minus(struct tm *tm, int delay) 289d78e98d2SNate Williams { 290b89321a5SAndrey A. Chernov int expectplur; 291d78e98d2SNate Williams 29293a2fe45SMartin Cracauer expectplur = (delay != 1 && delay != -1) ? 1 : 0; 293d78e98d2SNate Williams 294d78e98d2SNate Williams switch (token()) { 295b6c989ffSNick Sayer case YEARS: 296b6c989ffSNick Sayer tm->tm_year += delay; 297b6c989ffSNick Sayer break; 298b6c989ffSNick Sayer case MONTHS: 299b6c989ffSNick Sayer tm->tm_mon += delay; 300b6c989ffSNick Sayer break; 301d78e98d2SNate Williams case WEEKS: 302d78e98d2SNate Williams delay *= 7; 303d78e98d2SNate Williams case DAYS: 304b6c989ffSNick Sayer tm->tm_mday += delay; 305b6c989ffSNick Sayer break; 306d78e98d2SNate Williams case HOURS: 307b6c989ffSNick Sayer tm->tm_hour += delay; 308b6c989ffSNick Sayer break; 309d78e98d2SNate Williams case MINUTES: 310b6c989ffSNick Sayer tm->tm_min += delay; 311b6c989ffSNick Sayer break; 312b6c989ffSNick Sayer default: 313b6c989ffSNick Sayer plonk(sc_tokid); 314b6c989ffSNick Sayer break; 315b6c989ffSNick Sayer } 316b6c989ffSNick Sayer 317b89321a5SAndrey A. Chernov if (expectplur != sc_tokplur) 31812d20ef9SPhilippe Charnier warnx("pluralization is wrong"); 319b6c989ffSNick Sayer 320b6c989ffSNick Sayer tm->tm_isdst = -1; 321b6c989ffSNick Sayer if (mktime(tm) < 0) 322d78e98d2SNate Williams plonk(sc_tokid); 32393a2fe45SMartin Cracauer } /* plus_or_minus */ 324b6c989ffSNick Sayer 32593a2fe45SMartin Cracauer 32693a2fe45SMartin Cracauer /* 32793a2fe45SMartin Cracauer * plus() parses a now + time 32893a2fe45SMartin Cracauer * 32993a2fe45SMartin Cracauer * at [NOW] PLUS NUMBER [MINUTES|HOURS|DAYS|WEEKS|MONTHS|YEARS] 33093a2fe45SMartin Cracauer * 33193a2fe45SMartin Cracauer */ 33293a2fe45SMartin Cracauer static void 33393a2fe45SMartin Cracauer plus(struct tm *tm) 33493a2fe45SMartin Cracauer { 33593a2fe45SMartin Cracauer int delay; 33693a2fe45SMartin Cracauer 33793a2fe45SMartin Cracauer expect(NUMBER); 33893a2fe45SMartin Cracauer 33993a2fe45SMartin Cracauer delay = atoi(sc_token); 34093a2fe45SMartin Cracauer plus_or_minus(tm, delay); 341d78e98d2SNate Williams } /* plus */ 342d78e98d2SNate Williams 343d78e98d2SNate Williams 344d78e98d2SNate Williams /* 34593a2fe45SMartin Cracauer * minus() is like plus but can not be used with NOW 34693a2fe45SMartin Cracauer */ 34793a2fe45SMartin Cracauer static void 34893a2fe45SMartin Cracauer minus(struct tm *tm) 34993a2fe45SMartin Cracauer { 35093a2fe45SMartin Cracauer int delay; 35193a2fe45SMartin Cracauer 35293a2fe45SMartin Cracauer expect(NUMBER); 35393a2fe45SMartin Cracauer 35493a2fe45SMartin Cracauer delay = -atoi(sc_token); 35593a2fe45SMartin Cracauer plus_or_minus(tm, delay); 35693a2fe45SMartin Cracauer } /* minus */ 35793a2fe45SMartin Cracauer 35893a2fe45SMartin Cracauer 35993a2fe45SMartin Cracauer /* 360d78e98d2SNate Williams * tod() computes the time of day 361d78e98d2SNate Williams * [NUMBER [DOT NUMBER] [AM|PM]] 362d78e98d2SNate Williams */ 363d78e98d2SNate Williams static void 364b89321a5SAndrey A. Chernov tod(struct tm *tm) 365d78e98d2SNate Williams { 366d78e98d2SNate Williams int hour, minute = 0; 367d78e98d2SNate Williams int tlen; 368d78e98d2SNate Williams 369d78e98d2SNate Williams hour = atoi(sc_token); 370d78e98d2SNate Williams tlen = strlen(sc_token); 371d78e98d2SNate Williams 372b89321a5SAndrey A. Chernov /* first pick out the time of day - if it's 4 digits, we assume 373d78e98d2SNate Williams * a HHMM time, otherwise it's HH DOT MM time 374d78e98d2SNate Williams */ 375d78e98d2SNate Williams if (token() == DOT) { 376d78e98d2SNate Williams expect(NUMBER); 377d78e98d2SNate Williams minute = atoi(sc_token); 378d78e98d2SNate Williams if (minute > 59) 379d78e98d2SNate Williams panic("garbled time"); 380d78e98d2SNate Williams token(); 381b89321a5SAndrey A. Chernov } 382b89321a5SAndrey A. Chernov else if (tlen == 4) { 383d78e98d2SNate Williams minute = hour%100; 384d78e98d2SNate Williams if (minute > 59) 3853297b869SSteve Price panic("garbled time"); 386d78e98d2SNate Williams hour = hour/100; 387d78e98d2SNate Williams } 388d78e98d2SNate Williams 389b89321a5SAndrey A. Chernov /* check if an AM or PM specifier was given 390d78e98d2SNate Williams */ 391d78e98d2SNate Williams if (sc_tokid == AM || sc_tokid == PM) { 392d78e98d2SNate Williams if (hour > 12) 393d78e98d2SNate Williams panic("garbled time"); 394d78e98d2SNate Williams 3959b2ea11cSJohn Polstra if (sc_tokid == PM) { 3969b2ea11cSJohn Polstra if (hour != 12) /* 12:xx PM is 12:xx, not 24:xx */ 397d78e98d2SNate Williams hour += 12; 3989b2ea11cSJohn Polstra } else { 3999b2ea11cSJohn Polstra if (hour == 12) /* 12:xx AM is 00:xx, not 12:xx */ 40012d20ef9SPhilippe Charnier hour = 0; 4019b2ea11cSJohn Polstra } 402d78e98d2SNate Williams token(); 403b89321a5SAndrey A. Chernov } 404b89321a5SAndrey A. Chernov else if (hour > 23) 405d78e98d2SNate Williams panic("garbled time"); 406d78e98d2SNate Williams 407b89321a5SAndrey A. Chernov /* if we specify an absolute time, we don't want to bump the day even 408d78e98d2SNate Williams * if we've gone past that time - but if we're specifying a time plus 409d78e98d2SNate Williams * a relative offset, it's okay to bump things 410d78e98d2SNate Williams */ 41193a2fe45SMartin Cracauer if ((sc_tokid == EOF || sc_tokid == PLUS || sc_tokid == MINUS) && 41293a2fe45SMartin Cracauer tm->tm_hour > hour) { 413d78e98d2SNate Williams tm->tm_mday++; 414b89321a5SAndrey A. Chernov tm->tm_wday++; 415b89321a5SAndrey A. Chernov } 416d78e98d2SNate Williams 417d78e98d2SNate Williams tm->tm_hour = hour; 418d78e98d2SNate Williams tm->tm_min = minute; 419d78e98d2SNate Williams if (tm->tm_hour == 24) { 420d78e98d2SNate Williams tm->tm_hour = 0; 421d78e98d2SNate Williams tm->tm_mday++; 422d78e98d2SNate Williams } 423d78e98d2SNate Williams } /* tod */ 424d78e98d2SNate Williams 425d78e98d2SNate Williams 426d78e98d2SNate Williams /* 427d78e98d2SNate Williams * assign_date() assigns a date, wrapping to next year if needed 428d78e98d2SNate Williams */ 429d78e98d2SNate Williams static void 430b89321a5SAndrey A. Chernov assign_date(struct tm *tm, long mday, long mon, long year) 431d78e98d2SNate Williams { 432f4510294SSheldon Hearn 433f4510294SSheldon Hearn /* 434f4510294SSheldon Hearn * Convert year into tm_year format (year - 1900). 435f4510294SSheldon Hearn * We may be given the year in 2 digit, 4 digit, or tm_year format. 436f4510294SSheldon Hearn */ 437f4510294SSheldon Hearn if (year != -1) { 438f4510294SSheldon Hearn if (year >= 1900) 439f4510294SSheldon Hearn year -= 1900; /* convert from 4 digit year */ 440f4510294SSheldon Hearn else if (year < 100) { 441f4510294SSheldon Hearn /* convert from 2 digit year */ 4421dbfc421SAlexander Langer struct tm *lt; 4431dbfc421SAlexander Langer time_t now; 4441dbfc421SAlexander Langer 4451dbfc421SAlexander Langer time(&now); 4461dbfc421SAlexander Langer lt = localtime(&now); 4471dbfc421SAlexander Langer 448f4510294SSheldon Hearn /* Convert to tm_year assuming current century */ 449f4510294SSheldon Hearn year += (lt->tm_year / 100) * 100; 450f4510294SSheldon Hearn 451f4510294SSheldon Hearn if (year == lt->tm_year - 1) year++; 452f4510294SSheldon Hearn else if (year < lt->tm_year) 453f4510294SSheldon Hearn year += 100; /* must be in next century */ 454f4510294SSheldon Hearn } 455d78e98d2SNate Williams } 456d78e98d2SNate Williams 457d78e98d2SNate Williams if (year < 0 && 458d78e98d2SNate Williams (tm->tm_mon > mon ||(tm->tm_mon == mon && tm->tm_mday > mday))) 459d78e98d2SNate Williams year = tm->tm_year + 1; 460d78e98d2SNate Williams 461d78e98d2SNate Williams tm->tm_mday = mday; 462d78e98d2SNate Williams tm->tm_mon = mon; 463d78e98d2SNate Williams 464d78e98d2SNate Williams if (year >= 0) 465d78e98d2SNate Williams tm->tm_year = year; 466d78e98d2SNate Williams } /* assign_date */ 467d78e98d2SNate Williams 468d78e98d2SNate Williams 469d78e98d2SNate Williams /* 470d78e98d2SNate Williams * month() picks apart a month specification 471d78e98d2SNate Williams * 472d78e98d2SNate Williams * /[<month> NUMBER [NUMBER]] \ 473d78e98d2SNate Williams * |[TOMORROW] | 474b89321a5SAndrey A. Chernov * |[DAY OF WEEK] | 475d78e98d2SNate Williams * |NUMBER [SLASH NUMBER [SLASH NUMBER]]| 476d78e98d2SNate Williams * \PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS/ 477d78e98d2SNate Williams */ 478d78e98d2SNate Williams static void 479b89321a5SAndrey A. Chernov month(struct tm *tm) 480d78e98d2SNate Williams { 481d78e98d2SNate Williams long year= (-1); 4825f618f92SArchie Cobbs long mday = 0, wday, mon; 483d78e98d2SNate Williams int tlen; 484d78e98d2SNate Williams 485d78e98d2SNate Williams switch (sc_tokid) { 486d78e98d2SNate Williams case PLUS: 487d78e98d2SNate Williams plus(tm); 488d78e98d2SNate Williams break; 48993a2fe45SMartin Cracauer case MINUS: 49093a2fe45SMartin Cracauer minus(tm); 49193a2fe45SMartin Cracauer break; 492d78e98d2SNate Williams 493d78e98d2SNate Williams case TOMORROW: 494d78e98d2SNate Williams /* do something tomorrow */ 495d78e98d2SNate Williams tm->tm_mday ++; 496b89321a5SAndrey A. Chernov tm->tm_wday ++; 497d78e98d2SNate Williams case TODAY: /* force ourselves to stay in today - no further processing */ 498d78e98d2SNate Williams token(); 499d78e98d2SNate Williams break; 500d78e98d2SNate Williams 501d78e98d2SNate Williams case JAN: case FEB: case MAR: case APR: case MAY: case JUN: 502d78e98d2SNate Williams case JUL: case AUG: case SEP: case OCT: case NOV: case DEC: 503b89321a5SAndrey A. Chernov /* do month mday [year] 504d78e98d2SNate Williams */ 505d78e98d2SNate Williams mon = (sc_tokid-JAN); 506d78e98d2SNate Williams expect(NUMBER); 50768abb9f9SBruce Evans mday = atol(sc_token); 508d78e98d2SNate Williams if (token() == NUMBER) { 509d78e98d2SNate Williams year = atol(sc_token); 510d78e98d2SNate Williams token(); 511d78e98d2SNate Williams } 512d78e98d2SNate Williams assign_date(tm, mday, mon, year); 513d78e98d2SNate Williams break; 514d78e98d2SNate Williams 515b89321a5SAndrey A. Chernov case SUN: case MON: case TUE: 516b89321a5SAndrey A. Chernov case WED: case THU: case FRI: 517b89321a5SAndrey A. Chernov case SAT: 518b89321a5SAndrey A. Chernov /* do a particular day of the week 519b89321a5SAndrey A. Chernov */ 520b89321a5SAndrey A. Chernov wday = (sc_tokid-SUN); 521b89321a5SAndrey A. Chernov 522b89321a5SAndrey A. Chernov mday = tm->tm_mday; 523b89321a5SAndrey A. Chernov 524b89321a5SAndrey A. Chernov /* if this day is < today, then roll to next week 525b89321a5SAndrey A. Chernov */ 526b89321a5SAndrey A. Chernov if (wday < tm->tm_wday) 527b89321a5SAndrey A. Chernov mday += 7 - (tm->tm_wday - wday); 528b89321a5SAndrey A. Chernov else 529b89321a5SAndrey A. Chernov mday += (wday - tm->tm_wday); 530b89321a5SAndrey A. Chernov 531b89321a5SAndrey A. Chernov tm->tm_wday = wday; 532b89321a5SAndrey A. Chernov 533b89321a5SAndrey A. Chernov assign_date(tm, mday, tm->tm_mon, tm->tm_year); 534b89321a5SAndrey A. Chernov break; 535b89321a5SAndrey A. Chernov 536d78e98d2SNate Williams case NUMBER: 537b89321a5SAndrey A. Chernov /* get numeric MMDDYY, mm/dd/yy, or dd.mm.yy 538d78e98d2SNate Williams */ 539d78e98d2SNate Williams tlen = strlen(sc_token); 540d78e98d2SNate Williams mon = atol(sc_token); 541d78e98d2SNate Williams token(); 542d78e98d2SNate Williams 543d78e98d2SNate Williams if (sc_tokid == SLASH || sc_tokid == DOT) { 544ddcf8022SAndrey A. Chernov int sep; 545d78e98d2SNate Williams 546d78e98d2SNate Williams sep = sc_tokid; 547d78e98d2SNate Williams expect(NUMBER); 548d78e98d2SNate Williams mday = atol(sc_token); 549d78e98d2SNate Williams if (token() == sep) { 550d78e98d2SNate Williams expect(NUMBER); 551d78e98d2SNate Williams year = atol(sc_token); 552d78e98d2SNate Williams token(); 553d78e98d2SNate Williams } 554d78e98d2SNate Williams 55581c8c7a4SPhilippe Charnier /* flip months and days for European timing 556d78e98d2SNate Williams */ 557d78e98d2SNate Williams if (sep == DOT) { 558d78e98d2SNate Williams int x = mday; 559d78e98d2SNate Williams mday = mon; 560d78e98d2SNate Williams mon = x; 561d78e98d2SNate Williams } 562b89321a5SAndrey A. Chernov } 563b89321a5SAndrey A. Chernov else if (tlen == 6 || tlen == 8) { 564d78e98d2SNate Williams if (tlen == 8) { 565d78e98d2SNate Williams year = (mon % 10000) - 1900; 566d78e98d2SNate Williams mon /= 10000; 567b89321a5SAndrey A. Chernov } 568b89321a5SAndrey A. Chernov else { 569d78e98d2SNate Williams year = mon % 100; 570d78e98d2SNate Williams mon /= 100; 571d78e98d2SNate Williams } 572d78e98d2SNate Williams mday = mon % 100; 573d78e98d2SNate Williams mon /= 100; 574b89321a5SAndrey A. Chernov } 575b89321a5SAndrey A. Chernov else 576d78e98d2SNate Williams panic("garbled time"); 577d78e98d2SNate Williams 578d78e98d2SNate Williams mon--; 579d78e98d2SNate Williams if (mon < 0 || mon > 11 || mday < 1 || mday > 31) 580d78e98d2SNate Williams panic("garbled time"); 581d78e98d2SNate Williams 582d78e98d2SNate Williams assign_date(tm, mday, mon, year); 583d78e98d2SNate Williams break; 584d78e98d2SNate Williams } /* case */ 585d78e98d2SNate Williams } /* month */ 586d78e98d2SNate Williams 587d78e98d2SNate Williams 588d78e98d2SNate Williams /* Global functions */ 589d78e98d2SNate Williams 590d78e98d2SNate Williams time_t 591b89321a5SAndrey A. Chernov parsetime(int argc, char **argv) 592d78e98d2SNate Williams { 593b89321a5SAndrey A. Chernov /* Do the argument parsing, die if necessary, and return the time the job 594d78e98d2SNate Williams * should be run. 595d78e98d2SNate Williams */ 596d78e98d2SNate Williams time_t nowtimer, runtimer; 597d78e98d2SNate Williams struct tm nowtime, runtime; 598d78e98d2SNate Williams int hr = 0; 599d78e98d2SNate Williams /* this MUST be initialized to zero for midnight/noon/teatime */ 600d78e98d2SNate Williams 601d78e98d2SNate Williams nowtimer = time(NULL); 602d78e98d2SNate Williams nowtime = *localtime(&nowtimer); 603d78e98d2SNate Williams 604d78e98d2SNate Williams runtime = nowtime; 605d78e98d2SNate Williams runtime.tm_sec = 0; 606d78e98d2SNate Williams runtime.tm_isdst = 0; 607d78e98d2SNate Williams 608d78e98d2SNate Williams if (argc <= optind) 609d78e98d2SNate Williams usage(); 610d78e98d2SNate Williams 611d78e98d2SNate Williams init_scanner(argc-optind, argv+optind); 612d78e98d2SNate Williams 613d78e98d2SNate Williams switch (token()) { 614bafdc304SBrian Somers case NOW: 615bafdc304SBrian Somers if (scc < 1) { 616bafdc304SBrian Somers return nowtimer; 617bafdc304SBrian Somers } 618bafdc304SBrian Somers /* now is optional prefix for PLUS tree */ 619d78e98d2SNate Williams expect(PLUS); 62090de1ffbSAlan Somers /* FALLTHROUGH */ 621d78e98d2SNate Williams case PLUS: 622d78e98d2SNate Williams plus(&runtime); 623d78e98d2SNate Williams break; 624d78e98d2SNate Williams 62593a2fe45SMartin Cracauer /* MINUS is different from PLUS in that NOW is not 62693a2fe45SMartin Cracauer * an optional prefix for it 62793a2fe45SMartin Cracauer */ 62893a2fe45SMartin Cracauer case MINUS: 62993a2fe45SMartin Cracauer minus(&runtime); 63093a2fe45SMartin Cracauer break; 631d78e98d2SNate Williams case NUMBER: 632d78e98d2SNate Williams tod(&runtime); 633d78e98d2SNate Williams month(&runtime); 634d78e98d2SNate Williams break; 635d78e98d2SNate Williams 636b89321a5SAndrey A. Chernov /* evil coding for TEATIME|NOON|MIDNIGHT - we've initialised 637d78e98d2SNate Williams * hr to zero up above, then fall into this case in such a 638d78e98d2SNate Williams * way so we add +12 +4 hours to it for teatime, +12 hours 639d78e98d2SNate Williams * to it for noon, and nothing at all for midnight, then 640d78e98d2SNate Williams * set our runtime to that hour before leaping into the 641d78e98d2SNate Williams * month scanner 642d78e98d2SNate Williams */ 643d78e98d2SNate Williams case TEATIME: 644d78e98d2SNate Williams hr += 4; 64590de1ffbSAlan Somers /* FALLTHROUGH */ 646d78e98d2SNate Williams case NOON: 647d78e98d2SNate Williams hr += 12; 64890de1ffbSAlan Somers /* FALLTHROUGH */ 649d78e98d2SNate Williams case MIDNIGHT: 650b89321a5SAndrey A. Chernov if (runtime.tm_hour >= hr) { 651d78e98d2SNate Williams runtime.tm_mday++; 652b89321a5SAndrey A. Chernov runtime.tm_wday++; 653b89321a5SAndrey A. Chernov } 654d78e98d2SNate Williams runtime.tm_hour = hr; 655d78e98d2SNate Williams runtime.tm_min = 0; 656d78e98d2SNate Williams token(); 65781c8c7a4SPhilippe Charnier /* FALLTHROUGH to month setting */ 658d78e98d2SNate Williams default: 659d78e98d2SNate Williams month(&runtime); 660d78e98d2SNate Williams break; 661d78e98d2SNate Williams } /* ugly case statement */ 662d78e98d2SNate Williams expect(EOF); 663d78e98d2SNate Williams 6644ed70180SStefan Farfeleder /* convert back to time_t 665d78e98d2SNate Williams */ 666d78e98d2SNate Williams runtime.tm_isdst = -1; 667d78e98d2SNate Williams runtimer = mktime(&runtime); 668d78e98d2SNate Williams 669d78e98d2SNate Williams if (runtimer < 0) 670d78e98d2SNate Williams panic("garbled time"); 671d78e98d2SNate Williams 672d78e98d2SNate Williams if (nowtimer > runtimer) 67381c8c7a4SPhilippe Charnier panic("trying to travel back in time"); 674d78e98d2SNate Williams 675d78e98d2SNate Williams return runtimer; 676d78e98d2SNate Williams } /* parsetime */ 677