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