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