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