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