12b15cb3dSCy Schubert /* Parse a time duration and return a seconds count
2*a466cc55SCy Schubert Copyright (C) 2008-2018 Free Software Foundation, Inc.
32b15cb3dSCy Schubert Written by Bruce Korb <bkorb@gnu.org>, 2008.
42b15cb3dSCy Schubert
52b15cb3dSCy Schubert This program is free software: you can redistribute it and/or modify
62b15cb3dSCy Schubert it under the terms of the GNU Lesser General Public License as published by
72b15cb3dSCy Schubert the Free Software Foundation; either version 2.1 of the License, or
82b15cb3dSCy Schubert (at your option) any later version.
92b15cb3dSCy Schubert
102b15cb3dSCy Schubert This program is distributed in the hope that it will be useful,
112b15cb3dSCy Schubert but WITHOUT ANY WARRANTY; without even the implied warranty of
122b15cb3dSCy Schubert MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
132b15cb3dSCy Schubert GNU Lesser General Public License for more details.
142b15cb3dSCy Schubert
152b15cb3dSCy Schubert You should have received a copy of the GNU Lesser General Public License
16*a466cc55SCy Schubert along with this program. If not, see <https://www.gnu.org/licenses/>. */
172b15cb3dSCy Schubert
182b15cb3dSCy Schubert #include <config.h>
192b15cb3dSCy Schubert
202b15cb3dSCy Schubert /* Specification. */
212b15cb3dSCy Schubert #include "parse-duration.h"
222b15cb3dSCy Schubert
232b15cb3dSCy Schubert #include <ctype.h>
242b15cb3dSCy Schubert #include <errno.h>
252b15cb3dSCy Schubert #include <limits.h>
262b15cb3dSCy Schubert #include <stdio.h>
272b15cb3dSCy Schubert #include <stdlib.h>
282b15cb3dSCy Schubert #include <string.h>
292b15cb3dSCy Schubert
302b15cb3dSCy Schubert #include "intprops.h"
312b15cb3dSCy Schubert
322b15cb3dSCy Schubert #ifndef NUL
332b15cb3dSCy Schubert #define NUL '\0'
342b15cb3dSCy Schubert #endif
352b15cb3dSCy Schubert
362b15cb3dSCy Schubert #define cch_t char const
372b15cb3dSCy Schubert
382b15cb3dSCy Schubert typedef enum {
392b15cb3dSCy Schubert NOTHING_IS_DONE,
402b15cb3dSCy Schubert YEAR_IS_DONE,
412b15cb3dSCy Schubert MONTH_IS_DONE,
422b15cb3dSCy Schubert WEEK_IS_DONE,
432b15cb3dSCy Schubert DAY_IS_DONE,
442b15cb3dSCy Schubert HOUR_IS_DONE,
452b15cb3dSCy Schubert MINUTE_IS_DONE,
462b15cb3dSCy Schubert SECOND_IS_DONE
472b15cb3dSCy Schubert } whats_done_t;
482b15cb3dSCy Schubert
492b15cb3dSCy Schubert #define SEC_PER_MIN 60
502b15cb3dSCy Schubert #define SEC_PER_HR (SEC_PER_MIN * 60)
512b15cb3dSCy Schubert #define SEC_PER_DAY (SEC_PER_HR * 24)
522b15cb3dSCy Schubert #define SEC_PER_WEEK (SEC_PER_DAY * 7)
532b15cb3dSCy Schubert #define SEC_PER_MONTH (SEC_PER_DAY * 30)
542b15cb3dSCy Schubert #define SEC_PER_YEAR (SEC_PER_DAY * 365)
552b15cb3dSCy Schubert
562b15cb3dSCy Schubert #undef MAX_DURATION
572b15cb3dSCy Schubert #define MAX_DURATION TYPE_MAXIMUM(time_t)
582b15cb3dSCy Schubert
592b15cb3dSCy Schubert /* Wrapper around strtoul that does not require a cast. */
602b15cb3dSCy Schubert static unsigned long
str_const_to_ul(cch_t * str,cch_t ** ppz,int base)612b15cb3dSCy Schubert str_const_to_ul (cch_t * str, cch_t ** ppz, int base)
622b15cb3dSCy Schubert {
63*a466cc55SCy Schubert return strtoul (str, (char **)ppz, base);
642b15cb3dSCy Schubert }
652b15cb3dSCy Schubert
662b15cb3dSCy Schubert /* Wrapper around strtol that does not require a cast. */
672b15cb3dSCy Schubert static long
str_const_to_l(cch_t * str,cch_t ** ppz,int base)682b15cb3dSCy Schubert str_const_to_l (cch_t * str, cch_t ** ppz, int base)
692b15cb3dSCy Schubert {
70*a466cc55SCy Schubert return strtol (str, (char **)ppz, base);
712b15cb3dSCy Schubert }
722b15cb3dSCy Schubert
732b15cb3dSCy Schubert /* Returns BASE + VAL * SCALE, interpreting BASE = BAD_TIME
742b15cb3dSCy Schubert with errno set as an error situation, and returning BAD_TIME
752b15cb3dSCy Schubert with errno set in an error situation. */
762b15cb3dSCy Schubert static time_t
scale_n_add(time_t base,time_t val,int scale)772b15cb3dSCy Schubert scale_n_add (time_t base, time_t val, int scale)
782b15cb3dSCy Schubert {
792b15cb3dSCy Schubert if (base == BAD_TIME)
802b15cb3dSCy Schubert {
812b15cb3dSCy Schubert if (errno == 0)
822b15cb3dSCy Schubert errno = EINVAL;
832b15cb3dSCy Schubert return BAD_TIME;
842b15cb3dSCy Schubert }
852b15cb3dSCy Schubert
862b15cb3dSCy Schubert if (val > MAX_DURATION / scale)
872b15cb3dSCy Schubert {
882b15cb3dSCy Schubert errno = ERANGE;
892b15cb3dSCy Schubert return BAD_TIME;
902b15cb3dSCy Schubert }
912b15cb3dSCy Schubert
922b15cb3dSCy Schubert val *= scale;
932b15cb3dSCy Schubert if (base > MAX_DURATION - val)
942b15cb3dSCy Schubert {
952b15cb3dSCy Schubert errno = ERANGE;
962b15cb3dSCy Schubert return BAD_TIME;
972b15cb3dSCy Schubert }
982b15cb3dSCy Schubert
992b15cb3dSCy Schubert return base + val;
1002b15cb3dSCy Schubert }
1012b15cb3dSCy Schubert
1022b15cb3dSCy Schubert /* After a number HH has been parsed, parse subsequent :MM or :MM:SS. */
1032b15cb3dSCy Schubert static time_t
parse_hr_min_sec(time_t start,cch_t * pz)1042b15cb3dSCy Schubert parse_hr_min_sec (time_t start, cch_t * pz)
1052b15cb3dSCy Schubert {
1062b15cb3dSCy Schubert int lpct = 0;
1072b15cb3dSCy Schubert
1082b15cb3dSCy Schubert errno = 0;
1092b15cb3dSCy Schubert
1102b15cb3dSCy Schubert /* For as long as our scanner pointer points to a colon *AND*
1112b15cb3dSCy Schubert we've not looped before, then keep looping. (two iterations max) */
1122b15cb3dSCy Schubert while ((*pz == ':') && (lpct++ <= 1))
1132b15cb3dSCy Schubert {
1142b15cb3dSCy Schubert unsigned long v = str_const_to_ul (pz+1, &pz, 10);
1152b15cb3dSCy Schubert
1162b15cb3dSCy Schubert if (errno != 0)
1172b15cb3dSCy Schubert return BAD_TIME;
1182b15cb3dSCy Schubert
1192b15cb3dSCy Schubert start = scale_n_add (v, start, 60);
1202b15cb3dSCy Schubert
1212b15cb3dSCy Schubert if (errno != 0)
1222b15cb3dSCy Schubert return BAD_TIME;
1232b15cb3dSCy Schubert }
1242b15cb3dSCy Schubert
1252b15cb3dSCy Schubert /* allow for trailing spaces */
1262b15cb3dSCy Schubert while (isspace ((unsigned char)*pz))
1272b15cb3dSCy Schubert pz++;
1282b15cb3dSCy Schubert if (*pz != NUL)
1292b15cb3dSCy Schubert {
1302b15cb3dSCy Schubert errno = EINVAL;
1312b15cb3dSCy Schubert return BAD_TIME;
1322b15cb3dSCy Schubert }
1332b15cb3dSCy Schubert
1342b15cb3dSCy Schubert return start;
1352b15cb3dSCy Schubert }
1362b15cb3dSCy Schubert
1372b15cb3dSCy Schubert /* Parses a value and returns BASE + value * SCALE, interpreting
1382b15cb3dSCy Schubert BASE = BAD_TIME with errno set as an error situation, and returning
1392b15cb3dSCy Schubert BAD_TIME with errno set in an error situation. */
1402b15cb3dSCy Schubert static time_t
parse_scaled_value(time_t base,cch_t ** ppz,cch_t * endp,int scale)1412b15cb3dSCy Schubert parse_scaled_value (time_t base, cch_t ** ppz, cch_t * endp, int scale)
1422b15cb3dSCy Schubert {
1432b15cb3dSCy Schubert cch_t * pz = *ppz;
1442b15cb3dSCy Schubert time_t val;
1452b15cb3dSCy Schubert
1462b15cb3dSCy Schubert if (base == BAD_TIME)
1472b15cb3dSCy Schubert return base;
1482b15cb3dSCy Schubert
1492b15cb3dSCy Schubert errno = 0;
1502b15cb3dSCy Schubert val = str_const_to_ul (pz, &pz, 10);
1512b15cb3dSCy Schubert if (errno != 0)
1522b15cb3dSCy Schubert return BAD_TIME;
1532b15cb3dSCy Schubert while (isspace ((unsigned char)*pz))
1542b15cb3dSCy Schubert pz++;
1552b15cb3dSCy Schubert if (pz != endp)
1562b15cb3dSCy Schubert {
1572b15cb3dSCy Schubert errno = EINVAL;
1582b15cb3dSCy Schubert return BAD_TIME;
1592b15cb3dSCy Schubert }
1602b15cb3dSCy Schubert
1612b15cb3dSCy Schubert *ppz = pz;
1622b15cb3dSCy Schubert return scale_n_add (base, val, scale);
1632b15cb3dSCy Schubert }
1642b15cb3dSCy Schubert
1652b15cb3dSCy Schubert /* Parses the syntax YEAR-MONTH-DAY.
1662b15cb3dSCy Schubert PS points into the string, after "YEAR", before "-MONTH-DAY". */
1672b15cb3dSCy Schubert static time_t
parse_year_month_day(cch_t * pz,cch_t * ps)1682b15cb3dSCy Schubert parse_year_month_day (cch_t * pz, cch_t * ps)
1692b15cb3dSCy Schubert {
1702b15cb3dSCy Schubert time_t res = 0;
1712b15cb3dSCy Schubert
1722b15cb3dSCy Schubert res = parse_scaled_value (0, &pz, ps, SEC_PER_YEAR);
1732b15cb3dSCy Schubert
1742b15cb3dSCy Schubert pz++; /* over the first '-' */
1752b15cb3dSCy Schubert ps = strchr (pz, '-');
1762b15cb3dSCy Schubert if (ps == NULL)
1772b15cb3dSCy Schubert {
1782b15cb3dSCy Schubert errno = EINVAL;
1792b15cb3dSCy Schubert return BAD_TIME;
1802b15cb3dSCy Schubert }
1812b15cb3dSCy Schubert res = parse_scaled_value (res, &pz, ps, SEC_PER_MONTH);
1822b15cb3dSCy Schubert
1832b15cb3dSCy Schubert pz++; /* over the second '-' */
1842b15cb3dSCy Schubert ps = pz + strlen (pz);
1852b15cb3dSCy Schubert return parse_scaled_value (res, &pz, ps, SEC_PER_DAY);
1862b15cb3dSCy Schubert }
1872b15cb3dSCy Schubert
1882b15cb3dSCy Schubert /* Parses the syntax YYYYMMDD. */
1892b15cb3dSCy Schubert static time_t
parse_yearmonthday(cch_t * in_pz)1902b15cb3dSCy Schubert parse_yearmonthday (cch_t * in_pz)
1912b15cb3dSCy Schubert {
1922b15cb3dSCy Schubert time_t res = 0;
1932b15cb3dSCy Schubert char buf[8];
1942b15cb3dSCy Schubert cch_t * pz;
1952b15cb3dSCy Schubert
1962b15cb3dSCy Schubert if (strlen (in_pz) != 8)
1972b15cb3dSCy Schubert {
1982b15cb3dSCy Schubert errno = EINVAL;
1992b15cb3dSCy Schubert return BAD_TIME;
2002b15cb3dSCy Schubert }
2012b15cb3dSCy Schubert
2022b15cb3dSCy Schubert memcpy (buf, in_pz, 4);
2032b15cb3dSCy Schubert buf[4] = NUL;
2042b15cb3dSCy Schubert pz = buf;
2052b15cb3dSCy Schubert res = parse_scaled_value (0, &pz, buf + 4, SEC_PER_YEAR);
2062b15cb3dSCy Schubert
2072b15cb3dSCy Schubert memcpy (buf, in_pz + 4, 2);
2082b15cb3dSCy Schubert buf[2] = NUL;
2092b15cb3dSCy Schubert pz = buf;
2102b15cb3dSCy Schubert res = parse_scaled_value (res, &pz, buf + 2, SEC_PER_MONTH);
2112b15cb3dSCy Schubert
2122b15cb3dSCy Schubert memcpy (buf, in_pz + 6, 2);
2132b15cb3dSCy Schubert buf[2] = NUL;
2142b15cb3dSCy Schubert pz = buf;
2152b15cb3dSCy Schubert return parse_scaled_value (res, &pz, buf + 2, SEC_PER_DAY);
2162b15cb3dSCy Schubert }
2172b15cb3dSCy Schubert
2182b15cb3dSCy Schubert /* Parses the syntax yy Y mm M ww W dd D. */
2192b15cb3dSCy Schubert static time_t
parse_YMWD(cch_t * pz)2202b15cb3dSCy Schubert parse_YMWD (cch_t * pz)
2212b15cb3dSCy Schubert {
2222b15cb3dSCy Schubert time_t res = 0;
2232b15cb3dSCy Schubert cch_t * ps = strchr (pz, 'Y');
2242b15cb3dSCy Schubert if (ps != NULL)
2252b15cb3dSCy Schubert {
2262b15cb3dSCy Schubert res = parse_scaled_value (0, &pz, ps, SEC_PER_YEAR);
2272b15cb3dSCy Schubert pz++;
2282b15cb3dSCy Schubert }
2292b15cb3dSCy Schubert
2302b15cb3dSCy Schubert ps = strchr (pz, 'M');
2312b15cb3dSCy Schubert if (ps != NULL)
2322b15cb3dSCy Schubert {
2332b15cb3dSCy Schubert res = parse_scaled_value (res, &pz, ps, SEC_PER_MONTH);
2342b15cb3dSCy Schubert pz++;
2352b15cb3dSCy Schubert }
2362b15cb3dSCy Schubert
2372b15cb3dSCy Schubert ps = strchr (pz, 'W');
2382b15cb3dSCy Schubert if (ps != NULL)
2392b15cb3dSCy Schubert {
2402b15cb3dSCy Schubert res = parse_scaled_value (res, &pz, ps, SEC_PER_WEEK);
2412b15cb3dSCy Schubert pz++;
2422b15cb3dSCy Schubert }
2432b15cb3dSCy Schubert
2442b15cb3dSCy Schubert ps = strchr (pz, 'D');
2452b15cb3dSCy Schubert if (ps != NULL)
2462b15cb3dSCy Schubert {
2472b15cb3dSCy Schubert res = parse_scaled_value (res, &pz, ps, SEC_PER_DAY);
2482b15cb3dSCy Schubert pz++;
2492b15cb3dSCy Schubert }
2502b15cb3dSCy Schubert
2512b15cb3dSCy Schubert while (isspace ((unsigned char)*pz))
2522b15cb3dSCy Schubert pz++;
2532b15cb3dSCy Schubert if (*pz != NUL)
2542b15cb3dSCy Schubert {
2552b15cb3dSCy Schubert errno = EINVAL;
2562b15cb3dSCy Schubert return BAD_TIME;
2572b15cb3dSCy Schubert }
2582b15cb3dSCy Schubert
2592b15cb3dSCy Schubert return res;
2602b15cb3dSCy Schubert }
2612b15cb3dSCy Schubert
2622b15cb3dSCy Schubert /* Parses the syntax HH:MM:SS.
2632b15cb3dSCy Schubert PS points into the string, after "HH", before ":MM:SS". */
2642b15cb3dSCy Schubert static time_t
parse_hour_minute_second(cch_t * pz,cch_t * ps)2652b15cb3dSCy Schubert parse_hour_minute_second (cch_t * pz, cch_t * ps)
2662b15cb3dSCy Schubert {
2672b15cb3dSCy Schubert time_t res = 0;
2682b15cb3dSCy Schubert
2692b15cb3dSCy Schubert res = parse_scaled_value (0, &pz, ps, SEC_PER_HR);
2702b15cb3dSCy Schubert
2712b15cb3dSCy Schubert pz++;
2722b15cb3dSCy Schubert ps = strchr (pz, ':');
2732b15cb3dSCy Schubert if (ps == NULL)
2742b15cb3dSCy Schubert {
2752b15cb3dSCy Schubert errno = EINVAL;
2762b15cb3dSCy Schubert return BAD_TIME;
2772b15cb3dSCy Schubert }
2782b15cb3dSCy Schubert
2792b15cb3dSCy Schubert res = parse_scaled_value (res, &pz, ps, SEC_PER_MIN);
2802b15cb3dSCy Schubert
2812b15cb3dSCy Schubert pz++;
2822b15cb3dSCy Schubert ps = pz + strlen (pz);
2832b15cb3dSCy Schubert return parse_scaled_value (res, &pz, ps, 1);
2842b15cb3dSCy Schubert }
2852b15cb3dSCy Schubert
2862b15cb3dSCy Schubert /* Parses the syntax HHMMSS. */
2872b15cb3dSCy Schubert static time_t
parse_hourminutesecond(cch_t * in_pz)2882b15cb3dSCy Schubert parse_hourminutesecond (cch_t * in_pz)
2892b15cb3dSCy Schubert {
2902b15cb3dSCy Schubert time_t res = 0;
2912b15cb3dSCy Schubert char buf[4];
2922b15cb3dSCy Schubert cch_t * pz;
2932b15cb3dSCy Schubert
2942b15cb3dSCy Schubert if (strlen (in_pz) != 6)
2952b15cb3dSCy Schubert {
2962b15cb3dSCy Schubert errno = EINVAL;
2972b15cb3dSCy Schubert return BAD_TIME;
2982b15cb3dSCy Schubert }
2992b15cb3dSCy Schubert
3002b15cb3dSCy Schubert memcpy (buf, in_pz, 2);
3012b15cb3dSCy Schubert buf[2] = NUL;
3022b15cb3dSCy Schubert pz = buf;
3032b15cb3dSCy Schubert res = parse_scaled_value (0, &pz, buf + 2, SEC_PER_HR);
3042b15cb3dSCy Schubert
3052b15cb3dSCy Schubert memcpy (buf, in_pz + 2, 2);
3062b15cb3dSCy Schubert buf[2] = NUL;
3072b15cb3dSCy Schubert pz = buf;
3082b15cb3dSCy Schubert res = parse_scaled_value (res, &pz, buf + 2, SEC_PER_MIN);
3092b15cb3dSCy Schubert
3102b15cb3dSCy Schubert memcpy (buf, in_pz + 4, 2);
3112b15cb3dSCy Schubert buf[2] = NUL;
3122b15cb3dSCy Schubert pz = buf;
3132b15cb3dSCy Schubert return parse_scaled_value (res, &pz, buf + 2, 1);
3142b15cb3dSCy Schubert }
3152b15cb3dSCy Schubert
3162b15cb3dSCy Schubert /* Parses the syntax hh H mm M ss S. */
3172b15cb3dSCy Schubert static time_t
parse_HMS(cch_t * pz)3182b15cb3dSCy Schubert parse_HMS (cch_t * pz)
3192b15cb3dSCy Schubert {
3202b15cb3dSCy Schubert time_t res = 0;
3212b15cb3dSCy Schubert cch_t * ps = strchr (pz, 'H');
3222b15cb3dSCy Schubert if (ps != NULL)
3232b15cb3dSCy Schubert {
3242b15cb3dSCy Schubert res = parse_scaled_value (0, &pz, ps, SEC_PER_HR);
3252b15cb3dSCy Schubert pz++;
3262b15cb3dSCy Schubert }
3272b15cb3dSCy Schubert
3282b15cb3dSCy Schubert ps = strchr (pz, 'M');
3292b15cb3dSCy Schubert if (ps != NULL)
3302b15cb3dSCy Schubert {
3312b15cb3dSCy Schubert res = parse_scaled_value (res, &pz, ps, SEC_PER_MIN);
3322b15cb3dSCy Schubert pz++;
3332b15cb3dSCy Schubert }
3342b15cb3dSCy Schubert
3352b15cb3dSCy Schubert ps = strchr (pz, 'S');
3362b15cb3dSCy Schubert if (ps != NULL)
3372b15cb3dSCy Schubert {
3382b15cb3dSCy Schubert res = parse_scaled_value (res, &pz, ps, 1);
3392b15cb3dSCy Schubert pz++;
3402b15cb3dSCy Schubert }
3412b15cb3dSCy Schubert
3422b15cb3dSCy Schubert while (isspace ((unsigned char)*pz))
3432b15cb3dSCy Schubert pz++;
3442b15cb3dSCy Schubert if (*pz != NUL)
3452b15cb3dSCy Schubert {
3462b15cb3dSCy Schubert errno = EINVAL;
3472b15cb3dSCy Schubert return BAD_TIME;
3482b15cb3dSCy Schubert }
3492b15cb3dSCy Schubert
3502b15cb3dSCy Schubert return res;
3512b15cb3dSCy Schubert }
3522b15cb3dSCy Schubert
3532b15cb3dSCy Schubert /* Parses a time (hours, minutes, seconds) specification in either syntax. */
3542b15cb3dSCy Schubert static time_t
parse_time(cch_t * pz)3552b15cb3dSCy Schubert parse_time (cch_t * pz)
3562b15cb3dSCy Schubert {
3572b15cb3dSCy Schubert cch_t * ps;
3582b15cb3dSCy Schubert time_t res = 0;
3592b15cb3dSCy Schubert
3602b15cb3dSCy Schubert /*
3612b15cb3dSCy Schubert * Scan for a hyphen
3622b15cb3dSCy Schubert */
3632b15cb3dSCy Schubert ps = strchr (pz, ':');
3642b15cb3dSCy Schubert if (ps != NULL)
3652b15cb3dSCy Schubert {
3662b15cb3dSCy Schubert res = parse_hour_minute_second (pz, ps);
3672b15cb3dSCy Schubert }
3682b15cb3dSCy Schubert
3692b15cb3dSCy Schubert /*
3702b15cb3dSCy Schubert * Try for a 'H', 'M' or 'S' suffix
3712b15cb3dSCy Schubert */
3722b15cb3dSCy Schubert else if (ps = strpbrk (pz, "HMS"),
3732b15cb3dSCy Schubert ps == NULL)
3742b15cb3dSCy Schubert {
3752b15cb3dSCy Schubert /* Its a YYYYMMDD format: */
3762b15cb3dSCy Schubert res = parse_hourminutesecond (pz);
3772b15cb3dSCy Schubert }
3782b15cb3dSCy Schubert
3792b15cb3dSCy Schubert else
3802b15cb3dSCy Schubert res = parse_HMS (pz);
3812b15cb3dSCy Schubert
3822b15cb3dSCy Schubert return res;
3832b15cb3dSCy Schubert }
3842b15cb3dSCy Schubert
3852b15cb3dSCy Schubert /* Returns a substring of the given string, with spaces at the beginning and at
3862b15cb3dSCy Schubert the end destructively removed, per SNOBOL. */
3872b15cb3dSCy Schubert static char *
trim(char * pz)3882b15cb3dSCy Schubert trim (char * pz)
3892b15cb3dSCy Schubert {
3902b15cb3dSCy Schubert /* trim leading white space */
3912b15cb3dSCy Schubert while (isspace ((unsigned char)*pz))
3922b15cb3dSCy Schubert pz++;
3932b15cb3dSCy Schubert
3942b15cb3dSCy Schubert /* trim trailing white space */
3952b15cb3dSCy Schubert {
3962b15cb3dSCy Schubert char * pe = pz + strlen (pz);
3972b15cb3dSCy Schubert while ((pe > pz) && isspace ((unsigned char)pe[-1]))
3982b15cb3dSCy Schubert pe--;
3992b15cb3dSCy Schubert *pe = NUL;
4002b15cb3dSCy Schubert }
4012b15cb3dSCy Schubert
4022b15cb3dSCy Schubert return pz;
4032b15cb3dSCy Schubert }
4042b15cb3dSCy Schubert
4052b15cb3dSCy Schubert /*
4062b15cb3dSCy Schubert * Parse the year/months/days of a time period
4072b15cb3dSCy Schubert */
4082b15cb3dSCy Schubert static time_t
parse_period(cch_t * in_pz)4092b15cb3dSCy Schubert parse_period (cch_t * in_pz)
4102b15cb3dSCy Schubert {
4112b15cb3dSCy Schubert char * pT;
4122b15cb3dSCy Schubert char * ps;
4132b15cb3dSCy Schubert char * pz = strdup (in_pz);
4142b15cb3dSCy Schubert void * fptr = pz;
4152b15cb3dSCy Schubert time_t res = 0;
4162b15cb3dSCy Schubert
4172b15cb3dSCy Schubert if (pz == NULL)
4182b15cb3dSCy Schubert {
4192b15cb3dSCy Schubert errno = ENOMEM;
4202b15cb3dSCy Schubert return BAD_TIME;
4212b15cb3dSCy Schubert }
4222b15cb3dSCy Schubert
4232b15cb3dSCy Schubert pT = strchr (pz, 'T');
4242b15cb3dSCy Schubert if (pT != NULL)
4252b15cb3dSCy Schubert {
4262b15cb3dSCy Schubert *(pT++) = NUL;
4272b15cb3dSCy Schubert pz = trim (pz);
4282b15cb3dSCy Schubert pT = trim (pT);
4292b15cb3dSCy Schubert }
4302b15cb3dSCy Schubert
4312b15cb3dSCy Schubert /*
4322b15cb3dSCy Schubert * Scan for a hyphen
4332b15cb3dSCy Schubert */
4342b15cb3dSCy Schubert ps = strchr (pz, '-');
4352b15cb3dSCy Schubert if (ps != NULL)
4362b15cb3dSCy Schubert {
4372b15cb3dSCy Schubert res = parse_year_month_day (pz, ps);
4382b15cb3dSCy Schubert }
4392b15cb3dSCy Schubert
4402b15cb3dSCy Schubert /*
4412b15cb3dSCy Schubert * Try for a 'Y', 'M' or 'D' suffix
4422b15cb3dSCy Schubert */
4432b15cb3dSCy Schubert else if (ps = strpbrk (pz, "YMWD"),
4442b15cb3dSCy Schubert ps == NULL)
4452b15cb3dSCy Schubert {
4462b15cb3dSCy Schubert /* Its a YYYYMMDD format: */
4472b15cb3dSCy Schubert res = parse_yearmonthday (pz);
4482b15cb3dSCy Schubert }
4492b15cb3dSCy Schubert
4502b15cb3dSCy Schubert else
4512b15cb3dSCy Schubert res = parse_YMWD (pz);
4522b15cb3dSCy Schubert
4532b15cb3dSCy Schubert if ((errno == 0) && (pT != NULL))
4542b15cb3dSCy Schubert {
4552b15cb3dSCy Schubert time_t val = parse_time (pT);
4562b15cb3dSCy Schubert res = scale_n_add (res, val, 1);
4572b15cb3dSCy Schubert }
4582b15cb3dSCy Schubert
4592b15cb3dSCy Schubert free (fptr);
4602b15cb3dSCy Schubert return res;
4612b15cb3dSCy Schubert }
4622b15cb3dSCy Schubert
4632b15cb3dSCy Schubert static time_t
parse_non_iso8601(cch_t * pz)4642b15cb3dSCy Schubert parse_non_iso8601 (cch_t * pz)
4652b15cb3dSCy Schubert {
4662b15cb3dSCy Schubert whats_done_t whatd_we_do = NOTHING_IS_DONE;
4672b15cb3dSCy Schubert
4682b15cb3dSCy Schubert time_t res = 0;
4692b15cb3dSCy Schubert
4702b15cb3dSCy Schubert do {
4712b15cb3dSCy Schubert time_t val;
4722b15cb3dSCy Schubert
4732b15cb3dSCy Schubert errno = 0;
4742b15cb3dSCy Schubert val = str_const_to_l (pz, &pz, 10);
4752b15cb3dSCy Schubert if (errno != 0)
4762b15cb3dSCy Schubert goto bad_time;
4772b15cb3dSCy Schubert
4782b15cb3dSCy Schubert /* IF we find a colon, then we're going to have a seconds value.
4792b15cb3dSCy Schubert We will not loop here any more. We cannot already have parsed
4802b15cb3dSCy Schubert a minute value and if we've parsed an hour value, then the result
4812b15cb3dSCy Schubert value has to be less than an hour. */
4822b15cb3dSCy Schubert if (*pz == ':')
4832b15cb3dSCy Schubert {
4842b15cb3dSCy Schubert if (whatd_we_do >= MINUTE_IS_DONE)
4852b15cb3dSCy Schubert break;
4862b15cb3dSCy Schubert
4872b15cb3dSCy Schubert val = parse_hr_min_sec (val, pz);
4882b15cb3dSCy Schubert
4892b15cb3dSCy Schubert if ((whatd_we_do == HOUR_IS_DONE) && (val >= SEC_PER_HR))
4902b15cb3dSCy Schubert break;
4912b15cb3dSCy Schubert
4922b15cb3dSCy Schubert return scale_n_add (res, val, 1);
4932b15cb3dSCy Schubert }
4942b15cb3dSCy Schubert
4952b15cb3dSCy Schubert {
4962b15cb3dSCy Schubert unsigned int mult;
4972b15cb3dSCy Schubert
4982b15cb3dSCy Schubert /* Skip over white space following the number we just parsed. */
4992b15cb3dSCy Schubert while (isspace ((unsigned char)*pz))
5002b15cb3dSCy Schubert pz++;
5012b15cb3dSCy Schubert
5022b15cb3dSCy Schubert switch (*pz)
5032b15cb3dSCy Schubert {
5042b15cb3dSCy Schubert default: goto bad_time;
5052b15cb3dSCy Schubert case NUL:
5062b15cb3dSCy Schubert return scale_n_add (res, val, 1);
5072b15cb3dSCy Schubert
5082b15cb3dSCy Schubert case 'y': case 'Y':
5092b15cb3dSCy Schubert if (whatd_we_do >= YEAR_IS_DONE)
5102b15cb3dSCy Schubert goto bad_time;
5112b15cb3dSCy Schubert mult = SEC_PER_YEAR;
5122b15cb3dSCy Schubert whatd_we_do = YEAR_IS_DONE;
5132b15cb3dSCy Schubert break;
5142b15cb3dSCy Schubert
5152b15cb3dSCy Schubert case 'M':
5162b15cb3dSCy Schubert if (whatd_we_do >= MONTH_IS_DONE)
5172b15cb3dSCy Schubert goto bad_time;
5182b15cb3dSCy Schubert mult = SEC_PER_MONTH;
5192b15cb3dSCy Schubert whatd_we_do = MONTH_IS_DONE;
5202b15cb3dSCy Schubert break;
5212b15cb3dSCy Schubert
5222b15cb3dSCy Schubert case 'W':
5232b15cb3dSCy Schubert if (whatd_we_do >= WEEK_IS_DONE)
5242b15cb3dSCy Schubert goto bad_time;
5252b15cb3dSCy Schubert mult = SEC_PER_WEEK;
5262b15cb3dSCy Schubert whatd_we_do = WEEK_IS_DONE;
5272b15cb3dSCy Schubert break;
5282b15cb3dSCy Schubert
5292b15cb3dSCy Schubert case 'd': case 'D':
5302b15cb3dSCy Schubert if (whatd_we_do >= DAY_IS_DONE)
5312b15cb3dSCy Schubert goto bad_time;
5322b15cb3dSCy Schubert mult = SEC_PER_DAY;
5332b15cb3dSCy Schubert whatd_we_do = DAY_IS_DONE;
5342b15cb3dSCy Schubert break;
5352b15cb3dSCy Schubert
5362b15cb3dSCy Schubert case 'h':
5372b15cb3dSCy Schubert if (whatd_we_do >= HOUR_IS_DONE)
5382b15cb3dSCy Schubert goto bad_time;
5392b15cb3dSCy Schubert mult = SEC_PER_HR;
5402b15cb3dSCy Schubert whatd_we_do = HOUR_IS_DONE;
5412b15cb3dSCy Schubert break;
5422b15cb3dSCy Schubert
5432b15cb3dSCy Schubert case 'm':
5442b15cb3dSCy Schubert if (whatd_we_do >= MINUTE_IS_DONE)
5452b15cb3dSCy Schubert goto bad_time;
5462b15cb3dSCy Schubert mult = SEC_PER_MIN;
5472b15cb3dSCy Schubert whatd_we_do = MINUTE_IS_DONE;
5482b15cb3dSCy Schubert break;
5492b15cb3dSCy Schubert
5502b15cb3dSCy Schubert case 's':
5512b15cb3dSCy Schubert mult = 1;
5522b15cb3dSCy Schubert whatd_we_do = SECOND_IS_DONE;
5532b15cb3dSCy Schubert break;
5542b15cb3dSCy Schubert }
5552b15cb3dSCy Schubert
5562b15cb3dSCy Schubert res = scale_n_add (res, val, mult);
5572b15cb3dSCy Schubert
5582b15cb3dSCy Schubert pz++;
5592b15cb3dSCy Schubert while (isspace ((unsigned char)*pz))
5602b15cb3dSCy Schubert pz++;
5612b15cb3dSCy Schubert if (*pz == NUL)
5622b15cb3dSCy Schubert return res;
5632b15cb3dSCy Schubert
5642b15cb3dSCy Schubert if (! isdigit ((unsigned char)*pz))
5652b15cb3dSCy Schubert break;
5662b15cb3dSCy Schubert }
5672b15cb3dSCy Schubert
5682b15cb3dSCy Schubert } while (whatd_we_do < SECOND_IS_DONE);
5692b15cb3dSCy Schubert
5702b15cb3dSCy Schubert bad_time:
5712b15cb3dSCy Schubert errno = EINVAL;
5722b15cb3dSCy Schubert return BAD_TIME;
5732b15cb3dSCy Schubert }
5742b15cb3dSCy Schubert
5752b15cb3dSCy Schubert time_t
parse_duration(char const * pz)5762b15cb3dSCy Schubert parse_duration (char const * pz)
5772b15cb3dSCy Schubert {
5782b15cb3dSCy Schubert while (isspace ((unsigned char)*pz))
5792b15cb3dSCy Schubert pz++;
5802b15cb3dSCy Schubert
5812b15cb3dSCy Schubert switch (*pz)
5822b15cb3dSCy Schubert {
5832b15cb3dSCy Schubert case 'P':
5842b15cb3dSCy Schubert return parse_period (pz + 1);
5852b15cb3dSCy Schubert
5862b15cb3dSCy Schubert case 'T':
5872b15cb3dSCy Schubert return parse_time (pz + 1);
5882b15cb3dSCy Schubert
5892b15cb3dSCy Schubert default:
5902b15cb3dSCy Schubert if (isdigit ((unsigned char)*pz))
5912b15cb3dSCy Schubert return parse_non_iso8601 (pz);
5922b15cb3dSCy Schubert
5932b15cb3dSCy Schubert errno = EINVAL;
5942b15cb3dSCy Schubert return BAD_TIME;
5952b15cb3dSCy Schubert }
5962b15cb3dSCy Schubert }
5972b15cb3dSCy Schubert
5982b15cb3dSCy Schubert /*
5992b15cb3dSCy Schubert * Local Variables:
6002b15cb3dSCy Schubert * mode: C
6012b15cb3dSCy Schubert * c-file-style: "gnu"
6022b15cb3dSCy Schubert * indent-tabs-mode: nil
6032b15cb3dSCy Schubert * End:
6042b15cb3dSCy Schubert * end of parse-duration.c */
605