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 13 shift/reduce conflicts.
9 **
10 **  This code is in the public domain and has no copyright.
11 */
12 
13 #ifdef HAVE_CONFIG_H
14 # include <config.h>
15 #endif
16 
17 /* Since the code of getdate.y is not included in the Emacs executable
18    itself, there is no need to #define static in this file.  Even if
19    the code were included in the Emacs executable, it probably
20    wouldn't do any harm to #undef it here; this will only cause
21    problems if we try to write to a static variable, which I don't
22    think this code needs to do.  */
23 #ifdef emacs
24 # undef static
25 #endif
26 
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <ctype.h>
30 #include <time.h>
31 #include <string.h>
32 
33 #define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
34 
35 #define EPOCH		1970
36 #define HOUR(x)		((x) * 60)
37 
38 #define MAX_BUFF_LEN    128   /* size of buffer to read the date into */
39 
40 /*
41 **  An entry in the lexical lookup table.
42 */
43 typedef struct _TABLE {
44     const char	*name;
45     int		type;
46     int		value;
47 } TABLE;
48 
49 
50 /*
51 **  Meridian:  am, pm, or 24-hour style.
52 */
53 typedef enum _MERIDIAN {
54     MERam, MERpm, MER24
55 } MERIDIAN;
56 
57 struct global {
58   int	DayOrdinal;
59   int	DayNumber;
60   int	HaveDate;
61   int	HaveDay;
62   int	HaveRel;
63   int	HaveTime;
64   int	HaveZone;
65   int	Timezone;
66   int	Day;
67   int	Hour;
68   int	Minutes;
69   int	Month;
70   int	Seconds;
71   int	Year;
72   MERIDIAN	Meridian;
73   int	RelDay;
74   int	RelHour;
75   int	RelMinutes;
76   int	RelMonth;
77   int	RelSeconds;
78   int	RelYear;
79 };
80 
81 union YYSTYPE;
82 static int yylex (union YYSTYPE *lvalp, const char **yyInput);
83 static int yyerror (const char **yyInput, struct global *yy, char *s);
84 static int yyparse (const char **yyInput, struct global *yy);
85 
86 #define YYENABLE_NLS 0
87 #define YYLTYPE_IS_TRIVIAL 0
88 
89 %}
90 
91 %name-prefix "lu_gd"
92 %define api.pure
93 
94 %parse-param { const char **yyInput }
95 %lex-param { const char **yyInput }
96 %parse-param { struct global *yy }
97 
98 %union {
99     int			Number;
100     enum _MERIDIAN	Meridian;
101 }
102 
103 %token	tAGO tDAY tDAY_UNIT tDAYZONE tDST tHOUR_UNIT tID
104 %token	tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
105 %token	tSEC_UNIT tSNUMBER tUNUMBER tYEAR_UNIT tZONE
106 
107 %type	<Number>	tDAY tDAY_UNIT tDAYZONE tHOUR_UNIT tMINUTE_UNIT
108 %type	<Number>	tMONTH tMONTH_UNIT
109 %type	<Number>	tSEC_UNIT tSNUMBER tUNUMBER tYEAR_UNIT tZONE
110 %type	<Meridian>	tMERIDIAN o_merid
111 
112 %expect 13
113 
114 %%
115 
116 spec	: /* NULL */
117 	| spec item
118 	;
119 
120 item	: time {
121 	    yy->HaveTime++;
122 	}
123 	| zone {
124 	    yy->HaveZone++;
125 	}
126 	| date {
127 	    yy->HaveDate++;
128 	}
129 	| day {
130 	    yy->HaveDay++;
131 	}
132 	| rel {
133 	    yy->HaveRel++;
134 	}
135 	| number
136 	;
137 
138 time	: tUNUMBER tMERIDIAN {
139 	    yy->Hour = $1;
140 	    yy->Minutes = 0;
141 	    yy->Seconds = 0;
142 	    yy->Meridian = $2;
143 	}
144 	| tUNUMBER ':' tUNUMBER o_merid {
145 	    yy->Hour = $1;
146 	    yy->Minutes = $3;
147 	    yy->Seconds = 0;
148 	    yy->Meridian = $4;
149 	}
150 	| tUNUMBER ':' tUNUMBER tSNUMBER {
151 	    yy->Hour = $1;
152 	    yy->Minutes = $3;
153 	    yy->Meridian = MER24;
154 	    yy->HaveZone++;
155 	    yy->Timezone = ($4 < 0
156 			  ? -$4 % 100 + (-$4 / 100) * 60
157 			  : - ($4 % 100 + ($4 / 100) * 60));
158 	}
159 	| tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
160 	    yy->Hour = $1;
161 	    yy->Minutes = $3;
162 	    yy->Seconds = $5;
163 	    yy->Meridian = $6;
164 	}
165 	| tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
166 	    yy->Hour = $1;
167 	    yy->Minutes = $3;
168 	    yy->Seconds = $5;
169 	    yy->Meridian = MER24;
170 	    yy->HaveZone++;
171 	    yy->Timezone = ($6 < 0
172 			  ? -$6 % 100 + (-$6 / 100) * 60
173 			  : - ($6 % 100 + ($6 / 100) * 60));
174 	}
175 	;
176 
177 zone	: tZONE {
178 	    yy->Timezone = $1;
179 	}
180 	| tDAYZONE {
181 	    yy->Timezone = $1 - 60;
182 	}
183 	|
184 	  tZONE tDST {
185 	    yy->Timezone = $1 - 60;
186 	}
187 	;
188 
189 day	: tDAY {
190 	    yy->DayOrdinal = 1;
191 	    yy->DayNumber = $1;
192 	}
193 	| tDAY ',' {
194 	    yy->DayOrdinal = 1;
195 	    yy->DayNumber = $1;
196 	}
197 	| tUNUMBER tDAY {
198 	    yy->DayOrdinal = $1;
199 	    yy->DayNumber = $2;
200 	}
201 	;
202 
203 date	: tUNUMBER '/' tUNUMBER {
204 	    yy->Month = $1;
205 	    yy->Day = $3;
206 	}
207 	| tUNUMBER '/' tUNUMBER '/' tUNUMBER {
208 	  /* Interpret as YY->YY/MM/DD if $1 >= 1000, otherwise as MM/DD/YY.
209 	     The goal in recognizing YY->YY/MM/DD is solely to support legacy
210 	     machine-generated dates like those in an RCS log listing.  If
211 	     you want portability, use the ISO 8601 format.  */
212 	  if ($1 >= 1000)
213 	    {
214 	      yy->Year = $1;
215 	      yy->Month = $3;
216 	      yy->Day = $5;
217 	    }
218 	  else
219 	    {
220 	      yy->Month = $1;
221 	      yy->Day = $3;
222 	      yy->Year = $5;
223 	    }
224 	}
225 	| tUNUMBER tSNUMBER tSNUMBER {
226 	    /* ISO 8601 format.  yy->yy-mm-dd.  */
227 	    yy->Year = $1;
228 	    yy->Month = -$2;
229 	    yy->Day = -$3;
230 	}
231 	| tUNUMBER tMONTH tSNUMBER {
232 	    /* e.g. 17-JUN-1992.  */
233 	    yy->Day = $1;
234 	    yy->Month = $2;
235 	    yy->Year = -$3;
236 	}
237 	| tMONTH tUNUMBER {
238 	    yy->Month = $1;
239 	    yy->Day = $2;
240 	}
241 	| tMONTH tUNUMBER ',' tUNUMBER {
242 	    yy->Month = $1;
243 	    yy->Day = $2;
244 	    yy->Year = $4;
245 	}
246 	| tUNUMBER tMONTH {
247 	    yy->Month = $2;
248 	    yy->Day = $1;
249 	}
250 	| tUNUMBER tMONTH tUNUMBER {
251 	    yy->Month = $2;
252 	    yy->Day = $1;
253 	    yy->Year = $3;
254 	}
255 	;
256 
257 rel	: relunit tAGO {
258 	    yy->RelSeconds = -yy->RelSeconds;
259 	    yy->RelMinutes = -yy->RelMinutes;
260 	    yy->RelHour = -yy->RelHour;
261 	    yy->RelDay = -yy->RelDay;
262 	    yy->RelMonth = -yy->RelMonth;
263 	    yy->RelYear = -yy->RelYear;
264 	}
265 	| relunit
266 	;
267 
268 relunit	: tUNUMBER tYEAR_UNIT {
269 	    yy->RelYear += $1 * $2;
270 	}
271 	| tSNUMBER tYEAR_UNIT {
272 	    yy->RelYear += $1 * $2;
273 	}
274 	| tYEAR_UNIT {
275 	    yy->RelYear++;
276 	}
277 	| tUNUMBER tMONTH_UNIT {
278 	    yy->RelMonth += $1 * $2;
279 	}
280 	| tSNUMBER tMONTH_UNIT {
281 	    yy->RelMonth += $1 * $2;
282 	}
283 	| tMONTH_UNIT {
284 	    yy->RelMonth++;
285 	}
286 	| tUNUMBER tDAY_UNIT {
287 	    yy->RelDay += $1 * $2;
288 	}
289 	| tSNUMBER tDAY_UNIT {
290 	    yy->RelDay += $1 * $2;
291 	}
292 	| tDAY_UNIT {
293 	    yy->RelDay++;
294 	}
295 	| tUNUMBER tHOUR_UNIT {
296 	    yy->RelHour += $1 * $2;
297 	}
298 	| tSNUMBER tHOUR_UNIT {
299 	    yy->RelHour += $1 * $2;
300 	}
301 	| tHOUR_UNIT {
302 	    yy->RelHour++;
303 	}
304 	| tUNUMBER tMINUTE_UNIT {
305 	    yy->RelMinutes += $1 * $2;
306 	}
307 	| tSNUMBER tMINUTE_UNIT {
308 	    yy->RelMinutes += $1 * $2;
309 	}
310 	| tMINUTE_UNIT {
311 	    yy->RelMinutes++;
312 	}
313 	| tUNUMBER tSEC_UNIT {
314 	    yy->RelSeconds += $1 * $2;
315 	}
316 	| tSNUMBER tSEC_UNIT {
317 	    yy->RelSeconds += $1 * $2;
318 	}
319 	| tSEC_UNIT {
320 	    yy->RelSeconds++;
321 	}
322 	;
323 
324 number	: tUNUMBER
325           {
326 	    if (yy->HaveTime && yy->HaveDate && !yy->HaveRel)
327 	      yy->Year = $1;
328 	    else
329 	      {
330 		if ($1>10000)
331 		  {
332 		    yy->HaveDate++;
333 		    yy->Day= ($1)%100;
334 		    yy->Month= ($1/100)%100;
335 		    yy->Year = $1/10000;
336 		  }
337 		else
338 		  {
339 		    yy->HaveTime++;
340 		    if ($1 < 100)
341 		      {
342 			yy->Hour = $1;
343 			yy->Minutes = 0;
344 		      }
345 		    else
346 		      {
347 		    	yy->Hour = $1 / 100;
348 		    	yy->Minutes = $1 % 100;
349 		      }
350 		    yy->Seconds = 0;
351 		    yy->Meridian = MER24;
352 		  }
353 	      }
354 	  }
355 	;
356 
357 o_merid	: /* NULL */
358 	  {
359 	    $$ = MER24;
360 	  }
361 	| tMERIDIAN
362 	  {
363 	    $$ = $1;
364 	  }
365 	;
366 
367 %%
368 
369 /* Month and day table. */
370 static TABLE const MonthDayTable[] = {
371     { "january",	tMONTH,  1 },
372     { "february",	tMONTH,  2 },
373     { "march",		tMONTH,  3 },
374     { "april",		tMONTH,  4 },
375     { "may",		tMONTH,  5 },
376     { "june",		tMONTH,  6 },
377     { "july",		tMONTH,  7 },
378     { "august",		tMONTH,  8 },
379     { "september",	tMONTH,  9 },
380     { "sept",		tMONTH,  9 },
381     { "october",	tMONTH, 10 },
382     { "november",	tMONTH, 11 },
383     { "december",	tMONTH, 12 },
384     { "sunday",		tDAY, 0 },
385     { "monday",		tDAY, 1 },
386     { "tuesday",	tDAY, 2 },
387     { "tues",		tDAY, 2 },
388     { "wednesday",	tDAY, 3 },
389     { "wednes",		tDAY, 3 },
390     { "thursday",	tDAY, 4 },
391     { "thur",		tDAY, 4 },
392     { "thurs",		tDAY, 4 },
393     { "friday",		tDAY, 5 },
394     { "saturday",	tDAY, 6 },
395     { NULL, 0, 0 }
396 };
397 
398 /* Time units table. */
399 static TABLE const UnitsTable[] = {
400     { "year",		tYEAR_UNIT,	1 },
401     { "month",		tMONTH_UNIT,	1 },
402     { "fortnight",	tDAY_UNIT,	14 },
403     { "week",		tDAY_UNIT,	7 },
404     { "day",		tDAY_UNIT,	1 },
405     { "hour",		tHOUR_UNIT,	1 },
406     { "minute",		tMINUTE_UNIT,	1 },
407     { "min",		tMINUTE_UNIT,	1 },
408     { "second",		tSEC_UNIT,	1 },
409     { "sec",		tSEC_UNIT,	1 },
410     { NULL, 0, 0 }
411 };
412 
413 /* Assorted relative-time words. */
414 static TABLE const OtherTable[] = {
415     { "tomorrow",	tMINUTE_UNIT,	1 * 24 * 60 },
416     { "yesterday",	tMINUTE_UNIT,	-1 * 24 * 60 },
417     { "today",		tMINUTE_UNIT,	0 },
418     { "now",		tMINUTE_UNIT,	0 },
419     { "last",		tUNUMBER,	-1 },
420     { "this",		tMINUTE_UNIT,	0 },
421     { "next",		tUNUMBER,	2 },
422     { "first",		tUNUMBER,	1 },
423 /*  { "second",		tUNUMBER,	2 }, */
424     { "third",		tUNUMBER,	3 },
425     { "fourth",		tUNUMBER,	4 },
426     { "fifth",		tUNUMBER,	5 },
427     { "sixth",		tUNUMBER,	6 },
428     { "seventh",	tUNUMBER,	7 },
429     { "eighth",		tUNUMBER,	8 },
430     { "ninth",		tUNUMBER,	9 },
431     { "tenth",		tUNUMBER,	10 },
432     { "eleventh",	tUNUMBER,	11 },
433     { "twelfth",	tUNUMBER,	12 },
434     { "ago",		tAGO,	1 },
435     { NULL, 0, 0 }
436 };
437 
438 /* The timezone table. */
439 static TABLE const TimezoneTable[] = {
440     { "gmt",	tZONE,     HOUR ( 0) },	/* Greenwich Mean */
441     { "ut",	tZONE,     HOUR ( 0) },	/* Universal (Coordinated) */
442     { "utc",	tZONE,     HOUR ( 0) },
443     { "wet",	tZONE,     HOUR ( 0) },	/* Western European */
444     { "bst",	tDAYZONE,  HOUR ( 0) },	/* British Summer */
445     { "wat",	tZONE,     HOUR ( 1) },	/* West Africa */
446     { "at",	tZONE,     HOUR ( 2) },	/* Azores */
447     { "ast",	tZONE,     HOUR ( 4) },	/* Atlantic Standard */
448     { "adt",	tDAYZONE,  HOUR ( 4) },	/* Atlantic Daylight */
449     { "est",	tZONE,     HOUR ( 5) },	/* Eastern Standard */
450     { "edt",	tDAYZONE,  HOUR ( 5) },	/* Eastern Daylight */
451     { "cst",	tZONE,     HOUR ( 6) },	/* Central Standard */
452     { "cdt",	tDAYZONE,  HOUR ( 6) },	/* Central Daylight */
453     { "mst",	tZONE,     HOUR ( 7) },	/* Mountain Standard */
454     { "mdt",	tDAYZONE,  HOUR ( 7) },	/* Mountain Daylight */
455     { "pst",	tZONE,     HOUR ( 8) },	/* Pacific Standard */
456     { "pdt",	tDAYZONE,  HOUR ( 8) },	/* Pacific Daylight */
457     { "yst",	tZONE,     HOUR ( 9) },	/* Yukon Standard */
458     { "ydt",	tDAYZONE,  HOUR ( 9) },	/* Yukon Daylight */
459     { "hst",	tZONE,     HOUR (10) },	/* Hawaii Standard */
460     { "hdt",	tDAYZONE,  HOUR (10) },	/* Hawaii Daylight */
461     { "cat",	tZONE,     HOUR (10) },	/* Central Alaska */
462     { "ahst",	tZONE,     HOUR (10) },	/* Alaska-Hawaii Standard */
463     { "nt",	tZONE,     HOUR (11) },	/* Nome */
464     { "idlw",	tZONE,     HOUR (12) },	/* International Date Line West */
465     { "cet",	tZONE,     -HOUR (1) },	/* Central European */
466     { "met",	tZONE,     -HOUR (1) },	/* Middle European */
467     { "mewt",	tZONE,     -HOUR (1) },	/* Middle European Winter */
468     { "mest",	tDAYZONE,  -HOUR (1) },	/* Middle European Summer */
469     { "mesz",	tDAYZONE,  -HOUR (1) },	/* Middle European Summer */
470     { "swt",	tZONE,     -HOUR (1) },	/* Swedish Winter */
471     { "sst",	tDAYZONE,  -HOUR (1) },	/* Swedish Summer */
472     { "fwt",	tZONE,     -HOUR (1) },	/* French Winter */
473     { "fst",	tDAYZONE,  -HOUR (1) },	/* French Summer */
474     { "eet",	tZONE,     -HOUR (2) },	/* Eastern Europe, USSR Zone 1 */
475     { "bt",	tZONE,     -HOUR (3) },	/* Baghdad, USSR Zone 2 */
476     { "zp4",	tZONE,     -HOUR (4) },	/* USSR Zone 3 */
477     { "zp5",	tZONE,     -HOUR (5) },	/* USSR Zone 4 */
478     { "zp6",	tZONE,     -HOUR (6) },	/* USSR Zone 5 */
479     { "wst",	tZONE,     -HOUR (8) },	/* West Australian Standard */
480     { "cct",	tZONE,     -HOUR (8) },	/* China Coast, USSR Zone 7 */
481     { "jst",	tZONE,     -HOUR (9) },	/* Japan Standard, USSR Zone 8 */
482     { "acst",	tZONE,     -(HOUR (9) + 30)},	/* Australian Central Standard */
483     { "acdt",	tDAYZONE,  -(HOUR (9) + 30)},	/* Australian Central Daylight */
484     { "aest",	tZONE,     -HOUR (10) },	/* Australian Eastern Standard */
485     { "aedt",	tDAYZONE,  -HOUR (10) },	/* Australian Eastern Daylight */
486     { "gst",	tZONE,     -HOUR (10) },	/* Guam Standard, USSR Zone 9 */
487     { "nzt",	tZONE,     -HOUR (12) },	/* New Zealand */
488     { "nzst",	tZONE,     -HOUR (12) },	/* New Zealand Standard */
489     { "nzdt",	tDAYZONE,  -HOUR (12) },	/* New Zealand Daylight */
490     { "idle",	tZONE,     -HOUR (12) },	/* International Date Line East */
491     {  NULL, 0, 0  }
492 };
493 
494 /* Military timezone table. */
495 static TABLE const MilitaryTable[] = {
496     { "a",	tZONE,	HOUR (  1) },
497     { "b",	tZONE,	HOUR (  2) },
498     { "c",	tZONE,	HOUR (  3) },
499     { "d",	tZONE,	HOUR (  4) },
500     { "e",	tZONE,	HOUR (  5) },
501     { "f",	tZONE,	HOUR (  6) },
502     { "g",	tZONE,	HOUR (  7) },
503     { "h",	tZONE,	HOUR (  8) },
504     { "i",	tZONE,	HOUR (  9) },
505     { "k",	tZONE,	HOUR ( 10) },
506     { "l",	tZONE,	HOUR ( 11) },
507     { "m",	tZONE,	HOUR ( 12) },
508     { "n",	tZONE,	HOUR (- 1) },
509     { "o",	tZONE,	HOUR (- 2) },
510     { "p",	tZONE,	HOUR (- 3) },
511     { "q",	tZONE,	HOUR (- 4) },
512     { "r",	tZONE,	HOUR (- 5) },
513     { "s",	tZONE,	HOUR (- 6) },
514     { "t",	tZONE,	HOUR (- 7) },
515     { "u",	tZONE,	HOUR (- 8) },
516     { "v",	tZONE,	HOUR (- 9) },
517     { "w",	tZONE,	HOUR (-10) },
518     { "x",	tZONE,	HOUR (-11) },
519     { "y",	tZONE,	HOUR (-12) },
520     { "z",	tZONE,	HOUR (  0) },
521     { NULL, 0, 0 }
522 };
523 
524 
525 
526 
527 /* ARGSUSED */
528 static int
529 yyerror (const char **yyInput, struct global *yy, char *s)
530 {
531   (void)yyInput;
532   (void)yy;
533   (void)s;
534   return 0;
535 }
536 
537 static int
538 ToHour (int Hours, MERIDIAN Meridian)
539 {
540   switch (Meridian)
541     {
542     case MER24:
543       if (Hours < 0 || Hours > 23)
544 	return -1;
545       return Hours;
546     case MERam:
547       if (Hours < 1 || Hours > 12)
548 	return -1;
549       if (Hours == 12)
550 	Hours = 0;
551       return Hours;
552     case MERpm:
553       if (Hours < 1 || Hours > 12)
554 	return -1;
555       if (Hours == 12)
556 	Hours = 0;
557       return Hours + 12;
558     default:
559       abort ();
560     }
561   /* NOTREACHED */
562 }
563 
564 static int
565 ToYear (int Year)
566 {
567   if (Year < 0)
568     Year = -Year;
569 
570   /* XPG4 suggests that years 00-68 map to 2000-2068, and
571      years 69-99 map to 1969-1999.  */
572   if (Year < 69)
573     Year += 2000;
574   else if (Year < 100)
575     Year += 1900;
576 
577   return Year;
578 }
579 
580 static int
581 LookupWord (YYSTYPE *lvalp, char *buff)
582 {
583   register char *p;
584   register char *q;
585   register const TABLE *tp;
586   int i;
587   int abbrev;
588 
589   /* Make it lowercase. */
590   for (p = buff; *p; p++)
591     if (isupper ((unsigned char)*p))
592       *p = tolower (*p);
593 
594   if (strcmp (buff, "am") == 0 || strcmp (buff, "a.m.") == 0)
595     {
596       lvalp->Meridian = MERam;
597       return tMERIDIAN;
598     }
599   if (strcmp (buff, "pm") == 0 || strcmp (buff, "p.m.") == 0)
600     {
601       lvalp->Meridian = MERpm;
602       return tMERIDIAN;
603     }
604 
605   /* See if we have an abbreviation for a month. */
606   if (strlen (buff) == 3)
607     abbrev = 1;
608   else if (strlen (buff) == 4 && buff[3] == '.')
609     {
610       abbrev = 1;
611       buff[3] = '\0';
612     }
613   else
614     abbrev = 0;
615 
616   for (tp = MonthDayTable; tp->name; tp++)
617     {
618       if (abbrev)
619 	{
620 	  if (strncmp (buff, tp->name, 3) == 0)
621 	    {
622 	      lvalp->Number = tp->value;
623 	      return tp->type;
624 	    }
625 	}
626       else if (strcmp (buff, tp->name) == 0)
627 	{
628 	  lvalp->Number = tp->value;
629 	  return tp->type;
630 	}
631     }
632 
633   for (tp = TimezoneTable; tp->name; tp++)
634     if (strcmp (buff, tp->name) == 0)
635       {
636 	lvalp->Number = tp->value;
637 	return tp->type;
638       }
639 
640   if (strcmp (buff, "dst") == 0)
641     return tDST;
642 
643   for (tp = UnitsTable; tp->name; tp++)
644     if (strcmp (buff, tp->name) == 0)
645       {
646 	lvalp->Number = tp->value;
647 	return tp->type;
648       }
649 
650   /* Strip off any plural and try the units table again. */
651   i = strlen (buff) - 1;
652   if (buff[i] == 's')
653     {
654       buff[i] = '\0';
655       for (tp = UnitsTable; tp->name; tp++)
656 	if (strcmp (buff, tp->name) == 0)
657 	  {
658 	    lvalp->Number = tp->value;
659 	    return tp->type;
660 	  }
661       buff[i] = 's';		/* Put back for "this" in OtherTable. */
662     }
663 
664   for (tp = OtherTable; tp->name; tp++)
665     if (strcmp (buff, tp->name) == 0)
666       {
667 	lvalp->Number = tp->value;
668 	return tp->type;
669       }
670 
671   /* Military timezones. */
672   if (buff[1] == '\0' && isalpha ((unsigned char)*buff))
673     {
674       for (tp = MilitaryTable; tp->name; tp++)
675 	if (strcmp (buff, tp->name) == 0)
676 	  {
677 	    lvalp->Number = tp->value;
678 	    return tp->type;
679 	  }
680     }
681 
682   /* Drop out any periods and try the timezone table again. */
683   for (i = 0, p = q = buff; *q; q++)
684     if (*q != '.')
685       *p++ = *q;
686     else
687       i++;
688   *p = '\0';
689   if (i)
690     for (tp = TimezoneTable; tp->name; tp++)
691       if (strcmp (buff, tp->name) == 0)
692 	{
693 	  lvalp->Number = tp->value;
694 	  return tp->type;
695 	}
696 
697   return tID;
698 }
699 
700 static int
701 yylex (YYSTYPE *lvalp, const char **yyInput)
702 {
703   register char c;
704   register char *p;
705   char buff[20];
706   int Count;
707   int sign;
708 
709   for (;;)
710     {
711       while (isspace ((unsigned char)**yyInput))
712 	(*yyInput)++;
713 
714       if (ISDIGIT (c = **yyInput) || c == '-' || c == '+')
715 	{
716 	  if (c == '-' || c == '+')
717 	    {
718 	      sign = c == '-' ? -1 : 1;
719 	      if (!ISDIGIT (*++*yyInput))
720 		/* skip the '-' sign */
721 		continue;
722 	    }
723 	  else
724 	    sign = 0;
725 	  for (lvalp->Number = 0; ISDIGIT (c = *(*yyInput)++);)
726 	    lvalp->Number = 10 * lvalp->Number + c - '0';
727 	  (*yyInput)--;
728 	  if (sign < 0)
729 	    lvalp->Number = -lvalp->Number;
730 	  return sign ? tSNUMBER : tUNUMBER;
731 	}
732       if (isalpha ((unsigned char)c))
733 	{
734 	  for (p = buff; (c = *(*yyInput)++, isalpha ((unsigned char)c))
735 		 || c == '.';)
736 	    if (p < &buff[sizeof buff - 1])
737 	      *p++ = c;
738 	  *p = '\0';
739 	  (*yyInput)--;
740 	  return LookupWord (lvalp, buff);
741 	}
742       if (c != '(')
743 	return *(*yyInput)++;
744       Count = 0;
745       do
746 	{
747 	  c = *(*yyInput)++;
748 	  if (c == '\0')
749 	    return c;
750 	  if (c == '(')
751 	    Count++;
752 	  else if (c == ')')
753 	    Count--;
754 	}
755       while (Count > 0);
756     }
757 }
758 
759 #define TM_YEAR_ORIGIN 1900
760 
761 /* Yield A - B, measured in seconds.  */
762 static long
763 difftm (struct tm *a, struct tm *b)
764 {
765   int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
766   int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
767   long days = (
768   /* difference in day of year */
769 		a->tm_yday - b->tm_yday
770   /* + intervening leap days */
771 		+ ((ay >> 2) - (by >> 2))
772 		- (ay / 100 - by / 100)
773 		+ ((ay / 100 >> 2) - (by / 100 >> 2))
774   /* + difference in years * 365 */
775 		+ (long) (ay - by) * 365
776   );
777   return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
778 		+ (a->tm_min - b->tm_min))
779 	  + (a->tm_sec - b->tm_sec));
780 }
781 
782 time_t
783 dpl_get_date (const char *p, const time_t *now)
784 {
785   struct tm tm, tm0, *tmp;
786   struct global yy;
787   time_t Start;
788   struct tm res;
789 
790   memset(&res, 0, sizeof (res));
791   Start = now ? *now : time ((time_t *) NULL);
792   localtime_r(&Start, &res);
793   tmp = &res;
794 
795   memset (&yy, 0, sizeof(yy));
796   yy.Year = tmp->tm_year + TM_YEAR_ORIGIN;
797   yy.Month = tmp->tm_mon + 1;
798   yy.Day = tmp->tm_mday;
799   yy.Hour = tmp->tm_hour;
800   yy.Minutes = tmp->tm_min;
801   yy.Seconds = tmp->tm_sec;
802   yy.Meridian = MER24;
803   yy.RelSeconds = 0;
804   yy.RelMinutes = 0;
805   yy.RelHour = 0;
806   yy.RelDay = 0;
807   yy.RelMonth = 0;
808   yy.RelYear = 0;
809   yy.HaveDate = 0;
810   yy.HaveDay = 0;
811   yy.HaveRel = 0;
812   yy.HaveTime = 0;
813   yy.HaveZone = 0;
814 
815   if (yyparse (&p, &yy)
816       || yy.HaveTime > 1 || yy.HaveZone > 1 || yy.HaveDate > 1 || yy.HaveDay > 1)
817     return -1;
818 
819   tm.tm_year = ToYear (yy.Year) - TM_YEAR_ORIGIN + yy.RelYear;
820   tm.tm_mon = yy.Month - 1 + yy.RelMonth;
821   tm.tm_mday = yy.Day + yy.RelDay;
822   if (yy.HaveTime || (yy.HaveRel && !yy.HaveDate && !yy.HaveDay))
823     {
824       tm.tm_hour = ToHour (yy.Hour, yy.Meridian);
825       if (tm.tm_hour < 0)
826 	return -1;
827       tm.tm_min = yy.Minutes;
828       tm.tm_sec = yy.Seconds;
829     }
830   else
831     {
832       tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
833     }
834   tm.tm_hour += yy.RelHour;
835   tm.tm_min += yy.RelMinutes;
836   tm.tm_sec += yy.RelSeconds;
837   tm.tm_isdst = -1;
838   tm0 = tm;
839 
840   Start = mktime (&tm);
841 
842   if (Start == (time_t) -1)
843     {
844 
845       /* Guard against falsely reporting errors near the time_t boundaries
846          when parsing times in other time zones.  For example, if the min
847          time_t value is 1970-01-01 00:00:00 UTC and we are 8 hours ahead
848          of UTC, then the min localtime value is 1970-01-01 08:00:00; if
849          we apply mktime to 1970-01-01 00:00:00 we will get an error, so
850          we apply mktime to 1970-01-02 08:00:00 instead and adjust the time
851          zone by 24 hours to compensate.  This algorithm assumes that
852          there is no DST transition within a day of the time_t boundaries.  */
853       if (yy.HaveZone)
854 	{
855 	  tm = tm0;
856 	  if (tm.tm_year <= EPOCH - TM_YEAR_ORIGIN)
857 	    {
858 	      tm.tm_mday++;
859 	      yy.Timezone -= 24 * 60;
860 	    }
861 	  else
862 	    {
863 	      tm.tm_mday--;
864 	      yy.Timezone += 24 * 60;
865 	    }
866 	  Start = mktime (&tm);
867 	}
868 
869       if (Start == (time_t) -1)
870 	return Start;
871     }
872 
873   if (yy.HaveDay && !yy.HaveDate)
874     {
875       tm.tm_mday += ((yy.DayNumber - tm.tm_wday + 7) % 7
876 		     + 7 * (yy.DayOrdinal - (0 < yy.DayOrdinal)));
877       Start = mktime (&tm);
878       if (Start == (time_t) -1)
879 	return Start;
880     }
881 
882   if (yy.HaveZone)
883     {
884       long delta = yy.Timezone * 60L + difftm (&tm, gmtime (&Start));
885       if ((Start + delta < Start) != (delta < 0))
886 	return -1;		/* time_t overflow */
887       Start += delta;
888     }
889 
890   return Start;
891 }
892 
893