1*eabc0478Schristos /* $NetBSD: parse-duration.c,v 1.11 2024/08/18 20:47:25 christos Exp $ */ 2abb0f93cSkardel 3abb0f93cSkardel /* Parse a time duration and return a seconds count 4*eabc0478Schristos Copyright (C) 2008-2018 Free Software Foundation, Inc. 5abb0f93cSkardel Written by Bruce Korb <bkorb@gnu.org>, 2008. 6abb0f93cSkardel 7abb0f93cSkardel This program is free software: you can redistribute it and/or modify 82950cc38Schristos it under the terms of the GNU Lesser General Public License as published by 92950cc38Schristos the Free Software Foundation; either version 2.1 of the License, or 10abb0f93cSkardel (at your option) any later version. 11abb0f93cSkardel 12abb0f93cSkardel This program is distributed in the hope that it will be useful, 13abb0f93cSkardel but WITHOUT ANY WARRANTY; without even the implied warranty of 14abb0f93cSkardel MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 152950cc38Schristos GNU Lesser General Public License for more details. 16abb0f93cSkardel 172950cc38Schristos You should have received a copy of the GNU Lesser General Public License 18*eabc0478Schristos along with this program. If not, see <https://www.gnu.org/licenses/>. */ 19abb0f93cSkardel 20abb0f93cSkardel #include <config.h> 21abb0f93cSkardel 22f003fb54Skardel /* Specification. */ 23f003fb54Skardel #include "parse-duration.h" 24f003fb54Skardel 25abb0f93cSkardel #include <ctype.h> 26abb0f93cSkardel #include <errno.h> 27abb0f93cSkardel #include <limits.h> 28abb0f93cSkardel #include <stdio.h> 29abb0f93cSkardel #include <stdlib.h> 30abb0f93cSkardel #include <string.h> 31abb0f93cSkardel 32ea66d795Schristos #include "intprops.h" 33ea66d795Schristos 34abb0f93cSkardel #ifndef NUL 35abb0f93cSkardel #define NUL '\0' 36abb0f93cSkardel #endif 37abb0f93cSkardel 38abb0f93cSkardel #define cch_t char const 39abb0f93cSkardel 40abb0f93cSkardel typedef enum { 41abb0f93cSkardel NOTHING_IS_DONE, 42abb0f93cSkardel YEAR_IS_DONE, 43abb0f93cSkardel MONTH_IS_DONE, 44abb0f93cSkardel WEEK_IS_DONE, 45abb0f93cSkardel DAY_IS_DONE, 46abb0f93cSkardel HOUR_IS_DONE, 47abb0f93cSkardel MINUTE_IS_DONE, 48abb0f93cSkardel SECOND_IS_DONE 49abb0f93cSkardel } whats_done_t; 50abb0f93cSkardel 51abb0f93cSkardel #define SEC_PER_MIN 60 52abb0f93cSkardel #define SEC_PER_HR (SEC_PER_MIN * 60) 53abb0f93cSkardel #define SEC_PER_DAY (SEC_PER_HR * 24) 54abb0f93cSkardel #define SEC_PER_WEEK (SEC_PER_DAY * 7) 55abb0f93cSkardel #define SEC_PER_MONTH (SEC_PER_DAY * 30) 56abb0f93cSkardel #define SEC_PER_YEAR (SEC_PER_DAY * 365) 57abb0f93cSkardel 58ea66d795Schristos #undef MAX_DURATION 59ea66d795Schristos #define MAX_DURATION TYPE_MAXIMUM(time_t) 60abb0f93cSkardel 61f003fb54Skardel /* Wrapper around strtoul that does not require a cast. */ 622950cc38Schristos static unsigned long 63abb0f93cSkardel str_const_to_ul (cch_t * str, cch_t ** ppz, int base) 64abb0f93cSkardel { 65*eabc0478Schristos return strtoul (str, __UNCONST(ppz), base); 66abb0f93cSkardel } 67abb0f93cSkardel 68f003fb54Skardel /* Wrapper around strtol that does not require a cast. */ 692950cc38Schristos static long 70abb0f93cSkardel str_const_to_l (cch_t * str, cch_t ** ppz, int base) 71abb0f93cSkardel { 72*eabc0478Schristos return strtol (str, __UNCONST(ppz), base); 73abb0f93cSkardel } 74abb0f93cSkardel 75f003fb54Skardel /* Returns BASE + VAL * SCALE, interpreting BASE = BAD_TIME 76f003fb54Skardel with errno set as an error situation, and returning BAD_TIME 77f003fb54Skardel with errno set in an error situation. */ 782950cc38Schristos static time_t 79abb0f93cSkardel scale_n_add (time_t base, time_t val, int scale) 80abb0f93cSkardel { 81abb0f93cSkardel if (base == BAD_TIME) 82abb0f93cSkardel { 83abb0f93cSkardel if (errno == 0) 84abb0f93cSkardel errno = EINVAL; 85abb0f93cSkardel return BAD_TIME; 86abb0f93cSkardel } 87abb0f93cSkardel 88ea66d795Schristos if (val > MAX_DURATION / scale) 89abb0f93cSkardel { 90abb0f93cSkardel errno = ERANGE; 91abb0f93cSkardel return BAD_TIME; 92abb0f93cSkardel } 93abb0f93cSkardel 94abb0f93cSkardel val *= scale; 95ea66d795Schristos if (base > MAX_DURATION - val) 96abb0f93cSkardel { 97abb0f93cSkardel errno = ERANGE; 98abb0f93cSkardel return BAD_TIME; 99abb0f93cSkardel } 100abb0f93cSkardel 101abb0f93cSkardel return base + val; 102abb0f93cSkardel } 103abb0f93cSkardel 104f003fb54Skardel /* After a number HH has been parsed, parse subsequent :MM or :MM:SS. */ 105abb0f93cSkardel static time_t 106abb0f93cSkardel parse_hr_min_sec (time_t start, cch_t * pz) 107abb0f93cSkardel { 108abb0f93cSkardel int lpct = 0; 109abb0f93cSkardel 110abb0f93cSkardel errno = 0; 111abb0f93cSkardel 112abb0f93cSkardel /* For as long as our scanner pointer points to a colon *AND* 113abb0f93cSkardel we've not looped before, then keep looping. (two iterations max) */ 114abb0f93cSkardel while ((*pz == ':') && (lpct++ <= 1)) 115abb0f93cSkardel { 116abb0f93cSkardel unsigned long v = str_const_to_ul (pz+1, &pz, 10); 117abb0f93cSkardel 118abb0f93cSkardel if (errno != 0) 119abb0f93cSkardel return BAD_TIME; 120abb0f93cSkardel 121abb0f93cSkardel start = scale_n_add (v, start, 60); 122abb0f93cSkardel 123abb0f93cSkardel if (errno != 0) 124abb0f93cSkardel return BAD_TIME; 125abb0f93cSkardel } 126abb0f93cSkardel 127abb0f93cSkardel /* allow for trailing spaces */ 128f003fb54Skardel while (isspace ((unsigned char)*pz)) 129f003fb54Skardel pz++; 130abb0f93cSkardel if (*pz != NUL) 131abb0f93cSkardel { 132abb0f93cSkardel errno = EINVAL; 133abb0f93cSkardel return BAD_TIME; 134abb0f93cSkardel } 135abb0f93cSkardel 136abb0f93cSkardel return start; 137abb0f93cSkardel } 138abb0f93cSkardel 139f003fb54Skardel /* Parses a value and returns BASE + value * SCALE, interpreting 140f003fb54Skardel BASE = BAD_TIME with errno set as an error situation, and returning 141f003fb54Skardel BAD_TIME with errno set in an error situation. */ 142abb0f93cSkardel static time_t 143abb0f93cSkardel parse_scaled_value (time_t base, cch_t ** ppz, cch_t * endp, int scale) 144abb0f93cSkardel { 145abb0f93cSkardel cch_t * pz = *ppz; 146abb0f93cSkardel time_t val; 147abb0f93cSkardel 148abb0f93cSkardel if (base == BAD_TIME) 149abb0f93cSkardel return base; 150abb0f93cSkardel 151abb0f93cSkardel errno = 0; 152abb0f93cSkardel val = str_const_to_ul (pz, &pz, 10); 153abb0f93cSkardel if (errno != 0) 154abb0f93cSkardel return BAD_TIME; 155f003fb54Skardel while (isspace ((unsigned char)*pz)) 156f003fb54Skardel pz++; 157abb0f93cSkardel if (pz != endp) 158abb0f93cSkardel { 159abb0f93cSkardel errno = EINVAL; 160abb0f93cSkardel return BAD_TIME; 161abb0f93cSkardel } 162abb0f93cSkardel 163abb0f93cSkardel *ppz = pz; 164abb0f93cSkardel return scale_n_add (base, val, scale); 165abb0f93cSkardel } 166abb0f93cSkardel 167f003fb54Skardel /* Parses the syntax YEAR-MONTH-DAY. 168f003fb54Skardel PS points into the string, after "YEAR", before "-MONTH-DAY". */ 169abb0f93cSkardel static time_t 170abb0f93cSkardel parse_year_month_day (cch_t * pz, cch_t * ps) 171abb0f93cSkardel { 172abb0f93cSkardel time_t res = 0; 173abb0f93cSkardel 174abb0f93cSkardel res = parse_scaled_value (0, &pz, ps, SEC_PER_YEAR); 175abb0f93cSkardel 176f003fb54Skardel pz++; /* over the first '-' */ 177f003fb54Skardel ps = strchr (pz, '-'); 178abb0f93cSkardel if (ps == NULL) 179abb0f93cSkardel { 180abb0f93cSkardel errno = EINVAL; 181abb0f93cSkardel return BAD_TIME; 182abb0f93cSkardel } 183abb0f93cSkardel res = parse_scaled_value (res, &pz, ps, SEC_PER_MONTH); 184abb0f93cSkardel 185f003fb54Skardel pz++; /* over the second '-' */ 186abb0f93cSkardel ps = pz + strlen (pz); 187abb0f93cSkardel return parse_scaled_value (res, &pz, ps, SEC_PER_DAY); 188abb0f93cSkardel } 189abb0f93cSkardel 190f003fb54Skardel /* Parses the syntax YYYYMMDD. */ 191abb0f93cSkardel static time_t 192abb0f93cSkardel parse_yearmonthday (cch_t * in_pz) 193abb0f93cSkardel { 194abb0f93cSkardel time_t res = 0; 195abb0f93cSkardel char buf[8]; 196abb0f93cSkardel cch_t * pz; 197abb0f93cSkardel 198abb0f93cSkardel if (strlen (in_pz) != 8) 199abb0f93cSkardel { 200abb0f93cSkardel errno = EINVAL; 201abb0f93cSkardel return BAD_TIME; 202abb0f93cSkardel } 203abb0f93cSkardel 204abb0f93cSkardel memcpy (buf, in_pz, 4); 205abb0f93cSkardel buf[4] = NUL; 206abb0f93cSkardel pz = buf; 207abb0f93cSkardel res = parse_scaled_value (0, &pz, buf + 4, SEC_PER_YEAR); 208abb0f93cSkardel 209abb0f93cSkardel memcpy (buf, in_pz + 4, 2); 210abb0f93cSkardel buf[2] = NUL; 211abb0f93cSkardel pz = buf; 212abb0f93cSkardel res = parse_scaled_value (res, &pz, buf + 2, SEC_PER_MONTH); 213abb0f93cSkardel 214abb0f93cSkardel memcpy (buf, in_pz + 6, 2); 215abb0f93cSkardel buf[2] = NUL; 216abb0f93cSkardel pz = buf; 217abb0f93cSkardel return parse_scaled_value (res, &pz, buf + 2, SEC_PER_DAY); 218abb0f93cSkardel } 219abb0f93cSkardel 220f003fb54Skardel /* Parses the syntax yy Y mm M ww W dd D. */ 221abb0f93cSkardel static time_t 222abb0f93cSkardel parse_YMWD (cch_t * pz) 223abb0f93cSkardel { 224abb0f93cSkardel time_t res = 0; 225abb0f93cSkardel cch_t * ps = strchr (pz, 'Y'); 226abb0f93cSkardel if (ps != NULL) 227abb0f93cSkardel { 228abb0f93cSkardel res = parse_scaled_value (0, &pz, ps, SEC_PER_YEAR); 229abb0f93cSkardel pz++; 230abb0f93cSkardel } 231abb0f93cSkardel 232abb0f93cSkardel ps = strchr (pz, 'M'); 233abb0f93cSkardel if (ps != NULL) 234abb0f93cSkardel { 235abb0f93cSkardel res = parse_scaled_value (res, &pz, ps, SEC_PER_MONTH); 236abb0f93cSkardel pz++; 237abb0f93cSkardel } 238abb0f93cSkardel 239abb0f93cSkardel ps = strchr (pz, 'W'); 240abb0f93cSkardel if (ps != NULL) 241abb0f93cSkardel { 242abb0f93cSkardel res = parse_scaled_value (res, &pz, ps, SEC_PER_WEEK); 243abb0f93cSkardel pz++; 244abb0f93cSkardel } 245abb0f93cSkardel 246abb0f93cSkardel ps = strchr (pz, 'D'); 247abb0f93cSkardel if (ps != NULL) 248abb0f93cSkardel { 249abb0f93cSkardel res = parse_scaled_value (res, &pz, ps, SEC_PER_DAY); 250abb0f93cSkardel pz++; 251abb0f93cSkardel } 252abb0f93cSkardel 253f003fb54Skardel while (isspace ((unsigned char)*pz)) 254f003fb54Skardel pz++; 255abb0f93cSkardel if (*pz != NUL) 256abb0f93cSkardel { 257abb0f93cSkardel errno = EINVAL; 258abb0f93cSkardel return BAD_TIME; 259abb0f93cSkardel } 260abb0f93cSkardel 261abb0f93cSkardel return res; 262abb0f93cSkardel } 263abb0f93cSkardel 264f003fb54Skardel /* Parses the syntax HH:MM:SS. 265f003fb54Skardel PS points into the string, after "HH", before ":MM:SS". */ 266abb0f93cSkardel static time_t 267abb0f93cSkardel parse_hour_minute_second (cch_t * pz, cch_t * ps) 268abb0f93cSkardel { 269abb0f93cSkardel time_t res = 0; 270abb0f93cSkardel 271abb0f93cSkardel res = parse_scaled_value (0, &pz, ps, SEC_PER_HR); 272abb0f93cSkardel 273f003fb54Skardel pz++; 274f003fb54Skardel ps = strchr (pz, ':'); 275abb0f93cSkardel if (ps == NULL) 276abb0f93cSkardel { 277abb0f93cSkardel errno = EINVAL; 278abb0f93cSkardel return BAD_TIME; 279abb0f93cSkardel } 280abb0f93cSkardel 281abb0f93cSkardel res = parse_scaled_value (res, &pz, ps, SEC_PER_MIN); 282abb0f93cSkardel 283abb0f93cSkardel pz++; 284abb0f93cSkardel ps = pz + strlen (pz); 285abb0f93cSkardel return parse_scaled_value (res, &pz, ps, 1); 286abb0f93cSkardel } 287abb0f93cSkardel 288f003fb54Skardel /* Parses the syntax HHMMSS. */ 289abb0f93cSkardel static time_t 290abb0f93cSkardel parse_hourminutesecond (cch_t * in_pz) 291abb0f93cSkardel { 292abb0f93cSkardel time_t res = 0; 293abb0f93cSkardel char buf[4]; 294abb0f93cSkardel cch_t * pz; 295abb0f93cSkardel 296abb0f93cSkardel if (strlen (in_pz) != 6) 297abb0f93cSkardel { 298abb0f93cSkardel errno = EINVAL; 299abb0f93cSkardel return BAD_TIME; 300abb0f93cSkardel } 301abb0f93cSkardel 302abb0f93cSkardel memcpy (buf, in_pz, 2); 303abb0f93cSkardel buf[2] = NUL; 304abb0f93cSkardel pz = buf; 305abb0f93cSkardel res = parse_scaled_value (0, &pz, buf + 2, SEC_PER_HR); 306abb0f93cSkardel 307abb0f93cSkardel memcpy (buf, in_pz + 2, 2); 308abb0f93cSkardel buf[2] = NUL; 309abb0f93cSkardel pz = buf; 310abb0f93cSkardel res = parse_scaled_value (res, &pz, buf + 2, SEC_PER_MIN); 311abb0f93cSkardel 312abb0f93cSkardel memcpy (buf, in_pz + 4, 2); 313abb0f93cSkardel buf[2] = NUL; 314abb0f93cSkardel pz = buf; 315abb0f93cSkardel return parse_scaled_value (res, &pz, buf + 2, 1); 316abb0f93cSkardel } 317abb0f93cSkardel 318f003fb54Skardel /* Parses the syntax hh H mm M ss S. */ 319abb0f93cSkardel static time_t 320abb0f93cSkardel parse_HMS (cch_t * pz) 321abb0f93cSkardel { 322abb0f93cSkardel time_t res = 0; 323abb0f93cSkardel cch_t * ps = strchr (pz, 'H'); 324abb0f93cSkardel if (ps != NULL) 325abb0f93cSkardel { 326abb0f93cSkardel res = parse_scaled_value (0, &pz, ps, SEC_PER_HR); 327abb0f93cSkardel pz++; 328abb0f93cSkardel } 329abb0f93cSkardel 330abb0f93cSkardel ps = strchr (pz, 'M'); 331abb0f93cSkardel if (ps != NULL) 332abb0f93cSkardel { 333abb0f93cSkardel res = parse_scaled_value (res, &pz, ps, SEC_PER_MIN); 334abb0f93cSkardel pz++; 335abb0f93cSkardel } 336abb0f93cSkardel 337abb0f93cSkardel ps = strchr (pz, 'S'); 338abb0f93cSkardel if (ps != NULL) 339abb0f93cSkardel { 340abb0f93cSkardel res = parse_scaled_value (res, &pz, ps, 1); 341abb0f93cSkardel pz++; 342abb0f93cSkardel } 343abb0f93cSkardel 344f003fb54Skardel while (isspace ((unsigned char)*pz)) 345f003fb54Skardel pz++; 346abb0f93cSkardel if (*pz != NUL) 347abb0f93cSkardel { 348abb0f93cSkardel errno = EINVAL; 349abb0f93cSkardel return BAD_TIME; 350abb0f93cSkardel } 351abb0f93cSkardel 352abb0f93cSkardel return res; 353abb0f93cSkardel } 354abb0f93cSkardel 355f003fb54Skardel /* Parses a time (hours, minutes, seconds) specification in either syntax. */ 356abb0f93cSkardel static time_t 357abb0f93cSkardel parse_time (cch_t * pz) 358abb0f93cSkardel { 359abb0f93cSkardel cch_t * ps; 360abb0f93cSkardel time_t res = 0; 361abb0f93cSkardel 362abb0f93cSkardel /* 363abb0f93cSkardel * Scan for a hyphen 364abb0f93cSkardel */ 365abb0f93cSkardel ps = strchr (pz, ':'); 366abb0f93cSkardel if (ps != NULL) 367abb0f93cSkardel { 368abb0f93cSkardel res = parse_hour_minute_second (pz, ps); 369abb0f93cSkardel } 370abb0f93cSkardel 371abb0f93cSkardel /* 372abb0f93cSkardel * Try for a 'H', 'M' or 'S' suffix 373abb0f93cSkardel */ 374abb0f93cSkardel else if (ps = strpbrk (pz, "HMS"), 375abb0f93cSkardel ps == NULL) 376abb0f93cSkardel { 377abb0f93cSkardel /* Its a YYYYMMDD format: */ 378abb0f93cSkardel res = parse_hourminutesecond (pz); 379abb0f93cSkardel } 380abb0f93cSkardel 381abb0f93cSkardel else 382abb0f93cSkardel res = parse_HMS (pz); 383abb0f93cSkardel 384abb0f93cSkardel return res; 385abb0f93cSkardel } 386abb0f93cSkardel 387f003fb54Skardel /* Returns a substring of the given string, with spaces at the beginning and at 388f003fb54Skardel the end destructively removed, per SNOBOL. */ 389abb0f93cSkardel static char * 390abb0f93cSkardel trim (char * pz) 391abb0f93cSkardel { 392abb0f93cSkardel /* trim leading white space */ 393f003fb54Skardel while (isspace ((unsigned char)*pz)) 394f003fb54Skardel pz++; 395abb0f93cSkardel 396abb0f93cSkardel /* trim trailing white space */ 397abb0f93cSkardel { 398abb0f93cSkardel char * pe = pz + strlen (pz); 399f003fb54Skardel while ((pe > pz) && isspace ((unsigned char)pe[-1])) 400f003fb54Skardel pe--; 401abb0f93cSkardel *pe = NUL; 402abb0f93cSkardel } 403abb0f93cSkardel 404abb0f93cSkardel return pz; 405abb0f93cSkardel } 406abb0f93cSkardel 407abb0f93cSkardel /* 408abb0f93cSkardel * Parse the year/months/days of a time period 409abb0f93cSkardel */ 410abb0f93cSkardel static time_t 411abb0f93cSkardel parse_period (cch_t * in_pz) 412abb0f93cSkardel { 413f003fb54Skardel char * pT; 414abb0f93cSkardel char * ps; 415f003fb54Skardel char * pz = strdup (in_pz); 416abb0f93cSkardel void * fptr = pz; 417abb0f93cSkardel time_t res = 0; 418abb0f93cSkardel 419f003fb54Skardel if (pz == NULL) 420f003fb54Skardel { 421f003fb54Skardel errno = ENOMEM; 422f003fb54Skardel return BAD_TIME; 423f003fb54Skardel } 424f003fb54Skardel 425f003fb54Skardel pT = strchr (pz, 'T'); 426f003fb54Skardel if (pT != NULL) 427abb0f93cSkardel { 428abb0f93cSkardel *(pT++) = NUL; 429abb0f93cSkardel pz = trim (pz); 430abb0f93cSkardel pT = trim (pT); 431abb0f93cSkardel } 432abb0f93cSkardel 433abb0f93cSkardel /* 434abb0f93cSkardel * Scan for a hyphen 435abb0f93cSkardel */ 436abb0f93cSkardel ps = strchr (pz, '-'); 437abb0f93cSkardel if (ps != NULL) 438abb0f93cSkardel { 439abb0f93cSkardel res = parse_year_month_day (pz, ps); 440abb0f93cSkardel } 441abb0f93cSkardel 442abb0f93cSkardel /* 443abb0f93cSkardel * Try for a 'Y', 'M' or 'D' suffix 444abb0f93cSkardel */ 445abb0f93cSkardel else if (ps = strpbrk (pz, "YMWD"), 446abb0f93cSkardel ps == NULL) 447abb0f93cSkardel { 448abb0f93cSkardel /* Its a YYYYMMDD format: */ 449abb0f93cSkardel res = parse_yearmonthday (pz); 450abb0f93cSkardel } 451abb0f93cSkardel 452abb0f93cSkardel else 453abb0f93cSkardel res = parse_YMWD (pz); 454abb0f93cSkardel 455abb0f93cSkardel if ((errno == 0) && (pT != NULL)) 456abb0f93cSkardel { 457abb0f93cSkardel time_t val = parse_time (pT); 458abb0f93cSkardel res = scale_n_add (res, val, 1); 459abb0f93cSkardel } 460abb0f93cSkardel 461abb0f93cSkardel free (fptr); 462abb0f93cSkardel return res; 463abb0f93cSkardel } 464abb0f93cSkardel 465abb0f93cSkardel static time_t 466abb0f93cSkardel parse_non_iso8601 (cch_t * pz) 467abb0f93cSkardel { 468abb0f93cSkardel whats_done_t whatd_we_do = NOTHING_IS_DONE; 469abb0f93cSkardel 470abb0f93cSkardel time_t res = 0; 471abb0f93cSkardel 472abb0f93cSkardel do { 473abb0f93cSkardel time_t val; 474abb0f93cSkardel 475abb0f93cSkardel errno = 0; 476abb0f93cSkardel val = str_const_to_l (pz, &pz, 10); 477abb0f93cSkardel if (errno != 0) 478abb0f93cSkardel goto bad_time; 479abb0f93cSkardel 480abb0f93cSkardel /* IF we find a colon, then we're going to have a seconds value. 481abb0f93cSkardel We will not loop here any more. We cannot already have parsed 482abb0f93cSkardel a minute value and if we've parsed an hour value, then the result 483abb0f93cSkardel value has to be less than an hour. */ 484abb0f93cSkardel if (*pz == ':') 485abb0f93cSkardel { 486abb0f93cSkardel if (whatd_we_do >= MINUTE_IS_DONE) 487abb0f93cSkardel break; 488abb0f93cSkardel 489abb0f93cSkardel val = parse_hr_min_sec (val, pz); 490abb0f93cSkardel 491abb0f93cSkardel if ((whatd_we_do == HOUR_IS_DONE) && (val >= SEC_PER_HR)) 492abb0f93cSkardel break; 493abb0f93cSkardel 494abb0f93cSkardel return scale_n_add (res, val, 1); 495abb0f93cSkardel } 496abb0f93cSkardel 497abb0f93cSkardel { 498abb0f93cSkardel unsigned int mult; 499abb0f93cSkardel 500abb0f93cSkardel /* Skip over white space following the number we just parsed. */ 501f003fb54Skardel while (isspace ((unsigned char)*pz)) 502f003fb54Skardel pz++; 503abb0f93cSkardel 504abb0f93cSkardel switch (*pz) 505abb0f93cSkardel { 506abb0f93cSkardel default: goto bad_time; 507abb0f93cSkardel case NUL: 508abb0f93cSkardel return scale_n_add (res, val, 1); 509abb0f93cSkardel 510abb0f93cSkardel case 'y': case 'Y': 511abb0f93cSkardel if (whatd_we_do >= YEAR_IS_DONE) 512abb0f93cSkardel goto bad_time; 513abb0f93cSkardel mult = SEC_PER_YEAR; 514abb0f93cSkardel whatd_we_do = YEAR_IS_DONE; 515abb0f93cSkardel break; 516abb0f93cSkardel 517abb0f93cSkardel case 'M': 518abb0f93cSkardel if (whatd_we_do >= MONTH_IS_DONE) 519abb0f93cSkardel goto bad_time; 520abb0f93cSkardel mult = SEC_PER_MONTH; 521abb0f93cSkardel whatd_we_do = MONTH_IS_DONE; 522abb0f93cSkardel break; 523abb0f93cSkardel 524abb0f93cSkardel case 'W': 525abb0f93cSkardel if (whatd_we_do >= WEEK_IS_DONE) 526abb0f93cSkardel goto bad_time; 527abb0f93cSkardel mult = SEC_PER_WEEK; 528abb0f93cSkardel whatd_we_do = WEEK_IS_DONE; 529abb0f93cSkardel break; 530abb0f93cSkardel 531abb0f93cSkardel case 'd': case 'D': 532abb0f93cSkardel if (whatd_we_do >= DAY_IS_DONE) 533abb0f93cSkardel goto bad_time; 534abb0f93cSkardel mult = SEC_PER_DAY; 535abb0f93cSkardel whatd_we_do = DAY_IS_DONE; 536abb0f93cSkardel break; 537abb0f93cSkardel 538abb0f93cSkardel case 'h': 539abb0f93cSkardel if (whatd_we_do >= HOUR_IS_DONE) 540abb0f93cSkardel goto bad_time; 541abb0f93cSkardel mult = SEC_PER_HR; 542abb0f93cSkardel whatd_we_do = HOUR_IS_DONE; 543abb0f93cSkardel break; 544abb0f93cSkardel 545abb0f93cSkardel case 'm': 546abb0f93cSkardel if (whatd_we_do >= MINUTE_IS_DONE) 547abb0f93cSkardel goto bad_time; 548abb0f93cSkardel mult = SEC_PER_MIN; 549abb0f93cSkardel whatd_we_do = MINUTE_IS_DONE; 550abb0f93cSkardel break; 551abb0f93cSkardel 552abb0f93cSkardel case 's': 553abb0f93cSkardel mult = 1; 554abb0f93cSkardel whatd_we_do = SECOND_IS_DONE; 555abb0f93cSkardel break; 556abb0f93cSkardel } 557abb0f93cSkardel 558abb0f93cSkardel res = scale_n_add (res, val, mult); 559abb0f93cSkardel 560f003fb54Skardel pz++; 561f003fb54Skardel while (isspace ((unsigned char)*pz)) 562f003fb54Skardel pz++; 563abb0f93cSkardel if (*pz == NUL) 564abb0f93cSkardel return res; 565abb0f93cSkardel 566abb0f93cSkardel if (! isdigit ((unsigned char)*pz)) 567abb0f93cSkardel break; 568abb0f93cSkardel } 569abb0f93cSkardel 570abb0f93cSkardel } while (whatd_we_do < SECOND_IS_DONE); 571abb0f93cSkardel 572abb0f93cSkardel bad_time: 573abb0f93cSkardel errno = EINVAL; 574abb0f93cSkardel return BAD_TIME; 575abb0f93cSkardel } 576abb0f93cSkardel 577abb0f93cSkardel time_t 578abb0f93cSkardel parse_duration (char const * pz) 579abb0f93cSkardel { 580f003fb54Skardel while (isspace ((unsigned char)*pz)) 581f003fb54Skardel pz++; 582abb0f93cSkardel 583f003fb54Skardel switch (*pz) 584abb0f93cSkardel { 585f003fb54Skardel case 'P': 586f003fb54Skardel return parse_period (pz + 1); 587abb0f93cSkardel 588f003fb54Skardel case 'T': 589f003fb54Skardel return parse_time (pz + 1); 590abb0f93cSkardel 591f003fb54Skardel default: 592f003fb54Skardel if (isdigit ((unsigned char)*pz)) 593f003fb54Skardel return parse_non_iso8601 (pz); 594abb0f93cSkardel 595abb0f93cSkardel errno = EINVAL; 596abb0f93cSkardel return BAD_TIME; 597abb0f93cSkardel } 598f003fb54Skardel } 599abb0f93cSkardel 600abb0f93cSkardel /* 601abb0f93cSkardel * Local Variables: 602abb0f93cSkardel * mode: C 603abb0f93cSkardel * c-file-style: "gnu" 604abb0f93cSkardel * indent-tabs-mode: nil 605abb0f93cSkardel * End: 606abb0f93cSkardel * end of parse-duration.c */ 607