1 %{
2 #include <time.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <stdio.h>
6 #include "parsetime.h"
7
8 #define YYDEBUG 1
9
10 time_t currtime;
11 struct tm exectm;
12 static int isgmt;
13 static int time_only;
14
15 int add_date(int number, int period);
16 %}
17
18 %union {
19 char * charval;
20 int intval;
21 }
22
23 %token <charval> INT
24 %token NOW
25 %token AM PM
26 %token NOON MIDNIGHT TEATIME
27 %token SUN MON TUE WED THU FRI SAT
28 %token TODAY TOMORROW
29 %token NEXT
30 %token MINUTE HOUR DAY WEEK MONTH YEAR
31 %token JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC
32 %token <charval> WORD
33
34 %type <intval> inc_period
35 %type <intval> inc_number
36 %type <intval> day_of_week
37
38 %start timespec
39 %%
40 timespec : time
41 {
42 time_only = 1;
43 }
44 | time date
45 | time increment
46 | time date increment
47 | time decrement
48 | time date decrement
49 | nowspec
50 ;
51
52 nowspec : now
53 | now increment
54 | now decrement
55 ;
56
57 now : NOW
58 ;
59
60 time : hr24clock_hr_min
61 | hr24clock_hr_min timezone_name
62 | hr24clock_hour time_sep minute
63 | hr24clock_hour time_sep minute timezone_name
64 | hr24clock_hour am_pm
65 | hr24clock_hour am_pm timezone_name
66 | hr24clock_hour time_sep minute am_pm
67 | hr24clock_hour time_sep minute am_pm timezone_name
68 | NOON
69 {
70 exectm.tm_hour = 12;
71 exectm.tm_min = 0;
72 }
73 | MIDNIGHT
74 {
75 exectm.tm_hour = 0;
76 exectm.tm_min = 0;
77 add_date(1, DAY);
78 }
79 | TEATIME
80 {
81 exectm.tm_hour = 16;
82 exectm.tm_min = 0;
83 }
84 ;
85
86 date : month_name day_number
87 | month_name day_number ',' year_number
88 | day_of_week
89 {
90 add_date ((7 + $1 - exectm.tm_wday) %7, DAY);
91 }
92 | TODAY
93 | TOMORROW
94 {
95 add_date(1, DAY);
96 }
97 | year_number '-' month_number '-' day_number
98 | day_number '.' month_number '.' year_number
99 | day_number '.' month_number
100 | day_number month_name
101 | day_number month_name year_number
102 | month_number '/' day_number '/' year_number
103 ;
104
105 increment : '+' inc_number inc_period
106 {
107 add_date($2, $3);
108 }
109 | NEXT inc_period
110 {
111 add_date(1, $2);
112 }
113 | NEXT day_of_week
114 {
115 add_date ((7 + $2 - exectm.tm_wday) %7, DAY);
116 }
117 ;
118
119 decrement : '-' inc_number inc_period
120 {
121 add_date(-$2, $3);
122 }
123 ;
124
125 inc_period : MINUTE { $$ = MINUTE ; }
126 | HOUR { $$ = HOUR ; }
127 | DAY { $$ = DAY ; }
128 | WEEK { $$ = WEEK ; }
129 | MONTH { $$ = MONTH ; }
130 | YEAR { $$ = YEAR ; }
131 ;
132
133 hr24clock_hr_min: INT
134 {
135 exectm.tm_min = -1;
136 exectm.tm_hour = -1;
137 if (strlen($1) == 4) {
138 sscanf($1, "%2d %2d", &exectm.tm_hour,
139 &exectm.tm_min);
140 }
141 else {
142 sscanf($1, "%d", &exectm.tm_hour);
143 exectm.tm_min = 0;
144 }
145 free($1);
146
147 if (exectm.tm_min > 60 || exectm.tm_min < 0) {
148 yyerror("Problem in minutes specification");
149 YYERROR;
150 }
151 if (exectm.tm_hour > 24 || exectm.tm_hour < 0) {
152 yyerror("Problem in minutes specification");
153 YYERROR;
154 }
155 }
156 ;
157
158 timezone_name : WORD
159 {
160 if (strcasecmp($1,"utc") == 0) {
161 isgmt = 1;
162 }
163 else {
164 yyerror("Only UTC timezone is supported");
165 YYERROR;
166 }
167 free($1);
168 }
169 ;
170
171 hr24clock_hour : hr24clock_hr_min
172 ;
173
174 minute : INT
175 {
176 if (sscanf($1, "%d", &exectm.tm_min) != 1) {
177 yyerror("Error in minute");
178 YYERROR;
179 }
180 free($1);
181 }
182 ;
183
184 am_pm : AM
185 | PM
186 {
187 if (exectm.tm_hour > 12) {
188 yyerror("Hour too large for PM");
189 YYERROR;
190 }
191 else if (exectm.tm_hour < 12) {
192 exectm.tm_hour +=12;
193 }
194 }
195 ;
196
197
198 month_name : JAN { exectm.tm_mon = 0; }
199 | FEB { exectm.tm_mon = 1; }
200 | MAR { exectm.tm_mon = 2; }
201 | APR { exectm.tm_mon = 3; }
202 | MAY { exectm.tm_mon = 4; }
203 | JUN { exectm.tm_mon = 5; }
204 | JUL { exectm.tm_mon = 6; }
205 | AUG { exectm.tm_mon = 7; }
206 | SEP { exectm.tm_mon = 8; }
207 | OCT { exectm.tm_mon = 9; }
208 | NOV { exectm.tm_mon =10; }
209 | DEC { exectm.tm_mon =11; }
210 ;
211
212 month_number : INT
213 {
214 {
215 int mnum = -1;
216 sscanf($1, "%d", &mnum);
217
218 if (mnum < 1 || mnum > 12) {
219 yyerror("Error in month number");
220 YYERROR;
221 }
222 exectm.tm_mon = mnum -1;
223 free($1);
224 }
225 }
226 day_number : INT
227 {
228 exectm.tm_mday = -1;
229 sscanf($1, "%d", &exectm.tm_mday);
230 if (exectm.tm_mday < 0 || exectm.tm_mday > 31)
231 {
232 yyerror("Error in day of month");
233 YYERROR;
234 }
235 free($1);
236 }
237 ;
238
239 year_number : INT
240 {
241 {
242 int ynum;
243
244 if ( sscanf($1, "%d", &ynum) != 1) {
245 yyerror("Error in year");
246 YYERROR;
247 }
248 if (ynum < 70) {
249 ynum += 100;
250 }
251 else if (ynum > 1900) {
252 ynum -= 1900;
253 }
254
255 exectm.tm_year = ynum ;
256 free($1);
257 }
258 }
259 ;
260
261
262 day_of_week : SUN { $$ = 0; }
263 | MON { $$ = 1; }
264 | TUE { $$ = 2; }
265 | WED { $$ = 3; }
266 | THU { $$ = 4; }
267 | FRI { $$ = 5; }
268 | SAT { $$ = 6; }
269 ;
270
271 inc_number : INT
272 {
273 if (sscanf($1, "%d", &$$) != 1) {
274 yyerror("Unknown increment");
275 YYERROR;
276 }
277 free($1);
278 }
279 ;
280
281 time_sep : ':'
282 | '\''
283 | '.'
284 | 'h'
285 | ','
286 ;
287
288 %%
289
290
291 time_t parsetime(int, char **);
292
293 time_t
parsetime(int argc,char ** argv)294 parsetime(int argc, char **argv)
295 {
296 time_t exectime;
297 int dl;
298 int gmtoff;
299
300 my_argv = argv;
301 currtime = time(NULL);
302 exectm = *localtime(&currtime);
303 exectm.tm_sec = 0;
304
305 dl = exectm.tm_isdst;
306
307 /* Sigh. Bsd does it one way. Solaris does it another. Linux has it
308 both ways... what's a poor programmer to do?
309 */
310
311 #ifdef HAVE_TM_GMTMOFF
312 gmtoff = exectm.tm_gmtoff;
313 #else
314 #ifdef HAVE_TIMEZONE
315 gtmtoff = timezone;
316 #else
317 gmtoff = 0; /* And utc is broken. */
318 #endif
319 #endif
320
321 exectm.tm_isdst = -1;
322 time_only = 0;
323 if (yyparse() == 0) {
324 exectime = mktime(&exectm);
325
326 /* Note: This code has been rewritten by Tom. */
327 if (isgmt) {
328 exectime += gmtoff;
329
330 if (dl) {
331 exectime -= 3600;
332 }
333 } /* End Rewrite */
334
335 if (time_only && (currtime > exectime)) {
336 exectime += 24*3600;
337 }
338 return exectime;
339 }
340 else {
341 return 0;
342 }
343 }
344
345 #ifdef TEST_PARSER
346 int
main(int argc,char ** argv)347 main(int argc, char **argv)
348 {
349 time_t res;
350 res = parsetime(argc-1, &argv[1]);
351 if (res > 0) {
352 printf("%s",ctime(&res));
353 }
354 else {
355 printf("Ooops...\n");
356 }
357 return 0;
358 }
359
360 #endif
yyerror(char * s)361 int yyerror(char *s)
362 {
363 if (last_token == NULL)
364 last_token = "(empty)";
365 fprintf(stderr,"%s. Last token seen: %s\n",s, last_token);
366 return 0;
367 }
368
369 void
add_seconds(struct tm * tm,long numsec)370 add_seconds(struct tm *tm, long numsec)
371 {
372 time_t timeval;
373 timeval = mktime(tm);
374 timeval += numsec;
375 *tm = *localtime(&timeval);
376 }
377
378 int
add_date(int number,int period)379 add_date(int number, int period)
380 {
381 switch(period) {
382 case MINUTE:
383 add_seconds(&exectm , 60l*number);
384 break;
385
386 case HOUR:
387 add_seconds(&exectm, 3600l * number);
388 break;
389
390 case DAY:
391 add_seconds(&exectm, 24*3600l * number);
392 break;
393
394 case WEEK:
395 add_seconds(&exectm, 7*24*3600l*number);
396 break;
397
398 case MONTH:
399 {
400 int newmonth = exectm.tm_mon + number;
401 number = 0;
402 while (newmonth < 0) {
403 newmonth += 12;
404 number --;
405 }
406 exectm.tm_mon = newmonth % 12;
407 number += newmonth / 12 ;
408 }
409 if (number == 0) {
410 break;
411 }
412 /* fall through */
413
414 case YEAR:
415 exectm.tm_year += number;
416 break;
417
418 default:
419 yyerror("Internal parser error");
420 fprintf(stderr,"Unexpected case %d\n", period);
421 abort();
422 }
423 return 0;
424 }
425