xref: /freebsd/contrib/ntp/sntp/libopts/parse-duration.c (revision a466cc55373fc3cf86837f09da729535b57e69a1)
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