1 /*
2  * tclGetDate.y --
3  *
4  *	Contains yacc grammar for parsing date and time strings. The output of
5  *	this file should be the file tclDate.c which is used directly in the
6  *	Tcl sources. Note that this file is largely obsolete in Tcl 8.5; it is
7  *	only used when doing free-form date parsing, an ill-defined process
8  *	anyway.
9  *
10  * Copyright (c) 1992-1995 Karl Lehenbauer & Mark Diekhans.
11  * Copyright (c) 1995-1997 Sun Microsystems, Inc.
12  *
13  * See the file "license.terms" for information on usage and redistribution of
14  * this file, and for a DISCLAIMER OF ALL WARRANTIES.
15  */
16 
17 %parse-param {DateInfo* info}
18 %lex-param {DateInfo* info}
19 %define api.pure
20  /* %error-verbose would be nice, but our token names are meaningless */
21 %locations
22 
23 %{
24 /*
25  * tclDate.c --
26  *
27  *	This file is generated from a yacc grammar defined in the file
28  *	tclGetDate.y. It should not be edited directly.
29  *
30  * Copyright (c) 1992-1995 Karl Lehenbauer & Mark Diekhans.
31  * Copyright (c) 1995-1997 Sun Microsystems, Inc.
32  *
33  * See the file "license.terms" for information on usage and redistribution of
34  * this file, and for a DISCLAIMER OF ALL WARRANTIES.
35  *
36  */
37 #include "tclInt.h"
38 
39 /*
40  * Bison generates several labels that happen to be unused. MS Visual C++
41  * doesn't like that, and complains. Tell it to shut up.
42  */
43 
44 #ifdef _MSC_VER
45 #pragma warning( disable : 4102 )
46 #endif /* _MSC_VER */
47 
48 /*
49  * Meridian: am, pm, or 24-hour style.
50  */
51 
52 typedef enum _MERIDIAN {
53     MERam, MERpm, MER24
54 } MERIDIAN;
55 
56 /*
57  * yyparse will accept a 'struct DateInfo' as its parameter; that's where the
58  * parsed fields will be returned.
59  */
60 
61 typedef struct DateInfo {
62 
63     Tcl_Obj* messages;		/* Error messages */
64     const char* separatrix;	/* String separating messages */
65 
66     time_t dateYear;
67     time_t dateMonth;
68     time_t dateDay;
69     int dateHaveDate;
70 
71     time_t dateHour;
72     time_t dateMinutes;
73     time_t dateSeconds;
74     MERIDIAN dateMeridian;
75     int dateHaveTime;
76 
77     time_t dateTimezone;
78     int dateDSTmode;
79     int dateHaveZone;
80 
81     time_t dateRelMonth;
82     time_t dateRelDay;
83     time_t dateRelSeconds;
84     int dateHaveRel;
85 
86     time_t dateMonthOrdinal;
87     int dateHaveOrdinalMonth;
88 
89     time_t dateDayOrdinal;
90     time_t dateDayNumber;
91     int dateHaveDay;
92 
93     const char *dateStart;
94     const char *dateInput;
95     time_t *dateRelPointer;
96 
97     int dateDigitCount;
98 } DateInfo;
99 
100 #define YYMALLOC	ckalloc
101 #define YYFREE(x)	(ckfree((void*) (x)))
102 
103 #define yyDSTmode	(info->dateDSTmode)
104 #define yyDayOrdinal	(info->dateDayOrdinal)
105 #define yyDayNumber	(info->dateDayNumber)
106 #define yyMonthOrdinal	(info->dateMonthOrdinal)
107 #define yyHaveDate	(info->dateHaveDate)
108 #define yyHaveDay	(info->dateHaveDay)
109 #define yyHaveOrdinalMonth (info->dateHaveOrdinalMonth)
110 #define yyHaveRel	(info->dateHaveRel)
111 #define yyHaveTime	(info->dateHaveTime)
112 #define yyHaveZone	(info->dateHaveZone)
113 #define yyTimezone	(info->dateTimezone)
114 #define yyDay		(info->dateDay)
115 #define yyMonth		(info->dateMonth)
116 #define yyYear		(info->dateYear)
117 #define yyHour		(info->dateHour)
118 #define yyMinutes	(info->dateMinutes)
119 #define yySeconds	(info->dateSeconds)
120 #define yyMeridian	(info->dateMeridian)
121 #define yyRelMonth	(info->dateRelMonth)
122 #define yyRelDay	(info->dateRelDay)
123 #define yyRelSeconds	(info->dateRelSeconds)
124 #define yyRelPointer	(info->dateRelPointer)
125 #define yyInput		(info->dateInput)
126 #define yyDigitCount	(info->dateDigitCount)
127 
128 #define EPOCH		1970
129 #define START_OF_TIME	1902
130 #define END_OF_TIME	2037
131 
132 /*
133  * The offset of tm_year of struct tm returned by localtime, gmtime, etc.
134  * Posix requires 1900.
135  */
136 
137 #define TM_YEAR_BASE	1900
138 
139 #define HOUR(x)		((int) (60 * x))
140 #define SECSPERDAY	(24L * 60L * 60L)
141 #define IsLeapYear(x)	((x % 4 == 0) && (x % 100 != 0 || x % 400 == 0))
142 
143 /*
144  * An entry in the lexical lookup table.
145  */
146 
147 typedef struct _TABLE {
148     const char *name;
149     int type;
150     time_t value;
151 } TABLE;
152 
153 /*
154  * Daylight-savings mode: on, off, or not yet known.
155  */
156 
157 typedef enum _DSTMODE {
158     DSTon, DSToff, DSTmaybe
159 } DSTMODE;
160 
161 %}
162 
163 %union {
164     time_t Number;
165     enum _MERIDIAN Meridian;
166 }
167 
168 %{
169 
170 /*
171  * Prototypes of internal functions.
172  */
173 
174 static int		LookupWord(YYSTYPE* yylvalPtr, char *buff);
175  static void		TclDateerror(YYLTYPE* location,
176 				     DateInfo* info, const char *s);
177  static int		TclDatelex(YYSTYPE* yylvalPtr, YYLTYPE* location,
178 				   DateInfo* info);
179 static time_t		ToSeconds(time_t Hours, time_t Minutes,
180 			    time_t Seconds, MERIDIAN Meridian);
181 MODULE_SCOPE int	yyparse(DateInfo*);
182 
183 %}
184 
185 %token	tAGO
186 %token	tDAY
187 %token	tDAYZONE
188 %token	tID
189 %token	tMERIDIAN
190 %token	tMONTH
191 %token	tMONTH_UNIT
192 %token	tSTARDATE
193 %token	tSEC_UNIT
194 %token	tSNUMBER
195 %token	tUNUMBER
196 %token	tZONE
197 %token	tEPOCH
198 %token	tDST
199 %token	tISOBASE
200 %token	tDAY_UNIT
201 %token	tNEXT
202 
203 %type	<Number>	tDAY
204 %type	<Number>	tDAYZONE
205 %type	<Number>	tMONTH
206 %type	<Number>	tMONTH_UNIT
207 %type	<Number>	tDST
208 %type	<Number>	tSEC_UNIT
209 %type	<Number>	tSNUMBER
210 %type	<Number>	tUNUMBER
211 %type	<Number>	tZONE
212 %type	<Number>	tISOBASE
213 %type	<Number>	tDAY_UNIT
214 %type	<Number>	unit
215 %type	<Number>	sign
216 %type	<Number>	tNEXT
217 %type	<Number>	tSTARDATE
218 %type	<Meridian>	tMERIDIAN
219 %type	<Meridian>	o_merid
220 
221 %%
222 
223 spec	: /* NULL */
224 	| spec item
225 	;
226 
227 item	: time {
228 	    yyHaveTime++;
229 	}
230 	| zone {
231 	    yyHaveZone++;
232 	}
233 	| date {
234 	    yyHaveDate++;
235 	}
236 	| ordMonth {
237 	    yyHaveOrdinalMonth++;
238 	}
239 	| day {
240 	    yyHaveDay++;
241 	}
242 	| relspec {
243 	    yyHaveRel++;
244 	}
245 	| iso {
246 	    yyHaveTime++;
247 	    yyHaveDate++;
248 	}
249 	| trek {
250 	    yyHaveTime++;
251 	    yyHaveDate++;
252 	    yyHaveRel++;
253 	}
254 	| number
255 	;
256 
257 time	: tUNUMBER tMERIDIAN {
258 	    yyHour = $1;
259 	    yyMinutes = 0;
260 	    yySeconds = 0;
261 	    yyMeridian = $2;
262 	}
263 	| tUNUMBER ':' tUNUMBER o_merid {
264 	    yyHour = $1;
265 	    yyMinutes = $3;
266 	    yySeconds = 0;
267 	    yyMeridian = $4;
268 	}
269 	| tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
270 	    yyHour = $1;
271 	    yyMinutes = $3;
272 	    yySeconds = $5;
273 	    yyMeridian = $6;
274 	}
275 	;
276 
277 zone	: tZONE tDST {
278 	    yyTimezone = $1;
279 	    if (yyTimezone > HOUR( 12)) yyTimezone -= HOUR(100);
280 	    yyDSTmode = DSTon;
281 	}
282 	| tZONE {
283 	    yyTimezone = $1;
284 	    if (yyTimezone > HOUR( 12)) yyTimezone -= HOUR(100);
285 	    yyDSTmode = DSToff;
286 	}
287 	| tDAYZONE {
288 	    yyTimezone = $1;
289 	    yyDSTmode = DSTon;
290 	}
291 	| sign tUNUMBER {
292 	    yyTimezone = -$1*($2 % 100 + ($2 / 100) * 60);
293 	    yyDSTmode = DSToff;
294 	}
295 	;
296 
297 day	: tDAY {
298 	    yyDayOrdinal = 1;
299 	    yyDayNumber = $1;
300 	}
301 	| tDAY ',' {
302 	    yyDayOrdinal = 1;
303 	    yyDayNumber = $1;
304 	}
305 	| tUNUMBER tDAY {
306 	    yyDayOrdinal = $1;
307 	    yyDayNumber = $2;
308 	}
309 	| sign tUNUMBER tDAY {
310 	    yyDayOrdinal = $1 * $2;
311 	    yyDayNumber = $3;
312 	}
313 	| tNEXT tDAY {
314 	    yyDayOrdinal = 2;
315 	    yyDayNumber = $2;
316 	}
317 	;
318 
319 date	: tUNUMBER '/' tUNUMBER {
320 	    yyMonth = $1;
321 	    yyDay = $3;
322 	}
323 	| tUNUMBER '/' tUNUMBER '/' tUNUMBER {
324 	    yyMonth = $1;
325 	    yyDay = $3;
326 	    yyYear = $5;
327 	}
328 	| tISOBASE {
329 	    yyYear = $1 / 10000;
330 	    yyMonth = ($1 % 10000)/100;
331 	    yyDay = $1 % 100;
332 	}
333 	| tUNUMBER '-' tMONTH '-' tUNUMBER {
334 	    yyDay = $1;
335 	    yyMonth = $3;
336 	    yyYear = $5;
337 	}
338 	| tUNUMBER '-' tUNUMBER '-' tUNUMBER {
339 	    yyMonth = $3;
340 	    yyDay = $5;
341 	    yyYear = $1;
342 	}
343 	| tMONTH tUNUMBER {
344 	    yyMonth = $1;
345 	    yyDay = $2;
346 	}
347 	| tMONTH tUNUMBER ',' tUNUMBER {
348 	    yyMonth = $1;
349 	    yyDay = $2;
350 	    yyYear = $4;
351 	}
352 	| tUNUMBER tMONTH {
353 	    yyMonth = $2;
354 	    yyDay = $1;
355 	}
356 	| tEPOCH {
357 	    yyMonth = 1;
358 	    yyDay = 1;
359 	    yyYear = EPOCH;
360 	}
361 	| tUNUMBER tMONTH tUNUMBER {
362 	    yyMonth = $2;
363 	    yyDay = $1;
364 	    yyYear = $3;
365 	}
366 	;
367 
368 ordMonth: tNEXT tMONTH {
369 	    yyMonthOrdinal = 1;
370 	    yyMonth = $2;
371 	}
372 	| tNEXT tUNUMBER tMONTH {
373 	    yyMonthOrdinal = $2;
374 	    yyMonth = $3;
375 	}
376 	;
377 
378 iso	: tUNUMBER '-' tUNUMBER '-' tUNUMBER tZONE
379 		tUNUMBER ':' tUNUMBER ':' tUNUMBER {
380 	    if ($6 != HOUR( 7) + HOUR(100)) YYABORT;
381 	    yyYear = $1;
382 	    yyMonth = $3;
383 	    yyDay = $5;
384 	    yyHour = $7;
385 	    yyMinutes = $9;
386 	    yySeconds = $11;
387 	}
388 	| tISOBASE tZONE tISOBASE {
389 	    if ($2 != HOUR( 7) + HOUR(100)) YYABORT;
390 	    yyYear = $1 / 10000;
391 	    yyMonth = ($1 % 10000)/100;
392 	    yyDay = $1 % 100;
393 	    yyHour = $3 / 10000;
394 	    yyMinutes = ($3 % 10000)/100;
395 	    yySeconds = $3 % 100;
396 	}
397 	| tISOBASE tZONE tUNUMBER ':' tUNUMBER ':' tUNUMBER {
398 	    if ($2 != HOUR( 7) + HOUR(100)) YYABORT;
399 	    yyYear = $1 / 10000;
400 	    yyMonth = ($1 % 10000)/100;
401 	    yyDay = $1 % 100;
402 	    yyHour = $3;
403 	    yyMinutes = $5;
404 	    yySeconds = $7;
405 	}
406 	| tISOBASE tISOBASE {
407 	    yyYear = $1 / 10000;
408 	    yyMonth = ($1 % 10000)/100;
409 	    yyDay = $1 % 100;
410 	    yyHour = $2 / 10000;
411 	    yyMinutes = ($2 % 10000)/100;
412 	    yySeconds = $2 % 100;
413 	}
414 	;
415 
416 trek	: tSTARDATE tUNUMBER '.' tUNUMBER {
417 	    /*
418 	     * Offset computed year by -377 so that the returned years will be
419 	     * in a range accessible with a 32 bit clock seconds value.
420 	     */
421 
422 	    yyYear = $2/1000 + 2323 - 377;
423 	    yyDay  = 1;
424 	    yyMonth = 1;
425 	    yyRelDay += (($2%1000)*(365 + IsLeapYear(yyYear)))/1000;
426 	    yyRelSeconds += $4 * 144 * 60;
427 	}
428 	;
429 
430 relspec : relunits tAGO {
431 	    yyRelSeconds *= -1;
432 	    yyRelMonth *= -1;
433 	    yyRelDay *= -1;
434 	}
435 	| relunits
436 	;
437 
438 relunits : sign tUNUMBER unit {
439 	    *yyRelPointer += $1 * $2 * $3;
440 	}
441 	| tUNUMBER unit {
442 	    *yyRelPointer += $1 * $2;
443 	}
444 	| tNEXT unit {
445 	    *yyRelPointer += $2;
446 	}
447 	| tNEXT tUNUMBER unit {
448 	    *yyRelPointer += $2 * $3;
449 	}
450 	| unit {
451 	    *yyRelPointer += $1;
452 	}
453 	;
454 
455 sign	: '-' {
456 	    $$ = -1;
457 	}
458 	| '+' {
459 	    $$ =  1;
460 	}
461 	;
462 
463 unit	: tSEC_UNIT {
464 	    $$ = $1;
465 	    yyRelPointer = &yyRelSeconds;
466 	}
467 	| tDAY_UNIT {
468 	    $$ = $1;
469 	    yyRelPointer = &yyRelDay;
470 	}
471 	| tMONTH_UNIT {
472 	    $$ = $1;
473 	    yyRelPointer = &yyRelMonth;
474 	}
475 	;
476 
477 number	: tUNUMBER {
478 	    if (yyHaveTime && yyHaveDate && !yyHaveRel) {
479 		yyYear = $1;
480 	    } else {
481 		yyHaveTime++;
482 		if (yyDigitCount <= 2) {
483 		    yyHour = $1;
484 		    yyMinutes = 0;
485 		} else {
486 		    yyHour = $1 / 100;
487 		    yyMinutes = $1 % 100;
488 		}
489 		yySeconds = 0;
490 		yyMeridian = MER24;
491 	    }
492 	}
493 	;
494 
495 o_merid : /* NULL */ {
496 	    $$ = MER24;
497 	}
498 	| tMERIDIAN {
499 	    $$ = $1;
500 	}
501 	;
502 
503 %%
504 /*
505  * Month and day table.
506  */
507 
508 static const TABLE MonthDayTable[] = {
509     { "january",	tMONTH,	 1 },
510     { "february",	tMONTH,	 2 },
511     { "march",		tMONTH,	 3 },
512     { "april",		tMONTH,	 4 },
513     { "may",		tMONTH,	 5 },
514     { "june",		tMONTH,	 6 },
515     { "july",		tMONTH,	 7 },
516     { "august",		tMONTH,	 8 },
517     { "september",	tMONTH,	 9 },
518     { "sept",		tMONTH,	 9 },
519     { "october",	tMONTH, 10 },
520     { "november",	tMONTH, 11 },
521     { "december",	tMONTH, 12 },
522     { "sunday",		tDAY, 0 },
523     { "monday",		tDAY, 1 },
524     { "tuesday",	tDAY, 2 },
525     { "tues",		tDAY, 2 },
526     { "wednesday",	tDAY, 3 },
527     { "wednes",		tDAY, 3 },
528     { "thursday",	tDAY, 4 },
529     { "thur",		tDAY, 4 },
530     { "thurs",		tDAY, 4 },
531     { "friday",		tDAY, 5 },
532     { "saturday",	tDAY, 6 },
533     { NULL, 0, 0 }
534 };
535 
536 /*
537  * Time units table.
538  */
539 
540 static const TABLE UnitsTable[] = {
541     { "year",		tMONTH_UNIT,	12 },
542     { "month",		tMONTH_UNIT,	 1 },
543     { "fortnight",	tDAY_UNIT,	14 },
544     { "week",		tDAY_UNIT,	 7 },
545     { "day",		tDAY_UNIT,	 1 },
546     { "hour",		tSEC_UNIT, 60 * 60 },
547     { "minute",		tSEC_UNIT,	60 },
548     { "min",		tSEC_UNIT,	60 },
549     { "second",		tSEC_UNIT,	 1 },
550     { "sec",		tSEC_UNIT,	 1 },
551     { NULL, 0, 0 }
552 };
553 
554 /*
555  * Assorted relative-time words.
556  */
557 
558 static const TABLE OtherTable[] = {
559     { "tomorrow",	tDAY_UNIT,	1 },
560     { "yesterday",	tDAY_UNIT,	-1 },
561     { "today",		tDAY_UNIT,	0 },
562     { "now",		tSEC_UNIT,	0 },
563     { "last",		tUNUMBER,	-1 },
564     { "this",		tSEC_UNIT,	0 },
565     { "next",		tNEXT,		1 },
566 #if 0
567     { "first",		tUNUMBER,	1 },
568     { "second",		tUNUMBER,	2 },
569     { "third",		tUNUMBER,	3 },
570     { "fourth",		tUNUMBER,	4 },
571     { "fifth",		tUNUMBER,	5 },
572     { "sixth",		tUNUMBER,	6 },
573     { "seventh",	tUNUMBER,	7 },
574     { "eighth",		tUNUMBER,	8 },
575     { "ninth",		tUNUMBER,	9 },
576     { "tenth",		tUNUMBER,	10 },
577     { "eleventh",	tUNUMBER,	11 },
578     { "twelfth",	tUNUMBER,	12 },
579 #endif
580     { "ago",		tAGO,		1 },
581     { "epoch",		tEPOCH,		0 },
582     { "stardate",	tSTARDATE,	0 },
583     { NULL, 0, 0 }
584 };
585 
586 /*
587  * The timezone table. (Note: This table was modified to not use any floating
588  * point constants to work around an SGI compiler bug).
589  */
590 
591 static const TABLE TimezoneTable[] = {
592     { "gmt",	tZONE,	   HOUR( 0) },	    /* Greenwich Mean */
593     { "ut",	tZONE,	   HOUR( 0) },	    /* Universal (Coordinated) */
594     { "utc",	tZONE,	   HOUR( 0) },
595     { "uct",	tZONE,	   HOUR( 0) },	    /* Universal Coordinated Time */
596     { "wet",	tZONE,	   HOUR( 0) },	    /* Western European */
597     { "bst",	tDAYZONE,  HOUR( 0) },	    /* British Summer */
598     { "wat",	tZONE,	   HOUR( 1) },	    /* West Africa */
599     { "at",	tZONE,	   HOUR( 2) },	    /* Azores */
600 #if	0
601     /* For completeness.  BST is also British Summer, and GST is
602      * also Guam Standard. */
603     { "bst",	tZONE,	   HOUR( 3) },	    /* Brazil Standard */
604     { "gst",	tZONE,	   HOUR( 3) },	    /* Greenland Standard */
605 #endif
606     { "nft",	tZONE,	   HOUR( 7/2) },    /* Newfoundland */
607     { "nst",	tZONE,	   HOUR( 7/2) },    /* Newfoundland Standard */
608     { "ndt",	tDAYZONE,  HOUR( 7/2) },    /* Newfoundland Daylight */
609     { "ast",	tZONE,	   HOUR( 4) },	    /* Atlantic Standard */
610     { "adt",	tDAYZONE,  HOUR( 4) },	    /* Atlantic Daylight */
611     { "est",	tZONE,	   HOUR( 5) },	    /* Eastern Standard */
612     { "edt",	tDAYZONE,  HOUR( 5) },	    /* Eastern Daylight */
613     { "cst",	tZONE,	   HOUR( 6) },	    /* Central Standard */
614     { "cdt",	tDAYZONE,  HOUR( 6) },	    /* Central Daylight */
615     { "mst",	tZONE,	   HOUR( 7) },	    /* Mountain Standard */
616     { "mdt",	tDAYZONE,  HOUR( 7) },	    /* Mountain Daylight */
617     { "pst",	tZONE,	   HOUR( 8) },	    /* Pacific Standard */
618     { "pdt",	tDAYZONE,  HOUR( 8) },	    /* Pacific Daylight */
619     { "yst",	tZONE,	   HOUR( 9) },	    /* Yukon Standard */
620     { "ydt",	tDAYZONE,  HOUR( 9) },	    /* Yukon Daylight */
621     { "hst",	tZONE,	   HOUR(10) },	    /* Hawaii Standard */
622     { "hdt",	tDAYZONE,  HOUR(10) },	    /* Hawaii Daylight */
623     { "cat",	tZONE,	   HOUR(10) },	    /* Central Alaska */
624     { "ahst",	tZONE,	   HOUR(10) },	    /* Alaska-Hawaii Standard */
625     { "nt",	tZONE,	   HOUR(11) },	    /* Nome */
626     { "idlw",	tZONE,	   HOUR(12) },	    /* International Date Line West */
627     { "cet",	tZONE,	  -HOUR( 1) },	    /* Central European */
628     { "cest",	tDAYZONE, -HOUR( 1) },	    /* Central European Summer */
629     { "met",	tZONE,	  -HOUR( 1) },	    /* Middle European */
630     { "mewt",	tZONE,	  -HOUR( 1) },	    /* Middle European Winter */
631     { "mest",	tDAYZONE, -HOUR( 1) },	    /* Middle European Summer */
632     { "swt",	tZONE,	  -HOUR( 1) },	    /* Swedish Winter */
633     { "sst",	tDAYZONE, -HOUR( 1) },	    /* Swedish Summer */
634     { "fwt",	tZONE,	  -HOUR( 1) },	    /* French Winter */
635     { "fst",	tDAYZONE, -HOUR( 1) },	    /* French Summer */
636     { "eet",	tZONE,	  -HOUR( 2) },	    /* Eastern Europe, USSR Zone 1 */
637     { "bt",	tZONE,	  -HOUR( 3) },	    /* Baghdad, USSR Zone 2 */
638     { "it",	tZONE,	  -HOUR( 7/2) },    /* Iran */
639     { "zp4",	tZONE,	  -HOUR( 4) },	    /* USSR Zone 3 */
640     { "zp5",	tZONE,	  -HOUR( 5) },	    /* USSR Zone 4 */
641     { "ist",	tZONE,	  -HOUR(11/2) },    /* Indian Standard */
642     { "zp6",	tZONE,	  -HOUR( 6) },	    /* USSR Zone 5 */
643 #if	0
644     /* For completeness.  NST is also Newfoundland Stanard, nad SST is
645      * also Swedish Summer. */
646     { "nst",	tZONE,	  -HOUR(13/2) },    /* North Sumatra */
647     { "sst",	tZONE,	  -HOUR( 7) },	    /* South Sumatra, USSR Zone 6 */
648 #endif	/* 0 */
649     { "wast",	tZONE,	  -HOUR( 7) },	    /* West Australian Standard */
650     { "wadt",	tDAYZONE, -HOUR( 7) },	    /* West Australian Daylight */
651     { "jt",	tZONE,	  -HOUR(15/2) },    /* Java (3pm in Cronusland!) */
652     { "cct",	tZONE,	  -HOUR( 8) },	    /* China Coast, USSR Zone 7 */
653     { "jst",	tZONE,	  -HOUR( 9) },	    /* Japan Standard, USSR Zone 8 */
654     { "jdt",	tDAYZONE, -HOUR( 9) },	    /* Japan Daylight */
655     { "kst",	tZONE,	  -HOUR( 9) },	    /* Korea Standard */
656     { "kdt",	tDAYZONE, -HOUR( 9) },	    /* Korea Daylight */
657     { "cast",	tZONE,	  -HOUR(19/2) },    /* Central Australian Standard */
658     { "cadt",	tDAYZONE, -HOUR(19/2) },    /* Central Australian Daylight */
659     { "east",	tZONE,	  -HOUR(10) },	    /* Eastern Australian Standard */
660     { "eadt",	tDAYZONE, -HOUR(10) },	    /* Eastern Australian Daylight */
661     { "gst",	tZONE,	  -HOUR(10) },	    /* Guam Standard, USSR Zone 9 */
662     { "nzt",	tZONE,	  -HOUR(12) },	    /* New Zealand */
663     { "nzst",	tZONE,	  -HOUR(12) },	    /* New Zealand Standard */
664     { "nzdt",	tDAYZONE, -HOUR(12) },	    /* New Zealand Daylight */
665     { "idle",	tZONE,	  -HOUR(12) },	    /* International Date Line East */
666     /* ADDED BY Marco Nijdam */
667     { "dst",	tDST,	  HOUR( 0) },	    /* DST on (hour is ignored) */
668     /* End ADDED */
669     { NULL, 0, 0 }
670 };
671 
672 /*
673  * Military timezone table.
674  */
675 
676 static const TABLE MilitaryTable[] = {
677     { "a",	tZONE,	-HOUR( 1) + HOUR(100) },
678     { "b",	tZONE,	-HOUR( 2) + HOUR(100) },
679     { "c",	tZONE,	-HOUR( 3) + HOUR(100) },
680     { "d",	tZONE,	-HOUR( 4) + HOUR(100) },
681     { "e",	tZONE,	-HOUR( 5) + HOUR(100) },
682     { "f",	tZONE,	-HOUR( 6) + HOUR(100) },
683     { "g",	tZONE,	-HOUR( 7) + HOUR(100) },
684     { "h",	tZONE,	-HOUR( 8) + HOUR(100) },
685     { "i",	tZONE,	-HOUR( 9) + HOUR(100) },
686     { "k",	tZONE,	-HOUR(10) + HOUR(100) },
687     { "l",	tZONE,	-HOUR(11) + HOUR(100) },
688     { "m",	tZONE,	-HOUR(12) + HOUR(100) },
689     { "n",	tZONE,	HOUR(  1) + HOUR(100) },
690     { "o",	tZONE,	HOUR(  2) + HOUR(100) },
691     { "p",	tZONE,	HOUR(  3) + HOUR(100) },
692     { "q",	tZONE,	HOUR(  4) + HOUR(100) },
693     { "r",	tZONE,	HOUR(  5) + HOUR(100) },
694     { "s",	tZONE,	HOUR(  6) + HOUR(100) },
695     { "t",	tZONE,	HOUR(  7) + HOUR(100) },
696     { "u",	tZONE,	HOUR(  8) + HOUR(100) },
697     { "v",	tZONE,	HOUR(  9) + HOUR(100) },
698     { "w",	tZONE,	HOUR( 10) + HOUR(100) },
699     { "x",	tZONE,	HOUR( 11) + HOUR(100) },
700     { "y",	tZONE,	HOUR( 12) + HOUR(100) },
701     { "z",	tZONE,	HOUR( 0)  + HOUR(100) },
702     { NULL, 0, 0 }
703 };
704 
705 /*
706  * Dump error messages in the bit bucket.
707  */
708 
709 static void
TclDateerror(YYLTYPE * location,DateInfo * infoPtr,const char * s)710 TclDateerror(
711     YYLTYPE* location,
712     DateInfo* infoPtr,
713     const char *s)
714 {
715     Tcl_Obj* t;
716     Tcl_AppendToObj(infoPtr->messages, infoPtr->separatrix, -1);
717     Tcl_AppendToObj(infoPtr->messages, s, -1);
718     Tcl_AppendToObj(infoPtr->messages, " (characters ", -1);
719     TclNewIntObj(t, location->first_column);
720     Tcl_IncrRefCount(t);
721     Tcl_AppendObjToObj(infoPtr->messages, t);
722     Tcl_DecrRefCount(t);
723     Tcl_AppendToObj(infoPtr->messages, "-", -1);
724     TclNewIntObj(t, location->last_column);
725     Tcl_IncrRefCount(t);
726     Tcl_AppendObjToObj(infoPtr->messages, t);
727     Tcl_DecrRefCount(t);
728     Tcl_AppendToObj(infoPtr->messages, ")", -1);
729     infoPtr->separatrix = "\n";
730 }
731 
732 static time_t
ToSeconds(time_t Hours,time_t Minutes,time_t Seconds,MERIDIAN Meridian)733 ToSeconds(
734     time_t Hours,
735     time_t Minutes,
736     time_t Seconds,
737     MERIDIAN Meridian)
738 {
739     if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59) {
740 	return -1;
741     }
742     switch (Meridian) {
743     case MER24:
744 	if (Hours < 0 || Hours > 23) {
745 	    return -1;
746 	}
747 	return (Hours * 60L + Minutes) * 60L + Seconds;
748     case MERam:
749 	if (Hours < 1 || Hours > 12) {
750 	    return -1;
751 	}
752 	return ((Hours % 12) * 60L + Minutes) * 60L + Seconds;
753     case MERpm:
754 	if (Hours < 1 || Hours > 12) {
755 	    return -1;
756 	}
757 	return (((Hours % 12) + 12) * 60L + Minutes) * 60L + Seconds;
758     }
759     return -1;			/* Should never be reached */
760 }
761 
762 static int
LookupWord(YYSTYPE * yylvalPtr,char * buff)763 LookupWord(
764     YYSTYPE* yylvalPtr,
765     char *buff)
766 {
767     char *p;
768     char *q;
769     const TABLE *tp;
770     int i, abbrev;
771 
772     /*
773      * Make it lowercase.
774      */
775 
776     Tcl_UtfToLower(buff);
777 
778     if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
779 	yylvalPtr->Meridian = MERam;
780 	return tMERIDIAN;
781     }
782     if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
783 	yylvalPtr->Meridian = MERpm;
784 	return tMERIDIAN;
785     }
786 
787     /*
788      * See if we have an abbreviation for a month.
789      */
790 
791     if (strlen(buff) == 3) {
792 	abbrev = 1;
793     } else if (strlen(buff) == 4 && buff[3] == '.') {
794 	abbrev = 1;
795 	buff[3] = '\0';
796     } else {
797 	abbrev = 0;
798     }
799 
800     for (tp = MonthDayTable; tp->name; tp++) {
801 	if (abbrev) {
802 	    if (strncmp(buff, tp->name, 3) == 0) {
803 		yylvalPtr->Number = tp->value;
804 		return tp->type;
805 	    }
806 	} else if (strcmp(buff, tp->name) == 0) {
807 	    yylvalPtr->Number = tp->value;
808 	    return tp->type;
809 	}
810     }
811 
812     for (tp = TimezoneTable; tp->name; tp++) {
813 	if (strcmp(buff, tp->name) == 0) {
814 	    yylvalPtr->Number = tp->value;
815 	    return tp->type;
816 	}
817     }
818 
819     for (tp = UnitsTable; tp->name; tp++) {
820 	if (strcmp(buff, tp->name) == 0) {
821 	    yylvalPtr->Number = tp->value;
822 	    return tp->type;
823 	}
824     }
825 
826     /*
827      * Strip off any plural and try the units table again.
828      */
829 
830     i = strlen(buff) - 1;
831     if (i > 0 && buff[i] == 's') {
832 	buff[i] = '\0';
833 	for (tp = UnitsTable; tp->name; tp++) {
834 	    if (strcmp(buff, tp->name) == 0) {
835 		yylvalPtr->Number = tp->value;
836 		return tp->type;
837 	    }
838 	}
839     }
840 
841     for (tp = OtherTable; tp->name; tp++) {
842 	if (strcmp(buff, tp->name) == 0) {
843 	    yylvalPtr->Number = tp->value;
844 	    return tp->type;
845 	}
846     }
847 
848     /*
849      * Military timezones.
850      */
851 
852     if (buff[1] == '\0' && !(*buff & 0x80)
853 	    && isalpha(UCHAR(*buff))) {			/* INTL: ISO only */
854 	for (tp = MilitaryTable; tp->name; tp++) {
855 	    if (strcmp(buff, tp->name) == 0) {
856 		yylvalPtr->Number = tp->value;
857 		return tp->type;
858 	    }
859 	}
860     }
861 
862     /*
863      * Drop out any periods and try the timezone table again.
864      */
865 
866     for (i = 0, p = q = buff; *q; q++) {
867 	if (*q != '.') {
868 	    *p++ = *q;
869 	} else {
870 	    i++;
871 	}
872     }
873     *p = '\0';
874     if (i) {
875 	for (tp = TimezoneTable; tp->name; tp++) {
876 	    if (strcmp(buff, tp->name) == 0) {
877 		yylvalPtr->Number = tp->value;
878 		return tp->type;
879 	    }
880 	}
881     }
882 
883     return tID;
884 }
885 
886 static int
TclDatelex(YYSTYPE * yylvalPtr,YYLTYPE * location,DateInfo * info)887 TclDatelex(
888     YYSTYPE* yylvalPtr,
889     YYLTYPE* location,
890     DateInfo *info)
891 {
892     char c;
893     char *p;
894     char buff[20];
895     int Count;
896 
897     location->first_column = yyInput - info->dateStart;
898     for ( ; ; ) {
899 	while (TclIsSpaceProcM(*yyInput)) {
900 	    yyInput++;
901 	}
902 
903 	if (isdigit(UCHAR(c = *yyInput))) { /* INTL: digit */
904 	    /*
905 	     * Convert the string into a number; count the number of digits.
906 	     */
907 
908 	    Count = 0;
909 	    for (yylvalPtr->Number = 0;
910 		    isdigit(UCHAR(c = *yyInput++)); ) {	  /* INTL: digit */
911 		yylvalPtr->Number = 10 * yylvalPtr->Number + c - '0';
912 		Count++;
913 	    }
914 	    yyInput--;
915 	    yyDigitCount = Count;
916 
917 	    /*
918 	     * A number with 6 or more digits is considered an ISO 8601 base.
919 	     */
920 
921 	    if (Count >= 6) {
922 		location->last_column = yyInput - info->dateStart - 1;
923 		return tISOBASE;
924 	    } else {
925 		location->last_column = yyInput - info->dateStart - 1;
926 		return tUNUMBER;
927 	    }
928 	}
929 	if (!(c & 0x80) && isalpha(UCHAR(c))) {		  /* INTL: ISO only. */
930 	    for (p = buff; isalpha(UCHAR(c = *yyInput++)) /* INTL: ISO only. */
931 		     || c == '.'; ) {
932 		if (p < &buff[sizeof buff - 1]) {
933 		    *p++ = c;
934 		}
935 	    }
936 	    *p = '\0';
937 	    yyInput--;
938 	    location->last_column = yyInput - info->dateStart - 1;
939 	    return LookupWord(yylvalPtr, buff);
940 	}
941 	if (c != '(') {
942 	    location->last_column = yyInput - info->dateStart;
943 	    return *yyInput++;
944 	}
945 	Count = 0;
946 	do {
947 	    c = *yyInput++;
948 	    if (c == '\0') {
949 		location->last_column = yyInput - info->dateStart - 1;
950 		return c;
951 	    } else if (c == '(') {
952 		Count++;
953 	    } else if (c == ')') {
954 		Count--;
955 	    }
956 	} while (Count > 0);
957     }
958 }
959 
960 int
TclClockOldscanObjCmd(void * dummy,Tcl_Interp * interp,int objc,Tcl_Obj * const * objv)961 TclClockOldscanObjCmd(
962     void *dummy,		/* Unused */
963     Tcl_Interp *interp,		/* Tcl interpreter */
964     int objc,			/* Count of paraneters */
965     Tcl_Obj *const *objv)	/* Parameters */
966 {
967     Tcl_Obj *result, *resultElement;
968     int yr, mo, da;
969     DateInfo dateInfo;
970     DateInfo* info = &dateInfo;
971     int status;
972     (void)dummy;
973 
974     if (objc != 5) {
975 	Tcl_WrongNumArgs(interp, 1, objv,
976 		"stringToParse baseYear baseMonth baseDay" );
977 	return TCL_ERROR;
978     }
979 
980     yyInput = Tcl_GetString( objv[1] );
981     dateInfo.dateStart = yyInput;
982 
983     yyHaveDate = 0;
984     if (Tcl_GetIntFromObj(interp, objv[2], &yr) != TCL_OK
985 	    || Tcl_GetIntFromObj(interp, objv[3], &mo) != TCL_OK
986 	    || Tcl_GetIntFromObj(interp, objv[4], &da) != TCL_OK) {
987 	return TCL_ERROR;
988     }
989     yyYear = yr; yyMonth = mo; yyDay = da;
990 
991     yyHaveTime = 0;
992     yyHour = 0; yyMinutes = 0; yySeconds = 0; yyMeridian = MER24;
993 
994     yyHaveZone = 0;
995     yyTimezone = 0; yyDSTmode = DSTmaybe;
996 
997     yyHaveOrdinalMonth = 0;
998     yyMonthOrdinal = 0;
999 
1000     yyHaveDay = 0;
1001     yyDayOrdinal = 0; yyDayNumber = 0;
1002 
1003     yyHaveRel = 0;
1004     yyRelMonth = 0; yyRelDay = 0; yyRelSeconds = 0; yyRelPointer = NULL;
1005 
1006     TclNewObj(dateInfo.messages);
1007     dateInfo.separatrix = "";
1008     Tcl_IncrRefCount(dateInfo.messages);
1009 
1010     status = yyparse(&dateInfo);
1011     if (status == 1) {
1012 	Tcl_SetObjResult(interp, dateInfo.messages);
1013 	Tcl_DecrRefCount(dateInfo.messages);
1014 	Tcl_SetErrorCode(interp, "TCL", "VALUE", "DATE", "PARSE", NULL);
1015 	return TCL_ERROR;
1016     } else if (status == 2) {
1017 	Tcl_SetObjResult(interp, Tcl_NewStringObj("memory exhausted", -1));
1018 	Tcl_DecrRefCount(dateInfo.messages);
1019 	Tcl_SetErrorCode(interp, "TCL", "MEMORY", NULL);
1020 	return TCL_ERROR;
1021     } else if (status != 0) {
1022 	Tcl_SetObjResult(interp, Tcl_NewStringObj("Unknown status returned "
1023 						  "from date parser. Please "
1024 						  "report this error as a "
1025 						  "bug in Tcl.", -1));
1026 	Tcl_DecrRefCount(dateInfo.messages);
1027 	Tcl_SetErrorCode(interp, "TCL", "BUG", NULL);
1028 	return TCL_ERROR;
1029     }
1030     Tcl_DecrRefCount(dateInfo.messages);
1031 
1032     if (yyHaveDate > 1) {
1033 	Tcl_SetObjResult(interp,
1034 		Tcl_NewStringObj("more than one date in string", -1));
1035 	Tcl_SetErrorCode(interp, "TCL", "VALUE", "DATE", "MULTIPLE", NULL);
1036 	return TCL_ERROR;
1037     }
1038     if (yyHaveTime > 1) {
1039 	Tcl_SetObjResult(interp,
1040 		Tcl_NewStringObj("more than one time of day in string", -1));
1041 	Tcl_SetErrorCode(interp, "TCL", "VALUE", "DATE", "MULTIPLE", NULL);
1042 	return TCL_ERROR;
1043     }
1044     if (yyHaveZone > 1) {
1045 	Tcl_SetObjResult(interp,
1046 		Tcl_NewStringObj("more than one time zone in string", -1));
1047 	Tcl_SetErrorCode(interp, "TCL", "VALUE", "DATE", "MULTIPLE", NULL);
1048 	return TCL_ERROR;
1049     }
1050     if (yyHaveDay > 1) {
1051 	Tcl_SetObjResult(interp,
1052 		Tcl_NewStringObj("more than one weekday in string", -1));
1053 	Tcl_SetErrorCode(interp, "TCL", "VALUE", "DATE", "MULTIPLE", NULL);
1054 	return TCL_ERROR;
1055     }
1056     if (yyHaveOrdinalMonth > 1) {
1057 	Tcl_SetObjResult(interp,
1058 		Tcl_NewStringObj("more than one ordinal month in string", -1));
1059 	Tcl_SetErrorCode(interp, "TCL", "VALUE", "DATE", "MULTIPLE", NULL);
1060 	return TCL_ERROR;
1061     }
1062 
1063     TclNewObj(result);
1064     TclNewObj(resultElement);
1065     if (yyHaveDate) {
1066 	Tcl_ListObjAppendElement(interp, resultElement,
1067 		Tcl_NewIntObj((int) yyYear));
1068 	Tcl_ListObjAppendElement(interp, resultElement,
1069 		Tcl_NewIntObj((int) yyMonth));
1070 	Tcl_ListObjAppendElement(interp, resultElement,
1071 		Tcl_NewIntObj((int) yyDay));
1072     }
1073     Tcl_ListObjAppendElement(interp, result, resultElement);
1074 
1075     if (yyHaveTime) {
1076 	Tcl_ListObjAppendElement(interp, result, Tcl_NewIntObj((int)
1077 		ToSeconds(yyHour, yyMinutes, yySeconds, (MERIDIAN)yyMeridian)));
1078     } else {
1079 	Tcl_ListObjAppendElement(interp, result, Tcl_NewObj());
1080     }
1081 
1082     TclNewObj(resultElement);
1083     if (yyHaveZone) {
1084 	Tcl_ListObjAppendElement(interp, resultElement,
1085 		Tcl_NewIntObj((int) -yyTimezone));
1086 	Tcl_ListObjAppendElement(interp, resultElement,
1087 		Tcl_NewIntObj(1 - yyDSTmode));
1088     }
1089     Tcl_ListObjAppendElement(interp, result, resultElement);
1090 
1091     TclNewObj(resultElement);
1092     if (yyHaveRel) {
1093 	Tcl_ListObjAppendElement(interp, resultElement,
1094 		Tcl_NewIntObj((int) yyRelMonth));
1095 	Tcl_ListObjAppendElement(interp, resultElement,
1096 		Tcl_NewIntObj((int) yyRelDay));
1097 	Tcl_ListObjAppendElement(interp, resultElement,
1098 		Tcl_NewIntObj((int) yyRelSeconds));
1099     }
1100     Tcl_ListObjAppendElement(interp, result, resultElement);
1101 
1102     TcNewObj(resultElement);
1103     if (yyHaveDay && !yyHaveDate) {
1104 	Tcl_ListObjAppendElement(interp, resultElement,
1105 		Tcl_NewIntObj((int) yyDayOrdinal));
1106 	Tcl_ListObjAppendElement(interp, resultElement,
1107 		Tcl_NewIntObj((int) yyDayNumber));
1108     }
1109     Tcl_ListObjAppendElement(interp, result, resultElement);
1110 
1111     TclNewObj(resultElement);
1112     if (yyHaveOrdinalMonth) {
1113 	Tcl_ListObjAppendElement(interp, resultElement,
1114 		Tcl_NewIntObj((int) yyMonthOrdinal));
1115 	Tcl_ListObjAppendElement(interp, resultElement,
1116 		Tcl_NewIntObj((int) yyMonth));
1117     }
1118     Tcl_ListObjAppendElement(interp, result, resultElement);
1119 
1120     Tcl_SetObjResult(interp, result);
1121     return TCL_OK;
1122 }
1123 
1124 /*
1125  * Local Variables:
1126  * mode: c
1127  * c-basic-offset: 4
1128  * fill-column: 78
1129  * End:
1130  */
1131