1258945Sroberto/* Parse a time duration and return a seconds count
2285612Sdelphij   Copyright (C) 2008-2015 Free Software Foundation, Inc.
3258945Sroberto   Written by Bruce Korb <bkorb@gnu.org>, 2008.
4258945Sroberto
5258945Sroberto   This program is free software: you can redistribute it and/or modify
6280849Scy   it under the terms of the GNU Lesser General Public License as published by
7280849Scy   the Free Software Foundation; either version 2.1 of the License, or
8258945Sroberto   (at your option) any later version.
9258945Sroberto
10258945Sroberto   This program is distributed in the hope that it will be useful,
11258945Sroberto   but WITHOUT ANY WARRANTY; without even the implied warranty of
12258945Sroberto   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13280849Scy   GNU Lesser General Public License for more details.
14258945Sroberto
15280849Scy   You should have received a copy of the GNU Lesser General Public License
16258945Sroberto   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
17258945Sroberto
18258945Sroberto#include <config.h>
19258945Sroberto
20258945Sroberto/* Specification.  */
21258945Sroberto#include "parse-duration.h"
22258945Sroberto
23258945Sroberto#include <ctype.h>
24258945Sroberto#include <errno.h>
25258945Sroberto#include <limits.h>
26258945Sroberto#include <stdio.h>
27258945Sroberto#include <stdlib.h>
28258945Sroberto#include <string.h>
29258945Sroberto
30280849Scy#include "intprops.h"
31280849Scy
32258945Sroberto#ifndef NUL
33258945Sroberto#define NUL '\0'
34258945Sroberto#endif
35258945Sroberto
36258945Sroberto#define cch_t char const
37258945Sroberto
38258945Srobertotypedef enum {
39258945Sroberto  NOTHING_IS_DONE,
40258945Sroberto  YEAR_IS_DONE,
41258945Sroberto  MONTH_IS_DONE,
42258945Sroberto  WEEK_IS_DONE,
43258945Sroberto  DAY_IS_DONE,
44258945Sroberto  HOUR_IS_DONE,
45258945Sroberto  MINUTE_IS_DONE,
46258945Sroberto  SECOND_IS_DONE
47258945Sroberto} whats_done_t;
48258945Sroberto
49258945Sroberto#define SEC_PER_MIN     60
50258945Sroberto#define SEC_PER_HR      (SEC_PER_MIN * 60)
51258945Sroberto#define SEC_PER_DAY     (SEC_PER_HR  * 24)
52258945Sroberto#define SEC_PER_WEEK    (SEC_PER_DAY * 7)
53258945Sroberto#define SEC_PER_MONTH   (SEC_PER_DAY * 30)
54258945Sroberto#define SEC_PER_YEAR    (SEC_PER_DAY * 365)
55258945Sroberto
56280849Scy#undef  MAX_DURATION
57280849Scy#define MAX_DURATION    TYPE_MAXIMUM(time_t)
58258945Sroberto
59258945Sroberto/* Wrapper around strtoul that does not require a cast.  */
60280849Scystatic unsigned long
61258945Srobertostr_const_to_ul (cch_t * str, cch_t ** ppz, int base)
62258945Sroberto{
63294569Sdelphij  char * pz;
64294569Sdelphij  int rv = strtoul (str, &pz, base);
65294569Sdelphij  *ppz = pz;
66294569Sdelphij  return rv;
67258945Sroberto}
68258945Sroberto
69258945Sroberto/* Wrapper around strtol that does not require a cast.  */
70280849Scystatic long
71258945Srobertostr_const_to_l (cch_t * str, cch_t ** ppz, int base)
72258945Sroberto{
73294569Sdelphij  char * pz;
74294569Sdelphij  int rv = strtol (str, &pz, base);
75294569Sdelphij  *ppz = pz;
76294569Sdelphij  return rv;
77258945Sroberto}
78258945Sroberto
79258945Sroberto/* Returns BASE + VAL * SCALE, interpreting BASE = BAD_TIME
80258945Sroberto   with errno set as an error situation, and returning BAD_TIME
81258945Sroberto   with errno set in an error situation.  */
82280849Scystatic time_t
83258945Srobertoscale_n_add (time_t base, time_t val, int scale)
84258945Sroberto{
85258945Sroberto  if (base == BAD_TIME)
86258945Sroberto    {
87258945Sroberto      if (errno == 0)
88258945Sroberto        errno = EINVAL;
89258945Sroberto      return BAD_TIME;
90258945Sroberto    }
91258945Sroberto
92280849Scy  if (val > MAX_DURATION / scale)
93258945Sroberto    {
94258945Sroberto      errno = ERANGE;
95258945Sroberto      return BAD_TIME;
96258945Sroberto    }
97258945Sroberto
98258945Sroberto  val *= scale;
99280849Scy  if (base > MAX_DURATION - val)
100258945Sroberto    {
101258945Sroberto      errno = ERANGE;
102258945Sroberto      return BAD_TIME;
103258945Sroberto    }
104258945Sroberto
105258945Sroberto  return base + val;
106258945Sroberto}
107258945Sroberto
108258945Sroberto/* After a number HH has been parsed, parse subsequent :MM or :MM:SS.  */
109258945Srobertostatic time_t
110258945Srobertoparse_hr_min_sec (time_t start, cch_t * pz)
111258945Sroberto{
112258945Sroberto  int lpct = 0;
113258945Sroberto
114258945Sroberto  errno = 0;
115258945Sroberto
116258945Sroberto  /* For as long as our scanner pointer points to a colon *AND*
117258945Sroberto     we've not looped before, then keep looping.  (two iterations max) */
118258945Sroberto  while ((*pz == ':') && (lpct++ <= 1))
119258945Sroberto    {
120258945Sroberto      unsigned long v = str_const_to_ul (pz+1, &pz, 10);
121258945Sroberto
122258945Sroberto      if (errno != 0)
123258945Sroberto        return BAD_TIME;
124258945Sroberto
125258945Sroberto      start = scale_n_add (v, start, 60);
126258945Sroberto
127258945Sroberto      if (errno != 0)
128258945Sroberto        return BAD_TIME;
129258945Sroberto    }
130258945Sroberto
131258945Sroberto  /* allow for trailing spaces */
132258945Sroberto  while (isspace ((unsigned char)*pz))
133258945Sroberto    pz++;
134258945Sroberto  if (*pz != NUL)
135258945Sroberto    {
136258945Sroberto      errno = EINVAL;
137258945Sroberto      return BAD_TIME;
138258945Sroberto    }
139258945Sroberto
140258945Sroberto  return start;
141258945Sroberto}
142258945Sroberto
143258945Sroberto/* Parses a value and returns BASE + value * SCALE, interpreting
144258945Sroberto   BASE = BAD_TIME with errno set as an error situation, and returning
145258945Sroberto   BAD_TIME with errno set in an error situation.  */
146258945Srobertostatic time_t
147258945Srobertoparse_scaled_value (time_t base, cch_t ** ppz, cch_t * endp, int scale)
148258945Sroberto{
149258945Sroberto  cch_t * pz = *ppz;
150258945Sroberto  time_t val;
151258945Sroberto
152258945Sroberto  if (base == BAD_TIME)
153258945Sroberto    return base;
154258945Sroberto
155258945Sroberto  errno = 0;
156258945Sroberto  val = str_const_to_ul (pz, &pz, 10);
157258945Sroberto  if (errno != 0)
158258945Sroberto    return BAD_TIME;
159258945Sroberto  while (isspace ((unsigned char)*pz))
160258945Sroberto    pz++;
161258945Sroberto  if (pz != endp)
162258945Sroberto    {
163258945Sroberto      errno = EINVAL;
164258945Sroberto      return BAD_TIME;
165258945Sroberto    }
166258945Sroberto
167258945Sroberto  *ppz = pz;
168258945Sroberto  return scale_n_add (base, val, scale);
169258945Sroberto}
170258945Sroberto
171258945Sroberto/* Parses the syntax YEAR-MONTH-DAY.
172258945Sroberto   PS points into the string, after "YEAR", before "-MONTH-DAY".  */
173258945Srobertostatic time_t
174258945Srobertoparse_year_month_day (cch_t * pz, cch_t * ps)
175258945Sroberto{
176258945Sroberto  time_t res = 0;
177258945Sroberto
178258945Sroberto  res = parse_scaled_value (0, &pz, ps, SEC_PER_YEAR);
179258945Sroberto
180258945Sroberto  pz++; /* over the first '-' */
181258945Sroberto  ps = strchr (pz, '-');
182258945Sroberto  if (ps == NULL)
183258945Sroberto    {
184258945Sroberto      errno = EINVAL;
185258945Sroberto      return BAD_TIME;
186258945Sroberto    }
187258945Sroberto  res = parse_scaled_value (res, &pz, ps, SEC_PER_MONTH);
188258945Sroberto
189258945Sroberto  pz++; /* over the second '-' */
190258945Sroberto  ps = pz + strlen (pz);
191258945Sroberto  return parse_scaled_value (res, &pz, ps, SEC_PER_DAY);
192258945Sroberto}
193258945Sroberto
194258945Sroberto/* Parses the syntax YYYYMMDD.  */
195258945Srobertostatic time_t
196258945Srobertoparse_yearmonthday (cch_t * in_pz)
197258945Sroberto{
198258945Sroberto  time_t res = 0;
199258945Sroberto  char   buf[8];
200258945Sroberto  cch_t * pz;
201258945Sroberto
202258945Sroberto  if (strlen (in_pz) != 8)
203258945Sroberto    {
204258945Sroberto      errno = EINVAL;
205258945Sroberto      return BAD_TIME;
206258945Sroberto    }
207258945Sroberto
208258945Sroberto  memcpy (buf, in_pz, 4);
209258945Sroberto  buf[4] = NUL;
210258945Sroberto  pz = buf;
211258945Sroberto  res = parse_scaled_value (0, &pz, buf + 4, SEC_PER_YEAR);
212258945Sroberto
213258945Sroberto  memcpy (buf, in_pz + 4, 2);
214258945Sroberto  buf[2] = NUL;
215258945Sroberto  pz =   buf;
216258945Sroberto  res = parse_scaled_value (res, &pz, buf + 2, SEC_PER_MONTH);
217258945Sroberto
218258945Sroberto  memcpy (buf, in_pz + 6, 2);
219258945Sroberto  buf[2] = NUL;
220258945Sroberto  pz =   buf;
221258945Sroberto  return parse_scaled_value (res, &pz, buf + 2, SEC_PER_DAY);
222258945Sroberto}
223258945Sroberto
224258945Sroberto/* Parses the syntax yy Y mm M ww W dd D.  */
225258945Srobertostatic time_t
226258945Srobertoparse_YMWD (cch_t * pz)
227258945Sroberto{
228258945Sroberto  time_t res = 0;
229258945Sroberto  cch_t * ps = strchr (pz, 'Y');
230258945Sroberto  if (ps != NULL)
231258945Sroberto    {
232258945Sroberto      res = parse_scaled_value (0, &pz, ps, SEC_PER_YEAR);
233258945Sroberto      pz++;
234258945Sroberto    }
235258945Sroberto
236258945Sroberto  ps = strchr (pz, 'M');
237258945Sroberto  if (ps != NULL)
238258945Sroberto    {
239258945Sroberto      res = parse_scaled_value (res, &pz, ps, SEC_PER_MONTH);
240258945Sroberto      pz++;
241258945Sroberto    }
242258945Sroberto
243258945Sroberto  ps = strchr (pz, 'W');
244258945Sroberto  if (ps != NULL)
245258945Sroberto    {
246258945Sroberto      res = parse_scaled_value (res, &pz, ps, SEC_PER_WEEK);
247258945Sroberto      pz++;
248258945Sroberto    }
249258945Sroberto
250258945Sroberto  ps = strchr (pz, 'D');
251258945Sroberto  if (ps != NULL)
252258945Sroberto    {
253258945Sroberto      res = parse_scaled_value (res, &pz, ps, SEC_PER_DAY);
254258945Sroberto      pz++;
255258945Sroberto    }
256258945Sroberto
257258945Sroberto  while (isspace ((unsigned char)*pz))
258258945Sroberto    pz++;
259258945Sroberto  if (*pz != NUL)
260258945Sroberto    {
261258945Sroberto      errno = EINVAL;
262258945Sroberto      return BAD_TIME;
263258945Sroberto    }
264258945Sroberto
265258945Sroberto  return res;
266258945Sroberto}
267258945Sroberto
268258945Sroberto/* Parses the syntax HH:MM:SS.
269258945Sroberto   PS points into the string, after "HH", before ":MM:SS".  */
270258945Srobertostatic time_t
271258945Srobertoparse_hour_minute_second (cch_t * pz, cch_t * ps)
272258945Sroberto{
273258945Sroberto  time_t res = 0;
274258945Sroberto
275258945Sroberto  res = parse_scaled_value (0, &pz, ps, SEC_PER_HR);
276258945Sroberto
277258945Sroberto  pz++;
278258945Sroberto  ps = strchr (pz, ':');
279258945Sroberto  if (ps == NULL)
280258945Sroberto    {
281258945Sroberto      errno = EINVAL;
282258945Sroberto      return BAD_TIME;
283258945Sroberto    }
284258945Sroberto
285258945Sroberto  res = parse_scaled_value (res, &pz, ps, SEC_PER_MIN);
286258945Sroberto
287258945Sroberto  pz++;
288258945Sroberto  ps = pz + strlen (pz);
289258945Sroberto  return parse_scaled_value (res, &pz, ps, 1);
290258945Sroberto}
291258945Sroberto
292258945Sroberto/* Parses the syntax HHMMSS.  */
293258945Srobertostatic time_t
294258945Srobertoparse_hourminutesecond (cch_t * in_pz)
295258945Sroberto{
296258945Sroberto  time_t res = 0;
297258945Sroberto  char   buf[4];
298258945Sroberto  cch_t * pz;
299258945Sroberto
300258945Sroberto  if (strlen (in_pz) != 6)
301258945Sroberto    {
302258945Sroberto      errno = EINVAL;
303258945Sroberto      return BAD_TIME;
304258945Sroberto    }
305258945Sroberto
306258945Sroberto  memcpy (buf, in_pz, 2);
307258945Sroberto  buf[2] = NUL;
308258945Sroberto  pz = buf;
309258945Sroberto  res = parse_scaled_value (0, &pz, buf + 2, SEC_PER_HR);
310258945Sroberto
311258945Sroberto  memcpy (buf, in_pz + 2, 2);
312258945Sroberto  buf[2] = NUL;
313258945Sroberto  pz =   buf;
314258945Sroberto  res = parse_scaled_value (res, &pz, buf + 2, SEC_PER_MIN);
315258945Sroberto
316258945Sroberto  memcpy (buf, in_pz + 4, 2);
317258945Sroberto  buf[2] = NUL;
318258945Sroberto  pz =   buf;
319258945Sroberto  return parse_scaled_value (res, &pz, buf + 2, 1);
320258945Sroberto}
321258945Sroberto
322258945Sroberto/* Parses the syntax hh H mm M ss S.  */
323258945Srobertostatic time_t
324258945Srobertoparse_HMS (cch_t * pz)
325258945Sroberto{
326258945Sroberto  time_t res = 0;
327258945Sroberto  cch_t * ps = strchr (pz, 'H');
328258945Sroberto  if (ps != NULL)
329258945Sroberto    {
330258945Sroberto      res = parse_scaled_value (0, &pz, ps, SEC_PER_HR);
331258945Sroberto      pz++;
332258945Sroberto    }
333258945Sroberto
334258945Sroberto  ps = strchr (pz, 'M');
335258945Sroberto  if (ps != NULL)
336258945Sroberto    {
337258945Sroberto      res = parse_scaled_value (res, &pz, ps, SEC_PER_MIN);
338258945Sroberto      pz++;
339258945Sroberto    }
340258945Sroberto
341258945Sroberto  ps = strchr (pz, 'S');
342258945Sroberto  if (ps != NULL)
343258945Sroberto    {
344258945Sroberto      res = parse_scaled_value (res, &pz, ps, 1);
345258945Sroberto      pz++;
346258945Sroberto    }
347258945Sroberto
348258945Sroberto  while (isspace ((unsigned char)*pz))
349258945Sroberto    pz++;
350258945Sroberto  if (*pz != NUL)
351258945Sroberto    {
352258945Sroberto      errno = EINVAL;
353258945Sroberto      return BAD_TIME;
354258945Sroberto    }
355258945Sroberto
356258945Sroberto  return res;
357258945Sroberto}
358258945Sroberto
359258945Sroberto/* Parses a time (hours, minutes, seconds) specification in either syntax.  */
360258945Srobertostatic time_t
361258945Srobertoparse_time (cch_t * pz)
362258945Sroberto{
363258945Sroberto  cch_t * ps;
364258945Sroberto  time_t  res = 0;
365258945Sroberto
366258945Sroberto  /*
367258945Sroberto   *  Scan for a hyphen
368258945Sroberto   */
369258945Sroberto  ps = strchr (pz, ':');
370258945Sroberto  if (ps != NULL)
371258945Sroberto    {
372258945Sroberto      res = parse_hour_minute_second (pz, ps);
373258945Sroberto    }
374258945Sroberto
375258945Sroberto  /*
376258945Sroberto   *  Try for a 'H', 'M' or 'S' suffix
377258945Sroberto   */
378258945Sroberto  else if (ps = strpbrk (pz, "HMS"),
379258945Sroberto           ps == NULL)
380258945Sroberto    {
381258945Sroberto      /* Its a YYYYMMDD format: */
382258945Sroberto      res = parse_hourminutesecond (pz);
383258945Sroberto    }
384258945Sroberto
385258945Sroberto  else
386258945Sroberto    res = parse_HMS (pz);
387258945Sroberto
388258945Sroberto  return res;
389258945Sroberto}
390258945Sroberto
391258945Sroberto/* Returns a substring of the given string, with spaces at the beginning and at
392258945Sroberto   the end destructively removed, per SNOBOL.  */
393258945Srobertostatic char *
394258945Srobertotrim (char * pz)
395258945Sroberto{
396258945Sroberto  /* trim leading white space */
397258945Sroberto  while (isspace ((unsigned char)*pz))
398258945Sroberto    pz++;
399258945Sroberto
400258945Sroberto  /* trim trailing white space */
401258945Sroberto  {
402258945Sroberto    char * pe = pz + strlen (pz);
403258945Sroberto    while ((pe > pz) && isspace ((unsigned char)pe[-1]))
404258945Sroberto      pe--;
405258945Sroberto    *pe = NUL;
406258945Sroberto  }
407258945Sroberto
408258945Sroberto  return pz;
409258945Sroberto}
410258945Sroberto
411258945Sroberto/*
412258945Sroberto *  Parse the year/months/days of a time period
413258945Sroberto */
414258945Srobertostatic time_t
415258945Srobertoparse_period (cch_t * in_pz)
416258945Sroberto{
417258945Sroberto  char * pT;
418258945Sroberto  char * ps;
419258945Sroberto  char * pz   = strdup (in_pz);
420258945Sroberto  void * fptr = pz;
421258945Sroberto  time_t res  = 0;
422258945Sroberto
423258945Sroberto  if (pz == NULL)
424258945Sroberto    {
425258945Sroberto      errno = ENOMEM;
426258945Sroberto      return BAD_TIME;
427258945Sroberto    }
428258945Sroberto
429258945Sroberto  pT = strchr (pz, 'T');
430258945Sroberto  if (pT != NULL)
431258945Sroberto    {
432258945Sroberto      *(pT++) = NUL;
433258945Sroberto      pz = trim (pz);
434258945Sroberto      pT = trim (pT);
435258945Sroberto    }
436258945Sroberto
437258945Sroberto  /*
438258945Sroberto   *  Scan for a hyphen
439258945Sroberto   */
440258945Sroberto  ps = strchr (pz, '-');
441258945Sroberto  if (ps != NULL)
442258945Sroberto    {
443258945Sroberto      res = parse_year_month_day (pz, ps);
444258945Sroberto    }
445258945Sroberto
446258945Sroberto  /*
447258945Sroberto   *  Try for a 'Y', 'M' or 'D' suffix
448258945Sroberto   */
449258945Sroberto  else if (ps = strpbrk (pz, "YMWD"),
450258945Sroberto           ps == NULL)
451258945Sroberto    {
452258945Sroberto      /* Its a YYYYMMDD format: */
453258945Sroberto      res = parse_yearmonthday (pz);
454258945Sroberto    }
455258945Sroberto
456258945Sroberto  else
457258945Sroberto    res = parse_YMWD (pz);
458258945Sroberto
459258945Sroberto  if ((errno == 0) && (pT != NULL))
460258945Sroberto    {
461258945Sroberto      time_t val = parse_time (pT);
462258945Sroberto      res = scale_n_add (res, val, 1);
463258945Sroberto    }
464258945Sroberto
465258945Sroberto  free (fptr);
466258945Sroberto  return res;
467258945Sroberto}
468258945Sroberto
469258945Srobertostatic time_t
470258945Srobertoparse_non_iso8601 (cch_t * pz)
471258945Sroberto{
472258945Sroberto  whats_done_t whatd_we_do = NOTHING_IS_DONE;
473258945Sroberto
474258945Sroberto  time_t res = 0;
475258945Sroberto
476258945Sroberto  do  {
477258945Sroberto    time_t val;
478258945Sroberto
479258945Sroberto    errno = 0;
480258945Sroberto    val = str_const_to_l (pz, &pz, 10);
481258945Sroberto    if (errno != 0)
482258945Sroberto      goto bad_time;
483258945Sroberto
484258945Sroberto    /*  IF we find a colon, then we're going to have a seconds value.
485258945Sroberto        We will not loop here any more.  We cannot already have parsed
486258945Sroberto        a minute value and if we've parsed an hour value, then the result
487258945Sroberto        value has to be less than an hour. */
488258945Sroberto    if (*pz == ':')
489258945Sroberto      {
490258945Sroberto        if (whatd_we_do >= MINUTE_IS_DONE)
491258945Sroberto          break;
492258945Sroberto
493258945Sroberto        val = parse_hr_min_sec (val, pz);
494258945Sroberto
495258945Sroberto        if ((whatd_we_do == HOUR_IS_DONE) && (val >= SEC_PER_HR))
496258945Sroberto          break;
497258945Sroberto
498258945Sroberto        return scale_n_add (res, val, 1);
499258945Sroberto      }
500258945Sroberto
501258945Sroberto    {
502258945Sroberto      unsigned int mult;
503258945Sroberto
504258945Sroberto      /*  Skip over white space following the number we just parsed. */
505258945Sroberto      while (isspace ((unsigned char)*pz))
506258945Sroberto        pz++;
507258945Sroberto
508258945Sroberto      switch (*pz)
509258945Sroberto        {
510258945Sroberto        default:  goto bad_time;
511258945Sroberto        case NUL:
512258945Sroberto          return scale_n_add (res, val, 1);
513258945Sroberto
514258945Sroberto        case 'y': case 'Y':
515258945Sroberto          if (whatd_we_do >= YEAR_IS_DONE)
516258945Sroberto            goto bad_time;
517258945Sroberto          mult = SEC_PER_YEAR;
518258945Sroberto          whatd_we_do = YEAR_IS_DONE;
519258945Sroberto          break;
520258945Sroberto
521258945Sroberto        case 'M':
522258945Sroberto          if (whatd_we_do >= MONTH_IS_DONE)
523258945Sroberto            goto bad_time;
524258945Sroberto          mult = SEC_PER_MONTH;
525258945Sroberto          whatd_we_do = MONTH_IS_DONE;
526258945Sroberto          break;
527258945Sroberto
528258945Sroberto        case 'W':
529258945Sroberto          if (whatd_we_do >= WEEK_IS_DONE)
530258945Sroberto            goto bad_time;
531258945Sroberto          mult = SEC_PER_WEEK;
532258945Sroberto          whatd_we_do = WEEK_IS_DONE;
533258945Sroberto          break;
534258945Sroberto
535258945Sroberto        case 'd': case 'D':
536258945Sroberto          if (whatd_we_do >= DAY_IS_DONE)
537258945Sroberto            goto bad_time;
538258945Sroberto          mult = SEC_PER_DAY;
539258945Sroberto          whatd_we_do = DAY_IS_DONE;
540258945Sroberto          break;
541258945Sroberto
542258945Sroberto        case 'h':
543258945Sroberto          if (whatd_we_do >= HOUR_IS_DONE)
544258945Sroberto            goto bad_time;
545258945Sroberto          mult = SEC_PER_HR;
546258945Sroberto          whatd_we_do = HOUR_IS_DONE;
547258945Sroberto          break;
548258945Sroberto
549258945Sroberto        case 'm':
550258945Sroberto          if (whatd_we_do >= MINUTE_IS_DONE)
551258945Sroberto            goto bad_time;
552258945Sroberto          mult = SEC_PER_MIN;
553258945Sroberto          whatd_we_do = MINUTE_IS_DONE;
554258945Sroberto          break;
555258945Sroberto
556258945Sroberto        case 's':
557258945Sroberto          mult = 1;
558258945Sroberto          whatd_we_do = SECOND_IS_DONE;
559258945Sroberto          break;
560258945Sroberto        }
561258945Sroberto
562258945Sroberto      res = scale_n_add (res, val, mult);
563258945Sroberto
564258945Sroberto      pz++;
565258945Sroberto      while (isspace ((unsigned char)*pz))
566258945Sroberto        pz++;
567258945Sroberto      if (*pz == NUL)
568258945Sroberto        return res;
569258945Sroberto
570258945Sroberto      if (! isdigit ((unsigned char)*pz))
571258945Sroberto        break;
572258945Sroberto    }
573258945Sroberto
574258945Sroberto  } while (whatd_we_do < SECOND_IS_DONE);
575258945Sroberto
576258945Sroberto bad_time:
577258945Sroberto  errno = EINVAL;
578258945Sroberto  return BAD_TIME;
579258945Sroberto}
580258945Sroberto
581258945Srobertotime_t
582258945Srobertoparse_duration (char const * pz)
583258945Sroberto{
584258945Sroberto  while (isspace ((unsigned char)*pz))
585258945Sroberto    pz++;
586258945Sroberto
587258945Sroberto  switch (*pz)
588258945Sroberto    {
589258945Sroberto    case 'P':
590258945Sroberto      return parse_period (pz + 1);
591258945Sroberto
592258945Sroberto    case 'T':
593258945Sroberto      return parse_time (pz + 1);
594258945Sroberto
595258945Sroberto    default:
596258945Sroberto      if (isdigit ((unsigned char)*pz))
597258945Sroberto        return parse_non_iso8601 (pz);
598258945Sroberto
599258945Sroberto      errno = EINVAL;
600258945Sroberto      return BAD_TIME;
601258945Sroberto    }
602258945Sroberto}
603258945Sroberto
604258945Sroberto/*
605258945Sroberto * Local Variables:
606258945Sroberto * mode: C
607258945Sroberto * c-file-style: "gnu"
608258945Sroberto * indent-tabs-mode: nil
609258945Sroberto * End:
610258945Sroberto * end of parse-duration.c */
611