/[pkg-mixmaster]/trunk/Mix/Src/parsedate.y
ViewVC logotype

Contents of /trunk/Mix/Src/parsedate.y

Parent Directory Parent Directory | Revision Log Revision Log


Revision 647 - (hide annotations) (download)
Sat Oct 25 23:34:13 2003 UTC (9 years, 7 months ago) by weasel
File size: 23864 byte(s)
Set keyword expansion for Id on all files that already have $Id$ tags.
1 weaselp 477 %{
2 weasel 647 /* $Id$
3 weaselp 477 **
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) */

Properties

Name Value
svn:keywords Id

  ViewVC Help
Powered by ViewVC 1.1.5