[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.2

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:        token();
                    353:     } else if (tlen == 4) {
                    354:        minute = hour%100;
                    355:        hour = hour/100;
                    356:     }
                    357:
1.2     ! deraadt   358:     if (minute > 59)
        !           359:        panic("garbled time");
        !           360:
1.1       deraadt   361:     /*
                    362:      * check if an AM or PM specifier was given
                    363:      */
                    364:     if (sc_tokid == AM || sc_tokid == PM) {
                    365:        if (hour > 12)
                    366:            panic("garbled time");
1.2     ! deraadt   367:        else if (hour == 12)
        !           368:            hour = 0;
1.1       deraadt   369:
                    370:        if (sc_tokid == PM)
                    371:            hour += 12;
                    372:        token();
                    373:     } else if (hour > 23)
                    374:        panic("garbled time");
                    375:
                    376:     /*
                    377:      * if we specify an absolute time, we don't want to bump the day even
                    378:      * if we've gone past that time - but if we're specifying a time plus
                    379:      * a relative offset, it's okay to bump things
                    380:      */
                    381:     if ((sc_tokid == EOF || sc_tokid == PLUS) && tm->tm_hour > hour)
                    382:        tm->tm_mday++;
                    383:
                    384:     tm->tm_hour = hour;
                    385:     tm->tm_min = minute;
                    386: } /* tod */
                    387:
                    388:
                    389: /*
                    390:  * assign_date() assigns a date, wrapping to next year if needed
                    391:  */
                    392: static void
                    393: assign_date(tm, mday, mon, year)
                    394:        struct tm *tm;
                    395:        long mday, mon, year;
                    396: {
                    397:     if (year > 99) {
                    398:        if (year > 1899)
                    399:            year -= 1900;
                    400:        else
                    401:            panic("garbled time");
                    402:     }
                    403:
                    404:     if (year < 0 &&
                    405:        (tm->tm_mon > mon ||(tm->tm_mon == mon && tm->tm_mday > mday)))
                    406:        year = tm->tm_year + 1;
                    407:
                    408:     tm->tm_mday = mday;
                    409:     tm->tm_mon = mon;
                    410:
                    411:     if (year >= 0)
                    412:        tm->tm_year = year;
                    413: } /* assign_date */
                    414:
                    415:
                    416: /*
                    417:  * month() picks apart a month specification
                    418:  *
                    419:  *  /[<month> NUMBER [NUMBER]]           \
                    420:  *  |[TOMORROW]                          |
                    421:  *  |NUMBER [SLASH NUMBER [SLASH NUMBER]]|
                    422:  *  \PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS/
                    423:  */
                    424: static void
                    425: month(tm)
                    426:        struct tm *tm;
                    427: {
                    428:     long year= (-1);
                    429:     long mday, mon;
                    430:     int tlen;
                    431:
                    432:     switch (sc_tokid) {
                    433:     case PLUS:
                    434:            plus(tm);
                    435:            break;
                    436:
                    437:     case TOMORROW:
                    438:            /* do something tomorrow */
                    439:            tm->tm_mday ++;
                    440:     case TODAY:        /* force ourselves to stay in today - no further processing */
                    441:            token();
                    442:            break;
                    443:
                    444:     case JAN: case FEB: case MAR: case APR: case MAY: case JUN:
                    445:     case JUL: case AUG: case SEP: case OCT: case NOV: case DEC:
                    446:            /*
                    447:             * do month mday [year]
                    448:             */
                    449:            mon = (sc_tokid-JAN);
                    450:            expect(NUMBER);
                    451:            mday = atol(sc_token);
                    452:            if (token() == NUMBER) {
                    453:                year = atol(sc_token);
                    454:                token();
                    455:            }
                    456:            assign_date(tm, mday, mon, year);
                    457:            break;
                    458:
                    459:     case NUMBER:
                    460:            /*
                    461:             * get numeric MMDDYY, mm/dd/yy, or dd.mm.yy
                    462:             */
                    463:            tlen = strlen(sc_token);
                    464:            mon = atol(sc_token);
                    465:            token();
                    466:
                    467:            if (sc_tokid == SLASH || sc_tokid == DOT) {
                    468:                int sep;
                    469:
                    470:                sep = sc_tokid;
                    471:                expect(NUMBER);
                    472:                mday = atol(sc_token);
                    473:                if (token() == sep) {
                    474:                    expect(NUMBER);
                    475:                    year = atol(sc_token);
                    476:                    token();
                    477:                }
                    478:
                    479:                /*
                    480:                 * flip months and days for european timing
                    481:                 */
                    482:                if (sep == DOT) {
                    483:                    int x = mday;
                    484:                    mday = mon;
                    485:                    mon = x;
                    486:                }
                    487:            } else if (tlen == 6 || tlen == 8) {
                    488:                if (tlen == 8) {
                    489:                    year = (mon % 10000) - 1900;
                    490:                    mon /= 10000;
                    491:                } else {
                    492:                    year = mon % 100;
                    493:                    mon /= 100;
                    494:                }
                    495:                mday = mon % 100;
                    496:                mon /= 100;
                    497:            } else
                    498:                panic("garbled time");
                    499:
                    500:            mon--;
                    501:            if (mon < 0 || mon > 11 || mday < 1 || mday > 31)
                    502:                panic("garbled time");
                    503:
                    504:            assign_date(tm, mday, mon, year);
                    505:            break;
                    506:     } /* case */
                    507: } /* month */
                    508:
                    509:
                    510: /* Global functions */
                    511:
                    512: time_t
                    513: parsetime(argc, argv)
                    514:        int argc;
                    515:        char **argv;
                    516: {
                    517: /*
                    518:  * Do the argument parsing, die if necessary, and return the time the job
                    519:  * should be run.
                    520:  */
                    521:     time_t nowtimer, runtimer;
                    522:     struct tm nowtime, runtime;
                    523:     int hr = 0;
                    524:     /* this MUST be initialized to zero for midnight/noon/teatime */
                    525:
                    526:     nowtimer = time(NULL);
                    527:     nowtime = *localtime(&nowtimer);
                    528:
                    529:     runtime = nowtime;
                    530:     runtime.tm_sec = 0;
                    531:     runtime.tm_isdst = 0;
                    532:
                    533:     if (argc <= optind)
                    534:        usage();
                    535:
                    536:     init_scanner(argc-optind, argv+optind);
                    537:
                    538:     switch (token()) {
                    539:     case NOW:  /* now is optional prefix for PLUS tree */
                    540:            expect(PLUS);
                    541:     case PLUS:
                    542:            plus(&runtime);
                    543:            break;
                    544:
                    545:     case NUMBER:
                    546:            tod(&runtime);
                    547:            month(&runtime);
                    548:            break;
                    549:
                    550:            /*
                    551:             * evil coding for TEATIME|NOON|MIDNIGHT - we've initialised
                    552:             * hr to zero up above, then fall into this case in such a
                    553:             * way so we add +12 +4 hours to it for teatime, +12 hours
                    554:             * to it for noon, and nothing at all for midnight, then
                    555:             * set our runtime to that hour before leaping into the
                    556:             * month scanner
                    557:             */
                    558:     case TEATIME:
                    559:            hr += 4;
                    560:     case NOON:
                    561:            hr += 12;
                    562:     case MIDNIGHT:
                    563:            if (runtime.tm_hour >= hr)
                    564:                runtime.tm_mday++;
                    565:            runtime.tm_hour = hr;
                    566:            runtime.tm_min = 0;
                    567:            token();
                    568:            /* fall through to month setting */
                    569:     default:
                    570:            month(&runtime);
                    571:            break;
                    572:     } /* ugly case statement */
                    573:     expect(EOF);
                    574:
                    575:     /*
                    576:      * adjust for daylight savings time
                    577:      */
                    578:     runtime.tm_isdst = -1;
                    579:     runtimer = mktime(&runtime);
                    580:     if (runtime.tm_isdst > 0) {
                    581:        runtimer -= 3600;
                    582:        runtimer = mktime(&runtime);
                    583:     }
                    584:
                    585:     if (runtimer < 0)
                    586:        panic("garbled time");
                    587:
                    588:     if (nowtimer > runtimer)
                    589:        panic("Trying to travel back in time");
                    590:
                    591:     return runtimer;
                    592: } /* parsetime */