This source file includes following definitions.
- str_const_to_ul
- str_const_to_l
- scale_n_add
- parse_hr_min_sec
- parse_scaled_value
- parse_year_month_day
- parse_yearmonthday
- parse_YMWD
- parse_hour_minute_second
- parse_hourminutesecond
- parse_HMS
- parse_time
- trim
- parse_period
- parse_non_iso8601
- parse_duration
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 #include <config.h>
19
20
21 #include "parse-duration.h"
22
23 #include <ctype.h>
24 #include <errno.h>
25 #include <limits.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 #include "intprops.h"
31
32 #ifndef NUL
33 #define NUL '\0'
34 #endif
35
36 #define cch_t char const
37
38 typedef enum {
39 NOTHING_IS_DONE,
40 YEAR_IS_DONE,
41 MONTH_IS_DONE,
42 WEEK_IS_DONE,
43 DAY_IS_DONE,
44 HOUR_IS_DONE,
45 MINUTE_IS_DONE,
46 SECOND_IS_DONE
47 } whats_done_t;
48
49 #define SEC_PER_MIN 60
50 #define SEC_PER_HR (SEC_PER_MIN * 60)
51 #define SEC_PER_DAY (SEC_PER_HR * 24)
52 #define SEC_PER_WEEK (SEC_PER_DAY * 7)
53 #define SEC_PER_MONTH (SEC_PER_DAY * 30)
54 #define SEC_PER_YEAR (SEC_PER_DAY * 365)
55
56 #undef MAX_DURATION
57 #define MAX_DURATION TYPE_MAXIMUM(time_t)
58
59
60 static unsigned long
61 str_const_to_ul (cch_t * str, cch_t ** ppz, int base)
62 {
63 return strtoul (str, (char **)ppz, base);
64 }
65
66
67 static long
68 str_const_to_l (cch_t * str, cch_t ** ppz, int base)
69 {
70 return strtol (str, (char **)ppz, base);
71 }
72
73
74
75
76 static time_t
77 scale_n_add (time_t base, time_t val, int scale)
78 {
79 if (base == BAD_TIME)
80 {
81 if (errno == 0)
82 errno = EINVAL;
83 return BAD_TIME;
84 }
85
86 if (val > MAX_DURATION / scale)
87 {
88 errno = ERANGE;
89 return BAD_TIME;
90 }
91
92 val *= scale;
93 if (base > MAX_DURATION - val)
94 {
95 errno = ERANGE;
96 return BAD_TIME;
97 }
98
99 return base + val;
100 }
101
102
103 static time_t
104 parse_hr_min_sec (time_t start, cch_t * pz)
105 {
106 int lpct = 0;
107
108 errno = 0;
109
110
111
112 while ((*pz == ':') && (lpct++ <= 1))
113 {
114 unsigned long v = str_const_to_ul (pz+1, &pz, 10);
115
116 if (errno != 0)
117 return BAD_TIME;
118
119 start = scale_n_add (v, start, 60);
120
121 if (errno != 0)
122 return BAD_TIME;
123 }
124
125
126 while (isspace ((unsigned char)*pz))
127 pz++;
128 if (*pz != NUL)
129 {
130 errno = EINVAL;
131 return BAD_TIME;
132 }
133
134 return start;
135 }
136
137
138
139
140 static time_t
141 parse_scaled_value (time_t base, cch_t ** ppz, cch_t * endp, int scale)
142 {
143 cch_t * pz = *ppz;
144 time_t val;
145
146 if (base == BAD_TIME)
147 return base;
148
149 errno = 0;
150 val = str_const_to_ul (pz, &pz, 10);
151 if (errno != 0)
152 return BAD_TIME;
153 while (isspace ((unsigned char)*pz))
154 pz++;
155 if (pz != endp)
156 {
157 errno = EINVAL;
158 return BAD_TIME;
159 }
160
161 *ppz = pz;
162 return scale_n_add (base, val, scale);
163 }
164
165
166
167 static time_t
168 parse_year_month_day (cch_t * pz, cch_t * ps)
169 {
170 time_t res = 0;
171
172 res = parse_scaled_value (0, &pz, ps, SEC_PER_YEAR);
173
174 pz++;
175 ps = strchr (pz, '-');
176 if (ps == NULL)
177 {
178 errno = EINVAL;
179 return BAD_TIME;
180 }
181 res = parse_scaled_value (res, &pz, ps, SEC_PER_MONTH);
182
183 pz++;
184 ps = pz + strlen (pz);
185 return parse_scaled_value (res, &pz, ps, SEC_PER_DAY);
186 }
187
188
189 static time_t
190 parse_yearmonthday (cch_t * in_pz)
191 {
192 time_t res = 0;
193 char buf[8];
194 cch_t * pz;
195
196 if (strlen (in_pz) != 8)
197 {
198 errno = EINVAL;
199 return BAD_TIME;
200 }
201
202 memcpy (buf, in_pz, 4);
203 buf[4] = NUL;
204 pz = buf;
205 res = parse_scaled_value (0, &pz, buf + 4, SEC_PER_YEAR);
206
207 memcpy (buf, in_pz + 4, 2);
208 buf[2] = NUL;
209 pz = buf;
210 res = parse_scaled_value (res, &pz, buf + 2, SEC_PER_MONTH);
211
212 memcpy (buf, in_pz + 6, 2);
213 buf[2] = NUL;
214 pz = buf;
215 return parse_scaled_value (res, &pz, buf + 2, SEC_PER_DAY);
216 }
217
218
219 static time_t
220 parse_YMWD (cch_t * pz)
221 {
222 time_t res = 0;
223 cch_t * ps = strchr (pz, 'Y');
224 if (ps != NULL)
225 {
226 res = parse_scaled_value (0, &pz, ps, SEC_PER_YEAR);
227 pz++;
228 }
229
230 ps = strchr (pz, 'M');
231 if (ps != NULL)
232 {
233 res = parse_scaled_value (res, &pz, ps, SEC_PER_MONTH);
234 pz++;
235 }
236
237 ps = strchr (pz, 'W');
238 if (ps != NULL)
239 {
240 res = parse_scaled_value (res, &pz, ps, SEC_PER_WEEK);
241 pz++;
242 }
243
244 ps = strchr (pz, 'D');
245 if (ps != NULL)
246 {
247 res = parse_scaled_value (res, &pz, ps, SEC_PER_DAY);
248 pz++;
249 }
250
251 while (isspace ((unsigned char)*pz))
252 pz++;
253 if (*pz != NUL)
254 {
255 errno = EINVAL;
256 return BAD_TIME;
257 }
258
259 return res;
260 }
261
262
263
264 static time_t
265 parse_hour_minute_second (cch_t * pz, cch_t * ps)
266 {
267 time_t res = 0;
268
269 res = parse_scaled_value (0, &pz, ps, SEC_PER_HR);
270
271 pz++;
272 ps = strchr (pz, ':');
273 if (ps == NULL)
274 {
275 errno = EINVAL;
276 return BAD_TIME;
277 }
278
279 res = parse_scaled_value (res, &pz, ps, SEC_PER_MIN);
280
281 pz++;
282 ps = pz + strlen (pz);
283 return parse_scaled_value (res, &pz, ps, 1);
284 }
285
286
287 static time_t
288 parse_hourminutesecond (cch_t * in_pz)
289 {
290 time_t res = 0;
291 char buf[4];
292 cch_t * pz;
293
294 if (strlen (in_pz) != 6)
295 {
296 errno = EINVAL;
297 return BAD_TIME;
298 }
299
300 memcpy (buf, in_pz, 2);
301 buf[2] = NUL;
302 pz = buf;
303 res = parse_scaled_value (0, &pz, buf + 2, SEC_PER_HR);
304
305 memcpy (buf, in_pz + 2, 2);
306 buf[2] = NUL;
307 pz = buf;
308 res = parse_scaled_value (res, &pz, buf + 2, SEC_PER_MIN);
309
310 memcpy (buf, in_pz + 4, 2);
311 buf[2] = NUL;
312 pz = buf;
313 return parse_scaled_value (res, &pz, buf + 2, 1);
314 }
315
316
317 static time_t
318 parse_HMS (cch_t * pz)
319 {
320 time_t res = 0;
321 cch_t * ps = strchr (pz, 'H');
322 if (ps != NULL)
323 {
324 res = parse_scaled_value (0, &pz, ps, SEC_PER_HR);
325 pz++;
326 }
327
328 ps = strchr (pz, 'M');
329 if (ps != NULL)
330 {
331 res = parse_scaled_value (res, &pz, ps, SEC_PER_MIN);
332 pz++;
333 }
334
335 ps = strchr (pz, 'S');
336 if (ps != NULL)
337 {
338 res = parse_scaled_value (res, &pz, ps, 1);
339 pz++;
340 }
341
342 while (isspace ((unsigned char)*pz))
343 pz++;
344 if (*pz != NUL)
345 {
346 errno = EINVAL;
347 return BAD_TIME;
348 }
349
350 return res;
351 }
352
353
354 static time_t
355 parse_time (cch_t * pz)
356 {
357 cch_t * ps;
358 time_t res = 0;
359
360
361
362
363 ps = strchr (pz, ':');
364 if (ps != NULL)
365 {
366 res = parse_hour_minute_second (pz, ps);
367 }
368
369
370
371
372 else if (ps = strpbrk (pz, "HMS"),
373 ps == NULL)
374 {
375
376 res = parse_hourminutesecond (pz);
377 }
378
379 else
380 res = parse_HMS (pz);
381
382 return res;
383 }
384
385
386
387 static char *
388 trim (char * pz)
389 {
390
391 while (isspace ((unsigned char)*pz))
392 pz++;
393
394
395 {
396 char * pe = pz + strlen (pz);
397 while ((pe > pz) && isspace ((unsigned char)pe[-1]))
398 pe--;
399 *pe = NUL;
400 }
401
402 return pz;
403 }
404
405
406
407
408 static time_t
409 parse_period (cch_t * in_pz)
410 {
411 char * pT;
412 char * ps;
413 char * pz = strdup (in_pz);
414 void * fptr = pz;
415 time_t res = 0;
416
417 if (pz == NULL)
418 {
419 errno = ENOMEM;
420 return BAD_TIME;
421 }
422
423 pT = strchr (pz, 'T');
424 if (pT != NULL)
425 {
426 *(pT++) = NUL;
427 pz = trim (pz);
428 pT = trim (pT);
429 }
430
431
432
433
434 ps = strchr (pz, '-');
435 if (ps != NULL)
436 {
437 res = parse_year_month_day (pz, ps);
438 }
439
440
441
442
443 else if (ps = strpbrk (pz, "YMWD"),
444 ps == NULL)
445 {
446
447 res = parse_yearmonthday (pz);
448 }
449
450 else
451 res = parse_YMWD (pz);
452
453 if ((errno == 0) && (pT != NULL))
454 {
455 time_t val = parse_time (pT);
456 res = scale_n_add (res, val, 1);
457 }
458
459 free (fptr);
460 return res;
461 }
462
463 static time_t
464 parse_non_iso8601 (cch_t * pz)
465 {
466 whats_done_t whatd_we_do = NOTHING_IS_DONE;
467
468 time_t res = 0;
469
470 do {
471 time_t val;
472
473 errno = 0;
474 val = str_const_to_l (pz, &pz, 10);
475 if (errno != 0)
476 goto bad_time;
477
478
479
480
481
482 if (*pz == ':')
483 {
484 if (whatd_we_do >= MINUTE_IS_DONE)
485 break;
486
487 val = parse_hr_min_sec (val, pz);
488
489 if ((whatd_we_do == HOUR_IS_DONE) && (val >= SEC_PER_HR))
490 break;
491
492 return scale_n_add (res, val, 1);
493 }
494
495 {
496 unsigned int mult;
497
498
499 while (isspace ((unsigned char)*pz))
500 pz++;
501
502 switch (*pz)
503 {
504 default: goto bad_time;
505 case NUL:
506 return scale_n_add (res, val, 1);
507
508 case 'y': case 'Y':
509 if (whatd_we_do >= YEAR_IS_DONE)
510 goto bad_time;
511 mult = SEC_PER_YEAR;
512 whatd_we_do = YEAR_IS_DONE;
513 break;
514
515 case 'M':
516 if (whatd_we_do >= MONTH_IS_DONE)
517 goto bad_time;
518 mult = SEC_PER_MONTH;
519 whatd_we_do = MONTH_IS_DONE;
520 break;
521
522 case 'W':
523 if (whatd_we_do >= WEEK_IS_DONE)
524 goto bad_time;
525 mult = SEC_PER_WEEK;
526 whatd_we_do = WEEK_IS_DONE;
527 break;
528
529 case 'd': case 'D':
530 if (whatd_we_do >= DAY_IS_DONE)
531 goto bad_time;
532 mult = SEC_PER_DAY;
533 whatd_we_do = DAY_IS_DONE;
534 break;
535
536 case 'h':
537 if (whatd_we_do >= HOUR_IS_DONE)
538 goto bad_time;
539 mult = SEC_PER_HR;
540 whatd_we_do = HOUR_IS_DONE;
541 break;
542
543 case 'm':
544 if (whatd_we_do >= MINUTE_IS_DONE)
545 goto bad_time;
546 mult = SEC_PER_MIN;
547 whatd_we_do = MINUTE_IS_DONE;
548 break;
549
550 case 's':
551 mult = 1;
552 whatd_we_do = SECOND_IS_DONE;
553 break;
554 }
555
556 res = scale_n_add (res, val, mult);
557
558 pz++;
559 while (isspace ((unsigned char)*pz))
560 pz++;
561 if (*pz == NUL)
562 return res;
563
564 if (! isdigit ((unsigned char)*pz))
565 break;
566 }
567
568 } while (whatd_we_do < SECOND_IS_DONE);
569
570 bad_time:
571 errno = EINVAL;
572 return BAD_TIME;
573 }
574
575 time_t
576 parse_duration (char const * pz)
577 {
578 while (isspace ((unsigned char)*pz))
579 pz++;
580
581 switch (*pz)
582 {
583 case 'P':
584 return parse_period (pz + 1);
585
586 case 'T':
587 return parse_time (pz + 1);
588
589 default:
590 if (isdigit ((unsigned char)*pz))
591 return parse_non_iso8601 (pz);
592
593 errno = EINVAL;
594 return BAD_TIME;
595 }
596 }
597
598
599
600
601
602
603
604