Replace lookup tables for obscure hashes with more readable code
[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 static char *monthnames[] = {
60         "Jan", "Feb", "Mar", "Apr", "May", "Jun",
61         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
62         NULL
63 };
64
65 static char *daynames[] = {
66         "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL
67 };
68
69 static int
70 name2num(char *name, char *names[])
71 {
72         int i;
73
74         for (i=0; names[i]; i++) {
75                 if (strncasecmp(name, names[i], strlen(names[i]))==0) {
76                         return i;
77                 }
78         }
79         return 0;
80 }
81
82 /*
83 ** The SET* macros will parse for the appropriate field, and leave the
84 ** cp pointer at the first character after the desired field. Be
85 ** careful with variable-length fields or alpha-num mixes.
86 **
87 ** The SKIP* macros skip over characters of a particular class and
88 ** leave cp at the position of the first character that doesn't match
89 ** that class. Correspondingly, SKIPTO* skips until it reaches a
90 ** character of a particular class.
91 */
92
93 #define INIT() { cp = yytext;}
94 #define SETWDAY()  { tw.tw_wday = name2num(cp, daynames); \
95         tw.tw_flags &= ~TW_SDAY; tw.tw_flags |= TW_SEXP; SKIPA(); }
96 #define SETMON()  { tw.tw_mon = name2num(cp, monthnames); SKIPA(); }
97 #define SETMON_NUM()  { tw.tw_mon = atoi(cp)-1; SKIPD(); }
98 #define SETYEAR()  { tw.tw_year = atoi(cp); SKIPD(); }
99 #define SETDAY()  { tw.tw_mday = atoi(cp); tw.tw_flags |= TW_YES; SKIPD(); }
100 #define SETTIME()  { tw.tw_hour = atoi(cp); cp += 2; SKIPTOD(); \
101         tw.tw_min = atoi(cp); cp += 2; if(*cp == ':') { \
102         tw.tw_sec = atoi(++cp); SKIPD(); } }
103 #define SETZONE(x)  { tw.tw_zone = ((x)/100)*60+(x)%100; \
104         tw.tw_flags |= TW_SZEXP; SKIPD(); }
105 #define SETDST()  { tw.tw_flags |= TW_DST; }
106 #define SKIPD()  { while ( isdigit(*cp++) ) ; --cp; }
107 #define SKIPTOD()  { while ( !isdigit(*cp++) ) ; --cp; }
108 #define SKIPA()  { while ( isalpha(*cp++) ) ; --cp; }
109 #define SKIPTOA()  { while ( !isalpha(*cp++) ) ; --cp; }
110 #define SKIPSP()  { while ( isspace(*cp++) ) ; --cp; }
111 #define SKIPTOSP()  { while ( !isspace(*cp++) ) ; --cp; }
112
113 #ifdef ADJUST_NUMERIC_ONLY_TZ_OFFSETS_WRT_DST
114 # ifdef HAVE_SYS_TIME_H
115 #  include <sys/time.h>
116 # endif
117 #include <time.h>
118
119 static void
120 zonehack (struct tws *tw)
121 {
122         struct tm *tm;
123
124         if (dmktime (tw) == (time_t) -1)
125                 return;
126
127         tm = localtime (&tw->tw_clock);
128         if (tm->tm_isdst) {
129                 tw->tw_flags |= TW_DST;
130                 tw->tw_zone -= 60;
131         }
132 }
133 #endif /* ADJUST_NUMERIC_ONLY_TZ_OFFSETS_WRT_DST */
134 %}
135
136 sun     ([Ss]un(day)?)
137 mon     ([Mm]on(day)?)
138 tue     ([Tt]ue(sday)?)
139 wed     ([Ww]ed(nesday)?)
140 thu     ([Tt]hu(rsday)?)
141 fri     ([Ff]ri(day)?)
142 sat     ([Ss]at(urday)?)
143
144 DAY     ({sun}|{mon}|{tue}|{wed}|{thu}|{fri}|{sat})
145
146 jan     ([Jj]an(uary)?)
147 feb     ([Ff]eb(ruary)?)
148 mar     ([Mm]ar(ch)?)
149 apr     ([Aa]pr(il)?)
150 may     ([Mm]ay)
151 jun     ([Jj]un(e)?)
152 jul     ([Jj]ul(y)?)
153 aug     ([Aa]ug(ust)?)
154 sep     ([Ss]ep(tember)?)
155 oct     ([Oo]ct(ober)?)
156 nov     ([Nn]ov(ember)?)
157 dec     ([Dd]ec(ember)?)
158
159 MONTH   ({jan}|{feb}|{mar}|{apr}|{may}|{jun}|{jul}|{aug}|{sep}|{oct}|{nov}|{dec})
160
161 TIME    ({D}:{d}{d}(:{d}{d})?)
162
163 /*
164 ** The year can either be 2 digits, or 4. However, after
165 ** Y2K, we found that some MUA were reporting the year 100, hence
166 ** the middle term here. yyterminate() resolves the actual
167 ** issues with 2-digit years.
168 */
169
170 YEAR    (({d}{d})|(1{d}{d})|({d}{4}))
171
172 w       ([ \t]*)
173 W       ([ \t]+)
174 D       ([0-9]?[0-9])
175 d       [0-9]
176 nl      [ \t\n()]
177
178 %%
179 %{
180         /*
181         ** This section begins the definition of dparsetime().
182         ** Put here any local variable definitions and initializations
183         */
184         YY_BUFFER_STATE lexhandle;
185
186         unsigned char *cp;
187         static struct tws tw;
188
189         memset(&tw,0,sizeof(struct tws));
190
191         lexhandle = yy_scan_string(lexstr);
192 %}
193
194 {DAY}","?{W}{MONTH}{W}{D}{W}{TIME}{W}{YEAR}  {
195         INIT();
196         SETWDAY();
197         SKIPTOA();
198         SETMON();
199         SKIPTOD();
200         SETDAY();
201         SKIPTOD();
202         SETTIME();
203         SKIPTOD();
204         SETYEAR();
205 }
206
207 {DAY}","?{W}{D}{W}{MONTH}{W}{YEAR}{W}{TIME}  {
208         INIT();
209         SETWDAY();
210         SKIPTOD();
211         SETDAY();
212         SKIPTOA();
213         SETMON();
214         SKIPTOD();
215         SETYEAR();
216         SKIPTOD();
217         SETTIME();
218 }
219 {D}{W}{MONTH}{W}{YEAR}{W}{TIME}  {
220         INIT();
221         SETDAY();
222         SKIPTOA();
223         SETMON();
224         SKIPTOD();
225         SETYEAR();
226         SKIPTOD();
227         SETTIME();
228 }
229 {DAY}","?{W}{MONTH}{W}{D}","?{W}{YEAR}","?{W}{TIME}  {
230         INIT();
231         SETWDAY();
232         SKIPTOA();
233         SETMON();
234         SKIPTOD();
235         SETDAY();
236         SKIPTOD();
237         SETYEAR();
238         SKIPTOD();
239         SETTIME();
240 }
241 {DAY}","?{W}{MONTH}{W}{D}","?{W}{YEAR}  {
242         INIT();
243         SETWDAY();
244         SKIPTOA();
245         SETMON();
246         SKIPTOD();
247         SETDAY();
248         SKIPTOD();
249         SETYEAR();
250 }
251 {MONTH}{W}{D}","?{W}{YEAR}","?{W}{DAY}  {
252         INIT();
253         SETMON();
254         SKIPTOD();
255         SETDAY();
256         SKIPTOD();
257         SETYEAR();
258         SKIPTOA();
259         SETWDAY();
260 }
261 {MONTH}{W}{D}","?{W}{YEAR}  {
262         INIT();
263         SETMON();
264         SKIPTOD();
265         SETDAY();
266         SKIPTOD();
267         SETYEAR();
268 }
269 {D}("-"|"/"){D}("-"|"/"){YEAR}{W}{TIME}  {
270         INIT();
271         if(europeandate) {
272                 /* DD/MM/YY */
273                 SETDAY();
274                 SKIPTOD();
275                 SETMON_NUM();
276         } else {
277                 /* MM/DD/YY */
278                 SETMON_NUM();
279                 SKIPTOD();
280                 SETDAY();
281         }
282         SKIPTOD();
283         SETYEAR();
284         SKIPTOD();
285         SETTIME();
286 }
287 {D}("-"|"/"){D}("-"|"/"){YEAR}  {
288         INIT();
289         if(europeandate) {
290                 /* DD/MM/YY */
291                 SETDAY();
292                 SKIPTOD();
293                 SETMON_NUM();
294         } else {
295                 /* MM/DD/YY */
296                 SETMON_NUM();
297                 SKIPTOD();
298                 SETDAY();
299         }
300         SKIPTOD();
301         SETYEAR();
302 }
303
304 "[Aa][Mm]"
305 "[Pp][Mm]"  tw.tw_hour += 12;
306
307 "+"{D}{d}{d}  {
308         INIT();
309         SKIPTOD();
310         SETZONE(atoi(cp));
311 #ifdef ADJUST_NUMERIC_ONLY_TZ_OFFSETS_WRT_DST
312         zonehack (&tw);
313 #endif /* ADJUST_NUMERIC_ONLY_TZ_OFFSETS_WRT_DST */
314         yyterminate();
315 }
316 "-"{D}{d}{d}  {
317         INIT();
318         SKIPTOD();
319         SETZONE(-atoi(cp));
320 #ifdef ADJUST_NUMERIC_ONLY_TZ_OFFSETS_WRT_DST
321         zonehack (&tw);
322 #endif /* ADJUST_NUMERIC_ONLY_TZ_OFFSETS_WRT_DST */
323         yyterminate();
324
325 }
326 {nl}("ut"|"UT")         INIT(); SETZONE(0); yyterminate();
327 {nl}("gmt"|"GMT")       INIT(); SETZONE(0); yyterminate();
328 {nl}("est"|"EST")       INIT(); SETZONE(-500); yyterminate();
329 {nl}("edt"|"EDT")       { INIT(); SETDST(); SETZONE(-500); yyterminate(); }
330 {nl}("cst"|"CST")       INIT(); SETZONE(-600); yyterminate();
331 {nl}("cdt"|"CDT")       { INIT(); SETDST(); SETZONE(-600); yyterminate(); }
332 {nl}("mst"|"MST")       INIT(); SETZONE(-700); yyterminate();
333 {nl}("mdt"|"MDT")       { INIT(); SETDST(); SETZONE(-700); yyterminate(); }
334 {nl}("pst"|"PST")       INIT(); SETZONE(-800); yyterminate();
335 {nl}("pdt"|"PDT")       { INIT(); SETDST(); SETZONE(-800); yyterminate(); }
336 {nl}("nst"|"NST")       INIT(); SETZONE(-330); yyterminate();
337 {nl}("ast"|"AST")       INIT(); SETZONE(-400); yyterminate();
338 {nl}("adt"|"ADT")       { INIT(); SETDST(); SETZONE(-400); yyterminate(); }
339 {nl}("hst"|"HST")       INIT(); SETZONE(-1000); yyterminate();
340 {nl}("hdt"|"HDT")       { INIT(); SETDST(); SETZONE(-1000); yyterminate(); }
341 .|\n
342
343 %%
344 /*
345 ** This is a portable way to squash a warning about the yyunput()
346 ** function being static but never used. It costs us a tiny amount
347 ** of extra code in the binary but the other options are:
348 ** "%option nounput" which is flex-specific
349 ** makefile hackery just to compile dtimep.c with different flags
350 */
351 void dtimep_yyunput(int c)
352 {
353         unput(c);
354 }