[BACK]Return to parsetime.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / at

Annotation of src/usr.bin/at/parsetime.c, Revision 1.3

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