* When translating the input from a REGI command into a vCard, set the telephone...
[citadel.git] / citadel / parsedate.y
blobcf87f0e277d594b0667825fbe5bc93bbece333ee
1 %{
2 /* $Revision$
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. Art Cancro <ajc@uncensored.citadel.org> cleaned
11 ** it up for ANSI C in December, 1999.
13 ** This grammar has six shift/reduce conflicts.
15 ** This code is in the public domain and has no copyright.
17 /* SUPPRESS 530 *//* Empty body for statement */
18 /* SUPPRESS 593 on yyerrlab *//* Label was not used */
19 /* SUPPRESS 593 on yynewstate *//* Label was not used */
20 /* SUPPRESS 595 on yypvt *//* Automatic variable may be used before set */
22 #include "sysdep.h"
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <sys/types.h>
27 #include <ctype.h>
29 #if TIME_WITH_SYS_TIME
30 # include <sys/time.h>
31 # include <time.h>
32 #else
33 # if HAVE_SYS_TIME_H
34 # include <sys/time.h>
35 # else
36 # include <time.h>
37 # endif
38 #endif
40 #if HAVE_STRING_H
41 # if !STDC_HEADERS && HAVE_MEMORY_H
42 # include <memory.h>
43 # endif
44 # include <string.h>
45 #endif
46 #if HAVE_STRINGS_H
47 # include <strings.h>
48 #endif
50 #include "parsedate.h"
52 int date_lex(void);
54 #define yyparse date_parse
55 #define yylex date_lex
56 #define yyerror date_error
59 /* See the LeapYears table in Convert. */
60 #define EPOCH 1970
61 #define END_OF_TIME 2038
62 /* Constants for general time calculations. */
63 #define DST_OFFSET 1
64 #define SECSPERDAY (24L * 60L * 60L)
65 /* Readability for TABLE stuff. */
66 #define HOUR(x) (x * 60)
68 #define LPAREN '('
69 #define RPAREN ')'
70 #define IS7BIT(x) ((unsigned int)(x) < 0200)
72 #define SIZEOF(array) ((int)(sizeof array / sizeof array[0]))
73 #define ENDOF(array) (&array[SIZEOF(array)])
77 ** An entry in the lexical lookup table.
79 typedef struct _TABLE {
80 char *name;
81 int type;
82 time_t value;
83 } TABLE;
86 ** Daylight-savings mode: on, off, or not yet known.
88 typedef enum _DSTMODE {
89 DSTon, DSToff, DSTmaybe
90 } DSTMODE;
93 ** Meridian: am, pm, or 24-hour style.
95 typedef enum _MERIDIAN {
96 MERam, MERpm, MER24
97 } MERIDIAN;
101 ** Global variables. We could get rid of most of them by using a yacc
102 ** union, but this is more efficient. (This routine predates the
103 ** yacc %union construct.)
105 static char *yyInput;
106 static DSTMODE yyDSTmode;
107 static int yyHaveDate;
108 static int yyHaveRel;
109 static int yyHaveTime;
110 static time_t yyTimezone;
111 static time_t yyDay;
112 static time_t yyHour;
113 static time_t yyMinutes;
114 static time_t yyMonth;
115 static time_t yySeconds;
116 static time_t yyYear;
117 static MERIDIAN yyMeridian;
118 static time_t yyRelMonth;
119 static time_t yyRelSeconds;
122 static void date_error(char *);
125 %union {
126 time_t Number;
127 enum _MERIDIAN Meridian;
130 %token tDAY tDAYZONE tMERIDIAN tMONTH tMONTH_UNIT tSEC_UNIT tSNUMBER
131 %token tUNUMBER tZONE
133 %type <Number> tDAYZONE tMONTH tMONTH_UNIT tSEC_UNIT
134 %type <Number> tSNUMBER tUNUMBER tZONE numzone zone
135 %type <Meridian> tMERIDIAN o_merid
139 spec : /* NULL */
140 | spec item
143 item : time {
144 yyHaveTime++;
145 #ifdef lint
146 /* I am compulsive about lint natterings... */
147 if (yyHaveTime == -1) {
148 YYERROR;
150 #endif /* lint */
152 | time zone {
153 yyHaveTime++;
154 yyTimezone = $2;
156 | date {
157 yyHaveDate++;
159 | rel {
160 yyHaveRel = 1;
164 time : tUNUMBER o_merid {
165 if ($1 < 100) {
166 yyHour = $1;
167 yyMinutes = 0;
169 else {
170 yyHour = $1 / 100;
171 yyMinutes = $1 % 100;
173 yySeconds = 0;
174 yyMeridian = $2;
176 | tUNUMBER ':' tUNUMBER o_merid {
177 yyHour = $1;
178 yyMinutes = $3;
179 yySeconds = 0;
180 yyMeridian = $4;
182 | tUNUMBER ':' tUNUMBER numzone {
183 yyHour = $1;
184 yyMinutes = $3;
185 yyTimezone = $4;
186 yyMeridian = MER24;
187 yyDSTmode = DSToff;
189 | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
190 yyHour = $1;
191 yyMinutes = $3;
192 yySeconds = $5;
193 yyMeridian = $6;
195 | tUNUMBER ':' tUNUMBER ':' tUNUMBER numzone {
196 yyHour = $1;
197 yyMinutes = $3;
198 yySeconds = $5;
199 yyTimezone = $6;
200 yyMeridian = MER24;
201 yyDSTmode = DSToff;
205 zone : tZONE {
206 $$ = $1;
207 yyDSTmode = DSToff;
209 | tDAYZONE {
210 $$ = $1;
211 yyDSTmode = DSTon;
213 | tZONE numzone {
214 /* Only allow "GMT+300" and "GMT-0800" */
215 if ($1 != 0) {
216 YYABORT;
218 $$ = $2;
219 yyDSTmode = DSToff;
221 | numzone {
222 $$ = $1;
223 yyDSTmode = DSToff;
227 numzone : tSNUMBER {
228 int i;
230 /* Unix and GMT and numeric timezones -- a little confusing. */
231 if ($1 < 0) {
232 /* Don't work with negative modulus. */
233 $1 = -$1;
234 if ($1 > 9999 || (i = $1 % 100) >= 60) {
235 YYABORT;
237 $$ = ($1 / 100) * 60 + i;
239 else {
240 if ($1 > 9999 || (i = $1 % 100) >= 60) {
241 YYABORT;
243 $$ = -(($1 / 100) * 60 + i);
248 date : tUNUMBER '/' tUNUMBER {
249 yyMonth = $1;
250 yyDay = $3;
252 | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
253 if ($1 > 100) {
254 yyYear = $1;
255 yyMonth = $3;
256 yyDay = $5;
258 else {
259 yyMonth = $1;
260 yyDay = $3;
261 yyYear = $5;
264 | tMONTH tUNUMBER {
265 yyMonth = $1;
266 yyDay = $2;
268 | tMONTH tUNUMBER ',' tUNUMBER {
269 yyMonth = $1;
270 yyDay = $2;
271 yyYear = $4;
273 | tUNUMBER tMONTH {
274 yyDay = $1;
275 yyMonth = $2;
277 | tUNUMBER tMONTH tUNUMBER {
278 yyDay = $1;
279 yyMonth = $2;
280 yyYear = $3;
282 | tDAY ',' tUNUMBER tMONTH tUNUMBER {
283 yyDay = $3;
284 yyMonth = $4;
285 yyYear = $5;
289 rel : tSNUMBER tSEC_UNIT {
290 yyRelSeconds += $1 * $2;
292 | tUNUMBER tSEC_UNIT {
293 yyRelSeconds += $1 * $2;
295 | tSNUMBER tMONTH_UNIT {
296 yyRelMonth += $1 * $2;
298 | tUNUMBER tMONTH_UNIT {
299 yyRelMonth += $1 * $2;
303 o_merid : /* NULL */ {
304 $$ = MER24;
306 | tMERIDIAN {
307 $$ = $1;
313 /* Month and day table. */
314 static TABLE MonthDayTable[] = {
315 { "january", tMONTH, 1 },
316 { "february", tMONTH, 2 },
317 { "march", tMONTH, 3 },
318 { "april", tMONTH, 4 },
319 { "may", tMONTH, 5 },
320 { "june", tMONTH, 6 },
321 { "july", tMONTH, 7 },
322 { "august", tMONTH, 8 },
323 { "september", tMONTH, 9 },
324 { "october", tMONTH, 10 },
325 { "november", tMONTH, 11 },
326 { "december", tMONTH, 12 },
327 /* The value of the day isn't used... */
328 { "sunday", tDAY, 0 },
329 { "monday", tDAY, 0 },
330 { "tuesday", tDAY, 0 },
331 { "wednesday", tDAY, 0 },
332 { "thursday", tDAY, 0 },
333 { "friday", tDAY, 0 },
334 { "saturday", tDAY, 0 },
337 /* Time units table. */
338 static TABLE UnitsTable[] = {
339 { "year", tMONTH_UNIT, 12 },
340 { "month", tMONTH_UNIT, 1 },
341 { "week", tSEC_UNIT, 7L * 24 * 60 * 60 },
342 { "day", tSEC_UNIT, 1L * 24 * 60 * 60 },
343 { "hour", tSEC_UNIT, 60 * 60 },
344 { "minute", tSEC_UNIT, 60 },
345 { "min", tSEC_UNIT, 60 },
346 { "second", tSEC_UNIT, 1 },
347 { "sec", tSEC_UNIT, 1 },
350 /* Timezone table. */
351 static TABLE TimezoneTable[] = {
352 { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */
353 { "ut", tZONE, HOUR( 0) }, /* Universal */
354 { "utc", tZONE, HOUR( 0) }, /* Universal Coordinated */
355 { "cut", tZONE, HOUR( 0) }, /* Coordinated Universal */
356 { "z", tZONE, HOUR( 0) }, /* Greenwich Mean */
357 { "wet", tZONE, HOUR( 0) }, /* Western European */
358 { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */
359 { "nst", tZONE, HOUR(3)+30 }, /* Newfoundland Standard */
360 { "ndt", tDAYZONE, HOUR(3)+30 }, /* Newfoundland Daylight */
361 { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */
362 { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */
363 { "est", tZONE, HOUR( 5) }, /* Eastern Standard */
364 { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */
365 { "cst", tZONE, HOUR( 6) }, /* Central Standard */
366 { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */
367 { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */
368 { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */
369 { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */
370 { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */
371 { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */
372 { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */
373 { "akst", tZONE, HOUR( 9) }, /* Alaska Standard */
374 { "akdt", tDAYZONE, HOUR( 9) }, /* Alaska Daylight */
375 { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */
376 { "hast", tZONE, HOUR(10) }, /* Hawaii-Aleutian Standard */
377 { "hadt", tDAYZONE, HOUR(10) }, /* Hawaii-Aleutian Daylight */
378 { "ces", tDAYZONE, -HOUR(1) }, /* Central European Summer */
379 { "cest", tDAYZONE, -HOUR(1) }, /* Central European Summer */
380 { "mez", tZONE, -HOUR(1) }, /* Middle European */
381 { "mezt", tDAYZONE, -HOUR(1) }, /* Middle European Summer */
382 { "cet", tZONE, -HOUR(1) }, /* Central European */
383 { "met", tZONE, -HOUR(1) }, /* Middle European */
384 { "eet", tZONE, -HOUR(2) }, /* Eastern Europe */
385 { "msk", tZONE, -HOUR(3) }, /* Moscow Winter */
386 { "msd", tDAYZONE, -HOUR(3) }, /* Moscow Summer */
387 { "wast", tZONE, -HOUR(8) }, /* West Australian Standard */
388 { "wadt", tDAYZONE, -HOUR(8) }, /* West Australian Daylight */
389 { "hkt", tZONE, -HOUR(8) }, /* Hong Kong */
390 { "cct", tZONE, -HOUR(8) }, /* China Coast */
391 { "jst", tZONE, -HOUR(9) }, /* Japan Standard */
392 { "kst", tZONE, -HOUR(9) }, /* Korean Standard */
393 { "kdt", tZONE, -HOUR(9) }, /* Korean Daylight */
394 { "cast", tZONE, -(HOUR(9)+30) }, /* Central Australian Standard */
395 { "cadt", tDAYZONE, -(HOUR(9)+30) }, /* Central Australian Daylight */
396 { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */
397 { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */
398 { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */
399 { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */
401 /* For completeness we include the following entries. */
402 #if 0
404 /* Duplicate names. Either they conflict with a zone listed above
405 * (which is either more likely to be seen or just been in circulation
406 * longer), or they conflict with another zone in this section and
407 * we could not reasonably choose one over the other. */
408 { "fst", tZONE, HOUR( 2) }, /* Fernando De Noronha Standard */
409 { "fdt", tDAYZONE, HOUR( 2) }, /* Fernando De Noronha Daylight */
410 { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */
411 { "est", tZONE, HOUR( 3) }, /* Eastern Standard (Brazil) */
412 { "edt", tDAYZONE, HOUR( 3) }, /* Eastern Daylight (Brazil) */
413 { "wst", tZONE, HOUR( 4) }, /* Western Standard (Brazil) */
414 { "wdt", tDAYZONE, HOUR( 4) }, /* Western Daylight (Brazil) */
415 { "cst", tZONE, HOUR( 5) }, /* Chile Standard */
416 { "cdt", tDAYZONE, HOUR( 5) }, /* Chile Daylight */
417 { "ast", tZONE, HOUR( 5) }, /* Acre Standard */
418 { "adt", tDAYZONE, HOUR( 5) }, /* Acre Daylight */
419 { "cst", tZONE, HOUR( 5) }, /* Cuba Standard */
420 { "cdt", tDAYZONE, HOUR( 5) }, /* Cuba Daylight */
421 { "est", tZONE, HOUR( 6) }, /* Easter Island Standard */
422 { "edt", tDAYZONE, HOUR( 6) }, /* Easter Island Daylight */
423 { "sst", tZONE, HOUR(11) }, /* Samoa Standard */
424 { "ist", tZONE, -HOUR(2) }, /* Israel Standard */
425 { "idt", tDAYZONE, -HOUR(2) }, /* Israel Daylight */
426 { "idt", tDAYZONE, -(HOUR(3)+30) }, /* Iran Daylight */
427 { "ist", tZONE, -(HOUR(3)+30) }, /* Iran Standard */
428 { "cst", tZONE, -HOUR(8) }, /* China Standard */
429 { "cdt", tDAYZONE, -HOUR(8) }, /* China Daylight */
430 { "sst", tZONE, -HOUR(8) }, /* Singapore Standard */
432 /* Dubious (e.g., not in Olson's TIMEZONE package) or obsolete. */
433 { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */
434 { "wat", tZONE, -HOUR(1) }, /* West Africa */
435 { "at", tZONE, HOUR( 2) }, /* Azores */
436 { "gst", tZONE, -HOUR(10) }, /* Guam Standard */
437 { "nft", tZONE, HOUR(3)+30 }, /* Newfoundland */
438 { "idlw", tZONE, HOUR(12) }, /* International Date Line West */
439 { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */
440 { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */
441 { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */
442 { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */
443 { "fwt", tZONE, -HOUR(1) }, /* French Winter */
444 { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */
445 { "bt", tZONE, -HOUR(3) }, /* Baghdad */
446 { "it", tZONE, -(HOUR(3)+30) }, /* Iran */
447 { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */
448 { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */
449 { "ist", tZONE, -(HOUR(5)+30) }, /* Indian Standard */
450 { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */
451 { "nst", tZONE, -HOUR(7) }, /* North Sumatra */
452 { "sst", tZONE, -HOUR(7) }, /* South Sumatra */
453 { "jt", tZONE, -(HOUR(7)+30) }, /* Java (3pm in Cronusland!) */
454 { "nzt", tZONE, -HOUR(12) }, /* New Zealand */
455 { "idle", tZONE, -HOUR(12) }, /* International Date Line East */
456 { "cat", tZONE, HOUR(10) }, /* -- expired 1967 */
457 { "nt", tZONE, HOUR(11) }, /* -- expired 1967 */
458 { "ahst", tZONE, HOUR(10) }, /* -- expired 1983 */
459 { "hdt", tDAYZONE, HOUR(10) }, /* -- expired 1986 */
460 #endif /* 0 */
464 /* ARGSUSED */
465 static void
466 date_error(char *s)
468 /* NOTREACHED */
472 static time_t
473 ToSeconds(time_t Hours, time_t Minutes, time_t Seconds, MERIDIAN Meridian)
475 if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 61)
476 return -1;
477 if (Meridian == MER24) {
478 if (Hours < 0 || Hours > 23)
479 return -1;
481 else {
482 if (Hours < 1 || Hours > 12)
483 return -1;
484 if (Hours == 12)
485 Hours = 0;
486 if (Meridian == MERpm)
487 Hours += 12;
489 return (Hours * 60L + Minutes) * 60L + Seconds;
493 static time_t
494 Convert(time_t Month, time_t Day, time_t Year,
495 time_t Hours, time_t Minutes, time_t Seconds,
496 MERIDIAN Meridian, DSTMODE dst)
498 static int DaysNormal[13] = {
499 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
501 static int DaysLeap[13] = {
502 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
504 static int LeapYears[] = {
505 1972, 1976, 1980, 1984, 1988, 1992, 1996,
506 2000, 2004, 2008, 2012, 2016, 2020, 2024, 2028, 2032, 2036
508 register int *yp;
509 register int *mp;
510 register time_t Julian;
511 register int i;
512 time_t tod;
514 if (Year < 0)
515 Year = -Year;
516 if (Year < 100)
517 Year += 1900;
518 if (Year < EPOCH)
519 Year += 100;
520 for (mp = DaysNormal, yp = LeapYears; yp < ENDOF(LeapYears); yp++)
521 if (Year == *yp) {
522 mp = DaysLeap;
523 break;
525 if (Year < EPOCH || Year > END_OF_TIME
526 || Month < 1 || Month > 12
527 /* NOSTRICT *//* conversion from long may lose accuracy */
528 || Day < 1 || Day > mp[(int)Month])
529 return -1;
531 Julian = Day - 1 + (Year - EPOCH) * 365;
532 for (yp = LeapYears; yp < ENDOF(LeapYears); yp++, Julian++)
533 if (Year <= *yp)
534 break;
535 for (i = 1; i < Month; i++)
536 Julian += *++mp;
537 Julian *= SECSPERDAY;
538 Julian += yyTimezone * 60L;
539 if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
540 return -1;
541 Julian += tod;
542 tod = Julian;
543 if (dst == DSTon || (dst == DSTmaybe && localtime(&tod)->tm_isdst))
544 Julian -= DST_OFFSET * 60L * 60L;
545 return Julian;
549 static time_t
550 DSTcorrect(time_t Start, time_t Future)
552 time_t StartDay;
553 time_t FutureDay;
555 StartDay = (localtime(&Start)->tm_hour + 1) % 24;
556 FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
557 return (Future - Start) + (StartDay - FutureDay) * DST_OFFSET * 60L * 60L;
561 static time_t
562 RelativeMonth(time_t Start, time_t RelMonth)
564 struct tm *tm;
565 time_t Month;
566 time_t Year;
568 tm = localtime(&Start);
569 Month = 12 * tm->tm_year + tm->tm_mon + RelMonth;
570 Year = Month / 12;
571 Month = Month % 12 + 1;
572 return DSTcorrect(Start,
573 Convert(Month, (time_t)tm->tm_mday, Year,
574 (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
575 MER24, DSTmaybe));
579 static int
580 LookupWord(char *buff, register int length)
582 register char *p;
583 register char *q;
584 register TABLE *tp;
585 register int c;
587 p = buff;
588 c = p[0];
590 /* See if we have an abbreviation for a month. */
591 if (length == 3 || (length == 4 && p[3] == '.'))
592 for (tp = MonthDayTable; tp < ENDOF(MonthDayTable); tp++) {
593 q = tp->name;
594 if (c == q[0] && p[1] == q[1] && p[2] == q[2]) {
595 yylval.Number = tp->value;
596 return tp->type;
599 else
600 for (tp = MonthDayTable; tp < ENDOF(MonthDayTable); tp++)
601 if (c == tp->name[0] && strcmp(p, tp->name) == 0) {
602 yylval.Number = tp->value;
603 return tp->type;
606 /* Try for a timezone. */
607 for (tp = TimezoneTable; tp < ENDOF(TimezoneTable); tp++)
608 if (c == tp->name[0] && p[1] == tp->name[1]
609 && strcmp(p, tp->name) == 0) {
610 yylval.Number = tp->value;
611 return tp->type;
614 /* Try the units table. */
615 for (tp = UnitsTable; tp < ENDOF(UnitsTable); tp++)
616 if (c == tp->name[0] && strcmp(p, tp->name) == 0) {
617 yylval.Number = tp->value;
618 return tp->type;
621 /* Strip off any plural and try the units table again. */
622 if (--length > 0 && p[length] == 's') {
623 p[length] = '\0';
624 for (tp = UnitsTable; tp < ENDOF(UnitsTable); tp++)
625 if (c == tp->name[0] && strcmp(p, tp->name) == 0) {
626 p[length] = 's';
627 yylval.Number = tp->value;
628 return tp->type;
630 p[length] = 's';
632 length++;
634 /* Drop out any periods. */
635 for (p = buff, q = (char*)buff; *q; q++)
636 if (*q != '.')
637 *p++ = *q;
638 *p = '\0';
640 /* Try the meridians. */
641 if (buff[1] == 'm' && buff[2] == '\0') {
642 if (buff[0] == 'a') {
643 yylval.Meridian = MERam;
644 return tMERIDIAN;
646 if (buff[0] == 'p') {
647 yylval.Meridian = MERpm;
648 return tMERIDIAN;
652 /* If we saw any periods, try the timezones again. */
653 if (p - buff != length) {
654 c = buff[0];
655 for (p = buff, tp = TimezoneTable; tp < ENDOF(TimezoneTable); tp++)
656 if (c == tp->name[0] && p[1] == tp->name[1]
657 && strcmp(p, tp->name) == 0) {
658 yylval.Number = tp->value;
659 return tp->type;
663 /* Unknown word -- assume GMT timezone. */
664 yylval.Number = 0;
665 return tZONE;
670 date_lex(void)
672 register char c;
673 register char *p;
674 char buff[20];
675 register int sign;
676 register int i;
677 register int nesting;
679 for ( ; ; ) {
680 /* Get first character after the whitespace. */
681 for ( ; ; ) {
682 while (isspace(*yyInput))
683 yyInput++;
684 c = *yyInput;
686 /* Ignore RFC 822 comments, typically time zone names. */
687 if (c != LPAREN)
688 break;
689 for (nesting = 1; (c = *++yyInput) != RPAREN || --nesting; )
690 if (c == LPAREN)
691 nesting++;
692 else if (!IS7BIT(c) || c == '\0' || c == '\r'
693 || (c == '\\' && ((c = *++yyInput) == '\0' || !IS7BIT(c))))
694 /* Lexical error: bad comment. */
695 return '?';
696 yyInput++;
699 /* A number? */
700 if (isdigit(c) || c == '-' || c == '+') {
701 if (c == '-' || c == '+') {
702 sign = c == '-' ? -1 : 1;
703 yyInput++;
704 if (!isdigit(*yyInput))
705 /* Skip the plus or minus sign. */
706 continue;
708 else
709 sign = 0;
710 for (i = 0; (c = *yyInput++) != '\0' && isdigit(c); )
711 i = 10 * i + c - '0';
712 yyInput--;
713 yylval.Number = sign < 0 ? -i : i;
714 return sign ? tSNUMBER : tUNUMBER;
717 /* A word? */
718 if (isalpha(c)) {
719 for (p = buff; (c = *yyInput++) == '.' || isalpha(c); )
720 if (p < &buff[sizeof buff - 1])
721 *p++ = isupper(c) ? tolower(c) : c;
722 *p = '\0';
723 yyInput--;
724 return LookupWord(buff, p - buff);
727 return *yyInput++;
732 time_t
733 parsedate(char *p)
735 extern int date_parse(void);
736 time_t Start;
738 yyInput = p;
740 yyYear = 0;
741 yyMonth = 0;
742 yyDay = 0;
743 yyTimezone = 0;
744 yyDSTmode = DSTmaybe;
745 yyHour = 0;
746 yyMinutes = 0;
747 yySeconds = 0;
748 yyMeridian = MER24;
749 yyRelSeconds = 0;
750 yyRelMonth = 0;
751 yyHaveDate = 0;
752 yyHaveRel = 0;
753 yyHaveTime = 0;
755 if (date_parse() || yyHaveTime > 1 || yyHaveDate > 1)
756 return -1;
758 if (yyHaveDate || yyHaveTime) {
759 Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
760 yyMeridian, yyDSTmode);
761 if (Start < 0)
762 return -1;
764 else
765 return -1;
767 Start += yyRelSeconds;
768 if (yyRelMonth)
769 Start += RelativeMonth(Start, yyRelMonth);
771 /* Have to do *something* with a legitimate -1 so it's distinguishable
772 * from the error return value. (Alternately could set errno on error.) */
773 return Start == -1 ? 0 : Start;
777 #ifdef TEST
779 #if YYDEBUG
780 extern int yydebug;
781 #endif /* YYDEBUG */
783 /* ARGSUSED */
785 main(int ac, char *av[])
787 char buff[128];
788 time_t d;
790 #if YYDEBUG
791 yydebug = 1;
792 #endif /* YYDEBUG */
794 (void)printf("Enter date, or blank line to exit.\n\t> ");
795 for ( ; ; ) {
796 (void)printf("\t> ");
797 (void)fflush(stdout);
798 if (fgets(buff, sizeof buff, stdin) == NULL || buff[0] == '\n')
799 break;
800 #if YYDEBUG
801 if (strcmp(buff, "yydebug") == 0) {
802 yydebug = !yydebug;
803 printf("yydebug = %s\n", yydebug ? "on" : "off");
804 continue;
806 #endif /* YYDEBUG */
807 d = parsedate(buff, (TIMEINFO *)NULL);
808 if (d == -1)
809 (void)printf("Bad format - couldn't convert.\n");
810 else
811 (void)printf("%s", ctime(&d));
814 exit(0);
815 /* NOTREACHED */
817 #endif /* TEST */