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