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