Factor trim format function out
[mmh] / sbr / dtimep.lex
1 /*
2 ** dtimep.lex exceeds the default table capacities for some old versions
3 ** of lex (and the minimum defaults as specified by POSIX).  The following
4 ** choices meet or exceed the lex defaults for older SunOS4.x, Solaris,
5 ** HPUX, and AIX.
6 */
7 %e4000
8 %p7000
9 %n2500
10 %a5000
11 %{
12 #include <time.h>
13 #include <ctype.h>
14 #include <h/tws.h>
15
16 /*
17 ** Since we're looking at a string at a time, don't worry about
18 ** wrapping to the next buffer.
19 */
20 #define yywrap() 1
21 #define YY_SKIP_YYWRAP
22
23 #define YY_NO_INPUT
24
25 /*
26 ** This is the tricky thing that makes this function cool.  We
27 ** replace the traditional int yylex(void) declaration with our
28 ** dparsetime() declaration, essentially piggy-backing off the
29 ** utility of the yylex() function and adding what we need to make
30 ** the parsing function useful to us.
31 */
32 #define YY_DECL struct tws *dparsetime(char *lexstr)
33
34 /*
35 ** yyerminate() is called after the input string is matched to
36 ** completion (actually, when the lexer reaches an EOF).  The only
37 ** thing that really needs to be in this macro function is the
38 ** return call, which must be substituted inline into dparsetime.
39 */
40
41 #define yyterminate() (void)yy_delete_buffer(lexhandle); \
42         if(!(tw.tw_flags & TW_SUCC)) { \
43                 return (struct tws *)NULL; \
44         } \
45         if(tw.tw_year < 1970) \
46                 tw.tw_year += 1900; \
47         if(tw.tw_year < 1970) \
48                 tw.tw_year += 100; \
49         return(&tw)
50
51 /*
52 ** Patchable flag that says how to interpret NN/NN/NN dates. When
53 ** true, we do it European style: DD/MM/YY. When false, we do it
54 ** American style: MM/DD/YY.  Of course, these are all non-RFC822
55 ** compliant.
56 */
57 int europeandate = 0;
58
59 /*
60 ** Table to convert month names to numeric month.  We use the
61 ** fact that the low order 5 bits of the sum of the 2nd & 3rd
62 ** characters of the name is a hash with no collisions for the 12
63 ** valid month names.  (The mask to 5 bits maps any combination of
64 ** upper and lower case into the same hash value).
65 */
66 static int month_map[] = {
67         0,
68         6,      /* 1 - Jul */
69         3,      /* 2 - Apr */
70         5,      /* 3 - Jun */
71         0,
72         10,     /* 5 - Nov */
73         0,
74         1,      /* 7 - Feb */
75         11,     /* 8 - Dec */
76         0,
77         0,
78         0,
79         0,
80         0,
81         0,
82         0,      /*15 - Jan */
83         0,
84         0,
85         0,
86         2,      /*19 - Mar */
87         0,
88         8,      /*21 - Sep */
89         0,
90         9,      /*23 - Oct */
91         0,
92         0,
93         4,      /*26 - May */
94         0,
95         7       /*28 - Aug */
96 };
97
98 /*
99 ** Lookup table for day-of-week using the same hash trick as for above
100 ** name-of-month table, but using the first and second character, not
101 ** second and third.
102 **
103 ** Compute index into table using: (day_name[0] & 7) + (day_name[1] & 4)
104 */
105 static int day_map[] = {
106         0,
107         0,
108         0,
109         6,      /* 3 - Sat */
110         4,      /* 4 - Thu */
111         0,
112         5,      /* 6 - Fri */
113         0,      /* 7 - Sun */
114         2,      /* 8 - Tue */
115         1       /* 9 - Mon */,
116         0,
117         3       /*11 - Wed */
118 };
119
120 /*
121 ** The SET* macros will parse for the appropriate field, and leave the
122 ** cp pointer at the first character after the desired field. Be
123 ** careful with variable-length fields or alpha-num mixes.
124 **
125 ** The SKIP* macros skip over characters of a particular class and
126 ** leave cp at the position of the first character that doesn't match
127 ** that class. Correspondingly, SKIPTO* skips until it reaches a
128 ** character of a particular class.
129 */
130
131 #define INIT() { cp = yytext;}
132 #define SETWDAY()  { tw.tw_wday= day_map[(cp[0] & 7) + (cp[1] & 4)]; \
133         tw.tw_flags &= ~TW_SDAY; tw.tw_flags |= TW_SEXP; SKIPA(); }
134 #define SETMON()  { cp++; tw.tw_mon = month_map[(cp[0] + cp[1]) & 0x1f]; \
135         SKIPA(); }
136 #define SETMON_NUM()  { tw.tw_mon = atoi(cp)-1; SKIPD(); }
137 #define SETYEAR()  { tw.tw_year = atoi(cp); SKIPD(); }
138 #define SETDAY()  { tw.tw_mday = atoi(cp); tw.tw_flags |= TW_YES; SKIPD(); }
139 #define SETTIME()  { tw.tw_hour = atoi(cp); cp += 2; SKIPTOD(); \
140         tw.tw_min = atoi(cp); cp += 2; if(*cp == ':') { \
141         tw.tw_sec = atoi(++cp); SKIPD(); } }
142 #define SETZONE(x)  { tw.tw_zone = ((x)/100)*60+(x)%100; \
143         tw.tw_flags |= TW_SZEXP; SKIPD(); }
144 #define SETDST()  { tw.tw_flags |= TW_DST; }
145 #define SKIPD()  { while ( isdigit(*cp++) ) ; --cp; }
146 #define SKIPTOD()  { while ( !isdigit(*cp++) ) ; --cp; }
147 #define SKIPA()  { while ( isalpha(*cp++) ) ; --cp; }
148 #define SKIPTOA()  { while ( !isalpha(*cp++) ) ; --cp; }
149 #define SKIPSP()  { while ( isspace(*cp++) ) ; --cp; }
150 #define SKIPTOSP()  { while ( !isspace(*cp++) ) ; --cp; }
151
152 #ifdef ADJUST_NUMERIC_ONLY_TZ_OFFSETS_WRT_DST
153 # ifdef HAVE_SYS_TIME_H
154 #  include <sys/time.h>
155 # endif
156 #include <time.h>
157
158 static void
159 zonehack (struct tws *tw)
160 {
161         struct tm *tm;
162
163         if (dmktime (tw) == (time_t) -1)
164                 return;
165
166         tm = localtime (&tw->tw_clock);
167         if (tm->tm_isdst) {
168                 tw->tw_flags |= TW_DST;
169                 tw->tw_zone -= 60;
170         }
171 }
172 #endif /* ADJUST_NUMERIC_ONLY_TZ_OFFSETS_WRT_DST */
173 %}
174
175 sun     ([Ss]un(day)?)
176 mon     ([Mm]on(day)?)
177 tue     ([Tt]ue(sday)?)
178 wed     ([Ww]ed(nesday)?)
179 thu     ([Tt]hu(rsday)?)
180 fri     ([Ff]ri(day)?)
181 sat     ([Ss]at(urday)?)
182
183 DAY     ({sun}|{mon}|{tue}|{wed}|{thu}|{fri}|{sat})
184
185 jan     ([Jj]an(uary)?)
186 feb     ([Ff]eb(ruary)?)
187 mar     ([Mm]ar(ch)?)
188 apr     ([Aa]pr(il)?)
189 may     ([Mm]ay)
190 jun     ([Jj]un(e)?)
191 jul     ([Jj]ul(y)?)
192 aug     ([Aa]ug(ust)?)
193 sep     ([Ss]ep(tember)?)
194 oct     ([Oo]ct(ober)?)
195 nov     ([Nn]ov(ember)?)
196 dec     ([Dd]ec(ember)?)
197
198 MONTH   ({jan}|{feb}|{mar}|{apr}|{may}|{jun}|{jul}|{aug}|{sep}|{oct}|{nov}|{dec})
199
200 TIME    ({D}:{d}{d}(:{d}{d})?)
201
202 /*
203 ** The year can either be 2 digits, or 4. However, after
204 ** Y2K, we found that some MUA were reporting the year 100, hence
205 ** the middle term here. yyterminate() resolves the actual
206 ** issues with 2-digit years.
207 */
208
209 YEAR    (({d}{d})|(1{d}{d})|({d}{4}))
210
211 w       ([ \t]*)
212 W       ([ \t]+)
213 D       ([0-9]?[0-9])
214 d       [0-9]
215 nl      [ \t\n()]
216
217 %%
218 %{
219         /*
220         ** This section begins the definition of dparsetime().
221         ** Put here any local variable definitions and initializations
222         */
223         YY_BUFFER_STATE lexhandle;
224
225         unsigned char *cp;
226         static struct tws tw;
227
228         memset(&tw,0,sizeof(struct tws));
229
230         lexhandle = yy_scan_string(lexstr);
231 %}
232
233 {DAY}","?{W}{MONTH}{W}{D}{W}{TIME}{W}{YEAR}  {
234         INIT();
235         SETWDAY();
236         SKIPTOA();
237         SETMON();
238         SKIPTOD();
239         SETDAY();
240         SKIPTOD();
241         SETTIME();
242         SKIPTOD();
243         SETYEAR();
244 }
245
246 {DAY}","?{W}{D}{W}{MONTH}{W}{YEAR}{W}{TIME}  {
247         INIT();
248         SETWDAY();
249         SKIPTOD();
250         SETDAY();
251         SKIPTOA();
252         SETMON();
253         SKIPTOD();
254         SETYEAR();
255         SKIPTOD();
256         SETTIME();
257 }
258 {D}{W}{MONTH}{W}{YEAR}{W}{TIME}  {
259         INIT();
260         SETDAY();
261         SKIPTOA();
262         SETMON();
263         SKIPTOD();
264         SETYEAR();
265         SKIPTOD();
266         SETTIME();
267 }
268 {DAY}","?{W}{MONTH}{W}{D}","?{W}{YEAR}","?{W}{TIME}  {
269         INIT();
270         SETWDAY();
271         SKIPTOA();
272         SETMON();
273         SKIPTOD();
274         SETDAY();
275         SKIPTOD();
276         SETYEAR();
277         SKIPTOD();
278         SETTIME();
279 }
280 {DAY}","?{W}{MONTH}{W}{D}","?{W}{YEAR}  {
281         INIT();
282         SETWDAY();
283         SKIPTOA();
284         SETMON();
285         SKIPTOD();
286         SETDAY();
287         SKIPTOD();
288         SETYEAR();
289 }
290 {MONTH}{W}{D}","?{W}{YEAR}","?{W}{DAY}  {
291         INIT();
292         SETMON();
293         SKIPTOD();
294         SETDAY();
295         SKIPTOD();
296         SETYEAR();
297         SKIPTOA();
298         SETWDAY();
299 }
300 {MONTH}{W}{D}","?{W}{YEAR}  {
301         INIT();
302         SETMON();
303         SKIPTOD();
304         SETDAY();
305         SKIPTOD();
306         SETYEAR();
307 }
308 {D}("-"|"/"){D}("-"|"/"){YEAR}{W}{TIME}  {
309         INIT();
310         if(europeandate) {
311                 /* DD/MM/YY */
312                 SETDAY();
313                 SKIPTOD();
314                 SETMON_NUM();
315         } else {
316                 /* MM/DD/YY */
317                 SETMON_NUM();
318                 SKIPTOD();
319                 SETDAY();
320         }
321         SKIPTOD();
322         SETYEAR();
323         SKIPTOD();
324         SETTIME();
325 }
326 {D}("-"|"/"){D}("-"|"/"){YEAR}  {
327         INIT();
328         if(europeandate) {
329                 /* DD/MM/YY */
330                 SETDAY();
331                 SKIPTOD();
332                 SETMON_NUM();
333         } else {
334                 /* MM/DD/YY */
335                 SETMON_NUM();
336                 SKIPTOD();
337                 SETDAY();
338         }
339         SKIPTOD();
340         SETYEAR();
341 }
342
343 "[Aa][Mm]"
344 "[Pp][Mm]"  tw.tw_hour += 12;
345
346 "+"{D}{d}{d}  {
347         INIT();
348         SKIPTOD();
349         SETZONE(atoi(cp));
350 #ifdef ADJUST_NUMERIC_ONLY_TZ_OFFSETS_WRT_DST
351         zonehack (&tw);
352 #endif /* ADJUST_NUMERIC_ONLY_TZ_OFFSETS_WRT_DST */
353         yyterminate();
354 }
355 "-"{D}{d}{d}  {
356         INIT();
357         SKIPTOD();
358         SETZONE(-atoi(cp));
359 #ifdef ADJUST_NUMERIC_ONLY_TZ_OFFSETS_WRT_DST
360         zonehack (&tw);
361 #endif /* ADJUST_NUMERIC_ONLY_TZ_OFFSETS_WRT_DST */
362         yyterminate();
363
364 }
365 {nl}("ut"|"UT")         INIT(); SETZONE(0); yyterminate();
366 {nl}("gmt"|"GMT")       INIT(); SETZONE(0); yyterminate();
367 {nl}("est"|"EST")       INIT(); SETZONE(-500); yyterminate();
368 {nl}("edt"|"EDT")       { INIT(); SETDST(); SETZONE(-500); yyterminate(); }
369 {nl}("cst"|"CST")       INIT(); SETZONE(-600); yyterminate();
370 {nl}("cdt"|"CDT")       { INIT(); SETDST(); SETZONE(-600); yyterminate(); }
371 {nl}("mst"|"MST")       INIT(); SETZONE(-700); yyterminate();
372 {nl}("mdt"|"MDT")       { INIT(); SETDST(); SETZONE(-700); yyterminate(); }
373 {nl}("pst"|"PST")       INIT(); SETZONE(-800); yyterminate();
374 {nl}("pdt"|"PDT")       { INIT(); SETDST(); SETZONE(-800); yyterminate(); }
375 {nl}("nst"|"NST")       INIT(); SETZONE(-330); yyterminate();
376 {nl}("ast"|"AST")       INIT(); SETZONE(-400); yyterminate();
377 {nl}("adt"|"ADT")       { INIT(); SETDST(); SETZONE(-400); yyterminate(); }
378 {nl}("hst"|"HST")       INIT(); SETZONE(-1000); yyterminate();
379 {nl}("hdt"|"HDT")       { INIT(); SETDST(); SETZONE(-1000); yyterminate(); }
380 .|\n
381
382 %%
383 /*
384 ** This is a portable way to squash a warning about the yyunput()
385 ** function being static but never used. It costs us a tiny amount
386 ** of extra code in the binary but the other options are:
387 ** "%option nounput" which is flex-specific
388 ** makefile hackery just to compile dtimep.c with different flags
389 */
390 void dtimep_yyunput(int c)
391 {
392         unput(c);
393 }