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