1 %{
2
3 #include <LYLeaks.h>
4
5 /*
6 * $LynxId: parsdate.y,v 1.20 2013/01/05 01:58:50 tom Exp $
7 *
8 * This module is adapted and extended from tin, to use for LYmktime().
9 *
10 * Project : tin - a Usenet reader
11 * Module : parsedate.y
12 * Author : S. Bellovin, R. $alz, J. Berets, P. Eggert
13 * Created : 1990-08-01
14 * Updated : 2008-06-30 (by Thomas Dickey, for Lynx)
15 * Notes : This grammar has 8 shift/reduce conflicts.
16 *
17 * Originally written by Steven M. Bellovin <smb@research.att.com>
18 * while at the University of North Carolina at Chapel Hill.
19 * Later tweaked by a couple of people on Usenet. Completely
20 * overhauled by Rich $alz <rsalz@osf.org> and Jim Berets
21 * <jberets@bbn.com> in August, 1990.
22 *
23 * Further revised (removed obsolete constructs and cleaned up
24 * timezone names) in August, 1991, by Rich.
25 * Paul Eggert <eggert@twinsun.com> helped in September 1992.
26 * Roland Rosenfeld added MET DST code in April 1994.
27 *
28 * Revision : 1.13
29 * Copyright : This code is in the public domain and has no copyright.
30 */
31
32 /* SUPPRESS 530 */ /* Empty body for statement */
33 /* SUPPRESS 593 on yyerrlab */ /* Label was not used */
34 /* SUPPRESS 593 on yynewstate */ /* Label was not used */
35 /* SUPPRESS 595 on yypvt */ /* Automatic variable may be used before set */
36
37 #undef alloca /* conflicting def may be set by yacc */
38 #include <parsdate.h>
39
40 /*
41 ** Get the number of elements in a fixed-size array, or a pointer just
42 ** past the end of it.
43 */
44 #define ENDOF(array) (&array[ARRAY_SIZE(array)])
45
46 #ifdef EBCDIC
47 #define TO_ASCII(c) TOASCII(c)
48 #define TO_LOCAL(c) FROMASCII(c)
49 #else
50 #define TO_ASCII(c) (c)
51 #define TO_LOCAL(c) (c)
52 #endif
53
54 #define IS7BIT(x) ((unsigned) TO_ASCII(x) < 128)
55 #define CTYPE(isXXXXX, c) (IS7BIT(c) && isXXXXX(((unsigned char)c)))
56
57 typedef char *PD_STRING;
58
59 extern int date_parse(void);
60
61 #define yyparse date_parse
62 #define yylex date_lex
63 #define yyerror date_error
64
65 /* See the LeapYears table in Convert. */
66 #define EPOCH 1970
67 #define END_OF_TIME 2038
68
69 /* Constants for general time calculations. */
70 #define DST_OFFSET 1
71 #define SECSPERDAY (24L * 60L * 60L)
72 /* Readability for TABLE stuff. */
73 #define HOUR(x) (x * 60)
74
75 #define LPAREN '('
76 #define RPAREN ')'
77
78 /*
79 ** Daylight-savings mode: on, off, or not yet known.
80 */
81 typedef enum _DSTMODE {
82 DSTon, DSToff, DSTmaybe
83 } DSTMODE;
84
85 /*
86 ** Meridian: am, pm, or 24-hour style.
87 */
88 typedef enum _MERIDIAN {
89 MERam, MERpm, MER24
90 } MERIDIAN;
91
92 /*
93 ** Global variables. We could get rid of most of them by using a yacc
94 ** union, but this is more efficient. (This routine predates the
95 ** yacc %union construct.)
96 */
97 static char *yyInput;
98 static DSTMODE yyDSTmode;
99 static int yyHaveDate;
100 static int yyHaveRel;
101 static int yyHaveTime;
102 static time_t yyTimezone;
103 static time_t yyDay;
104 static time_t yyHour;
105 static time_t yyMinutes;
106 static time_t yyMonth;
107 static time_t yySeconds;
108 static time_t yyYear;
109 static MERIDIAN yyMeridian;
110 static time_t yyRelMonth;
111 static time_t yyRelSeconds;
112
113 static time_t ToSeconds(time_t, time_t, time_t, MERIDIAN);
114 static time_t Convert(time_t, time_t, time_t, time_t, time_t, time_t,
115 MERIDIAN, DSTMODE);
116 static time_t DSTcorrect(time_t, time_t);
117 static time_t RelativeMonth(time_t, time_t);
118 static int LookupWord(char *, int);
119 static int date_lex(void);
120 static int GetTimeInfo(TIMEINFO * Now);
121
122 /*
123 * The 'date_error()' function is declared here to work around a defect in
124 * bison 1.22, which redefines 'const' further down in this file, making it
125 * impossible to put a prototype here, and the function later. We're using
126 * 'const' on the parameter to quiet gcc's -Wwrite-strings warning.
127 */
128 /*ARGSUSED*/
date_error(const char GCC_UNUSED * s)129 static void date_error(const char GCC_UNUSED *s)
130 {
131 /*NOTREACHED */
132 }
133
134 %}
135
136 %union {
137 time_t Number;
138 enum _MERIDIAN Meridian;
139 }
140
141 %token tDAY tDAYZONE tMERIDIAN tMONTH tMONTH_UNIT tSEC_UNIT tSNUMBER
142 %token tUNUMBER tZONE tDST
143
144 %type <Number> tDAYZONE tMONTH tMONTH_UNIT tSEC_UNIT
145 %type <Number> tSNUMBER tUNUMBER tZONE numzone zone
146 %type <Meridian> tMERIDIAN o_merid
147
148 %%
149
150 spec : /* NULL */
151 | spec item
152 ;
153
154 item : time {
155 yyHaveTime++;
156 #if defined(lint)
157 /* I am compulsive about lint natterings... */
158 if (yyHaveTime == -1) {
159 YYERROR;
160 }
161 #endif /* defined(lint) */
162 }
163 | time zone {
164 yyHaveTime++;
165 yyTimezone = $2;
166 }
167 | date {
168 yyHaveDate++;
169 }
170 | both {
171 yyHaveDate++;
172 yyHaveTime++;
173 }
174 | both zone {
175 yyHaveDate++;
176 yyHaveTime++;
177 yyTimezone = $2;
178 }
179 | rel {
180 yyHaveRel = 1;
181 }
182 ;
183
184 time : tUNUMBER o_merid {
185 if ($1 < 100) {
186 yyHour = $1;
187 yyMinutes = 0;
188 }
189 else {
190 yyHour = $1 / 100;
191 yyMinutes = $1 % 100;
192 }
193 yySeconds = 0;
194 yyMeridian = $2;
195 }
196 | tUNUMBER ':' tUNUMBER o_merid {
197 yyHour = $1;
198 yyMinutes = $3;
199 yySeconds = 0;
200 yyMeridian = $4;
201 }
202 | tUNUMBER ':' tUNUMBER numzone {
203 yyHour = $1;
204 yyMinutes = $3;
205 yyTimezone = $4;
206 yyMeridian = MER24;
207 yyDSTmode = DSToff;
208 }
209 | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
210 yyHour = $1;
211 yyMinutes = $3;
212 yySeconds = $5;
213 yyMeridian = $6;
214 }
215 | tUNUMBER ':' tUNUMBER ':' tUNUMBER numzone {
216 yyHour = $1;
217 yyMinutes = $3;
218 yySeconds = $5;
219 yyTimezone = $6;
220 yyMeridian = MER24;
221 yyDSTmode = DSToff;
222 }
223 ;
224
225 zone : tZONE {
226 $$ = $1;
227 yyDSTmode = DSToff;
228 }
229 | tDAYZONE {
230 $$ = $1;
231 yyDSTmode = DSTon;
232 }
233 | tDAYZONE tDST {
234 yyTimezone = $1;
235 yyDSTmode = DSTon;
236 }
237 | tZONE numzone {
238 /* Only allow "GMT+300" and "GMT-0800" */
239 if ($1 != 0) {
240 YYABORT;
241 }
242 $$ = $2;
243 yyDSTmode = DSToff;
244 }
245 | numzone {
246 $$ = $1;
247 yyDSTmode = DSToff;
248 }
249 ;
250
251 numzone : tSNUMBER {
252 int i;
253
254 /* Unix and GMT and numeric timezones -- a little confusing. */
255 if ((int)$1 < 0) {
256 /* Don't work with negative modulus. */
257 $1 = -(int)$1;
258 if ($1 > 9999 || (i = (int) ($1 % 100)) >= 60) {
259 YYABORT;
260 }
261 $$ = ($1 / 100) * 60 + i;
262 }
263 else {
264 if ($1 > 9999 || (i = (int) ($1 % 100)) >= 60) {
265 YYABORT;
266 }
267 $$ = -(($1 / 100) * 60 + i);
268 }
269 }
270 ;
271
272 date : tUNUMBER '/' tUNUMBER {
273 yyMonth = $1;
274 yyDay = $3;
275 }
276 | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
277 if ($1 > 100) {
278 yyYear = $1;
279 yyMonth = $3;
280 yyDay = $5;
281 }
282 else {
283 yyMonth = $1;
284 yyDay = $3;
285 yyYear = $5;
286 }
287 }
288 | tMONTH tUNUMBER {
289 yyMonth = $1;
290 yyDay = $2;
291 }
292 | tMONTH tUNUMBER ',' tUNUMBER {
293 yyMonth = $1;
294 yyDay = $2;
295 yyYear = $4;
296 }
297 | tUNUMBER tMONTH {
298 yyDay = $1;
299 yyMonth = $2;
300 }
301 | tUNUMBER tMONTH tUNUMBER {
302 yyDay = $1;
303 yyMonth = $2;
304 yyYear = $3;
305 }
306 | tDAY ',' tUNUMBER tMONTH tUNUMBER {
307 yyDay = $3;
308 yyMonth = $4;
309 yyYear = $5;
310 }
311 | tDAY ',' tUNUMBER '-' tMONTH tSNUMBER {
312 yyDay = $3;
313 yyMonth = $5;
314 yyYear = -$6;
315 }
316 | tUNUMBER tSNUMBER tSNUMBER {
317 yyDay = $1;
318 yyMonth = -$2;
319 yyYear = -$3;
320 yyDSTmode = DSToff; /* assume midnight if no time given */
321 yyTimezone = 0; /* Lynx assumes GMT for this format */
322 }
323 ;
324
325 both : tDAY tMONTH tUNUMBER tUNUMBER ':' tUNUMBER ':' tUNUMBER tUNUMBER {
326 yyMonth = $2;
327 yyDay = $3;
328 yyYear = $9;
329 yyHour = $4;
330 yyMinutes = $6;
331 yySeconds = $8;
332 }
333 ;
334
335 rel : tSNUMBER tSEC_UNIT {
336 yyRelSeconds += $1 * $2;
337 }
338 | tUNUMBER tSEC_UNIT {
339 yyRelSeconds += $1 * $2;
340 }
341 | tSNUMBER tMONTH_UNIT {
342 yyRelMonth += $1 * $2;
343 }
344 | tUNUMBER tMONTH_UNIT {
345 yyRelMonth += $1 * $2;
346 }
347 ;
348
349 o_merid : /* NULL */ {
350 $$ = MER24;
351 }
352 | tMERIDIAN {
353 $$ = $1;
354 }
355 ;
356
357 %%
358
359
360 /*
361 ** An entry in the lexical lookup table.
362 */
363 /* *INDENT-OFF* */
364 typedef struct _TABLE {
365 const char *name;
366 int type;
367 time_t value;
368 } TABLE;
369
370 /* Month and day table. */
371 static const TABLE MonthDayTable[] = {
372 { "january", tMONTH, 1 },
373 { "february", tMONTH, 2 },
374 { "march", tMONTH, 3 },
375 { "april", tMONTH, 4 },
376 { "may", tMONTH, 5 },
377 { "june", tMONTH, 6 },
378 { "july", tMONTH, 7 },
379 { "august", tMONTH, 8 },
380 { "september", tMONTH, 9 },
381 { "october", tMONTH, 10 },
382 { "november", tMONTH, 11 },
383 { "december", tMONTH, 12 },
384 /* The value of the day isn't used... */
385 { "sunday", tDAY, 0 },
386 { "monday", tDAY, 0 },
387 { "tuesday", tDAY, 0 },
388 { "wednesday", tDAY, 0 },
389 { "thursday", tDAY, 0 },
390 { "friday", tDAY, 0 },
391 { "saturday", tDAY, 0 },
392 };
393
394 /* Time units table. */
395 static const TABLE UnitsTable[] = {
396 { "year", tMONTH_UNIT, 12 },
397 { "month", tMONTH_UNIT, 1 },
398 { "week", tSEC_UNIT, 7 * 24 * 60 * 60 },
399 { "day", tSEC_UNIT, 1 * 24 * 60 * 60 },
400 { "hour", tSEC_UNIT, 60 * 60 },
401 { "minute", tSEC_UNIT, 60 },
402 { "min", tSEC_UNIT, 60 },
403 { "second", tSEC_UNIT, 1 },
404 { "sec", tSEC_UNIT, 1 },
405 };
406
407 /* Timezone table. */
408 static const TABLE TimezoneTable[] = {
409 { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */
410 { "ut", tZONE, HOUR( 0) }, /* Universal */
411 { "utc", tZONE, HOUR( 0) }, /* Universal Coordinated */
412 { "cut", tZONE, HOUR( 0) }, /* Coordinated Universal */
413 { "z", tZONE, HOUR( 0) }, /* Greenwich Mean */
414 { "wet", tZONE, HOUR( 0) }, /* Western European */
415 { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */
416 { "nst", tZONE, HOUR(3)+30 }, /* Newfoundland Standard */
417 { "ndt", tDAYZONE, HOUR(3)+30 }, /* Newfoundland Daylight */
418 { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */
419 { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */
420 { "est", tZONE, HOUR( 5) }, /* Eastern Standard */
421 { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */
422 { "cst", tZONE, HOUR( 6) }, /* Central Standard */
423 { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */
424 { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */
425 { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */
426 { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */
427 { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */
428 { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */
429 { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */
430 { "akst", tZONE, HOUR( 9) }, /* Alaska Standard */
431 { "akdt", tDAYZONE, HOUR( 9) }, /* Alaska Daylight */
432 { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */
433 { "hast", tZONE, HOUR(10) }, /* Hawaii-Aleutian Standard */
434 { "hadt", tDAYZONE, HOUR(10) }, /* Hawaii-Aleutian Daylight */
435 { "ces", tDAYZONE, -HOUR(1) }, /* Central European Summer */
436 { "cest", tDAYZONE, -HOUR(1) }, /* Central European Summer */
437 { "mez", tZONE, -HOUR(1) }, /* Middle European */
438 { "mezt", tDAYZONE, -HOUR(1) }, /* Middle European Summer */
439 { "cet", tZONE, -HOUR(1) }, /* Central European */
440 { "met", tZONE, -HOUR(1) }, /* Middle European */
441 /* Additional aliases for MET / MET DST *************************************/
442 { "mez", tZONE, -HOUR(1) }, /* Middle European */
443 { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */
444 { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */
445 { "mes", tDAYZONE, -HOUR(1) }, /* Middle European Summer */
446 { "mesz", tDAYZONE, -HOUR(1) }, /* Middle European Summer */
447 { "msz", tDAYZONE, -HOUR(1) }, /* Middle European Summer */
448 { "metdst", tDAYZONE, -HOUR(1) }, /* Middle European Summer */
449 /****************************************************************************/
450 { "eet", tZONE, -HOUR(2) }, /* Eastern Europe */
451 { "msk", tZONE, -HOUR(3) }, /* Moscow Winter */
452 { "msd", tDAYZONE, -HOUR(3) }, /* Moscow Summer */
453 { "wast", tZONE, -HOUR(8) }, /* West Australian Standard */
454 { "wadt", tDAYZONE, -HOUR(8) }, /* West Australian Daylight */
455 { "hkt", tZONE, -HOUR(8) }, /* Hong Kong */
456 { "cct", tZONE, -HOUR(8) }, /* China Coast */
457 { "jst", tZONE, -HOUR(9) }, /* Japan Standard */
458 { "kst", tZONE, -HOUR(9) }, /* Korean Standard */
459 { "kdt", tZONE, -HOUR(9) }, /* Korean Daylight */
460 { "cast", tZONE, -(HOUR(9)+30) }, /* Central Australian Standard */
461 { "cadt", tDAYZONE, -(HOUR(9)+30) }, /* Central Australian Daylight */
462 { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */
463 { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */
464 { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */
465 { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */
466
467 /* For completeness we include the following entries. */
468 #if 0
469
470 /* Duplicate names. Either they conflict with a zone listed above
471 * (which is either more likely to be seen or just been in circulation
472 * longer), or they conflict with another zone in this section and
473 * we could not reasonably choose one over the other. */
474 { "fst", tZONE, HOUR( 2) }, /* Fernando De Noronha Standard */
475 { "fdt", tDAYZONE, HOUR( 2) }, /* Fernando De Noronha Daylight */
476 { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */
477 { "est", tZONE, HOUR( 3) }, /* Eastern Standard (Brazil) */
478 { "edt", tDAYZONE, HOUR( 3) }, /* Eastern Daylight (Brazil) */
479 { "wst", tZONE, HOUR( 4) }, /* Western Standard (Brazil) */
480 { "wdt", tDAYZONE, HOUR( 4) }, /* Western Daylight (Brazil) */
481 { "cst", tZONE, HOUR( 5) }, /* Chile Standard */
482 { "cdt", tDAYZONE, HOUR( 5) }, /* Chile Daylight */
483 { "ast", tZONE, HOUR( 5) }, /* Acre Standard */
484 { "adt", tDAYZONE, HOUR( 5) }, /* Acre Daylight */
485 { "cst", tZONE, HOUR( 5) }, /* Cuba Standard */
486 { "cdt", tDAYZONE, HOUR( 5) }, /* Cuba Daylight */
487 { "est", tZONE, HOUR( 6) }, /* Easter Island Standard */
488 { "edt", tDAYZONE, HOUR( 6) }, /* Easter Island Daylight */
489 { "sst", tZONE, HOUR(11) }, /* Samoa Standard */
490 { "ist", tZONE, -HOUR(2) }, /* Israel Standard */
491 { "idt", tDAYZONE, -HOUR(2) }, /* Israel Daylight */
492 { "idt", tDAYZONE, -(HOUR(3)+30) }, /* Iran Daylight */
493 { "ist", tZONE, -(HOUR(3)+30) }, /* Iran Standard */
494 { "cst", tZONE, -HOUR(8) }, /* China Standard */
495 { "cdt", tDAYZONE, -HOUR(8) }, /* China Daylight */
496 { "sst", tZONE, -HOUR(8) }, /* Singapore Standard */
497
498 /* Dubious (e.g., not in Olson's TIMEZONE package) or obsolete. */
499 { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */
500 { "wat", tZONE, -HOUR(1) }, /* West Africa */
501 { "at", tZONE, HOUR( 2) }, /* Azores */
502 { "gst", tZONE, -HOUR(10) }, /* Guam Standard */
503 { "nft", tZONE, HOUR(3)+30 }, /* Newfoundland */
504 { "idlw", tZONE, HOUR(12) }, /* International Date Line West */
505 { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */
506 { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */
507 { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */
508 { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */
509 { "fwt", tZONE, -HOUR(1) }, /* French Winter */
510 { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */
511 { "bt", tZONE, -HOUR(3) }, /* Baghdad */
512 { "it", tZONE, -(HOUR(3)+30) }, /* Iran */
513 { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */
514 { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */
515 { "ist", tZONE, -(HOUR(5)+30) }, /* Indian Standard */
516 { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */
517 { "nst", tZONE, -HOUR(7) }, /* North Sumatra */
518 { "sst", tZONE, -HOUR(7) }, /* South Sumatra */
519 { "jt", tZONE, -(HOUR(7)+30) }, /* Java (3pm in Cronusland!) */
520 { "nzt", tZONE, -HOUR(12) }, /* New Zealand */
521 { "idle", tZONE, -HOUR(12) }, /* International Date Line East */
522 { "cat", tZONE, HOUR(10) }, /* -- expired 1967 */
523 { "nt", tZONE, HOUR(11) }, /* -- expired 1967 */
524 { "ahst", tZONE, HOUR(10) }, /* -- expired 1983 */
525 { "hdt", tDAYZONE, HOUR(10) }, /* -- expired 1986 */
526 #endif /* 0 */
527 };
528 /* *INDENT-ON* */
529
ToSeconds(time_t Hours,time_t Minutes,time_t Seconds,MERIDIAN Meridian)530 static time_t ToSeconds(time_t Hours, time_t Minutes, time_t Seconds, MERIDIAN Meridian)
531 {
532 if ((long) Minutes < 0 || Minutes > 59 || (long) Seconds < 0 || Seconds > 61)
533 return -1;
534 if (Meridian == MER24) {
535 if ((long) Hours < 0 || Hours > 23)
536 return -1;
537 } else {
538 if (Hours < 1 || Hours > 12)
539 return -1;
540 if (Hours == 12)
541 Hours = 0;
542 if (Meridian == MERpm)
543 Hours += 12;
544 }
545 return (Hours * 60L + Minutes) * 60L + Seconds;
546 }
547
Convert(time_t Month,time_t Day,time_t Year,time_t Hours,time_t Minutes,time_t Seconds,MERIDIAN Meridian,DSTMODE dst)548 static time_t Convert(time_t Month, time_t Day, time_t Year, time_t Hours,
549 time_t Minutes, time_t Seconds, MERIDIAN Meridian,
550 DSTMODE dst)
551 {
552 static const int DaysNormal[13] =
553 {
554 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
555 };
556 static const int DaysLeap[13] =
557 {
558 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
559 };
560 static const int LeapYears[] =
561 {
562 1972, 1976, 1980, 1984, 1988, 1992, 1996,
563 2000, 2004, 2008, 2012, 2016, 2020, 2024, 2028, 2032, 2036
564 };
565 const int *yp;
566 const int *mp;
567 int i;
568 time_t Julian;
569 time_t tod;
570
571 if ((long) Year < 0)
572 Year = -Year;
573 if (Year < 70)
574 Year += 2000;
575 if (Year < 100)
576 Year += 1900;
577 if (Year < EPOCH)
578 Year += 100;
579 for (mp = DaysNormal, yp = LeapYears; yp < ENDOF(LeapYears); yp++)
580 if (Year == *yp) {
581 mp = DaysLeap;
582 break;
583 }
584 if (Year < EPOCH || Year > END_OF_TIME
585 || Month < 1 || Month > 12
586 /* NOSTRICT */
587 /* conversion from long may lose accuracy */
588 || Day < 1 || Day > mp[(int) Month]) {
589 return -1;
590 }
591
592 Julian = Day - 1 + (Year - EPOCH) * 365;
593 for (yp = LeapYears; yp < ENDOF(LeapYears); yp++, Julian++) {
594 if (Year <= *yp)
595 break;
596 }
597 for (i = 1; i < Month; i++)
598 Julian += *++mp;
599 Julian *= SECSPERDAY;
600 Julian += yyTimezone * 60L;
601 if ((long) (tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0) {
602 return -1;
603 }
604 Julian += tod;
605 tod = Julian;
606 if (dst == DSTon || (dst == DSTmaybe && localtime(&tod)->tm_isdst))
607 Julian -= DST_OFFSET * 60 * 60;
608 return Julian;
609 }
610
DSTcorrect(time_t Start,time_t Future)611 static time_t DSTcorrect(time_t Start, time_t Future)
612 {
613 time_t StartDay;
614 time_t FutureDay;
615
616 StartDay = (localtime(&Start)->tm_hour + 1) % 24;
617 FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
618 return (Future - Start) + (StartDay - FutureDay) * DST_OFFSET * 60 * 60;
619 }
620
RelativeMonth(time_t Start,time_t RelMonth)621 static time_t RelativeMonth(time_t Start, time_t RelMonth)
622 {
623 struct tm *tm;
624 time_t Month;
625 time_t Year;
626
627 tm = localtime(&Start);
628 Month = 12 * tm->tm_year + tm->tm_mon + RelMonth;
629 Year = Month / 12 + 1900;
630 Month = Month % 12 + 1;
631 return DSTcorrect(Start,
632 Convert(Month, (time_t) tm->tm_mday, Year,
633 (time_t) tm->tm_hour, (time_t) tm->tm_min,
634 (time_t) tm->tm_sec,
635 MER24, DSTmaybe));
636 }
637
LookupWord(char * buff,int length)638 static int LookupWord(char *buff,
639 int length)
640 {
641 char *p;
642 const char *q;
643 const TABLE *tp;
644 int c;
645
646 p = buff;
647 c = p[0];
648
649 /* See if we have an abbreviation for a month. */
650 if (length == 3 || (length == 4 && p[3] == '.')) {
651 for (tp = MonthDayTable; tp < ENDOF(MonthDayTable); tp++) {
652 q = tp->name;
653 if (c == q[0] && p[1] == q[1] && p[2] == q[2]) {
654 yylval.Number = tp->value;
655 return tp->type;
656 }
657 }
658 } else {
659 for (tp = MonthDayTable; tp < ENDOF(MonthDayTable); tp++) {
660 if (c == tp->name[0] && strcmp(p, tp->name) == 0) {
661 yylval.Number = tp->value;
662 return tp->type;
663 }
664 }
665 }
666
667 /* Try for a timezone. */
668 for (tp = TimezoneTable; tp < ENDOF(TimezoneTable); tp++) {
669 if (c == tp->name[0] && p[1] == tp->name[1]
670 && strcmp(p, tp->name) == 0) {
671 yylval.Number = tp->value;
672 return tp->type;
673 }
674 }
675
676 if (strcmp(buff, "dst") == 0)
677 return tDST;
678
679 /* Try the units table. */
680 for (tp = UnitsTable; tp < ENDOF(UnitsTable); tp++) {
681 if (c == tp->name[0] && strcmp(p, tp->name) == 0) {
682 yylval.Number = tp->value;
683 return tp->type;
684 }
685 }
686
687 /* Strip off any plural and try the units table again. */
688 if (--length > 0 && p[length] == 's') {
689 p[length] = '\0';
690 for (tp = UnitsTable; tp < ENDOF(UnitsTable); tp++) {
691 if (c == tp->name[0] && strcmp(p, tp->name) == 0) {
692 p[length] = 's';
693 yylval.Number = tp->value;
694 return tp->type;
695 }
696 }
697 p[length] = 's';
698 }
699 length++;
700
701 /* Drop out any periods. */
702 for (p = buff, q = (PD_STRING) buff; *q; q++) {
703 if (*q != '.')
704 *p++ = *q;
705 }
706 *p = '\0';
707
708 /* Try the meridians. */
709 if (buff[1] == 'm' && buff[2] == '\0') {
710 if (buff[0] == 'a') {
711 yylval.Meridian = MERam;
712 return tMERIDIAN;
713 }
714 if (buff[0] == 'p') {
715 yylval.Meridian = MERpm;
716 return tMERIDIAN;
717 }
718 }
719
720 /* If we saw any periods, try the timezones again. */
721 if (p - buff != length) {
722 c = buff[0];
723 for (p = buff, tp = TimezoneTable; tp < ENDOF(TimezoneTable); tp++) {
724 if (c == tp->name[0] && p[1] == tp->name[1]
725 && strcmp(p, tp->name) == 0) {
726 yylval.Number = tp->value;
727 return tp->type;
728 }
729 }
730 }
731
732 /* Unknown word -- assume GMT timezone. */
733 yylval.Number = 0;
734 return tZONE;
735 }
736
737 /*
738 * This returns characters as-is (the ones that are not part of some token),
739 * and codes greater than 256 (the token values).
740 *
741 * yacc generates tables that may use the character value. In particular,
742 * byacc's yycheck[] table contains integer values for the expected codes from
743 * this function, which (unless byacc is run locally) are ASCII codes.
744 *
745 * The TO_LOCAL() function assumes its input is in ASCII, and the output is
746 * whatever native encoding is used on the machine, e.g., EBCDIC.
747 *
748 * The TO_ASCII() function is the inverse of TO_LOCAL().
749 */
date_lex(void)750 static int date_lex(void)
751 {
752 int c;
753 char *p;
754 char buff[20];
755 int sign;
756 int i;
757 int nesting;
758
759 /* Get first character after the whitespace. */
760 for (;;) {
761 while (CTYPE(isspace, *yyInput))
762 yyInput++;
763 c = *yyInput;
764
765 /* Ignore RFC 822 comments, typically time zone names. */
766 if (c != LPAREN)
767 break;
768 for (nesting = 1;
769 (c = *++yyInput) != RPAREN || --nesting;
770 ) {
771 if (c == LPAREN) {
772 nesting++;
773 } else if (!IS7BIT(c) || c == '\0' || c == '\r'
774 || (c == '\\'
775 && ((c = *++yyInput) == '\0'
776 || !IS7BIT(c)))) {
777 /* Lexical error: bad comment. */
778 return '?';
779 }
780 }
781 yyInput++;
782 }
783
784 /* A number? */
785 if (CTYPE(isdigit, c) || c == '-' || c == '+') {
786 if (c == '-' || c == '+') {
787 sign = c == '-' ? -1 : 1;
788 yyInput++;
789 if (!CTYPE(isdigit, *yyInput)) {
790 /* Return the isolated plus or minus sign. */
791 --yyInput;
792 return *yyInput++;
793 }
794 } else {
795 sign = 0;
796 }
797 for (p = buff;
798 (c = *yyInput++) != '\0' && CTYPE(isdigit, c);
799 ) {
800 if (p < &buff[sizeof buff - 1])
801 *p++ = (char) c;
802 }
803 *p = '\0';
804 i = atoi(buff);
805
806 yyInput--;
807 yylval.Number = sign < 0 ? -i : i;
808 return sign ? tSNUMBER : tUNUMBER;
809 }
810
811 /* A word? */
812 if (CTYPE(isalpha, c)) {
813 for (p = buff;
814 (c = *yyInput++) == '.' || CTYPE(isalpha, c);
815 ) {
816 if (p < &buff[sizeof buff - 1])
817 *p++ = (char) (CTYPE(isupper, c) ? tolower(c) : c);
818 }
819 *p = '\0';
820 yyInput--;
821 return LookupWord(buff, (int) (p - buff));
822 }
823
824 return *yyInput++;
825 }
826
GetTimeInfo(TIMEINFO * Now)827 static int GetTimeInfo(TIMEINFO * Now)
828 {
829 static time_t LastTime;
830 static long LastTzone;
831 struct tm *tm;
832
833 #if defined(HAVE_GETTIMEOFDAY)
834 struct timeval tv;
835 #endif /* defined(HAVE_GETTIMEOFDAY) */
836 #if defined(DONT_HAVE_TM_GMTOFF)
837 struct tm local;
838 struct tm gmt;
839 #endif /* !defined(DONT_HAVE_TM_GMTOFF) */
840
841 /* Get the basic time. */
842 #if defined(HAVE_GETTIMEOFDAY)
843 if (gettimeofday(&tv, (struct timezone *) NULL) == -1)
844 return -1;
845 Now->time = tv.tv_sec;
846 Now->usec = tv.tv_usec;
847 #else
848 /* Can't check for -1 since that might be a time, I guess. */
849 (void) time(&Now->time);
850 Now->usec = 0;
851 #endif /* defined(HAVE_GETTIMEOFDAY) */
852
853 /* Now get the timezone if it's been an hour since the last time. */
854 if (Now->time - LastTime > 60 * 60) {
855 LastTime = Now->time;
856 if ((tm = localtime(&Now->time)) == NULL)
857 return -1;
858 #if defined(DONT_HAVE_TM_GMTOFF)
859 /* To get the timezone, compare localtime with GMT. */
860 local = *tm;
861 if ((tm = gmtime(&Now->time)) == NULL)
862 return -1;
863 gmt = *tm;
864
865 /* Assume we are never more than 24 hours away. */
866 LastTzone = gmt.tm_yday - local.tm_yday;
867 if (LastTzone > 1)
868 LastTzone = -24;
869 else if (LastTzone < -1)
870 LastTzone = 24;
871 else
872 LastTzone *= 24;
873
874 /* Scale in the hours and minutes; ignore seconds. */
875 LastTzone += gmt.tm_hour - local.tm_hour;
876 LastTzone *= 60;
877 LastTzone += gmt.tm_min - local.tm_min;
878 #else
879 LastTzone = (0 - tm->tm_gmtoff) / 60;
880 #endif /* defined(DONT_HAVE_TM_GMTOFF) */
881 }
882 Now->tzone = LastTzone;
883 return 0;
884 }
885
parsedate(char * p,TIMEINFO * now)886 time_t parsedate(char *p,
887 TIMEINFO * now)
888 {
889 struct tm *tm;
890 TIMEINFO ti;
891 time_t Start;
892
893 yyInput = p;
894 if (now == NULL) {
895 now = &ti;
896 (void) GetTimeInfo(&ti);
897 }
898
899 tm = localtime(&now->time);
900 yyYear = tm->tm_year + 1900;
901 yyMonth = tm->tm_mon + 1;
902 yyDay = tm->tm_mday;
903 yyTimezone = now->tzone;
904 if (tm->tm_isdst) /* Correct timezone offset for DST */
905 yyTimezone += DST_OFFSET * 60;
906 yyDSTmode = DSTmaybe;
907 yyHour = 0;
908 yyMinutes = 0;
909 yySeconds = 0;
910 yyMeridian = MER24;
911 yyRelSeconds = 0;
912 yyRelMonth = 0;
913 yyHaveDate = 0;
914 yyHaveRel = 0;
915 yyHaveTime = 0;
916
917 if (date_parse() || yyHaveTime > 1 || yyHaveDate > 1)
918 return -1;
919
920 if (yyHaveDate || yyHaveTime) {
921 Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
922 yyMeridian, yyDSTmode);
923 if ((long) Start < 0)
924 return -1;
925 } else {
926 Start = now->time;
927 if (!yyHaveRel)
928 Start -= (tm->tm_hour * 60L + tm->tm_min) * 60L + tm->tm_sec;
929 }
930
931 Start += yyRelSeconds;
932 if (yyRelMonth)
933 Start += RelativeMonth(Start, yyRelMonth);
934
935 /* Have to do *something* with a legitimate -1 so it's distinguishable
936 * from the error return value. (Alternately could set errno on error.) */
937 return (Start == (time_t) -1) ? 0 : Start;
938 }
939