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