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