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>