Home | History | Annotate | Line # | Download | only in libutil
parsedate.y revision 1.22
      1   1.1  christos %{
      2   1.1  christos /*
      3   1.1  christos **  Originally written by Steven M. Bellovin <smb (at) research.att.com> while
      4   1.1  christos **  at the University of North Carolina at Chapel Hill.  Later tweaked by
      5   1.1  christos **  a couple of people on Usenet.  Completely overhauled by Rich $alz
      6   1.1  christos **  <rsalz (at) bbn.com> and Jim Berets <jberets (at) bbn.com> in August, 1990;
      7   1.1  christos **
      8   1.1  christos **  This grammar has 10 shift/reduce conflicts.
      9   1.1  christos **
     10   1.1  christos **  This code is in the public domain and has no copyright.
     11   1.1  christos */
     12   1.1  christos /* SUPPRESS 287 on yaccpar_sccsid *//* Unused static variable */
     13   1.1  christos /* SUPPRESS 288 on yyerrlab *//* Label unused */
     14   1.1  christos 
     15  1.15      yamt #include <sys/cdefs.h>
     16  1.15      yamt #ifdef __RCSID
     17  1.22  christos __RCSID("$NetBSD: parsedate.y,v 1.22 2015/12/06 14:43:59 christos Exp $");
     18  1.15      yamt #endif
     19  1.15      yamt 
     20   1.1  christos #include <stdio.h>
     21   1.1  christos #include <ctype.h>
     22  1.14       apb #include <errno.h>
     23   1.1  christos #include <string.h>
     24   1.1  christos #include <time.h>
     25   1.1  christos #include <util.h>
     26   1.3  drochner #include <stdlib.h>
     27   1.1  christos 
     28   1.1  christos /* NOTES on rebuilding parsedate.c (particularly for inclusion in CVS
     29   1.1  christos    releases):
     30   1.1  christos 
     31   1.1  christos    We don't want to mess with all the portability hassles of alloca.
     32   1.1  christos    In particular, most (all?) versions of bison will use alloca in
     33   1.1  christos    their parser.  If bison works on your system (e.g. it should work
     34   1.1  christos    with gcc), then go ahead and use it, but the more general solution
     35   1.1  christos    is to use byacc instead of bison, which should generate a portable
     36   1.1  christos    parser.  I played with adding "#define alloca dont_use_alloca", to
     37   1.1  christos    give an error if the parser generator uses alloca (and thus detect
     38   1.1  christos    unportable parsedate.c's), but that seems to cause as many problems
     39   1.1  christos    as it solves.  */
     40   1.1  christos 
     41   1.1  christos #define EPOCH		1970
     42  1.21  christos #define HOUR(x)		((time_t)((x) * 60))
     43   1.1  christos #define SECSPERDAY	(24L * 60L * 60L)
     44   1.1  christos 
     45  1.20       apb #define USE_LOCAL_TIME	99999 /* special case for Convert() and yyTimezone */
     46   1.1  christos 
     47   1.1  christos /*
     48   1.1  christos **  An entry in the lexical lookup table.
     49   1.1  christos */
     50   1.1  christos typedef struct _TABLE {
     51   1.1  christos     const char	*name;
     52   1.1  christos     int		type;
     53   1.1  christos     time_t	value;
     54   1.1  christos } TABLE;
     55   1.1  christos 
     56   1.1  christos 
     57   1.1  christos /*
     58   1.1  christos **  Daylight-savings mode:  on, off, or not yet known.
     59   1.1  christos */
     60   1.1  christos typedef enum _DSTMODE {
     61   1.1  christos     DSTon, DSToff, DSTmaybe
     62   1.1  christos } DSTMODE;
     63   1.1  christos 
     64   1.1  christos /*
     65   1.1  christos **  Meridian:  am, pm, or 24-hour style.
     66   1.1  christos */
     67   1.1  christos typedef enum _MERIDIAN {
     68   1.1  christos     MERam, MERpm, MER24
     69   1.1  christos } MERIDIAN;
     70   1.1  christos 
     71   1.1  christos 
     72   1.9  christos struct dateinfo {
     73  1.17       apb 	DSTMODE	yyDSTmode;	/* DST on/off/maybe */
     74   1.9  christos 	time_t	yyDayOrdinal;
     75   1.9  christos 	time_t	yyDayNumber;
     76   1.9  christos 	int	yyHaveDate;
     77  1.17       apb 	int	yyHaveFullYear;	/* if true, year is not abbreviated. */
     78  1.17       apb 				/* if false, need to call AdjustYear(). */
     79   1.9  christos 	int	yyHaveDay;
     80   1.9  christos 	int	yyHaveRel;
     81   1.9  christos 	int	yyHaveTime;
     82   1.9  christos 	int	yyHaveZone;
     83  1.17       apb 	time_t	yyTimezone;	/* Timezone as minutes ahead/east of UTC */
     84  1.17       apb 	time_t	yyDay;		/* Day of month [1-31] */
     85  1.17       apb 	time_t	yyHour;		/* Hour of day [0-24] or [1-12] */
     86  1.17       apb 	time_t	yyMinutes;	/* Minute of hour [0-59] */
     87  1.17       apb 	time_t	yyMonth;	/* Month of year [1-12] */
     88  1.17       apb 	time_t	yySeconds;	/* Second of minute [0-60] */
     89  1.17       apb 	time_t	yyYear;		/* Year, see also yyHaveFullYear */
     90  1.17       apb 	MERIDIAN yyMeridian;	/* Interpret yyHour as AM/PM/24 hour clock */
     91   1.9  christos 	time_t	yyRelMonth;
     92   1.9  christos 	time_t	yyRelSeconds;
     93   1.9  christos };
     94   1.1  christos %}
     95   1.1  christos 
     96   1.1  christos %union {
     97   1.1  christos     time_t		Number;
     98   1.1  christos     enum _MERIDIAN	Meridian;
     99   1.1  christos }
    100   1.1  christos 
    101   1.1  christos %token	tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
    102   1.5      tron %token	tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST AT_SIGN
    103   1.1  christos 
    104   1.1  christos %type	<Number>	tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
    105   1.1  christos %type	<Number>	tSEC_UNIT tSNUMBER tUNUMBER tZONE
    106   1.1  christos %type	<Meridian>	tMERIDIAN o_merid
    107   1.1  christos 
    108   1.9  christos %parse-param	{ struct dateinfo *param }
    109   1.9  christos %parse-param 	{ const char **yyInput }
    110   1.9  christos %lex-param	{ const char **yyInput }
    111   1.9  christos %pure-parser
    112   1.9  christos 
    113   1.1  christos %%
    114   1.1  christos 
    115   1.1  christos spec	: /* NULL */
    116   1.1  christos 	| spec item
    117   1.1  christos 	;
    118   1.1  christos 
    119   1.1  christos item	: time {
    120   1.9  christos 	    param->yyHaveTime++;
    121   1.1  christos 	}
    122  1.19       apb 	| time_numericzone {
    123  1.19       apb 	    param->yyHaveTime++;
    124  1.19       apb 	    param->yyHaveZone++;
    125  1.19       apb 	}
    126   1.1  christos 	| zone {
    127   1.9  christos 	    param->yyHaveZone++;
    128   1.1  christos 	}
    129   1.1  christos 	| date {
    130   1.9  christos 	    param->yyHaveDate++;
    131   1.1  christos 	}
    132   1.1  christos 	| day {
    133   1.9  christos 	    param->yyHaveDay++;
    134   1.1  christos 	}
    135   1.1  christos 	| rel {
    136   1.9  christos 	    param->yyHaveRel++;
    137   1.1  christos 	}
    138   1.1  christos 	| cvsstamp {
    139   1.9  christos 	    param->yyHaveTime++;
    140   1.9  christos 	    param->yyHaveDate++;
    141   1.9  christos 	    param->yyHaveZone++;
    142   1.1  christos 	}
    143   1.5      tron 	| epochdate {
    144   1.9  christos 	    param->yyHaveTime++;
    145   1.9  christos 	    param->yyHaveDate++;
    146   1.9  christos 	    param->yyHaveZone++;
    147   1.5      tron 	}
    148   1.1  christos 	| number
    149   1.1  christos 	;
    150   1.1  christos 
    151   1.1  christos cvsstamp: tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER {
    152   1.9  christos 	    param->yyYear = $1;
    153   1.9  christos 	    if (param->yyYear < 100) param->yyYear += 1900;
    154  1.17       apb 	    param->yyHaveFullYear = 1;
    155   1.9  christos 	    param->yyMonth = $3;
    156   1.9  christos 	    param->yyDay = $5;
    157   1.9  christos 	    param->yyHour = $7;
    158   1.9  christos 	    param->yyMinutes = $9;
    159   1.9  christos 	    param->yySeconds = $11;
    160   1.9  christos 	    param->yyDSTmode = DSToff;
    161   1.9  christos 	    param->yyTimezone = 0;
    162   1.1  christos 	}
    163   1.1  christos 	;
    164   1.1  christos 
    165  1.14       apb epochdate: AT_SIGN at_number {
    166  1.14       apb             time_t    when = $<Number>2;
    167   1.5      tron             struct tm tmbuf;
    168   1.5      tron             if (gmtime_r(&when, &tmbuf) != NULL) {
    169   1.9  christos 		param->yyYear = tmbuf.tm_year + 1900;
    170   1.9  christos 		param->yyMonth = tmbuf.tm_mon + 1;
    171   1.9  christos 		param->yyDay = tmbuf.tm_mday;
    172   1.9  christos 
    173   1.9  christos 		param->yyHour = tmbuf.tm_hour;
    174   1.9  christos 		param->yyMinutes = tmbuf.tm_min;
    175   1.9  christos 		param->yySeconds = tmbuf.tm_sec;
    176   1.5      tron 	    } else {
    177   1.9  christos 		param->yyYear = EPOCH;
    178   1.9  christos 		param->yyMonth = 1;
    179   1.9  christos 		param->yyDay = 1;
    180   1.9  christos 
    181   1.9  christos 		param->yyHour = 0;
    182   1.9  christos 		param->yyMinutes = 0;
    183   1.9  christos 		param->yySeconds = 0;
    184   1.5      tron 	    }
    185  1.17       apb 	    param->yyHaveFullYear = 1;
    186   1.9  christos 	    param->yyDSTmode = DSToff;
    187   1.9  christos 	    param->yyTimezone = 0;
    188   1.5      tron 	}
    189   1.5      tron 	;
    190   1.5      tron 
    191  1.14       apb at_number : tUNUMBER | tSNUMBER ;
    192  1.14       apb 
    193   1.1  christos time	: tUNUMBER tMERIDIAN {
    194   1.9  christos 	    param->yyHour = $1;
    195   1.9  christos 	    param->yyMinutes = 0;
    196   1.9  christos 	    param->yySeconds = 0;
    197   1.9  christos 	    param->yyMeridian = $2;
    198   1.1  christos 	}
    199   1.1  christos 	| tUNUMBER ':' tUNUMBER o_merid {
    200   1.9  christos 	    param->yyHour = $1;
    201   1.9  christos 	    param->yyMinutes = $3;
    202   1.9  christos 	    param->yySeconds = 0;
    203   1.9  christos 	    param->yyMeridian = $4;
    204   1.1  christos 	}
    205  1.19       apb 	| tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
    206   1.9  christos 	    param->yyHour = $1;
    207   1.9  christos 	    param->yyMinutes = $3;
    208  1.19       apb 	    param->yySeconds = $5;
    209  1.19       apb 	    param->yyMeridian = $6;
    210   1.1  christos 	}
    211  1.19       apb 	| tUNUMBER ':' tUNUMBER ':' tUNUMBER '.' tUNUMBER {
    212   1.9  christos 	    param->yyHour = $1;
    213   1.9  christos 	    param->yyMinutes = $3;
    214   1.9  christos 	    param->yySeconds = $5;
    215  1.19       apb 	    param->yyMeridian = MER24;
    216  1.19       apb /* XXX: Do nothing with millis */
    217   1.1  christos 	}
    218  1.19       apb 	;
    219  1.19       apb 
    220  1.19       apb time_numericzone : tUNUMBER ':' tUNUMBER tSNUMBER {
    221   1.9  christos 	    param->yyHour = $1;
    222   1.9  christos 	    param->yyMinutes = $3;
    223   1.9  christos 	    param->yyMeridian = MER24;
    224   1.9  christos 	    param->yyDSTmode = DSToff;
    225  1.19       apb 	    param->yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
    226   1.1  christos 	}
    227  1.19       apb 	| tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
    228   1.9  christos 	    param->yyHour = $1;
    229   1.9  christos 	    param->yyMinutes = $3;
    230   1.9  christos 	    param->yySeconds = $5;
    231   1.9  christos 	    param->yyMeridian = MER24;
    232  1.19       apb 	    param->yyDSTmode = DSToff;
    233  1.19       apb 	    param->yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
    234   1.7  christos 	}
    235   1.1  christos 	;
    236   1.1  christos 
    237   1.1  christos zone	: tZONE {
    238   1.9  christos 	    param->yyTimezone = $1;
    239   1.9  christos 	    param->yyDSTmode = DSToff;
    240   1.1  christos 	}
    241   1.1  christos 	| tDAYZONE {
    242   1.9  christos 	    param->yyTimezone = $1;
    243   1.9  christos 	    param->yyDSTmode = DSTon;
    244   1.1  christos 	}
    245   1.1  christos 	|
    246   1.1  christos 	  tZONE tDST {
    247   1.9  christos 	    param->yyTimezone = $1;
    248   1.9  christos 	    param->yyDSTmode = DSTon;
    249   1.1  christos 	}
    250   1.1  christos 	;
    251   1.1  christos 
    252   1.1  christos day	: tDAY {
    253   1.9  christos 	    param->yyDayOrdinal = 1;
    254   1.9  christos 	    param->yyDayNumber = $1;
    255   1.1  christos 	}
    256   1.1  christos 	| tDAY ',' {
    257   1.9  christos 	    param->yyDayOrdinal = 1;
    258   1.9  christos 	    param->yyDayNumber = $1;
    259   1.1  christos 	}
    260   1.1  christos 	| tUNUMBER tDAY {
    261   1.9  christos 	    param->yyDayOrdinal = $1;
    262   1.9  christos 	    param->yyDayNumber = $2;
    263   1.1  christos 	}
    264   1.1  christos 	;
    265   1.1  christos 
    266   1.1  christos date	: tUNUMBER '/' tUNUMBER {
    267   1.9  christos 	    param->yyMonth = $1;
    268   1.9  christos 	    param->yyDay = $3;
    269   1.1  christos 	}
    270   1.1  christos 	| tUNUMBER '/' tUNUMBER '/' tUNUMBER {
    271   1.1  christos 	    if ($1 >= 100) {
    272   1.9  christos 		param->yyYear = $1;
    273   1.9  christos 		param->yyMonth = $3;
    274   1.9  christos 		param->yyDay = $5;
    275   1.1  christos 	    } else {
    276   1.9  christos 		param->yyMonth = $1;
    277   1.9  christos 		param->yyDay = $3;
    278   1.9  christos 		param->yyYear = $5;
    279   1.1  christos 	    }
    280   1.1  christos 	}
    281   1.1  christos 	| tUNUMBER tSNUMBER tSNUMBER {
    282   1.1  christos 	    /* ISO 8601 format.  yyyy-mm-dd.  */
    283   1.9  christos 	    param->yyYear = $1;
    284  1.17       apb 	    param->yyHaveFullYear = 1;
    285   1.9  christos 	    param->yyMonth = -$2;
    286   1.9  christos 	    param->yyDay = -$3;
    287   1.1  christos 	}
    288   1.1  christos 	| tUNUMBER tMONTH tSNUMBER {
    289   1.1  christos 	    /* e.g. 17-JUN-1992.  */
    290   1.9  christos 	    param->yyDay = $1;
    291   1.9  christos 	    param->yyMonth = $2;
    292   1.9  christos 	    param->yyYear = -$3;
    293   1.1  christos 	}
    294   1.1  christos 	| tMONTH tUNUMBER {
    295   1.9  christos 	    param->yyMonth = $1;
    296   1.9  christos 	    param->yyDay = $2;
    297   1.1  christos 	}
    298   1.1  christos 	| tMONTH tUNUMBER ',' tUNUMBER {
    299   1.9  christos 	    param->yyMonth = $1;
    300   1.9  christos 	    param->yyDay = $2;
    301   1.9  christos 	    param->yyYear = $4;
    302   1.1  christos 	}
    303   1.1  christos 	| tUNUMBER tMONTH {
    304   1.9  christos 	    param->yyMonth = $2;
    305   1.9  christos 	    param->yyDay = $1;
    306   1.1  christos 	}
    307   1.1  christos 	| tUNUMBER tMONTH tUNUMBER {
    308   1.9  christos 	    param->yyMonth = $2;
    309   1.9  christos 	    param->yyDay = $1;
    310   1.9  christos 	    param->yyYear = $3;
    311   1.1  christos 	}
    312   1.1  christos 	;
    313   1.1  christos 
    314   1.1  christos rel	: relunit tAGO {
    315   1.9  christos 	    param->yyRelSeconds = -param->yyRelSeconds;
    316   1.9  christos 	    param->yyRelMonth = -param->yyRelMonth;
    317   1.1  christos 	}
    318   1.1  christos 	| relunit
    319   1.1  christos 	;
    320   1.1  christos 
    321   1.1  christos relunit	: tUNUMBER tMINUTE_UNIT {
    322   1.9  christos 	    param->yyRelSeconds += $1 * $2 * 60L;
    323   1.1  christos 	}
    324   1.1  christos 	| tSNUMBER tMINUTE_UNIT {
    325   1.9  christos 	    param->yyRelSeconds += $1 * $2 * 60L;
    326   1.1  christos 	}
    327   1.1  christos 	| tMINUTE_UNIT {
    328   1.9  christos 	    param->yyRelSeconds += $1 * 60L;
    329   1.1  christos 	}
    330   1.1  christos 	| tSNUMBER tSEC_UNIT {
    331   1.9  christos 	    param->yyRelSeconds += $1;
    332   1.1  christos 	}
    333   1.1  christos 	| tUNUMBER tSEC_UNIT {
    334   1.9  christos 	    param->yyRelSeconds += $1;
    335   1.1  christos 	}
    336   1.1  christos 	| tSEC_UNIT {
    337   1.9  christos 	    param->yyRelSeconds++;
    338   1.1  christos 	}
    339   1.1  christos 	| tSNUMBER tMONTH_UNIT {
    340   1.9  christos 	    param->yyRelMonth += $1 * $2;
    341   1.1  christos 	}
    342   1.1  christos 	| tUNUMBER tMONTH_UNIT {
    343   1.9  christos 	    param->yyRelMonth += $1 * $2;
    344   1.1  christos 	}
    345   1.1  christos 	| tMONTH_UNIT {
    346   1.9  christos 	    param->yyRelMonth += $1;
    347   1.1  christos 	}
    348   1.1  christos 	;
    349   1.1  christos 
    350   1.1  christos number	: tUNUMBER {
    351   1.9  christos 	    if (param->yyHaveTime && param->yyHaveDate && !param->yyHaveRel)
    352   1.9  christos 		param->yyYear = $1;
    353   1.1  christos 	    else {
    354   1.1  christos 		if($1>10000) {
    355   1.9  christos 		    param->yyHaveDate++;
    356   1.9  christos 		    param->yyDay= ($1)%100;
    357   1.9  christos 		    param->yyMonth= ($1/100)%100;
    358   1.9  christos 		    param->yyYear = $1/10000;
    359   1.1  christos 		}
    360   1.1  christos 		else {
    361   1.9  christos 		    param->yyHaveTime++;
    362   1.1  christos 		    if ($1 < 100) {
    363   1.9  christos 			param->yyHour = $1;
    364   1.9  christos 			param->yyMinutes = 0;
    365   1.1  christos 		    }
    366   1.1  christos 		    else {
    367   1.9  christos 		    	param->yyHour = $1 / 100;
    368   1.9  christos 		    	param->yyMinutes = $1 % 100;
    369   1.1  christos 		    }
    370   1.9  christos 		    param->yySeconds = 0;
    371   1.9  christos 		    param->yyMeridian = MER24;
    372   1.1  christos 	        }
    373   1.1  christos 	    }
    374   1.1  christos 	}
    375   1.1  christos 	;
    376   1.1  christos 
    377   1.1  christos o_merid	: /* NULL */ {
    378   1.1  christos 	    $$ = MER24;
    379   1.1  christos 	}
    380   1.1  christos 	| tMERIDIAN {
    381   1.1  christos 	    $$ = $1;
    382   1.1  christos 	}
    383   1.1  christos 	;
    384   1.1  christos 
    385   1.1  christos %%
    386   1.1  christos 
    387   1.1  christos /* Month and day table. */
    388  1.12     joerg static const TABLE MonthDayTable[] = {
    389   1.1  christos     { "january",	tMONTH,  1 },
    390   1.1  christos     { "february",	tMONTH,  2 },
    391   1.1  christos     { "march",		tMONTH,  3 },
    392   1.1  christos     { "april",		tMONTH,  4 },
    393   1.1  christos     { "may",		tMONTH,  5 },
    394   1.1  christos     { "june",		tMONTH,  6 },
    395   1.1  christos     { "july",		tMONTH,  7 },
    396   1.1  christos     { "august",		tMONTH,  8 },
    397   1.1  christos     { "september",	tMONTH,  9 },
    398   1.1  christos     { "sept",		tMONTH,  9 },
    399   1.1  christos     { "october",	tMONTH, 10 },
    400   1.1  christos     { "november",	tMONTH, 11 },
    401   1.1  christos     { "december",	tMONTH, 12 },
    402   1.1  christos     { "sunday",		tDAY, 0 },
    403  1.21  christos     { "su",		tDAY, 0 },
    404   1.1  christos     { "monday",		tDAY, 1 },
    405  1.21  christos     { "mo",		tDAY, 1 },
    406   1.1  christos     { "tuesday",	tDAY, 2 },
    407   1.1  christos     { "tues",		tDAY, 2 },
    408  1.21  christos     { "tu",		tDAY, 2 },
    409   1.1  christos     { "wednesday",	tDAY, 3 },
    410   1.1  christos     { "wednes",		tDAY, 3 },
    411  1.21  christos     { "weds",		tDAY, 3 },
    412  1.21  christos     { "we",		tDAY, 3 },
    413   1.1  christos     { "thursday",	tDAY, 4 },
    414  1.21  christos     { "thurs",		tDAY, 4 },
    415   1.1  christos     { "thur",		tDAY, 4 },
    416  1.21  christos     { "th",		tDAY, 4 },
    417   1.1  christos     { "friday",		tDAY, 5 },
    418  1.21  christos     { "fr",		tDAY, 5 },
    419   1.1  christos     { "saturday",	tDAY, 6 },
    420  1.21  christos     { "sa",		tDAY, 6 },
    421   1.1  christos     { NULL,		0,    0 }
    422   1.1  christos };
    423   1.1  christos 
    424   1.1  christos /* Time units table. */
    425  1.12     joerg static const TABLE UnitsTable[] = {
    426   1.1  christos     { "year",		tMONTH_UNIT,	12 },
    427   1.1  christos     { "month",		tMONTH_UNIT,	1 },
    428   1.1  christos     { "fortnight",	tMINUTE_UNIT,	14 * 24 * 60 },
    429   1.1  christos     { "week",		tMINUTE_UNIT,	7 * 24 * 60 },
    430   1.1  christos     { "day",		tMINUTE_UNIT,	1 * 24 * 60 },
    431   1.1  christos     { "hour",		tMINUTE_UNIT,	60 },
    432   1.1  christos     { "minute",		tMINUTE_UNIT,	1 },
    433   1.1  christos     { "min",		tMINUTE_UNIT,	1 },
    434   1.1  christos     { "second",		tSEC_UNIT,	1 },
    435   1.1  christos     { "sec",		tSEC_UNIT,	1 },
    436   1.1  christos     { NULL,		0,		0 }
    437   1.1  christos };
    438   1.1  christos 
    439   1.1  christos /* Assorted relative-time words. */
    440  1.12     joerg static const TABLE OtherTable[] = {
    441   1.1  christos     { "tomorrow",	tMINUTE_UNIT,	1 * 24 * 60 },
    442   1.1  christos     { "yesterday",	tMINUTE_UNIT,	-1 * 24 * 60 },
    443   1.1  christos     { "today",		tMINUTE_UNIT,	0 },
    444   1.1  christos     { "now",		tMINUTE_UNIT,	0 },
    445   1.1  christos     { "last",		tUNUMBER,	-1 },
    446   1.1  christos     { "this",		tMINUTE_UNIT,	0 },
    447   1.1  christos     { "next",		tUNUMBER,	2 },
    448   1.1  christos     { "first",		tUNUMBER,	1 },
    449   1.7  christos     { "one",		tUNUMBER,	1 },
    450   1.1  christos /*  { "second",		tUNUMBER,	2 }, */
    451   1.7  christos     { "two",		tUNUMBER,	2 },
    452   1.1  christos     { "third",		tUNUMBER,	3 },
    453   1.7  christos     { "three",		tUNUMBER,	3 },
    454   1.1  christos     { "fourth",		tUNUMBER,	4 },
    455   1.7  christos     { "four",		tUNUMBER,	4 },
    456   1.1  christos     { "fifth",		tUNUMBER,	5 },
    457   1.7  christos     { "five",		tUNUMBER,	5 },
    458   1.1  christos     { "sixth",		tUNUMBER,	6 },
    459   1.7  christos     { "six",		tUNUMBER,	6 },
    460   1.1  christos     { "seventh",	tUNUMBER,	7 },
    461   1.7  christos     { "seven",		tUNUMBER,	7 },
    462   1.1  christos     { "eighth",		tUNUMBER,	8 },
    463   1.7  christos     { "eight",		tUNUMBER,	8 },
    464   1.1  christos     { "ninth",		tUNUMBER,	9 },
    465   1.7  christos     { "nine",		tUNUMBER,	9 },
    466   1.1  christos     { "tenth",		tUNUMBER,	10 },
    467   1.7  christos     { "ten",		tUNUMBER,	10 },
    468   1.1  christos     { "eleventh",	tUNUMBER,	11 },
    469   1.7  christos     { "eleven",		tUNUMBER,	11 },
    470   1.1  christos     { "twelfth",	tUNUMBER,	12 },
    471   1.7  christos     { "twelve",		tUNUMBER,	12 },
    472   1.1  christos     { "ago",		tAGO,	1 },
    473   1.1  christos     { NULL,		0,	0 }
    474   1.1  christos };
    475   1.1  christos 
    476   1.1  christos /* The timezone table. */
    477   1.1  christos /* Some of these are commented out because a time_t can't store a float. */
    478  1.12     joerg static const TABLE TimezoneTable[] = {
    479   1.1  christos     { "gmt",	tZONE,     HOUR( 0) },	/* Greenwich Mean */
    480   1.1  christos     { "ut",	tZONE,     HOUR( 0) },	/* Universal (Coordinated) */
    481   1.1  christos     { "utc",	tZONE,     HOUR( 0) },
    482   1.1  christos     { "wet",	tZONE,     HOUR( 0) },	/* Western European */
    483   1.1  christos     { "bst",	tDAYZONE,  HOUR( 0) },	/* British Summer */
    484   1.1  christos     { "wat",	tZONE,     HOUR( 1) },	/* West Africa */
    485   1.1  christos     { "at",	tZONE,     HOUR( 2) },	/* Azores */
    486   1.1  christos #if	0
    487   1.1  christos     /* For completeness.  BST is also British Summer, and GST is
    488   1.1  christos      * also Guam Standard. */
    489   1.1  christos     { "bst",	tZONE,     HOUR( 3) },	/* Brazil Standard */
    490   1.1  christos     { "gst",	tZONE,     HOUR( 3) },	/* Greenland Standard */
    491   1.1  christos #endif
    492   1.1  christos     { "nft",	tZONE,     HOUR(3.5) },	/* Newfoundland */
    493   1.1  christos     { "nst",	tZONE,     HOUR(3.5) },	/* Newfoundland Standard */
    494   1.1  christos     { "ndt",	tDAYZONE,  HOUR(3.5) },	/* Newfoundland Daylight */
    495   1.1  christos     { "ast",	tZONE,     HOUR( 4) },	/* Atlantic Standard */
    496   1.1  christos     { "adt",	tDAYZONE,  HOUR( 4) },	/* Atlantic Daylight */
    497   1.1  christos     { "est",	tZONE,     HOUR( 5) },	/* Eastern Standard */
    498   1.1  christos     { "edt",	tDAYZONE,  HOUR( 5) },	/* Eastern Daylight */
    499   1.1  christos     { "cst",	tZONE,     HOUR( 6) },	/* Central Standard */
    500   1.1  christos     { "cdt",	tDAYZONE,  HOUR( 6) },	/* Central Daylight */
    501   1.1  christos     { "mst",	tZONE,     HOUR( 7) },	/* Mountain Standard */
    502   1.1  christos     { "mdt",	tDAYZONE,  HOUR( 7) },	/* Mountain Daylight */
    503   1.1  christos     { "pst",	tZONE,     HOUR( 8) },	/* Pacific Standard */
    504   1.1  christos     { "pdt",	tDAYZONE,  HOUR( 8) },	/* Pacific Daylight */
    505   1.1  christos     { "yst",	tZONE,     HOUR( 9) },	/* Yukon Standard */
    506   1.1  christos     { "ydt",	tDAYZONE,  HOUR( 9) },	/* Yukon Daylight */
    507   1.1  christos     { "hst",	tZONE,     HOUR(10) },	/* Hawaii Standard */
    508   1.1  christos     { "hdt",	tDAYZONE,  HOUR(10) },	/* Hawaii Daylight */
    509   1.1  christos     { "cat",	tZONE,     HOUR(10) },	/* Central Alaska */
    510   1.1  christos     { "ahst",	tZONE,     HOUR(10) },	/* Alaska-Hawaii Standard */
    511   1.1  christos     { "nt",	tZONE,     HOUR(11) },	/* Nome */
    512   1.1  christos     { "idlw",	tZONE,     HOUR(12) },	/* International Date Line West */
    513   1.1  christos     { "cet",	tZONE,     -HOUR(1) },	/* Central European */
    514   1.1  christos     { "met",	tZONE,     -HOUR(1) },	/* Middle European */
    515   1.1  christos     { "mewt",	tZONE,     -HOUR(1) },	/* Middle European Winter */
    516   1.1  christos     { "mest",	tDAYZONE,  -HOUR(1) },	/* Middle European Summer */
    517   1.1  christos     { "swt",	tZONE,     -HOUR(1) },	/* Swedish Winter */
    518   1.1  christos     { "sst",	tDAYZONE,  -HOUR(1) },	/* Swedish Summer */
    519   1.1  christos     { "fwt",	tZONE,     -HOUR(1) },	/* French Winter */
    520   1.1  christos     { "fst",	tDAYZONE,  -HOUR(1) },	/* French Summer */
    521   1.1  christos     { "eet",	tZONE,     -HOUR(2) },	/* Eastern Europe, USSR Zone 1 */
    522   1.1  christos     { "bt",	tZONE,     -HOUR(3) },	/* Baghdad, USSR Zone 2 */
    523   1.1  christos     { "it",	tZONE,     -HOUR(3.5) },/* Iran */
    524   1.1  christos     { "zp4",	tZONE,     -HOUR(4) },	/* USSR Zone 3 */
    525   1.1  christos     { "zp5",	tZONE,     -HOUR(5) },	/* USSR Zone 4 */
    526   1.1  christos     { "ist",	tZONE,     -HOUR(5.5) },/* Indian Standard */
    527   1.1  christos     { "zp6",	tZONE,     -HOUR(6) },	/* USSR Zone 5 */
    528   1.1  christos #if	0
    529   1.1  christos     /* For completeness.  NST is also Newfoundland Stanard, and SST is
    530   1.1  christos      * also Swedish Summer. */
    531   1.1  christos     { "nst",	tZONE,     -HOUR(6.5) },/* North Sumatra */
    532   1.1  christos     { "sst",	tZONE,     -HOUR(7) },	/* South Sumatra, USSR Zone 6 */
    533   1.1  christos #endif	/* 0 */
    534  1.21  christos     { "ict",	tZONE,     -HOUR(7) },	/* Indo China Time (Thai) */
    535  1.21  christos #if 0	/* this one looks to be bogus */
    536   1.1  christos     { "jt",	tZONE,     -HOUR(7.5) },/* Java (3pm in Cronusland!) */
    537   1.1  christos #endif
    538  1.22  christos     { "wast",	tZONE,     -HOUR(8) },	/* West Australian Standard */
    539  1.22  christos     { "awst",	tZONE,     -HOUR(8) },	/* West Australian Standard */
    540  1.22  christos     { "wadt",	tDAYZONE,  -HOUR(8) },	/* West Australian Daylight */
    541  1.22  christos     { "awdt",	tDAYZONE,  -HOUR(8) },	/* West Australian Daylight */
    542   1.1  christos     { "cct",	tZONE,     -HOUR(8) },	/* China Coast, USSR Zone 7 */
    543  1.21  christos     { "sgt",	tZONE,     -HOUR(8) },	/* Singapore */
    544  1.21  christos     { "hkt",	tZONE,     -HOUR(8) },	/* Hong Kong */
    545   1.1  christos     { "jst",	tZONE,     -HOUR(9) },	/* Japan Standard, USSR Zone 8 */
    546   1.1  christos     { "cast",	tZONE,     -HOUR(9.5) },/* Central Australian Standard */
    547  1.21  christos     { "acst",	tZONE,     -HOUR(9.5) },/* Central Australian Standard */
    548   1.1  christos     { "cadt",	tDAYZONE,  -HOUR(9.5) },/* Central Australian Daylight */
    549  1.21  christos     { "acdt",	tDAYZONE,  -HOUR(9.5) },/* Central Australian Daylight */
    550   1.1  christos     { "east",	tZONE,     -HOUR(10) },	/* Eastern Australian Standard */
    551  1.21  christos     { "aest",	tZONE,     -HOUR(10) },	/* Eastern Australian Standard */
    552   1.1  christos     { "eadt",	tDAYZONE,  -HOUR(10) },	/* Eastern Australian Daylight */
    553  1.21  christos     { "aedt",	tDAYZONE,  -HOUR(10) },	/* Eastern Australian Daylight */
    554   1.1  christos     { "gst",	tZONE,     -HOUR(10) },	/* Guam Standard, USSR Zone 9 */
    555   1.1  christos     { "nzt",	tZONE,     -HOUR(12) },	/* New Zealand */
    556   1.1  christos     { "nzst",	tZONE,     -HOUR(12) },	/* New Zealand Standard */
    557   1.1  christos     { "nzdt",	tDAYZONE,  -HOUR(12) },	/* New Zealand Daylight */
    558   1.1  christos     { "idle",	tZONE,     -HOUR(12) },	/* International Date Line East */
    559   1.1  christos     {  NULL,	0,	    0 }
    560   1.1  christos };
    561   1.1  christos 
    562   1.1  christos /* Military timezone table. */
    563  1.12     joerg static const TABLE MilitaryTable[] = {
    564   1.1  christos     { "a",	tZONE,	HOUR(  1) },
    565   1.1  christos     { "b",	tZONE,	HOUR(  2) },
    566   1.1  christos     { "c",	tZONE,	HOUR(  3) },
    567   1.1  christos     { "d",	tZONE,	HOUR(  4) },
    568   1.1  christos     { "e",	tZONE,	HOUR(  5) },
    569   1.1  christos     { "f",	tZONE,	HOUR(  6) },
    570   1.1  christos     { "g",	tZONE,	HOUR(  7) },
    571   1.1  christos     { "h",	tZONE,	HOUR(  8) },
    572   1.1  christos     { "i",	tZONE,	HOUR(  9) },
    573   1.1  christos     { "k",	tZONE,	HOUR( 10) },
    574   1.1  christos     { "l",	tZONE,	HOUR( 11) },
    575   1.1  christos     { "m",	tZONE,	HOUR( 12) },
    576   1.1  christos     { "n",	tZONE,	HOUR(- 1) },
    577   1.1  christos     { "o",	tZONE,	HOUR(- 2) },
    578   1.1  christos     { "p",	tZONE,	HOUR(- 3) },
    579   1.1  christos     { "q",	tZONE,	HOUR(- 4) },
    580   1.1  christos     { "r",	tZONE,	HOUR(- 5) },
    581   1.1  christos     { "s",	tZONE,	HOUR(- 6) },
    582   1.1  christos     { "t",	tZONE,	HOUR(- 7) },
    583   1.1  christos     { "u",	tZONE,	HOUR(- 8) },
    584   1.1  christos     { "v",	tZONE,	HOUR(- 9) },
    585   1.1  christos     { "w",	tZONE,	HOUR(-10) },
    586   1.1  christos     { "x",	tZONE,	HOUR(-11) },
    587   1.1  christos     { "y",	tZONE,	HOUR(-12) },
    588   1.1  christos     { "z",	tZONE,	HOUR(  0) },
    589   1.1  christos     { NULL,	0,	0 }
    590   1.1  christos };
    591   1.1  christos 
    592   1.1  christos 
    593   1.1  christos 
    595   1.1  christos 
    596   1.1  christos /* ARGSUSED */
    597   1.9  christos static int
    598   1.1  christos yyerror(struct dateinfo *param, const char **inp, const char *s __unused)
    599   1.1  christos {
    600   1.1  christos   return 0;
    601   1.1  christos }
    602   1.1  christos 
    603  1.17       apb 
    604  1.17       apb /* Adjust year from a value that might be abbreviated, to a full value.
    605  1.17       apb  * e.g. convert 70 to 1970.
    606  1.17       apb  * Input Year is either:
    607  1.17       apb  *  - A negative number, which means to use its absolute value (why?)
    608  1.17       apb  *  - A number from 0 to 99, which means a year from 1900 to 1999, or
    609  1.17       apb  *  - The actual year (>=100).
    610  1.17       apb  * Returns the full year. */
    611  1.17       apb static time_t
    612  1.17       apb AdjustYear(time_t Year)
    613  1.17       apb {
    614  1.17       apb     /* XXX Y2K */
    615  1.17       apb     if (Year < 0)
    616  1.17       apb 	Year = -Year;
    617  1.17       apb     if (Year < 70)
    618  1.17       apb 	Year += 2000;
    619  1.17       apb     else if (Year < 100)
    620  1.17       apb 	Year += 1900;
    621  1.17       apb     return Year;
    622  1.17       apb }
    623   1.1  christos 
    624   1.1  christos static time_t
    625  1.11       apb Convert(
    626  1.11       apb     time_t	Month,		/* month of year [1-12] */
    627  1.17       apb     time_t	Day,		/* day of month [1-31] */
    628  1.11       apb     time_t	Year,		/* year, not abbreviated in any way */
    629  1.11       apb     time_t	Hours,		/* Hour of day [0-24] */
    630  1.11       apb     time_t	Minutes,	/* Minute of hour [0-59] */
    631  1.20       apb     time_t	Seconds,	/* Second of minute [0-60] */
    632  1.20       apb     time_t	Timezone,	/* Timezone as minutes east of UTC,
    633  1.11       apb 				 * or USE_LOCAL_TIME special case */
    634  1.11       apb     MERIDIAN	Meridian,	/* Hours are am/pm/24 hour clock */
    635   1.1  christos     DSTMODE	DSTmode		/* DST on/off/maybe */
    636   1.1  christos )
    637  1.13       apb {
    638  1.13       apb     struct tm tm = {.tm_sec = 0};
    639   1.1  christos     time_t result;
    640  1.11       apb 
    641  1.11       apb     tm.tm_sec = Seconds;
    642  1.11       apb     tm.tm_min = Minutes;
    643  1.11       apb     tm.tm_hour = Hours + (Meridian == MERpm ? 12 : 0);
    644  1.11       apb     tm.tm_mday = Day;
    645  1.11       apb     tm.tm_mon = Month - 1;
    646  1.20       apb     tm.tm_year = Year - 1900;
    647  1.21  christos     if (Timezone == USE_LOCAL_TIME) {
    648  1.21  christos 	    switch (DSTmode) {
    649  1.21  christos 	    case DSTon:  tm.tm_isdst = 1; break;
    650  1.21  christos 	    case DSToff: tm.tm_isdst = 0; break;
    651  1.21  christos 	    default:     tm.tm_isdst = -1; break;
    652  1.20       apb 	    }
    653  1.20       apb 	    result = mktime(&tm);
    654  1.20       apb     } else {
    655  1.21  christos 	    /* We rely on mktime_z(NULL, ...) working in UTC */
    656  1.21  christos 	    tm.tm_isdst = 0;	/* hence cannot be summer time */
    657  1.20       apb 	    errno = 0;
    658  1.21  christos 	    result = mktime_z(NULL, &tm);
    659  1.21  christos 	    if (result != -1 || errno == 0) {
    660  1.21  christos 		    result += Timezone * 60;
    661  1.21  christos 		    if (DSTmode == DSTon)	/* if specified sumer time */
    662  1.21  christos 			result -= 3600;		/* UTC is 1 hour earlier XXX */
    663  1.20       apb 	    }
    664  1.20       apb     }
    665  1.20       apb 
    666  1.20       apb #if PARSEDATE_DEBUG
    667  1.20       apb     fprintf(stderr, "%s(M=%jd D=%jd Y=%jd H=%jd M=%jd S=%jd Z=%jd"
    668  1.20       apb 		    " mer=%d DST=%d)",
    669  1.20       apb 	__func__,
    670  1.20       apb 	(intmax_t)Month, (intmax_t)Day, (intmax_t)Year,
    671  1.20       apb 	(intmax_t)Hours, (intmax_t)Minutes, (intmax_t)Seconds,
    672  1.20       apb 	(intmax_t)Timezone, (int)Meridian, (int)DSTmode);
    673  1.20       apb     fprintf(stderr, " -> %jd", (intmax_t)result);
    674  1.20       apb     fprintf(stderr, " %s", ctime(&result));
    675  1.20       apb #endif
    676  1.13       apb 
    677   1.1  christos     return result;
    678   1.1  christos }
    679   1.1  christos 
    680   1.1  christos 
    681   1.1  christos static time_t
    682   1.1  christos DSTcorrect(
    683   1.1  christos     time_t	Start,
    684   1.1  christos     time_t	Future
    685   1.1  christos )
    686   1.1  christos {
    687   1.1  christos     time_t	StartDay;
    688   1.6  christos     time_t	FutureDay;
    689   1.6  christos     struct tm  *tm;
    690   1.6  christos 
    691   1.6  christos     if ((tm = localtime(&Start)) == NULL)
    692   1.6  christos 	return -1;
    693   1.6  christos     StartDay = (tm->tm_hour + 1) % 24;
    694   1.6  christos 
    695   1.6  christos     if ((tm = localtime(&Future)) == NULL)
    696   1.6  christos 	return -1;
    697   1.1  christos     FutureDay = (tm->tm_hour + 1) % 24;
    698   1.1  christos 
    699   1.1  christos     return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
    700   1.1  christos }
    701   1.1  christos 
    702   1.1  christos 
    703   1.1  christos static time_t
    704   1.1  christos RelativeDate(
    705   1.1  christos     time_t	Start,
    706   1.1  christos     time_t	DayOrdinal,
    707   1.1  christos     time_t	DayNumber
    708   1.1  christos )
    709   1.1  christos {
    710   1.1  christos     struct tm	*tm;
    711   1.1  christos     time_t	now;
    712   1.1  christos 
    713   1.1  christos     now = Start;
    714  1.21  christos     tm = localtime(&now);
    715  1.21  christos     if (tm == NULL)
    716   1.1  christos 	return -1;
    717   1.1  christos     now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
    718   1.1  christos     now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
    719   1.1  christos     return DSTcorrect(Start, now);
    720   1.1  christos }
    721   1.1  christos 
    722   1.1  christos 
    723   1.1  christos static time_t
    724   1.1  christos RelativeMonth(
    725   1.9  christos     time_t	Start,
    726   1.9  christos     time_t	RelMonth,
    727   1.1  christos     time_t	Timezone
    728   1.1  christos )
    729   1.1  christos {
    730   1.1  christos     struct tm	*tm;
    731   1.1  christos     time_t	Month;
    732   1.1  christos     time_t	Year;
    733   1.1  christos 
    734   1.1  christos     if (RelMonth == 0)
    735   1.1  christos 	return 0;
    736   1.6  christos     tm = localtime(&Start);
    737   1.6  christos     if (tm == NULL)
    738   1.1  christos 	return -1;
    739   1.1  christos     Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth;
    740   1.1  christos     Year = Month / 12;
    741   1.1  christos     Month = Month % 12 + 1;
    742   1.1  christos     return DSTcorrect(Start,
    743   1.1  christos 	    Convert(Month, (time_t)tm->tm_mday, Year,
    744   1.9  christos 		(time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
    745   1.1  christos 		Timezone, MER24, DSTmaybe));
    746   1.1  christos }
    747   1.1  christos 
    748   1.1  christos 
    749   1.9  christos static int
    750   1.1  christos LookupWord(YYSTYPE *yylval, char *buff)
    751   1.1  christos {
    752   1.1  christos     register char	*p;
    753   1.1  christos     register char	*q;
    754   1.1  christos     register const TABLE	*tp;
    755   1.1  christos     int			i;
    756   1.1  christos     int			abbrev;
    757   1.1  christos 
    758   1.1  christos     /* Make it lowercase. */
    759   1.1  christos     for (p = buff; *p; p++)
    760   1.1  christos 	if (isupper((unsigned char)*p))
    761   1.1  christos 	    *p = tolower((unsigned char)*p);
    762   1.1  christos 
    763   1.9  christos     if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
    764   1.1  christos 	yylval->Meridian = MERam;
    765   1.1  christos 	return tMERIDIAN;
    766   1.1  christos     }
    767   1.9  christos     if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
    768   1.1  christos 	yylval->Meridian = MERpm;
    769   1.1  christos 	return tMERIDIAN;
    770   1.1  christos     }
    771   1.1  christos 
    772   1.1  christos     /* See if we have an abbreviation for a month. */
    773   1.1  christos     if (strlen(buff) == 3)
    774   1.1  christos 	abbrev = 1;
    775   1.1  christos     else if (strlen(buff) == 4 && buff[3] == '.') {
    776   1.1  christos 	abbrev = 1;
    777   1.1  christos 	buff[3] = '\0';
    778   1.1  christos     }
    779   1.1  christos     else
    780   1.1  christos 	abbrev = 0;
    781   1.1  christos 
    782   1.1  christos     for (tp = MonthDayTable; tp->name; tp++) {
    783   1.1  christos 	if (abbrev) {
    784   1.9  christos 	    if (strncmp(buff, tp->name, 3) == 0) {
    785   1.1  christos 		yylval->Number = tp->value;
    786   1.1  christos 		return tp->type;
    787   1.1  christos 	    }
    788   1.1  christos 	}
    789   1.9  christos 	else if (strcmp(buff, tp->name) == 0) {
    790   1.1  christos 	    yylval->Number = tp->value;
    791   1.1  christos 	    return tp->type;
    792   1.1  christos 	}
    793   1.1  christos     }
    794   1.1  christos 
    795   1.1  christos     for (tp = TimezoneTable; tp->name; tp++)
    796   1.9  christos 	if (strcmp(buff, tp->name) == 0) {
    797   1.1  christos 	    yylval->Number = tp->value;
    798   1.1  christos 	    return tp->type;
    799   1.1  christos 	}
    800   1.1  christos 
    801   1.1  christos     if (strcmp(buff, "dst") == 0)
    802   1.1  christos 	return tDST;
    803   1.1  christos 
    804   1.1  christos     for (tp = UnitsTable; tp->name; tp++)
    805   1.9  christos 	if (strcmp(buff, tp->name) == 0) {
    806   1.1  christos 	    yylval->Number = tp->value;
    807   1.1  christos 	    return tp->type;
    808   1.1  christos 	}
    809   1.1  christos 
    810   1.1  christos     /* Strip off any plural and try the units table again. */
    811   1.1  christos     i = strlen(buff) - 1;
    812   1.1  christos     if (buff[i] == 's') {
    813   1.1  christos 	buff[i] = '\0';
    814   1.1  christos 	for (tp = UnitsTable; tp->name; tp++)
    815   1.9  christos 	    if (strcmp(buff, tp->name) == 0) {
    816   1.1  christos 		yylval->Number = tp->value;
    817   1.1  christos 		return tp->type;
    818   1.1  christos 	    }
    819   1.1  christos 	buff[i] = 's';		/* Put back for "this" in OtherTable. */
    820   1.1  christos     }
    821   1.1  christos 
    822   1.1  christos     for (tp = OtherTable; tp->name; tp++)
    823   1.9  christos 	if (strcmp(buff, tp->name) == 0) {
    824   1.1  christos 	    yylval->Number = tp->value;
    825   1.1  christos 	    return tp->type;
    826   1.1  christos 	}
    827   1.1  christos 
    828   1.1  christos     /* Military timezones. */
    829   1.1  christos     if (buff[1] == '\0' && isalpha((unsigned char)*buff)) {
    830   1.1  christos 	for (tp = MilitaryTable; tp->name; tp++)
    831   1.9  christos 	    if (strcmp(buff, tp->name) == 0) {
    832   1.1  christos 		yylval->Number = tp->value;
    833   1.1  christos 		return tp->type;
    834   1.1  christos 	    }
    835   1.1  christos     }
    836   1.1  christos 
    837   1.1  christos     /* Drop out any periods and try the timezone table again. */
    838   1.1  christos     for (i = 0, p = q = buff; *q; q++)
    839   1.1  christos 	if (*q != '.')
    840   1.1  christos 	    *p++ = *q;
    841   1.1  christos 	else
    842   1.1  christos 	    i++;
    843   1.1  christos     *p = '\0';
    844   1.1  christos     if (i)
    845   1.1  christos 	for (tp = TimezoneTable; tp->name; tp++)
    846   1.9  christos 	    if (strcmp(buff, tp->name) == 0) {
    847   1.1  christos 		yylval->Number = tp->value;
    848   1.1  christos 		return tp->type;
    849   1.1  christos 	    }
    850   1.1  christos 
    851   1.1  christos     return tID;
    852   1.1  christos }
    853   1.1  christos 
    854   1.1  christos 
    855   1.9  christos static int
    856   1.1  christos yylex(YYSTYPE *yylval, const char **yyInput)
    857   1.1  christos {
    858   1.1  christos     register char	c;
    859   1.1  christos     register char	*p;
    860   1.1  christos     char		buff[20];
    861   1.1  christos     int			Count;
    862   1.9  christos     int			sign;
    863   1.1  christos     const char		*inp = *yyInput;
    864   1.1  christos 
    865   1.9  christos     for ( ; ; ) {
    866   1.9  christos 	while (isspace((unsigned char)*inp))
    867   1.1  christos 	    inp++;
    868   1.9  christos 
    869   1.1  christos 	if (isdigit((unsigned char)(c = *inp)) || c == '-' || c == '+') {
    870   1.1  christos 	    if (c == '-' || c == '+') {
    871   1.9  christos 		sign = c == '-' ? -1 : 1;
    872   1.1  christos 		if (!isdigit((unsigned char)*++inp))
    873   1.1  christos 		    /* skip the '-' sign */
    874   1.1  christos 		    continue;
    875   1.1  christos 	    }
    876   1.1  christos 	    else
    877   1.9  christos 		sign = 0;
    878   1.9  christos 	    for (yylval->Number = 0; isdigit((unsigned char)(c = *inp++)); )
    879   1.1  christos 		yylval->Number = 10 * yylval->Number + c - '0';
    880   1.9  christos 	    if (sign < 0)
    881   1.9  christos 		yylval->Number = -yylval->Number;
    882   1.1  christos 	    *yyInput = --inp;
    883   1.1  christos 	    return sign ? tSNUMBER : tUNUMBER;
    884   1.1  christos 	}
    885   1.9  christos 	if (isalpha((unsigned char)c)) {
    886   1.1  christos 	    for (p = buff; isalpha((unsigned char)(c = *inp++)) || c == '.'; )
    887   1.1  christos 		if (p < &buff[sizeof buff - 1])
    888   1.1  christos 		    *p++ = c;
    889   1.9  christos 	    *p = '\0';
    890   1.9  christos 	    *yyInput = --inp;
    891   1.1  christos 	    return LookupWord(yylval, buff);
    892   1.5      tron 	}
    893   1.9  christos 	if (c == '@') {
    894   1.5      tron 	    *yyInput = ++inp;
    895   1.5      tron 	    return AT_SIGN;
    896   1.9  christos 	}
    897   1.9  christos 	if (c != '(') {
    898   1.9  christos 	    *yyInput = ++inp;
    899   1.9  christos 	    return c;
    900   1.1  christos 	}
    901   1.1  christos 	Count = 0;
    902   1.9  christos 	do {
    903   1.1  christos 	    c = *inp++;
    904   1.1  christos 	    if (c == '\0')
    905   1.1  christos 		return c;
    906   1.1  christos 	    if (c == '(')
    907   1.1  christos 		Count++;
    908   1.1  christos 	    else if (c == ')')
    909   1.1  christos 		Count--;
    910   1.1  christos 	} while (Count > 0);
    911   1.1  christos     }
    912   1.1  christos }
    913   1.1  christos 
    914   1.1  christos #define TM_YEAR_ORIGIN 1900
    915   1.1  christos 
    916   1.1  christos time_t
    917   1.1  christos parsedate(const char *p, const time_t *now, const int *zone)
    918  1.20       apb {
    919   1.1  christos     struct tm		local, *tm;
    920   1.1  christos     time_t		nowt;
    921   1.1  christos     int			zonet;
    922   1.6  christos     time_t		Start;
    923   1.9  christos     time_t		tod, rm;
    924  1.14       apb     struct dateinfo	param;
    925  1.14       apb     int			saved_errno;
    926  1.14       apb 
    927  1.14       apb     saved_errno = errno;
    928   1.1  christos     errno = 0;
    929  1.20       apb 
    930   1.1  christos     if (now == NULL) {
    931  1.20       apb         now = &nowt;
    932  1.20       apb 	(void)time(&nowt);
    933  1.20       apb     }
    934   1.1  christos     if (zone == NULL) {
    935  1.20       apb 	zone = &zonet;
    936   1.1  christos 	zonet = USE_LOCAL_TIME;
    937   1.1  christos 	if ((tm = localtime_r(now, &local)) == NULL)
    938   1.1  christos 	    return -1;
    939  1.20       apb     } else {
    940  1.20       apb 	/*
    941  1.20       apb 	 * Should use the specified zone, not localtime.
    942  1.20       apb 	 * Fake it using gmtime and arithmetic.
    943  1.20       apb 	 * This is good enough because we use only the year/month/day,
    944  1.20       apb 	 * not other fields of struct tm.
    945  1.20       apb 	 */
    946  1.20       apb 	time_t fake = *now + (*zone * 60);
    947   1.1  christos 	if ((tm = gmtime_r(&fake, &local)) == NULL)
    948   1.1  christos 	    return -1;
    949   1.9  christos     }
    950   1.9  christos     param.yyYear = tm->tm_year + 1900;
    951   1.9  christos     param.yyMonth = tm->tm_mon + 1;
    952   1.9  christos     param.yyDay = tm->tm_mday;
    953   1.9  christos     param.yyTimezone = *zone;
    954   1.9  christos     param.yyDSTmode = DSTmaybe;
    955   1.9  christos     param.yyHour = 0;
    956   1.9  christos     param.yyMinutes = 0;
    957   1.9  christos     param.yySeconds = 0;
    958   1.9  christos     param.yyMeridian = MER24;
    959   1.9  christos     param.yyRelSeconds = 0;
    960   1.9  christos     param.yyRelMonth = 0;
    961  1.17       apb     param.yyHaveDate = 0;
    962   1.9  christos     param.yyHaveFullYear = 0;
    963   1.9  christos     param.yyHaveDay = 0;
    964   1.9  christos     param.yyHaveRel = 0;
    965   1.9  christos     param.yyHaveTime = 0;
    966   1.9  christos     param.yyHaveZone = 0;
    967   1.9  christos 
    968  1.14       apb     if (yyparse(&param, &p) || param.yyHaveTime > 1 || param.yyHaveZone > 1 ||
    969  1.14       apb 	param.yyHaveDate > 1 || param.yyHaveDay > 1) {
    970   1.9  christos 	errno = EINVAL;
    971  1.14       apb 	return -1;
    972   1.9  christos     }
    973   1.9  christos 
    974  1.17       apb     if (param.yyHaveDate || param.yyHaveTime || param.yyHaveDay) {
    975  1.17       apb 	if (! param.yyHaveFullYear) {
    976  1.17       apb 		param.yyYear = AdjustYear(param.yyYear);
    977  1.17       apb 		param.yyHaveFullYear = 1;
    978  1.21  christos 	}
    979   1.9  christos 	errno = 0;
    980   1.9  christos 	Start = Convert(param.yyMonth, param.yyDay, param.yyYear, param.yyHour,
    981   1.9  christos 	    param.yyMinutes, param.yySeconds, param.yyTimezone,
    982  1.14       apb 	    param.yyMeridian, param.yyDSTmode);
    983   1.1  christos 	if (Start == -1 && errno != 0)
    984   1.1  christos 	    return -1;
    985   1.1  christos     }
    986   1.1  christos     else {
    987   1.9  christos 	Start = *now;
    988   1.1  christos 	if (!param.yyHaveRel)
    989   1.1  christos 	    Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
    990   1.1  christos     }
    991   1.9  christos 
    992  1.21  christos     Start += param.yyRelSeconds;
    993   1.9  christos     errno = 0;
    994  1.14       apb     rm = RelativeMonth(Start, param.yyRelMonth, param.yyTimezone);
    995   1.6  christos     if (rm == -1 && errno != 0)
    996   1.6  christos 	return -1;
    997   1.1  christos     Start += rm;
    998   1.9  christos 
    999  1.21  christos     if (param.yyHaveDay && !param.yyHaveDate) {
   1000   1.9  christos 	errno = 0;
   1001  1.21  christos 	tod = RelativeDate(Start, param.yyDayOrdinal, param.yyDayNumber);
   1002  1.21  christos 	if (tod == -1 && errno != 0)
   1003   1.1  christos 	    return -1;
   1004   1.1  christos 	Start += tod;
   1005   1.1  christos     }
   1006  1.21  christos 
   1007   1.6  christos     errno = saved_errno;
   1008   1.1  christos     return Start;
   1009   1.1  christos }
   1010   1.1  christos 
   1011   1.1  christos 
   1012   1.1  christos #if	defined(TEST)
   1013   1.1  christos 
   1014   1.1  christos /* ARGSUSED */
   1015  1.14       apb int
   1016   1.1  christos main(int ac, char *av[])
   1017   1.1  christos {
   1018   1.1  christos     char	buff[128];
   1019   1.1  christos     time_t	d;
   1020   1.1  christos 
   1021   1.1  christos     (void)printf("Enter date, or blank line to exit.\n\t> ");
   1022  1.14       apb     (void)fflush(stdout);
   1023  1.14       apb     while (fgets(buff, sizeof(buff), stdin) && buff[0] != '\n') {
   1024   1.1  christos 	errno = 0;
   1025  1.14       apb 	d = parsedate(buff, NULL, NULL);
   1026  1.14       apb 	if (d == -1 && errno != 0)
   1027  1.14       apb 	    (void)printf("Bad format - couldn't convert: %s\n",
   1028   1.1  christos 	        strerror(errno));
   1029  1.14       apb 	else
   1030   1.1  christos 	    (void)printf("%jd\t%s", (intmax_t)d, ctime(&d));
   1031   1.1  christos 	(void)printf("\t> ");
   1032   1.1  christos 	(void)fflush(stdout);
   1033   1.1  christos     }
   1034   1.1  christos     exit(0);
   1035   1.1  christos     /* NOTREACHED */
   1036   1.1  christos }
   1037                 #endif	/* defined(TEST) */
   1038