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