Applied Simon Burge <simonb@thistledown.com.au>'s dtimep.lex patch:
[mmh] / zotnet / tws / dtimep.lex
1 %e 2000
2 %p 5000
3 %n 1000
4 %a 4000
5 %START  Z
6 sun     (sun(day)?)
7 mon     (mon(day)?)
8 tue     (tue(sday)?)
9 wed     (wed(nesday)?)
10 thu     (thu(rsday)?)
11 fri     (fri(day)?)
12 sat     (sat(urday)?)
13
14 DAY     ({sun}|{mon}|{tue}|{wed}|{thu}|{fri}|{sat})
15
16 jan     (jan(uary)?)
17 feb     (feb(ruary)?)
18 mar     (mar(ch)?)
19 apr     (apr(il)?)
20 may     (may)
21 jun     (jun(e)?)
22 jul     (jul(y)?)
23 aug     (aug(ust)?)
24 sep     (sep(tember)?)
25 oct     (oct(ober)?)
26 nov     (nov(ember)?)
27 dec     (dec(ember)?)
28
29 MONTH   ({jan}|{feb}|{mar}|{apr}|{may}|{jun}|{jul}|{aug}|{sep}|{oct}|{nov}|{dec})
30
31 w       ([ \t]*)
32 W       ([ \t]+)
33 D       ([0-9]?[0-9])
34 d       [0-9]
35 %{
36 #include <h/nmh.h>
37 #include <tws.h>
38 #if !defined(HAVE_TM_GMTOFF) && !defined(HAVE_TZSET)
39 # include <sys/timeb.h>
40 #endif
41
42 #if !defined(HAVE_TM_GMTOFF) && defined(HAVE_TZSET)
43 extern int  daylight;
44 extern long timezone;
45 extern char *tzname[];
46 #endif
47
48 /*
49  * Patchable flag that says how to interpret NN/NN/NN dates. When
50  * true, we do it European style: DD/MM/YY. When false, we do it
51  * American style: MM/DD/YY.  Of course, these are all non-RFC822
52  * compliant.
53  */
54 int europeandate = 0;
55
56 /*
57  * Table to convert month names to numeric month.  We use the
58  * fact that the low order 5 bits of the sum of the 2nd & 3rd
59  * characters of the name is a hash with no collisions for the 12
60  * valid month names.  (The mask to 5 bits maps any combination of
61  * upper and lower case into the same hash value).
62  */
63 static int month_map[] = {
64         0,
65         6,      /* 1 - Jul */
66         3,      /* 2 - Apr */
67         5,      /* 3 - Jun */
68         0,
69         10,     /* 5 - Nov */
70         0,
71         1,      /* 7 - Feb */
72         11,     /* 8 - Dec */
73         0,
74         0,
75         0,
76         0,
77         0,
78         0,
79         0,      /*15 - Jan */
80         0,
81         0,
82         0,
83         2,      /*19 - Mar */
84         0,
85         8,      /*21 - Sep */
86         0,
87         9,      /*23 - Oct */
88         0,
89         0,
90         4,      /*26 - May */
91         0,
92         7       /*28 - Aug */
93 };
94 /*
95  * Same trick for day-of-week using the hash function
96  *  (c1 & 7) + (c2 & 4)
97  */
98 static int day_map[] = {
99         0,
100         0,
101         0,
102         6,      /* 3 - Sat */
103         4,      /* 4 - Thu */
104         0,
105         5,      /* 6 - Fri */
106         0,      /* 7 - Sun */
107         2,      /* 8 - Tue */
108         1       /* 9 - Mon */,
109         0,
110         3       /*11 - Wed */
111 };
112 #define SETDAY  { tw.tw_wday= day_map[(cp[0] & 7) + (cp[1] & 4)];\
113                 tw.tw_flags &= ~TW_SDAY; tw.tw_flags |= TW_SEXP;\
114                 cp += 2; }
115 #define SETMONTH { tw.tw_mon = month_map[(cp[0] + cp[1]) & 0x1f]; gotdate++;\
116                  cp += 2;\
117                  SKIPD;}
118 #define CVT1OR2 (i=(*cp++ - '0'), isdigit(*cp)? i*10 + (*cp++ - '0') : i)
119 #define CVT2      ((cp[0] - '0')*10 + (cp[1] - '0'))
120 #define CVT4    ((((cp[0] - '0')*10 + (cp[1] - '0'))*10 + \
121                                       (cp[2] - '0'))*10 + (cp[3] - '0'))
122 #define SKIPD   { while ( !isdigit(*cp++) ) ;  --cp; }
123 #define EXPZONE { tw.tw_flags &= ~TW_SZONE; tw.tw_flags |= TW_SZEXP; }
124 #define ZONE(x) { tw.tw_zone=(x); EXPZONE; }
125 #define ZONED(x) { ZONE(x); tw.tw_flags |= TW_DST; }
126 #define LC(c)   (isupper (c) ? tolower (c) : (c))
127
128 #ifdef DSTXXX
129 # ifdef TIME_WITH_SYS_TIME
130 #  include <sys/time.h>
131 #  include <time.h>
132 # else
133 #  ifdef HAVE_SYS_TIME_H
134 #   include <sys/time.h>
135 #  else
136 #   include <time.h>
137 #  endif
138 # endif
139
140 static void
141 zonehack (struct tws *tw)
142 {
143     register struct tm *tm;
144
145     if (dmktime (tw) == (time_t) -1)
146         return;
147
148     tm = localtime (&tw->tw_clock);
149     if (tm->tm_isdst) {
150         tw->tw_flags |= TW_DST;
151         tw->tw_zone -= 60;
152     }
153 }
154 #endif  /* DSTXXX */
155 %}
156 %%
157 %{
158 struct tws *
159 dparsetime (char *str)
160 {
161         register int i;
162         static struct tws tw;
163         register char *cp;
164         register int gotdate = 0;
165         time_t tclock;
166
167 #ifdef HAVE_TM_GMTOFF
168         struct tm *tm;
169         time_t clock;
170 #else
171 # ifndef HAVE_TZSET
172         struct timeb tb;
173 # endif /* not HAVE_TZSET */
174 #endif /* HAVE_TM_GMTOFF */
175
176         start_cond = 0;
177
178         /* Zero out the struct. */
179         memset( (char *) &tw, 0, sizeof(tw));
180
181         /* Set default time zone. */
182 #ifdef HAVE_TM_GMTOFF
183         time (&clock);
184         tm = localtime(&clock);
185         tw.tw_zone = tm->tm_gmtoff / 60;
186         if (tm->tm_isdst)                       /* if DST is in effect */
187                 tw.tw_zone -= 60;               /* reset to normal offset */
188 #else
189 # ifdef HAVE_TZSET
190         tzset();
191         tw.tw_zone = -(timezone / 60);
192 # else
193         ftime(&tb);
194         tw.tw_zone = -tb.timezone;
195 # endif /* HAVE_TZSET */
196 #endif /* HAVE_TM_GMTOFF */
197
198         while (isspace(*str))
199                 str++;
200         while (1)
201                 switch (cp = str, *cp ? lex_string( &str, start_cond) : 0) {
202
203                 case -1:
204                         if (!gotdate || tw.tw_year == 0)
205                                 return (struct tws *)0;
206                         /* fall through */
207                 case 0:
208                         if (tw.tw_year == 0) {
209                                 /* Set default year. */
210                                 time (&tclock);
211                                 tw.tw_year = localtime(&tclock)->tm_year + 1900;
212                         } else if (tw.tw_year < 100) {
213                                 /* assume no 2-digit years > 1999 */
214                                 tw.tw_year += 1900;
215                         }
216                         return &tw;
217
218 %}
219 {DAY}","?{w}                            SETDAY;
220 "("{DAY}")"(","?)                       {
221                                         cp++;
222                                         SETDAY;
223                                         }
224 {D}(("-"{D}"-")|("/"{D}"/")){D}?{d}{d}{w}       {
225                                         if (europeandate) {
226                                                 /* European: DD/MM/YY */
227                                                 tw.tw_mday = CVT1OR2;
228                                                 cp++;
229                                                 tw.tw_mon  = CVT1OR2 - 1;
230                                         } else {
231                                                 /* American: MM/DD/YY */
232                                                 tw.tw_mon  = CVT1OR2 - 1;
233                                                 cp++;
234                                                 tw.tw_mday = CVT1OR2;
235                                         }
236                                         cp++;
237                                         for (i = 0; isdigit(*cp); )
238                                                 i = i*10 + (*cp++ - '0');
239                                         tw.tw_year = i;
240                                         gotdate++;      /* XXX */
241                                         }
242 {D}("/"|"-"){D}{w}                      {
243                                         if (europeandate) {
244                                                 tw.tw_mday = CVT1OR2; cp++;
245                                                 tw.tw_mon  = CVT1OR2 - 1;
246                                         } else {
247                                                 tw.tw_mon = CVT1OR2 - 1; cp++;
248                                                 tw.tw_mday  = CVT1OR2;
249                                         }
250                                         gotdate++;
251                                         }
252 {D}{w}(-)?{w}{MONTH}{w}(-)?{w}{D}?{d}{d}({W}at)?{w}     {
253                                         tw.tw_mday = CVT1OR2;
254                                         while ( !isalpha(*cp++) )
255                                                 ;
256                                         SETMONTH;
257                                         for (i = 0; isdigit(*cp); )
258                                                 i = i*10 + (*cp++ - '0');
259                                         tw.tw_year = i;
260 #ifdef FIX_NON_Y2K_COMPLIANT_MUA_DATES
261                                         /* handle broken mua's that don't add
262                                            1900, or just use the last two
263                                            digits.  Assume no email before
264                                            1972. */
265                                         if (tw.tw_year < 72)
266                                                 tw.tw_year += 100;
267                                         if (tw.tw_year < 1900)
268                                                 tw.tw_year += 1900;
269 #endif /* FIX_NON_Y2K_COMPLIANT_MUA_DATES */
270                                         }
271 {D}"-"?{MONTH}({W}at)?{w}               {
272                                         tw.tw_mday = CVT1OR2;
273                                         while ( ! isalpha( *cp++ ) )
274                                                 ;
275                                         SETMONTH;
276                                         }
277 {MONTH}{W}{D}","{W}{D}?{d}{d}{w}        {
278                                         cp++;
279                                         SETMONTH;
280                                         tw.tw_mday = CVT1OR2;
281                                         SKIPD;
282                                         for (i = 0; isdigit(*cp); )
283                                                 i = i*10 + (*cp++ - '0');
284                                         tw.tw_year = i;
285                                         }
286 {MONTH}{W}{D}{w}                        {
287                                         cp++;
288                                         SETMONTH;
289                                         tw.tw_mday = CVT1OR2;
290                                         }
291
292 {D}:{D}:{D}{W}19[6-9]{d}                {       /* hack: ctime w/o TZ */
293                                         tw.tw_hour = CVT1OR2; cp++;
294                                         tw.tw_min  = CVT1OR2; cp++;
295                                         tw.tw_sec  = CVT1OR2;
296                                         SKIPD;
297                                         tw.tw_year = CVT4; cp+=4;
298                                         }
299 {D}:{D}:{D}{w}                          {
300                                         tw.tw_hour = CVT1OR2; cp++;
301                                         tw.tw_min  = CVT1OR2; cp++;
302                                         tw.tw_sec  = CVT1OR2;
303                                         BEGIN Z;
304                                         }
305 {D}:{D}{w}                              {
306                                         tw.tw_hour = CVT1OR2; cp++;
307                                         tw.tw_min = CVT1OR2;
308                                         BEGIN Z;
309                                         }
310 {D}:{D}{w}am{w}                         {
311                                         tw.tw_hour = CVT1OR2; cp++;
312                                         if (tw.tw_hour == 12)
313                                                 tw.tw_hour = 0;
314                                         tw.tw_min  = CVT1OR2;
315                                         BEGIN Z;
316                                         }
317 {D}:{D}:{D}{w}am{w}                     {
318                                         tw.tw_hour = CVT1OR2; cp++;
319                                         if (tw.tw_hour == 12)
320                                                 tw.tw_hour = 0;
321                                         tw.tw_min  = CVT1OR2; cp++;
322                                         tw.tw_sec  = CVT1OR2;
323                                         BEGIN Z;
324                                         }
325 {D}:{D}{w}pm{w}                         {
326                                         tw.tw_hour = CVT1OR2; cp++;
327                                         if (tw.tw_hour != 12)
328                                                 tw.tw_hour += 12;
329                                         tw.tw_min  = CVT1OR2;
330                                         BEGIN Z;
331                                         }
332 {D}:{D}:{D}{w}pm{w}                     {
333                                         tw.tw_hour = CVT1OR2; cp++;
334                                         if (tw.tw_hour != 12)
335                                                 tw.tw_hour += 12;
336                                         tw.tw_min  = CVT1OR2; cp++;
337                                         tw.tw_sec  = CVT1OR2;
338                                         BEGIN Z;
339                                         }
340 [0-2]{d}{d}{d}{d}{d}{w}                 {
341                                         tw.tw_hour = CVT2; cp+=2;
342                                         tw.tw_min  = CVT2; cp+=2;
343                                         tw.tw_sec  = CVT2; cp+=2;
344                                         BEGIN Z;
345                                         }
346 19[6-9]{d}{w}                           {
347                                         /*
348                                          * Luckly, 4 digit times in the range
349                                          * 1960-1999 aren't legal as hour
350                                          * and minutes.
351                                          */
352                                         tw.tw_year = CVT4; cp+=4;
353                                         }
354 [0-2]{d}{d}{d}{w}                       {
355                                         if (tw.tw_hour || tw.tw_min 
356                                                             || tw.tw_sec) {
357                                             tw.tw_year = CVT4; cp+=4;
358                                             tw.tw_zone = 0;
359                                         } else {
360                                             tw.tw_hour = CVT2; cp+=2;
361                                             tw.tw_min  = CVT2; cp+=2;
362                                             BEGIN Z;
363                                         }
364                                         }
365 <Z>"-"?ut                               ZONE(0 * 60);
366 <Z>"-"?gmt                              ZONE(0 * 60);
367 <Z>"-"?jst                              ZONE(2 * 60);
368 <Z>"-"?jdt                              ZONED(2 * 60);
369 <Z>"-"?est                              ZONE(-5 * 60);
370 <Z>"-"?edt                              ZONED(-5 * 60);
371 <Z>"-"?cst                              ZONE(-6 * 60);
372 <Z>"-"?cdt                              ZONED(-6 * 60);
373 <Z>"-"?mst                              ZONE(-7 * 60);
374 <Z>"-"?mdt                              ZONED(-7 * 60);
375 <Z>"-"?pst                              ZONE(-8 * 60);
376 <Z>"-"?pdt                              ZONED(-8 * 60);
377 <Z>"-"?nst                              ZONE(-(3 * 60 + 30));
378 <Z>"-"?ast                              ZONE(-4 * 60);
379 <Z>"-"?adt                              ZONED(-4 * 60);
380 <Z>"-"?yst                              ZONE(-9 * 60);
381 <Z>"-"?ydt                              ZONED(-9 * 60);
382 <Z>"-"?hst                              ZONE(-10 * 60);
383 <Z>"-"?hdt                              ZONED(-10 * 60);
384 <Z>"-"?bst                              ZONED(-1 * 60);
385 <Z>[a-i]                                {
386                                         tw.tw_zone = 60 * (('a'-1) - LC(*cp));
387                                         EXPZONE; 
388                                         }
389 <Z>[k-m]                                {
390                                         tw.tw_zone = 60 * ('a' - LC(*cp));
391                                         EXPZONE; 
392                                         }
393 <Z>[n-y]                                {
394                                         tw.tw_zone = 60 * (LC(*cp) - 'm');
395                                         EXPZONE; 
396                                         }
397 <Z>"+"[0-1]{d}{d}{d}                    {
398                                         cp++;
399                                         tw.tw_zone = ((cp[0] * 10 + cp[1])
400                                                      -('0' * 10   + '0'))*60
401                                                     +((cp[2] * 10 + cp[3])
402                                                      -('0' * 10   + '0'));
403                                         EXPZONE;
404 #ifdef  DSTXXX
405                                         zonehack (&tw);
406 #endif  /* DSTXXX */
407                                         cp += 4;
408                                         }
409 <Z>"-"[0-1]{d}{d}{d}                    {
410                                         cp++;
411                                         tw.tw_zone = (('0' * 10   + '0')
412                                                      -(cp[0] * 10 + cp[1]))*60
413                                                     +(('0' * 10   + '0')
414                                                      -(cp[2] * 10 + cp[3]));
415                                         EXPZONE;
416 #ifdef  DSTXXX
417                                         zonehack (&tw);
418 #endif  /* DSTXXX */
419                                         cp += 4;
420                                         }
421 <Z>{W}{d}{d}{d}{d}                      {
422                                         SKIPD;
423                                         tw.tw_year = CVT4; cp+=4;
424                                         }
425 \n      |
426 {W}     ;
427 %%