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