mixmaster

mixmaster 3.0 patched for libressl
git clone git://parazyd.org/mixmaster.git
Log | Files | Refs | README

parsedate.y (23910B)


      1 %{
      2 /*  $Id: parsedate.y 647 2003-10-25 23:34:13Z weasel $
      3 **
      4 **  Originally written by Steven M. Bellovin <smb@research.att.com> while
      5 **  at the University of North Carolina at Chapel Hill.  Later tweaked by
      6 **  a couple of people on Usenet.  Completely overhauled by Rich $alz
      7 **  <rsalz@osf.org> and Jim Berets <jberets@bbn.com> in August, 1990.
      8 **  Further revised (removed obsolete constructs and cleaned up timezone
      9 **  names) in August, 1991, by Rich.  Paul Eggert <eggert@twinsun.com>
     10 **  helped in September, 1992.
     11 **
     12 **  This grammar has six shift/reduce conflicts.
     13 **
     14 **  This code is in the public domain and has no copyright.
     15 */
     16 
     17 #include <stdio.h>
     18 #include <string.h>
     19 
     20 // #include "config.h"
     21 // #include "clibrary.h"
     22 #include <ctype.h>
     23 
     24 #if defined(_HPUX_SOURCE)
     25 # include <alloca.h>
     26 #endif
     27 
     28 #ifdef TM_IN_SYS_TIME
     29 # include <sys/time.h>
     30 #else
     31 # include <time.h>
     32 #endif
     33 
     34 // #include "libinn.h"
     35 
     36 /* Used for iterating through arrays.  ARRAY_SIZE returns the number of
     37    elements in the array (useful for a < upper bound in a for loop) and
     38    ARRAY_END returns a pointer to the element past the end (ISO C99 makes it
     39    legal to refer to such a pointer as long as it's never dereferenced). */
     40 #define ARRAY_SIZE(array)       (sizeof(array) / sizeof((array)[0]))
     41 #define ARRAY_END(array)        (&(array)[ARRAY_SIZE(array)])
     42 
     43 /* On some systems, the macros defined by <ctype.h> are only vaild on ASCII
     44    characters (those characters that isascii() says are ASCII).  This comes
     45    into play when applying <ctype.h> macros to eight-bit data.  autoconf
     46    checks for this with as part of AC_HEADER_STDC, so if autoconf doesn't
     47    think our headers are standard, check isascii() first. */
     48 #if STDC_HEADERS
     49 # define CTYPE(isXXXXX, c) (isXXXXX((unsigned char)(c)))
     50 #else
     51 # define CTYPE(isXXXXX, c) \
     52     (isascii((unsigned char)(c)) && isXXXXX((unsigned char)(c)))
     53 #endif
     54 
     55 
     56 #define yylhs		date_yylhs
     57 #define yylen		date_yylen
     58 #define yydefred	date_yydefred
     59 #define yydgoto		date_yydgoto
     60 #define yysindex	date_yysindex
     61 #define yyrindex	date_yyrindex
     62 #define yygindex	date_yygindex
     63 #define yytable		date_yytable
     64 #define yycheck		date_yycheck
     65 #define yyparse		date_parse
     66 #define yylex		date_lex
     67 #define yyerror		date_error
     68 #define yymaxdepth	date_yymaxdepth
     69 
     70 
     71 static int date_lex(void);
     72 
     73 
     74     /* See the LeapYears table in Convert. */
     75 #define EPOCH		1970
     76 #define END_OF_TIME	2038
     77     /* Constants for general time calculations. */
     78 #define DST_OFFSET	1
     79 #define SECSPERDAY	(24L * 60L * 60L)
     80     /* Readability for TABLE stuff. */
     81 #define HOUR(x)		(x * 60)
     82 
     83 #define LPAREN		'('
     84 #define RPAREN		')'
     85 #define IS7BIT(x)	((unsigned int)(x) < 0200)
     86 
     87 
     88 /*
     89 **  An entry in the lexical lookup table.
     90 */
     91 typedef struct _TABLE {
     92     const char * name;
     93     int          type;
     94     time_t       value;
     95 } TABLE;
     96 
     97 /*
     98 **  Daylight-savings mode:  on, off, or not yet known.
     99 */
    100 typedef enum _DSTMODE {
    101     DSTon, DSToff, DSTmaybe
    102 } DSTMODE;
    103 
    104 /*
    105 **  Meridian:  am, pm, or 24-hour style.
    106 */
    107 typedef enum _MERIDIAN {
    108     MERam, MERpm, MER24
    109 } MERIDIAN;
    110 
    111 
    112 /*
    113 **  Global variables.  We could get rid of most of them by using a yacc
    114 **  union, but this is more efficient.  (This routine predates the
    115 **  yacc %union construct.)
    116 */
    117 static char	*yyInput;
    118 static DSTMODE	yyDSTmode;
    119 static int	yyHaveDate;
    120 static int	yyHaveRel;
    121 static int	yyHaveTime;
    122 static time_t	yyTimezone;
    123 static time_t	yyDay;
    124 static time_t	yyHour;
    125 static time_t	yyMinutes;
    126 static time_t	yyMonth;
    127 static time_t	yySeconds;
    128 static time_t	yyYear;
    129 static MERIDIAN	yyMeridian;
    130 static time_t	yyRelMonth;
    131 static time_t	yyRelSeconds;
    132 
    133 
    134 /* extern struct tm	*localtime(); */
    135 
    136 static void		date_error(const char *s);
    137 %}
    138 
    139 %union {
    140     time_t		Number;
    141     enum _MERIDIAN	Meridian;
    142 }
    143 
    144 %token	tDAY tDAYZONE tMERIDIAN tMONTH tMONTH_UNIT tSEC_UNIT tSNUMBER
    145 %token	tUNUMBER tZONE
    146 
    147 %type	<Number>	tDAYZONE tMONTH tMONTH_UNIT tSEC_UNIT
    148 %type	<Number>	tSNUMBER tUNUMBER tZONE numzone zone
    149 %type	<Meridian>	tMERIDIAN o_merid
    150 
    151 %%
    152 
    153 spec	: /* NULL */
    154 	| spec item
    155 	;
    156 
    157 item	: time {
    158 	    yyHaveTime++;
    159 #if	defined(lint)
    160 	    /* I am compulsive about lint natterings... */
    161 	    if (yyHaveTime == -1) {
    162 		YYERROR;
    163 	    }
    164 #endif	/* defined(lint) */
    165 	}
    166 	| time zone {
    167 	    yyHaveTime++;
    168 	    yyTimezone = $2;
    169 	}
    170 	| date {
    171 	    yyHaveDate++;
    172 	}
    173 	| rel {
    174 	    yyHaveRel = 1;
    175 	}
    176 	;
    177 
    178 time	: tUNUMBER o_merid {
    179 	    if ($1 < 100) {
    180 		yyHour = $1;
    181 		yyMinutes = 0;
    182 	    }
    183 	    else {
    184 		yyHour = $1 / 100;
    185 		yyMinutes = $1 % 100;
    186 	    }
    187 	    yySeconds = 0;
    188 	    yyMeridian = $2;
    189 	}
    190 	| tUNUMBER ':' tUNUMBER o_merid {
    191 	    yyHour = $1;
    192 	    yyMinutes = $3;
    193 	    yySeconds = 0;
    194 	    yyMeridian = $4;
    195 	}
    196 	| tUNUMBER ':' tUNUMBER numzone {
    197 	    yyHour = $1;
    198 	    yyMinutes = $3;
    199 	    yyTimezone = $4;
    200 	    yyMeridian = MER24;
    201 	    yyDSTmode = DSToff;
    202 	}
    203 	| tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
    204 	    yyHour = $1;
    205 	    yyMinutes = $3;
    206 	    yySeconds = $5;
    207 	    yyMeridian = $6;
    208 	}
    209 	| tUNUMBER ':' tUNUMBER ':' tUNUMBER numzone {
    210 	    yyHour = $1;
    211 	    yyMinutes = $3;
    212 	    yySeconds = $5;
    213 	    yyTimezone = $6;
    214 	    yyMeridian = MER24;
    215 	    yyDSTmode = DSToff;
    216 	}
    217 	;
    218 
    219 zone	: tZONE {
    220 	    $$ = $1;
    221 	    yyDSTmode = DSToff;
    222 	}
    223 	| tDAYZONE {
    224 	    $$ = $1;
    225 	    yyDSTmode = DSTon;
    226 	}
    227 	| tZONE numzone {
    228 	    /* Only allow "GMT+300" and "GMT-0800" */
    229 	    if ($1 != 0) {
    230 		YYABORT;
    231 	    }
    232 	    $$ = $2;
    233 	    yyDSTmode = DSToff;
    234 	}
    235 	| numzone {
    236 	    $$ = $1;
    237 	    yyDSTmode = DSToff;
    238 	}
    239 	;
    240 
    241 numzone	: tSNUMBER {
    242 	    int		i;
    243 
    244 	    /* Unix and GMT and numeric timezones -- a little confusing. */
    245 	    if ($1 < 0) {
    246 		/* Don't work with negative modulus. */
    247 		$1 = -$1;
    248 		if ($1 > 9999 || (i = $1 % 100) >= 60) {
    249 		    YYABORT;
    250 		}
    251 		$$ = ($1 / 100) * 60 + i;
    252 	    }
    253 	    else {
    254 		if ($1 > 9999 || (i = $1 % 100) >= 60) {
    255 		    YYABORT;
    256 		}
    257 		$$ = -(($1 / 100) * 60 + i);
    258 	    }
    259 	}
    260 	;
    261 
    262 date	: tUNUMBER '/' tUNUMBER {
    263 	    yyMonth = $1;
    264 	    yyDay = $3;
    265 	}
    266 	| tUNUMBER '/' tUNUMBER '/' tUNUMBER {
    267 	    if ($1 > 100) {
    268 		/* assume YYYY/MM/DD format, so need not to add 1900 */
    269 		if ($1 > 999) {
    270 		    yyYear = $1;
    271 		} else {
    272 		    yyYear = 1900 + $1;
    273 		}
    274 		yyMonth = $3;
    275 		yyDay = $5;
    276 	    }
    277 	    else {
    278 		/* assume MM/DD/YY* format */
    279 		yyMonth = $1;
    280 		yyDay = $3;
    281 		if ($5 > 999) {
    282 		    /* assume year is YYYY format, so need not to add 1900 */
    283 		    yyYear = $5;
    284 		} else if ($5 < 100) {
    285 		    /* assume year is YY format, so need to add 1900 */
    286 		    yyYear = $5 + (yyYear / 100 + (yyYear % 100 - $5) / 50) * 100;
    287 		} else {
    288 		    yyYear = 1900 + $5;
    289 		}
    290 	    }
    291 	}
    292 	| tMONTH tUNUMBER {
    293 	    yyMonth = $1;
    294 	    yyDay = $2;
    295 	}
    296 	| tMONTH tUNUMBER ',' tUNUMBER {
    297 	    yyMonth = $1;
    298 	    yyDay = $2;
    299 	    if ($4 > 999) {
    300 		/* assume year is YYYY format, so need not to add 1900 */
    301 		yyYear = $4;
    302 	    } else if ($4 < 100) {
    303 		/* assume year is YY format, so need to add 1900 */
    304 		yyYear = $4 + (yyYear / 100 + (yyYear % 100 - $4) / 50) * 100;
    305 	    } else {
    306 		yyYear = 1900 + $4;
    307 	    }
    308 	}
    309 	| tUNUMBER tMONTH {
    310 	    yyDay = $1;
    311 	    yyMonth = $2;
    312 	}
    313 	| tUNUMBER tMONTH tUNUMBER {
    314 	    yyDay = $1;
    315 	    yyMonth = $2;
    316 	    if ($3 > 999) {
    317 		/* assume year is YYYY format, so need not to add 1900 */
    318 		yyYear = $3;
    319 	    } else if ($3 < 100) {
    320 		/* assume year is YY format, so need to add 1900 */
    321 		yyYear = $3 + (yyYear / 100 + (yyYear % 100 - $3) / 50) * 100;
    322 	    } else {
    323 		yyYear = 1900 + $3;
    324 	    }
    325 	}
    326 	| tDAY ',' tUNUMBER tMONTH tUNUMBER {
    327 	    yyDay = $3;
    328 	    yyMonth = $4;
    329 	    if ($5 > 999) {
    330 		/* assume year is YYYY format, so need not to add 1900 */
    331 		yyYear = $5;
    332 	    } else if ($5 < 100) {
    333 		/* assume year is YY format, so need to add 1900 */
    334 		yyYear = $5 + (yyYear / 100 + (yyYear % 100 - $5) / 50) * 100;
    335 	    } else {
    336 		yyYear = 1900 + $5;
    337 	    }
    338 	}
    339 	;
    340 
    341 rel	: tSNUMBER tSEC_UNIT {
    342 	    yyRelSeconds += $1 * $2;
    343 	}
    344 	| tUNUMBER tSEC_UNIT {
    345 	    yyRelSeconds += $1 * $2;
    346 	}
    347 	| tSNUMBER tMONTH_UNIT {
    348 	    yyRelMonth += $1 * $2;
    349 	}
    350 	| tUNUMBER tMONTH_UNIT {
    351 	    yyRelMonth += $1 * $2;
    352 	}
    353 	;
    354 
    355 o_merid	: /* NULL */ {
    356 	    $$ = MER24;
    357 	}
    358 	| tMERIDIAN {
    359 	    $$ = $1;
    360 	}
    361 	;
    362 
    363 %%
    364 
    365 /* Month and day table. */
    366 static TABLE	MonthDayTable[] = {
    367     { "january",	tMONTH,  1 },
    368     { "february",	tMONTH,  2 },
    369     { "march",		tMONTH,  3 },
    370     { "april",		tMONTH,  4 },
    371     { "may",		tMONTH,  5 },
    372     { "june",		tMONTH,  6 },
    373     { "july",		tMONTH,  7 },
    374     { "august",		tMONTH,  8 },
    375     { "september",	tMONTH,  9 },
    376     { "october",	tMONTH, 10 },
    377     { "november",	tMONTH, 11 },
    378     { "december",	tMONTH, 12 },
    379 	/* The value of the day isn't used... */
    380     { "sunday",		tDAY, 0 },
    381     { "monday",		tDAY, 0 },
    382     { "tuesday",	tDAY, 0 },
    383     { "wednesday",	tDAY, 0 },
    384     { "thursday",	tDAY, 0 },
    385     { "friday",		tDAY, 0 },
    386     { "saturday",	tDAY, 0 },
    387 };
    388 
    389 /* Time units table. */
    390 static TABLE	UnitsTable[] = {
    391     { "year",		tMONTH_UNIT,	12 },
    392     { "month",		tMONTH_UNIT,	1 },
    393     { "week",		tSEC_UNIT,	7 * 24 * 60 * 60 },
    394     { "day",		tSEC_UNIT,	1 * 24 * 60 * 60 },
    395     { "hour",		tSEC_UNIT,	60 * 60 },
    396     { "minute",		tSEC_UNIT,	60 },
    397     { "min",		tSEC_UNIT,	60 },
    398     { "second",		tSEC_UNIT,	1 },
    399     { "sec",		tSEC_UNIT,	1 },
    400 };
    401 
    402 /* Timezone table. */
    403 static TABLE	TimezoneTable[] = {
    404     { "gmt",	tZONE,     HOUR( 0) },	/* Greenwich Mean */
    405     { "ut",	tZONE,     HOUR( 0) },	/* Universal */
    406     { "utc",	tZONE,     HOUR( 0) },	/* Universal Coordinated */
    407     { "cut",	tZONE,     HOUR( 0) },	/* Coordinated Universal */
    408     { "z",	tZONE,     HOUR( 0) },	/* Greenwich Mean */
    409     { "wet",	tZONE,     HOUR( 0) },	/* Western European */
    410     { "bst",	tDAYZONE,  HOUR( 0) },	/* British Summer */
    411     { "nst",	tZONE,     HOUR(3)+30 }, /* Newfoundland Standard */
    412     { "ndt",	tDAYZONE,  HOUR(3)+30 }, /* Newfoundland Daylight */
    413     { "ast",	tZONE,     HOUR( 4) },	/* Atlantic Standard */
    414     { "adt",	tDAYZONE,  HOUR( 4) },	/* Atlantic Daylight */
    415     { "est",	tZONE,     HOUR( 5) },	/* Eastern Standard */
    416     { "edt",	tDAYZONE,  HOUR( 5) },	/* Eastern Daylight */
    417     { "cst",	tZONE,     HOUR( 6) },	/* Central Standard */
    418     { "cdt",	tDAYZONE,  HOUR( 6) },	/* Central Daylight */
    419     { "mst",	tZONE,     HOUR( 7) },	/* Mountain Standard */
    420     { "mdt",	tDAYZONE,  HOUR( 7) },	/* Mountain Daylight */
    421     { "pst",	tZONE,     HOUR( 8) },	/* Pacific Standard */
    422     { "pdt",	tDAYZONE,  HOUR( 8) },	/* Pacific Daylight */
    423     { "yst",	tZONE,     HOUR( 9) },	/* Yukon Standard */
    424     { "ydt",	tDAYZONE,  HOUR( 9) },	/* Yukon Daylight */
    425     { "akst",	tZONE,     HOUR( 9) },	/* Alaska Standard */
    426     { "akdt",	tDAYZONE,  HOUR( 9) },	/* Alaska Daylight */
    427     { "hst",	tZONE,     HOUR(10) },	/* Hawaii Standard */
    428     { "hast",	tZONE,     HOUR(10) },	/* Hawaii-Aleutian Standard */
    429     { "hadt",	tDAYZONE,  HOUR(10) },	/* Hawaii-Aleutian Daylight */
    430     { "ces",	tDAYZONE,  -HOUR(1) },	/* Central European Summer */
    431     { "cest",	tDAYZONE,  -HOUR(1) },	/* Central European Summer */
    432     { "mez",	tZONE,     -HOUR(1) },	/* Middle European */
    433     { "mezt",	tDAYZONE,  -HOUR(1) },	/* Middle European Summer */
    434     { "cet",	tZONE,     -HOUR(1) },	/* Central European */
    435     { "met",	tZONE,     -HOUR(1) },	/* Middle European */
    436     { "eet",	tZONE,     -HOUR(2) },	/* Eastern Europe */
    437     { "msk",	tZONE,     -HOUR(3) },	/* Moscow Winter */
    438     { "msd",	tDAYZONE,  -HOUR(3) },	/* Moscow Summer */
    439     { "wast",	tZONE,     -HOUR(8) },	/* West Australian Standard */
    440     { "wadt",	tDAYZONE,  -HOUR(8) },	/* West Australian Daylight */
    441     { "hkt",	tZONE,     -HOUR(8) },	/* Hong Kong */
    442     { "cct",	tZONE,     -HOUR(8) },	/* China Coast */
    443     { "jst",	tZONE,     -HOUR(9) },	/* Japan Standard */
    444     { "kst",	tZONE,     -HOUR(9) },	/* Korean Standard */
    445     { "kdt",	tZONE,     -HOUR(9) },	/* Korean Daylight */
    446     { "cast",	tZONE,     -(HOUR(9)+30) }, /* Central Australian Standard */
    447     { "cadt",	tDAYZONE,  -(HOUR(9)+30) }, /* Central Australian Daylight */
    448     { "east",	tZONE,     -HOUR(10) },	/* Eastern Australian Standard */
    449     { "eadt",	tDAYZONE,  -HOUR(10) },	/* Eastern Australian Daylight */
    450     { "nzst",	tZONE,     -HOUR(12) },	/* New Zealand Standard */
    451     { "nzdt",	tDAYZONE,  -HOUR(12) },	/* New Zealand Daylight */
    452 
    453     /* For completeness we include the following entries. */
    454 #if	0
    455 
    456     /* Duplicate names.  Either they conflict with a zone listed above
    457      * (which is either more likely to be seen or just been in circulation
    458      * longer), or they conflict with another zone in this section and
    459      * we could not reasonably choose one over the other. */
    460     { "fst",	tZONE,     HOUR( 2) },	/* Fernando De Noronha Standard */
    461     { "fdt",	tDAYZONE,  HOUR( 2) },	/* Fernando De Noronha Daylight */
    462     { "bst",	tZONE,     HOUR( 3) },	/* Brazil Standard */
    463     { "est",	tZONE,     HOUR( 3) },	/* Eastern Standard (Brazil) */
    464     { "edt",	tDAYZONE,  HOUR( 3) },	/* Eastern Daylight (Brazil) */
    465     { "wst",	tZONE,     HOUR( 4) },	/* Western Standard (Brazil) */
    466     { "wdt",	tDAYZONE,  HOUR( 4) },	/* Western Daylight (Brazil) */
    467     { "cst",	tZONE,     HOUR( 5) },	/* Chile Standard */
    468     { "cdt",	tDAYZONE,  HOUR( 5) },	/* Chile Daylight */
    469     { "ast",	tZONE,     HOUR( 5) },	/* Acre Standard */
    470     { "adt",	tDAYZONE,  HOUR( 5) },	/* Acre Daylight */
    471     { "cst",	tZONE,     HOUR( 5) },	/* Cuba Standard */
    472     { "cdt",	tDAYZONE,  HOUR( 5) },	/* Cuba Daylight */
    473     { "est",	tZONE,     HOUR( 6) },	/* Easter Island Standard */
    474     { "edt",	tDAYZONE,  HOUR( 6) },	/* Easter Island Daylight */
    475     { "sst",	tZONE,     HOUR(11) },	/* Samoa Standard */
    476     { "ist",	tZONE,     -HOUR(2) },	/* Israel Standard */
    477     { "idt",	tDAYZONE,  -HOUR(2) },	/* Israel Daylight */
    478     { "idt",	tDAYZONE,  -(HOUR(3)+30) }, /* Iran Daylight */
    479     { "ist",	tZONE,     -(HOUR(3)+30) }, /* Iran Standard */
    480     { "cst",	 tZONE,     -HOUR(8) },	/* China Standard */
    481     { "cdt",	 tDAYZONE,  -HOUR(8) },	/* China Daylight */
    482     { "sst",	 tZONE,     -HOUR(8) },	/* Singapore Standard */
    483 
    484     /* Dubious (e.g., not in Olson's TIMEZONE package) or obsolete. */
    485     { "gst",	tZONE,     HOUR( 3) },	/* Greenland Standard */
    486     { "wat",	tZONE,     -HOUR(1) },	/* West Africa */
    487     { "at",	tZONE,     HOUR( 2) },	/* Azores */
    488     { "gst",	tZONE,     -HOUR(10) },	/* Guam Standard */
    489     { "nft",	tZONE,     HOUR(3)+30 }, /* Newfoundland */
    490     { "idlw",	tZONE,     HOUR(12) },	/* International Date Line West */
    491     { "mewt",	tZONE,     -HOUR(1) },	/* Middle European Winter */
    492     { "mest",	tDAYZONE,  -HOUR(1) },	/* Middle European Summer */
    493     { "swt",	tZONE,     -HOUR(1) },	/* Swedish Winter */
    494     { "sst",	tDAYZONE,  -HOUR(1) },	/* Swedish Summer */
    495     { "fwt",	tZONE,     -HOUR(1) },	/* French Winter */
    496     { "fst",	tDAYZONE,  -HOUR(1) },	/* French Summer */
    497     { "bt",	tZONE,     -HOUR(3) },	/* Baghdad */
    498     { "it",	tZONE,     -(HOUR(3)+30) }, /* Iran */
    499     { "zp4",	tZONE,     -HOUR(4) },	/* USSR Zone 3 */
    500     { "zp5",	tZONE,     -HOUR(5) },	/* USSR Zone 4 */
    501     { "ist",	tZONE,     -(HOUR(5)+30) }, /* Indian Standard */
    502     { "zp6",	tZONE,     -HOUR(6) },	/* USSR Zone 5 */
    503     { "nst",	tZONE,     -HOUR(7) },	/* North Sumatra */
    504     { "sst",	tZONE,     -HOUR(7) },	/* South Sumatra */
    505     { "jt",	tZONE,     -(HOUR(7)+30) }, /* Java (3pm in Cronusland!) */
    506     { "nzt",	tZONE,     -HOUR(12) },	/* New Zealand */
    507     { "idle",	tZONE,     -HOUR(12) },	/* International Date Line East */
    508     { "cat",	tZONE,     HOUR(10) },	/* -- expired 1967 */
    509     { "nt",	tZONE,     HOUR(11) },	/* -- expired 1967 */
    510     { "ahst",	tZONE,     HOUR(10) },	/* -- expired 1983 */
    511     { "hdt",	tDAYZONE,  HOUR(10) },	/* -- expired 1986 */
    512 #endif	/* 0 */
    513 };
    514 
    515 
    516 
    517 static void
    518 date_error(const char *s)
    519 {
    520     s = s;			/* ARGSUSED */
    521     /* NOTREACHED */
    522 }
    523 
    524 
    525 static time_t
    526 ToSeconds(time_t Hours, time_t Minutes, time_t Seconds, MERIDIAN Meridian)
    527 {
    528     if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 61)
    529 	return -1;
    530     if (Meridian == MER24) {
    531 	if (Hours < 0 || Hours > 23)
    532 	    return -1;
    533     }
    534     else {
    535 	if (Hours < 1 || Hours > 12)
    536 	    return -1;
    537 	if (Hours == 12)
    538 	    Hours = 0;
    539 	if (Meridian == MERpm)
    540 	    Hours += 12;
    541     }
    542     return (Hours * 60L + Minutes) * 60L + Seconds;
    543 }
    544 
    545 
    546 static time_t
    547 Convert(time_t Month, time_t Day, time_t Year, time_t Hours, time_t Minutes,
    548 	time_t Seconds, MERIDIAN Meridian, DSTMODE dst)
    549 {
    550     static int	DaysNormal[13] = {
    551 	0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
    552     };
    553     static int	DaysLeap[13] = {
    554 	0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
    555     };
    556     static int	LeapYears[] = {
    557 	1972, 1976, 1980, 1984, 1988, 1992, 1996,
    558 	2000, 2004, 2008, 2012, 2016, 2020, 2024, 2028, 2032, 2036
    559     };
    560     int	                *yp;
    561     int	                *mp;
    562     time_t	        Julian;
    563     int	                i;
    564     time_t		tod;
    565 
    566     /* Year should not be passed as a relative value, but absolute one.
    567        so this should not happen, but just ensure it */
    568     if (Year < 0)
    569 	Year = -Year;
    570     if (Year < 100) {
    571 	Year += 1900;
    572 	if (Year < EPOCH)
    573 	    Year += 100;
    574     }
    575     for (mp = DaysNormal, yp = LeapYears; yp < ARRAY_END(LeapYears); yp++)
    576 	if (Year == *yp) {
    577 	    mp = DaysLeap;
    578 	    break;
    579 	}
    580     if (Year < EPOCH || Year > END_OF_TIME
    581      || Month < 1 || Month > 12
    582      /* NOSTRICT *//* conversion from long may lose accuracy */
    583      || Day < 1 || Day > mp[(int)Month])
    584 	return -1;
    585 
    586     Julian = Day - 1 + (Year - EPOCH) * 365;
    587     for (yp = LeapYears; yp < ARRAY_END(LeapYears); yp++, Julian++)
    588 	if (Year <= *yp)
    589 	    break;
    590     for (i = 1; i < Month; i++)
    591 	Julian += *++mp;
    592     Julian *= SECSPERDAY;
    593     Julian += yyTimezone * 60L;
    594     if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
    595 	return -1;
    596     Julian += tod;
    597     tod = Julian;
    598     if (dst == DSTon || (dst == DSTmaybe && localtime(&tod)->tm_isdst))
    599 	Julian -= DST_OFFSET * 60 * 60;
    600     return Julian;
    601 }
    602 
    603 
    604 static time_t
    605 DSTcorrect(time_t Start, time_t Future)
    606 {
    607     time_t	StartDay;
    608     time_t	FutureDay;
    609 
    610     StartDay = (localtime(&Start)->tm_hour + 1) % 24;
    611     FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
    612     return (Future - Start) + (StartDay - FutureDay) * DST_OFFSET * 60 * 60;
    613 }
    614 
    615 
    616 static time_t
    617 RelativeMonth(time_t Start, time_t RelMonth)
    618 {
    619     struct tm	*tm;
    620     time_t	Month;
    621     time_t	Year;
    622 
    623     tm = localtime(&Start);
    624     Month = 12 * tm->tm_year + tm->tm_mon + RelMonth;
    625     Year = Month / 12;
    626     Year += 1900;
    627     Month = Month % 12 + 1;
    628     return DSTcorrect(Start,
    629 	    Convert(Month, (time_t)tm->tm_mday, Year,
    630 		(time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
    631 		MER24, DSTmaybe));
    632 }
    633 
    634 
    635 static int
    636 LookupWord(char *buff, int length)
    637 {
    638     char *p;
    639     const char *q;
    640     TABLE *tp;
    641     int	c;
    642 
    643     p = buff;
    644     c = p[0];
    645 
    646     /* See if we have an abbreviation for a month. */
    647     if (length == 3 || (length == 4 && p[3] == '.'))
    648 	for (tp = MonthDayTable; tp < ARRAY_END(MonthDayTable); tp++) {
    649 	    q = tp->name;
    650 	    if (c == q[0] && p[1] == q[1] && p[2] == q[2]) {
    651 		yylval.Number = tp->value;
    652 		return tp->type;
    653 	    }
    654 	}
    655     else
    656 	for (tp = MonthDayTable; tp < ARRAY_END(MonthDayTable); tp++)
    657 	    if (c == tp->name[0] && strcmp(p, tp->name) == 0) {
    658 		yylval.Number = tp->value;
    659 		return tp->type;
    660 	    }
    661 
    662     /* Try for a timezone. */
    663     for (tp = TimezoneTable; tp < ARRAY_END(TimezoneTable); tp++)
    664 	if (c == tp->name[0] && p[1] == tp->name[1]
    665 	 && strcmp(p, tp->name) == 0) {
    666 	    yylval.Number = tp->value;
    667 	    return tp->type;
    668 	}
    669 
    670     /* Try the units table. */
    671     for (tp = UnitsTable; tp < ARRAY_END(UnitsTable); tp++)
    672 	if (c == tp->name[0] && strcmp(p, tp->name) == 0) {
    673 	    yylval.Number = tp->value;
    674 	    return tp->type;
    675 	}
    676 
    677     /* Strip off any plural and try the units table again. */
    678     if (--length > 0 && p[length] == 's') {
    679 	p[length] = '\0';
    680 	for (tp = UnitsTable; tp < ARRAY_END(UnitsTable); tp++)
    681 	    if (c == tp->name[0] && strcmp(p, tp->name) == 0) {
    682 		p[length] = 's';
    683 		yylval.Number = tp->value;
    684 		return tp->type;
    685 	    }
    686 	p[length] = 's';
    687     }
    688     length++;
    689 
    690     /* Drop out any periods. */
    691     for (p = buff, q = buff; *q; q++)
    692 	if (*q != '.')
    693 	    *p++ = *q;
    694     *p = '\0';
    695 
    696     /* Try the meridians. */
    697     if (buff[1] == 'm' && buff[2] == '\0') {
    698 	if (buff[0] == 'a') {
    699 	    yylval.Meridian = MERam;
    700 	    return tMERIDIAN;
    701 	}
    702 	if (buff[0] == 'p') {
    703 	    yylval.Meridian = MERpm;
    704 	    return tMERIDIAN;
    705 	}
    706     }
    707 
    708     /* If we saw any periods, try the timezones again. */
    709     if (p - buff != length) {
    710 	c = buff[0];
    711 	for (p = buff, tp = TimezoneTable; tp < ARRAY_END(TimezoneTable); tp++)
    712 	    if (c == tp->name[0] && p[1] == tp->name[1]
    713 	    && strcmp(p, tp->name) == 0) {
    714 		yylval.Number = tp->value;
    715 		return tp->type;
    716 	    }
    717     }
    718 
    719     /* Unknown word -- assume GMT timezone. */
    720     yylval.Number = 0;
    721     return tZONE;
    722 }
    723 
    724 
    725 static int
    726 date_lex(void)
    727 {
    728     char	        c;
    729     char	        *p;
    730     char		buff[20];
    731     int	                sign;
    732     int	                i;
    733     int	                nesting;
    734 
    735     for ( ; ; ) {
    736 	/* Get first character after the whitespace. */
    737 	for ( ; ; ) {
    738 	    while (CTYPE(isspace, (int)*yyInput))
    739 		yyInput++;
    740 	    c = *yyInput;
    741 
    742 	    /* Ignore RFC 822 comments, typically time zone names. */
    743 	    if (c != LPAREN)
    744 		break;
    745 	    for (nesting = 1; (c = *++yyInput) != RPAREN || --nesting; )
    746 		if (c == LPAREN)
    747 		    nesting++;
    748 		else if (!IS7BIT(c) || c == '\0' || c == '\r'
    749 		     || (c == '\\' && ((c = *++yyInput) == '\0' || !IS7BIT(c))))
    750 		    /* Lexical error: bad comment. */
    751 		    return '?';
    752 	    yyInput++;
    753 	}
    754 
    755 	/* A number? */
    756 	if (CTYPE(isdigit, (int)c) || c == '-' || c == '+') {
    757 	    if (c == '-' || c == '+') {
    758 		sign = c == '-' ? -1 : 1;
    759 		yyInput++;
    760 		if (!CTYPE(isdigit, (int)*yyInput))
    761 		    /* Skip the plus or minus sign. */
    762 		    continue;
    763 	    }
    764 	    else
    765 		sign = 0;
    766 	    for (i = 0; (c = *yyInput++) != '\0' && CTYPE(isdigit, (int)c); )
    767 		i = 10 * i + c - '0';
    768 	    yyInput--;
    769 	    yylval.Number = sign < 0 ? -i : i;
    770 	    return sign ? tSNUMBER : tUNUMBER;
    771 	}
    772 
    773 	/* A word? */
    774 	if (CTYPE(isalpha, (int)c)) {
    775 	    for (p = buff; (c = *yyInput++) == '.' || CTYPE(isalpha, (int)c); )
    776 		if (p < &buff[sizeof buff - 1])
    777 		    *p++ = CTYPE(isupper, (int)c) ? tolower(c) : c;
    778 	    *p = '\0';
    779 	    yyInput--;
    780 	    return LookupWord(buff, p - buff);
    781 	}
    782 
    783 	return *yyInput++;
    784     }
    785 }
    786 
    787 
    788 time_t
    789 parsedate(char *p)
    790 {
    791     time_t		now;
    792     struct tm		*tm;
    793     time_t		Start;
    794 
    795     now = time(NULL);
    796     yyInput = p;
    797 
    798     tm = gmtime(&now);
    799     yyYear = tm->tm_year + 1900;
    800     yyMonth = tm->tm_mon + 1;
    801     yyDay = tm->tm_mday;
    802     yyTimezone = 0;
    803     yyDSTmode = DSTmaybe;
    804     yyHour = 0;
    805     yyMinutes = 0;
    806     yySeconds = 0;
    807     yyMeridian = MER24;
    808     yyRelSeconds = 0;
    809     yyRelMonth = 0;
    810     yyHaveDate = 0;
    811     yyHaveRel = 0;
    812     yyHaveTime = 0;
    813 
    814     if (date_parse() || yyHaveTime > 1 || yyHaveDate > 1)
    815 	return -1;
    816 
    817     if (yyHaveDate || yyHaveTime) {
    818 	Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
    819 		    yyMeridian, yyDSTmode);
    820 	if (Start < 0)
    821 	    return -1;
    822     }
    823     else {
    824 	Start = now;
    825 	if (!yyHaveRel)
    826 	    Start -= (tm->tm_hour * 60L + tm->tm_min) * 60L + tm->tm_sec;
    827     }
    828 
    829     Start += yyRelSeconds;
    830     if (yyRelMonth)
    831 	Start += RelativeMonth(Start, yyRelMonth);
    832 
    833     /* Have to do *something* with a legitimate -1 so it's distinguishable
    834      * from the error return value.  (Alternately could set errno on error.) */
    835     return Start == -1 ? 0 : Start;
    836 }
    837 
    838 
    839 #if	defined(TEST)
    840 
    841 #if	YYDEBUG
    842 extern int	yydebug;
    843 #endif	/* YYDEBUG */
    844 
    845 /* ARGSUSED */
    846 int
    847 main(int ac, char *av[])
    848 {
    849     char	buff[128];
    850     time_t	d;
    851 
    852 #if	YYDEBUG
    853     yydebug = 1;
    854 #endif	/* YYDEBUG */
    855 
    856     printf("Enter date, or blank line to exit.\n\t> ");
    857     for ( ; ; ) {
    858 	printf("\t> ");
    859 	fflush(stdout);
    860 	if (gets(buff) == NULL || buff[0] == '\n')
    861 	    break;
    862 #if	YYDEBUG
    863 	if (strcmp(buff, "yydebug") == 0) {
    864 	    yydebug = !yydebug;
    865 	    printf("yydebug = %s\n", yydebug ? "on" : "off");
    866 	    continue;
    867 	}
    868 #endif	/* YYDEBUG */
    869 	d = parsedate(buff);
    870 	if (d == -1)
    871 	    printf("Bad format - couldn't convert.\n");
    872 	else
    873 	    printf("%s", ctime(&d));
    874     }
    875 
    876     exit(0);
    877     /* NOTREACHED */
    878 }
    879 #endif	/* defined(TEST) */