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