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