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