1 /* -*- mode: c -*-
2 | Time-stamp: <2007-01-19 01:52:29 jcs>
3 |
4 | Copyright (C) 2002-2003 Jorg Schuler <jcsjcs at users.sourceforge.net>
5 | Part of the gtkpod project.
6 |
7 | URL: http://gtkpod.sourceforge.net/
8 |
9 | This program is free software; you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation; either version 2 of the License, or
12 | (at your option) any later version.
13 |
14 | This program is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with this program; if not, write to the Free Software
21 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 |
23 | iTunes and iPod are trademarks of Apple
24 |
25 | This product is not supported/written/published by Apple!
26 */
27
28 /* This parser (dp_parse()) will take one date specification (like
29 * 9/6/03, or -5d) and convert it into a timestamp. More details
30 * below. */
31
32 %{
33 #include <stdlib.h>
34 #include "date_parser.h"
35 #include "misc.h"
36 static gchar *dp_strp = NULL;
37 /* time stamp (result!) */
38 static time_t tstamp;
39 /* already parsed time? */
40 static gboolean parsed_time;
41 /* are we reading a lower bound (TRUE) or upper bound (FALSE) */
42 static gboolean lower;
43 /* did an error occur? */
44 static gboolean dp_error;
45 /* should relative dates be read strictly (TRUE: no changing of seconds
46 * to 0 or 59 when minutes are set) or not. It's only set to FALSE
47 * for the "=..." format. */
48 static gboolean rel_strict;
49 typedef enum {
50 DP_SEC,
51 DP_MIN,
52 DP_HOUR,
53 DP_DAY,
54 DP_WEEK,
55 DP_MONTH,
56 DP_YEAR,
57 DP_INF
58 } RelTime;
59 /* Which is the lowest relative time specifier already parsed? */
60 RelTime reltime;
61 static void dp_reltime (gchar *str, gint32 sign);
62 /* We don't read from a stream but from a string buffer. This macro
63 will copy a maximum of @max_size chars from dp_strp to @buf,
64 writing the number of chars copied into @result. If no characters
65 are copied, YY_NULL is written into @result. */
66 #define YY_INPUT(buf,result,max_size) \
67 { \
68 if (!dp_strp || !dp_strp[0]) result = YY_NULL; \
69 else \
70 { \
71 gint i; \
72 for (i=0; (i<max_size && *dp_strp); ++i) buf[i] = *dp_strp++; \
73 result = i; \
74 } \
75 }
76 %}
77
78 /* stop parsing after end of string is reached */
79 %option noyywrap
80 /* avoid compiler warning: `yyunput' defined but not used */
81 %option nounput
82 /* We have several parsers in one executable, so we must use the
83 prefix option to avoid name space clashes. Note that this only
84 works with flex, but not with lex. */
85 %option prefix="lexdp"
86 /* Unfortunately, the prefix option also changes the output filename
87 and thereby breaks the automake script. Therefore I define the
88 output filename here. However, this is not portable -- on other
89 systems the default output filename expected by the automake
90 scripts may be lexyy.c or similar. To make the code portable again
91 (hopefully), I also define "LEX_OUTPUT_ROOT = lex.yy" in
92 Makefile.am */
93 %option outfile="lex.yy.c"
94
95
96 DIGIT [0-9]
97 NUM {DIGIT}+
98 /* definitions for time formats */
99 /* e.g. 8:54 */
100 TIMESHORT {NUM}":"{NUM}
101 /* e.g. 8:54:23 */
102 TIMEFULL {NUM}":"{NUM}":"{NUM}
103 /* e.g. 9/6 (for June 9th) */
104 DATESHORT {NUM}"/"{NUM}
105 /* e.g. 9/6/3 or 9/6/2003 */
106 DATEFULL {NUM}"/"{NUM}"/"{NUM}
107 /* e.g. -8:54 */
108 RELTIME_S [+-]{TIMESHORT}
109 /* e.g. +8:54.23 */
110 RELTIME_F [+-]{TIMEFULL}
111 /* e.g. 5d6m3s */
112 RELTIME ({NUM}[smhdwMy])+
113 /* e.g. -3M5d */
114 SRELTIME [+-]{RELTIME}
115
116 %%
117
118 {TIMESHORT} {
119 gchar *ptr1 = yytext;
120 struct tm *lt = localtime (&tstamp);
121 lt->tm_hour = strtol (ptr1, &ptr1, 10);
122 ++ptr1;
123 lt->tm_min = strtol (ptr1, &ptr1, 10);
124 if (lower) lt->tm_sec = 0;
125 else lt->tm_sec = 59;
126 tstamp = mktime (lt);
127 parsed_time = TRUE;
128 #if DP_DEBUG
129 printf ("Time with minutes: '%s'\n", yytext);
130 printf ("tstamp: %d: %s", (int)tstamp, asctime (lt));
131 #endif
132 }
133
134 {TIMEFULL} {
135 gchar *ptr1 = yytext;
136 struct tm *lt = localtime (&tstamp);
137 lt->tm_hour = strtol (ptr1, &ptr1, 10);
138 ++ptr1;
139 lt->tm_min = strtol (ptr1, &ptr1, 10);
140 ++ptr1;
141 lt->tm_sec = strtol (ptr1, &ptr1, 10);
142 tstamp = mktime (lt);
143 parsed_time = TRUE;
144 #if DP_DEBUG
145 printf ("Time with seconds: '%s'\n", yytext);
146 printf ("tstamp: %d: %s", (int)tstamp, asctime (lt));
147 #endif
148 }
149
150 {DATESHORT} {
151 gchar *ptr1 = yytext;
152 struct tm *lt = localtime (&tstamp);
153 if (!parsed_time)
154 {
155 if (lower)
156 {
157 lt->tm_hour = 0;
158 lt->tm_min = 0;
159 lt->tm_sec = 0;
160 }
161 else
162 {
163 lt->tm_hour = 23;
164 lt->tm_min = 59;
165 lt->tm_sec = 59;
166 }
167 }
168 lt->tm_mday = strtol (ptr1, &ptr1, 10);
169 ++ptr1;
170 lt->tm_mon = strtol (ptr1, &ptr1, 10) - 1;
171 tstamp = mktime (lt);
172 #if DP_DEBUG
173 printf ("Date without year: '%s'\n", yytext);
174 printf ("tstamp: %d: %s", (int)tstamp, asctime (lt));
175 #endif
176 }
177
178 {DATEFULL} {
179 gchar *ptr1 = yytext;
180 struct tm *lt = localtime (&tstamp);
181 if (!parsed_time)
182 {
183 if (lower)
184 {
185 lt->tm_hour = 0;
186 lt->tm_min = 0;
187 lt->tm_sec = 0;
188 }
189 else
190 {
191 lt->tm_hour = 23;
192 lt->tm_min = 59;
193 lt->tm_sec = 59;
194 }
195 }
196 lt->tm_mday = strtol (ptr1, &ptr1, 10);
197 ++ptr1;
198 lt->tm_mon = strtol (ptr1, &ptr1, 10) - 1;
199 ++ptr1;
200 lt->tm_year = strtol (ptr1, &ptr1, 10);
201 if (lt->tm_year < 70)
202 lt->tm_year += 2000;
203 if ((lt->tm_year < 100) && (lt->tm_year >=70))
204 lt->tm_year += 1900;
205 /* tm_year is years since 1900 */
206 lt->tm_year -= 1900;
207 tstamp = mktime (lt);
208 #if DP_DEBUG
209 printf ("Date with year: '%s'\n", yytext);
210 printf ("tstamp: %d: %s", (int)tstamp, asctime (lt));
211 #endif
212 }
213
214 {RELTIME_S} { /* [+-]{TIMESHORT} */
215 gchar *ptr1 = yytext;
216 gint32 hours, mins, sign;
217 if (*ptr1 == '+') sign = 1;
218 else sign = -1;
219 ++ptr1;
220 hours = strtol (ptr1, &ptr1, 10);
221 ++ptr1;
222 mins = strtol (ptr1, &ptr1, 10);
223 tstamp += sign * (hours*3600 + mins*60);
224 if (DP_MIN < reltime) reltime = DP_MIN;
225 #if DP_DEBUG
226 printf ("[+-]{TIMESHORT} '%s'\n", yytext);
227 printf ("tstamp: %d: %s", (int)tstamp, ctime (&tstamp));
228 #endif
229 }
230
231 {RELTIME_F} { /* [+-]{TIMEFULL} */
232 gchar *ptr1 = yytext;
233 gint32 hours, mins, secs, sign;
234 if (*ptr1 == '+') sign = 1;
235 else sign = -1;
236 ++ptr1;
237 hours = strtol (ptr1, &ptr1, 10);
238 ++ptr1;
239 mins = strtol (ptr1, &ptr1, 10);
240 ++ptr1;
241 secs = strtol (ptr1, &ptr1, 10);
242 tstamp += sign * (hours*3600 + mins*60 + secs);
243 reltime = DP_SEC;
244 #if DP_DEBUG
245 printf ("[+-]{TIMEFULL} '%s'\n", yytext);
246 printf ("tstamp: %d: %s", (int)tstamp, ctime (&tstamp));
247 #endif
248 }
249
250 {RELTIME} { /* ({NUM}[smhdwMy])+ */
251 #if DP_DEBUG
252 printf ("RELTIME: '%s'\n", yytext);
253 #endif
254 /* call reltime with negative sign */
255 dp_reltime (yytext, -1);
256 }
257
258 {SRELTIME} { /* [+-]{RELTIME} */
259 #if DP_DEBUG
260 printf ("SRELTIME: '%s'\n", yytext);
261 #endif
262 if (*yytext == '+') dp_reltime (yytext+1, +1);
263 else dp_reltime (yytext+1, -1);
264 }
265
266 [ \t]* /* ignore */
267
268 . {
269 gtkpod_warning (_("Date format error: unrecognized character: '%s'\n"), yytext );
270 dp_error = TRUE;
271 }
272
273 %%
274
275 /* handle ({NUM}[smhdwMy])+, assuming @sign */
276 static void dp_reltime (gchar *str, gint32 sign)
277 {
278 gchar *ptr1 = str;
279 gint32 arg;
280 time_t secs = 0;
281
282 while (*ptr1)
283 {
284 arg = strtol (ptr1, &ptr1, 10);
285 switch (*ptr1)
286 {
287 case 's':
288 secs += arg;
289 reltime = DP_SEC;
290 break;
291 case 'm':
292 secs += 60*arg;
293 if (DP_MIN < reltime) reltime = DP_MIN;
294 break;
295 case 'h':
296 secs += 3600*arg;
297 if (DP_HOUR < reltime) reltime = DP_HOUR;
298 break;
299 case 'd':
300 secs += 24*3600*arg;
301 if (DP_DAY < reltime) reltime = DP_DAY;
302 break;
303 case 'w':
304 secs += 7*24*3600*arg;
305 if (DP_WEEK < reltime) reltime = DP_WEEK;
306 break;
307 case 'M':
308 secs += 30*7*24*3600*arg;
309 if (DP_MONTH < reltime) reltime = DP_MONTH;
310 break;
311 case 'y':
312 secs += 365*7*24*3600*arg;
313 if (DP_YEAR < reltime) reltime = DP_YEAR;
314 break;
315 }
316 ++ptr1;
317 }
318 tstamp += sign*secs;
319 #if DP_DEBUG
320 printf ("secs: %d, tstamp: %d: %s", (int)secs, (int)tstamp, ctime (&tstamp));
321 #endif
322 }
323
324
325 /* after reading the date string check if we should round down the
326 interval (round if reltime is not supposed to be strict and no
327 absolute time string has been parsed */
round_reltime(void)328 static void round_reltime (void)
329 {
330 if (!rel_strict && !parsed_time)
331 { /* Round this datestamp to make a lower/upper margin */
332 struct tm *lt = localtime (&tstamp);
333 switch (reltime)
334 {
335 case DP_INF:
336 break;
337 case DP_YEAR:
338 if (lower) lt->tm_mon = 0;
339 else lt->tm_mon = 11;
340 case DP_MONTH:
341 if (lower) lt->tm_mday = 1;
342 else
343 {
344 switch (lt->tm_mon)
345 {
346 case 1:
347 case 3:
348 case 5:
349 case 7:
350 case 8:
351 case 10:
352 case 12:
353 lt->tm_mday = 31;
354 break;
355 case 2:
356 if ((lt->tm_year % 4) != 0) lt->tm_mday = 28;
357 else
358 {
359 if ((lt->tm_year % 100) != 0) lt->tm_mday = 29;
360 else
361 { /* centuries are only leap years if they
362 * are a multiple of 400 */
363 if (((lt->tm_year+300) % 400) == 0)
364 lt->tm_mday = 29;
365 else lt->tm_mday = 28;
366 }
367 }
368 break;
369 case 4:
370 case 6:
371 case 9:
372 case 11:
373 lt->tm_mday = 30;
374 break;
375 }
376 }
377 case DP_WEEK:
378 if (reltime == DP_WEEK)
379 { /* only do this if week was set manually */
380 /* tm_wday gives us the number of days since Sunday (0
381 to 6). Let's declare Monday to be the start of the
382 week and Sunday the end. */
383 if (lower)
384 { /* Round down to Monday */
385 if (lt->tm_wday == 0) lt->tm_mday -= 6;
386 else lt->tm_mday -= (lt->tm_wday-1);
387 }
388 else
389 { /* Round up to Sunday */
390 if (lt->tm_wday != 0) lt->tm_mday += (7-lt->tm_wday);
391 }
392 }
393 case DP_DAY:
394 if (lower) lt->tm_hour = 0;
395 else lt->tm_hour = 23;
396 case DP_HOUR:
397 if (lower) lt->tm_min = 0;
398 else lt->tm_min = 59;
399 case DP_MIN:
400 if (lower) lt->tm_sec = 0;
401 else lt->tm_sec = 59;
402 case DP_SEC:
403 break;
404 }
405 tstamp = mktime (lt);
406 #if DP_DEBUG
407 printf ("tstamp: %d: %s", (int)tstamp, ctime (&tstamp));
408 #endif
409 }
410 }
411
412 /* dp_parse() will take one date specification (like 9/6/03, or -5d,
413 * for details have a look at the definitions above) and convert it
414 * into a timestamp.
415 *
416 * @dp_str: the string to parse
417 *
418 * @result: the parsed timestamp is written into here
419 *
420 * @lower_margin: if TRUE, absolute time or date values are "rounded
421 * down" (e.g. "9/6/03" would compute to "June 6 2003, 0:00:00"),
422 * otherwise the are rounded up (e.g. "9/6/03" would compute to "June
423 * 6 2003, 23:59:59"). Similarily, "8:05" would either compute to
424 * "8:05:00" or "8:05:59" depending on whether the lower or upper
425 * boundary is to be determined.
426 *
427 * @strict: should relative dates be read strictly (TRUE: no changing
428 * of seconds to 0 or 59 when minutes are set, etc.) or not. Actually,
429 * it's only set to FALSE * for the "=..." format.
430 *
431 * Return value:
432 *
433 * TRUE: no error occurred
434 * FALSE: error occurred
435 */
dp_parse(gchar * dp_str,time_t * result,gboolean lower_margin,gboolean strict)436 gboolean dp_parse (gchar *dp_str, time_t *result,
437 gboolean lower_margin, gboolean strict)
438 {
439 dp_strp = dp_str;
440 /* set timestamp to current time */
441 tstamp = time (NULL);
442 /* did not yet parse any absolute time string */
443 parsed_time = FALSE;
444 /* did not yet parse any relative time string */
445 reltime = DP_INF;
446 /* no error occurred (yet) */
447 dp_error = FALSE;
448 /* set parameters */
449 lower = lower_margin;
450 rel_strict = strict;
451 /* call lexer */
452 yylex ();
453 /* round relative time up or down, if necessary */
454 round_reltime ();
455 /* set the result */
456 if (result) *result = tstamp;
457 /* return the (inverted) error variable */
458 return !dp_error;
459 }
460