[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.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 */