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