parse-duration.c revision 290001
1290001Sglebius/* Parse a time duration and return a seconds count
2290001Sglebius   Copyright (C) 2008-2015 Free Software Foundation, Inc.
3290001Sglebius   Written by Bruce Korb <bkorb@gnu.org>, 2008.
4290001Sglebius
5290001Sglebius   This program is free software: you can redistribute it and/or modify
6290001Sglebius   it under the terms of the GNU Lesser General Public License as published by
7290001Sglebius   the Free Software Foundation; either version 2.1 of the License, or
8290001Sglebius   (at your option) any later version.
9290001Sglebius
10290001Sglebius   This program is distributed in the hope that it will be useful,
11290001Sglebius   but WITHOUT ANY WARRANTY; without even the implied warranty of
12290001Sglebius   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13290001Sglebius   GNU Lesser General Public License for more details.
14290001Sglebius
15290001Sglebius   You should have received a copy of the GNU Lesser General Public License
16290001Sglebius   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
17290001Sglebius
18290001Sglebius#include <config.h>
19290001Sglebius
20290001Sglebius/* Specification.  */
21290001Sglebius#include "parse-duration.h"
22290001Sglebius
23290001Sglebius#include <ctype.h>
24290001Sglebius#include <errno.h>
25290001Sglebius#include <limits.h>
26290001Sglebius#include <stdio.h>
27290001Sglebius#include <stdlib.h>
28290001Sglebius#include <string.h>
29290001Sglebius
30290001Sglebius#include "intprops.h"
31290001Sglebius
32290001Sglebius#ifndef NUL
33290001Sglebius#define NUL '\0'
34290001Sglebius#endif
35290001Sglebius
36290001Sglebius#define cch_t char const
37290001Sglebius
38290001Sglebiustypedef enum {
39290001Sglebius  NOTHING_IS_DONE,
40290001Sglebius  YEAR_IS_DONE,
41290001Sglebius  MONTH_IS_DONE,
42290001Sglebius  WEEK_IS_DONE,
43290001Sglebius  DAY_IS_DONE,
44290001Sglebius  HOUR_IS_DONE,
45290001Sglebius  MINUTE_IS_DONE,
46290001Sglebius  SECOND_IS_DONE
47290001Sglebius} whats_done_t;
48290001Sglebius
49290001Sglebius#define SEC_PER_MIN     60
50290001Sglebius#define SEC_PER_HR      (SEC_PER_MIN * 60)
51290001Sglebius#define SEC_PER_DAY     (SEC_PER_HR  * 24)
52290001Sglebius#define SEC_PER_WEEK    (SEC_PER_DAY * 7)
53290001Sglebius#define SEC_PER_MONTH   (SEC_PER_DAY * 30)
54290001Sglebius#define SEC_PER_YEAR    (SEC_PER_DAY * 365)
55290001Sglebius
56290001Sglebius#undef  MAX_DURATION
57290001Sglebius#define MAX_DURATION    TYPE_MAXIMUM(time_t)
58290001Sglebius
59290001Sglebius/* Wrapper around strtoul that does not require a cast.  */
60290001Sglebiusstatic unsigned long
61290001Sglebiusstr_const_to_ul (cch_t * str, cch_t ** ppz, int base)
62290001Sglebius{
63290001Sglebius  return strtoul (str, (char **)ppz, base);
64290001Sglebius}
65290001Sglebius
66290001Sglebius/* Wrapper around strtol that does not require a cast.  */
67290001Sglebiusstatic long
68290001Sglebiusstr_const_to_l (cch_t * str, cch_t ** ppz, int base)
69290001Sglebius{
70290001Sglebius  return strtol (str, (char **)ppz, base);
71290001Sglebius}
72290001Sglebius
73290001Sglebius/* Returns BASE + VAL * SCALE, interpreting BASE = BAD_TIME
74290001Sglebius   with errno set as an error situation, and returning BAD_TIME
75290001Sglebius   with errno set in an error situation.  */
76290001Sglebiusstatic time_t
77290001Sglebiusscale_n_add (time_t base, time_t val, int scale)
78290001Sglebius{
79290001Sglebius  if (base == BAD_TIME)
80290001Sglebius    {
81290001Sglebius      if (errno == 0)
82290001Sglebius        errno = EINVAL;
83290001Sglebius      return BAD_TIME;
84290001Sglebius    }
85290001Sglebius
86290001Sglebius  if (val > MAX_DURATION / scale)
87290001Sglebius    {
88290001Sglebius      errno = ERANGE;
89290001Sglebius      return BAD_TIME;
90290001Sglebius    }
91290001Sglebius
92290001Sglebius  val *= scale;
93290001Sglebius  if (base > MAX_DURATION - val)
94290001Sglebius    {
95290001Sglebius      errno = ERANGE;
96290001Sglebius      return BAD_TIME;
97290001Sglebius    }
98290001Sglebius
99290001Sglebius  return base + val;
100290001Sglebius}
101290001Sglebius
102290001Sglebius/* After a number HH has been parsed, parse subsequent :MM or :MM:SS.  */
103290001Sglebiusstatic time_t
104290001Sglebiusparse_hr_min_sec (time_t start, cch_t * pz)
105290001Sglebius{
106290001Sglebius  int lpct = 0;
107290001Sglebius
108290001Sglebius  errno = 0;
109290001Sglebius
110290001Sglebius  /* For as long as our scanner pointer points to a colon *AND*
111290001Sglebius     we've not looped before, then keep looping.  (two iterations max) */
112290001Sglebius  while ((*pz == ':') && (lpct++ <= 1))
113290001Sglebius    {
114290001Sglebius      unsigned long v = str_const_to_ul (pz+1, &pz, 10);
115290001Sglebius
116290001Sglebius      if (errno != 0)
117290001Sglebius        return BAD_TIME;
118290001Sglebius
119290001Sglebius      start = scale_n_add (v, start, 60);
120290001Sglebius
121290001Sglebius      if (errno != 0)
122290001Sglebius        return BAD_TIME;
123290001Sglebius    }
124290001Sglebius
125290001Sglebius  /* allow for trailing spaces */
126290001Sglebius  while (isspace ((unsigned char)*pz))
127290001Sglebius    pz++;
128290001Sglebius  if (*pz != NUL)
129290001Sglebius    {
130290001Sglebius      errno = EINVAL;
131290001Sglebius      return BAD_TIME;
132290001Sglebius    }
133290001Sglebius
134290001Sglebius  return start;
135290001Sglebius}
136290001Sglebius
137290001Sglebius/* Parses a value and returns BASE + value * SCALE, interpreting
138290001Sglebius   BASE = BAD_TIME with errno set as an error situation, and returning
139290001Sglebius   BAD_TIME with errno set in an error situation.  */
140290001Sglebiusstatic time_t
141290001Sglebiusparse_scaled_value (time_t base, cch_t ** ppz, cch_t * endp, int scale)
142290001Sglebius{
143290001Sglebius  cch_t * pz = *ppz;
144290001Sglebius  time_t val;
145290001Sglebius
146290001Sglebius  if (base == BAD_TIME)
147290001Sglebius    return base;
148290001Sglebius
149290001Sglebius  errno = 0;
150290001Sglebius  val = str_const_to_ul (pz, &pz, 10);
151290001Sglebius  if (errno != 0)
152290001Sglebius    return BAD_TIME;
153290001Sglebius  while (isspace ((unsigned char)*pz))
154290001Sglebius    pz++;
155290001Sglebius  if (pz != endp)
156290001Sglebius    {
157290001Sglebius      errno = EINVAL;
158290001Sglebius      return BAD_TIME;
159290001Sglebius    }
160290001Sglebius
161290001Sglebius  *ppz = pz;
162290001Sglebius  return scale_n_add (base, val, scale);
163290001Sglebius}
164290001Sglebius
165290001Sglebius/* Parses the syntax YEAR-MONTH-DAY.
166290001Sglebius   PS points into the string, after "YEAR", before "-MONTH-DAY".  */
167290001Sglebiusstatic time_t
168290001Sglebiusparse_year_month_day (cch_t * pz, cch_t * ps)
169290001Sglebius{
170290001Sglebius  time_t res = 0;
171290001Sglebius
172290001Sglebius  res = parse_scaled_value (0, &pz, ps, SEC_PER_YEAR);
173290001Sglebius
174290001Sglebius  pz++; /* over the first '-' */
175290001Sglebius  ps = strchr (pz, '-');
176290001Sglebius  if (ps == NULL)
177290001Sglebius    {
178290001Sglebius      errno = EINVAL;
179290001Sglebius      return BAD_TIME;
180290001Sglebius    }
181290001Sglebius  res = parse_scaled_value (res, &pz, ps, SEC_PER_MONTH);
182290001Sglebius
183290001Sglebius  pz++; /* over the second '-' */
184290001Sglebius  ps = pz + strlen (pz);
185290001Sglebius  return parse_scaled_value (res, &pz, ps, SEC_PER_DAY);
186290001Sglebius}
187290001Sglebius
188290001Sglebius/* Parses the syntax YYYYMMDD.  */
189290001Sglebiusstatic time_t
190290001Sglebiusparse_yearmonthday (cch_t * in_pz)
191290001Sglebius{
192290001Sglebius  time_t res = 0;
193290001Sglebius  char   buf[8];
194290001Sglebius  cch_t * pz;
195290001Sglebius
196290001Sglebius  if (strlen (in_pz) != 8)
197290001Sglebius    {
198290001Sglebius      errno = EINVAL;
199290001Sglebius      return BAD_TIME;
200290001Sglebius    }
201290001Sglebius
202290001Sglebius  memcpy (buf, in_pz, 4);
203290001Sglebius  buf[4] = NUL;
204290001Sglebius  pz = buf;
205290001Sglebius  res = parse_scaled_value (0, &pz, buf + 4, SEC_PER_YEAR);
206290001Sglebius
207290001Sglebius  memcpy (buf, in_pz + 4, 2);
208290001Sglebius  buf[2] = NUL;
209290001Sglebius  pz =   buf;
210290001Sglebius  res = parse_scaled_value (res, &pz, buf + 2, SEC_PER_MONTH);
211290001Sglebius
212290001Sglebius  memcpy (buf, in_pz + 6, 2);
213290001Sglebius  buf[2] = NUL;
214290001Sglebius  pz =   buf;
215290001Sglebius  return parse_scaled_value (res, &pz, buf + 2, SEC_PER_DAY);
216290001Sglebius}
217290001Sglebius
218290001Sglebius/* Parses the syntax yy Y mm M ww W dd D.  */
219290001Sglebiusstatic time_t
220290001Sglebiusparse_YMWD (cch_t * pz)
221290001Sglebius{
222290001Sglebius  time_t res = 0;
223290001Sglebius  cch_t * ps = strchr (pz, 'Y');
224290001Sglebius  if (ps != NULL)
225290001Sglebius    {
226290001Sglebius      res = parse_scaled_value (0, &pz, ps, SEC_PER_YEAR);
227290001Sglebius      pz++;
228290001Sglebius    }
229290001Sglebius
230290001Sglebius  ps = strchr (pz, 'M');
231290001Sglebius  if (ps != NULL)
232290001Sglebius    {
233290001Sglebius      res = parse_scaled_value (res, &pz, ps, SEC_PER_MONTH);
234290001Sglebius      pz++;
235290001Sglebius    }
236290001Sglebius
237290001Sglebius  ps = strchr (pz, 'W');
238290001Sglebius  if (ps != NULL)
239290001Sglebius    {
240290001Sglebius      res = parse_scaled_value (res, &pz, ps, SEC_PER_WEEK);
241290001Sglebius      pz++;
242290001Sglebius    }
243290001Sglebius
244290001Sglebius  ps = strchr (pz, 'D');
245290001Sglebius  if (ps != NULL)
246290001Sglebius    {
247290001Sglebius      res = parse_scaled_value (res, &pz, ps, SEC_PER_DAY);
248290001Sglebius      pz++;
249290001Sglebius    }
250290001Sglebius
251290001Sglebius  while (isspace ((unsigned char)*pz))
252290001Sglebius    pz++;
253290001Sglebius  if (*pz != NUL)
254290001Sglebius    {
255290001Sglebius      errno = EINVAL;
256290001Sglebius      return BAD_TIME;
257290001Sglebius    }
258290001Sglebius
259290001Sglebius  return res;
260290001Sglebius}
261290001Sglebius
262290001Sglebius/* Parses the syntax HH:MM:SS.
263290001Sglebius   PS points into the string, after "HH", before ":MM:SS".  */
264290001Sglebiusstatic time_t
265290001Sglebiusparse_hour_minute_second (cch_t * pz, cch_t * ps)
266290001Sglebius{
267290001Sglebius  time_t res = 0;
268290001Sglebius
269290001Sglebius  res = parse_scaled_value (0, &pz, ps, SEC_PER_HR);
270290001Sglebius
271290001Sglebius  pz++;
272290001Sglebius  ps = strchr (pz, ':');
273290001Sglebius  if (ps == NULL)
274290001Sglebius    {
275290001Sglebius      errno = EINVAL;
276290001Sglebius      return BAD_TIME;
277290001Sglebius    }
278290001Sglebius
279290001Sglebius  res = parse_scaled_value (res, &pz, ps, SEC_PER_MIN);
280290001Sglebius
281290001Sglebius  pz++;
282290001Sglebius  ps = pz + strlen (pz);
283290001Sglebius  return parse_scaled_value (res, &pz, ps, 1);
284290001Sglebius}
285290001Sglebius
286290001Sglebius/* Parses the syntax HHMMSS.  */
287290001Sglebiusstatic time_t
288290001Sglebiusparse_hourminutesecond (cch_t * in_pz)
289290001Sglebius{
290290001Sglebius  time_t res = 0;
291290001Sglebius  char   buf[4];
292290001Sglebius  cch_t * pz;
293290001Sglebius
294290001Sglebius  if (strlen (in_pz) != 6)
295290001Sglebius    {
296290001Sglebius      errno = EINVAL;
297290001Sglebius      return BAD_TIME;
298290001Sglebius    }
299290001Sglebius
300290001Sglebius  memcpy (buf, in_pz, 2);
301290001Sglebius  buf[2] = NUL;
302290001Sglebius  pz = buf;
303290001Sglebius  res = parse_scaled_value (0, &pz, buf + 2, SEC_PER_HR);
304290001Sglebius
305290001Sglebius  memcpy (buf, in_pz + 2, 2);
306290001Sglebius  buf[2] = NUL;
307290001Sglebius  pz =   buf;
308290001Sglebius  res = parse_scaled_value (res, &pz, buf + 2, SEC_PER_MIN);
309290001Sglebius
310290001Sglebius  memcpy (buf, in_pz + 4, 2);
311290001Sglebius  buf[2] = NUL;
312290001Sglebius  pz =   buf;
313290001Sglebius  return parse_scaled_value (res, &pz, buf + 2, 1);
314290001Sglebius}
315290001Sglebius
316290001Sglebius/* Parses the syntax hh H mm M ss S.  */
317290001Sglebiusstatic time_t
318290001Sglebiusparse_HMS (cch_t * pz)
319290001Sglebius{
320290001Sglebius  time_t res = 0;
321290001Sglebius  cch_t * ps = strchr (pz, 'H');
322290001Sglebius  if (ps != NULL)
323290001Sglebius    {
324290001Sglebius      res = parse_scaled_value (0, &pz, ps, SEC_PER_HR);
325290001Sglebius      pz++;
326290001Sglebius    }
327290001Sglebius
328290001Sglebius  ps = strchr (pz, 'M');
329290001Sglebius  if (ps != NULL)
330290001Sglebius    {
331290001Sglebius      res = parse_scaled_value (res, &pz, ps, SEC_PER_MIN);
332290001Sglebius      pz++;
333290001Sglebius    }
334290001Sglebius
335290001Sglebius  ps = strchr (pz, 'S');
336290001Sglebius  if (ps != NULL)
337290001Sglebius    {
338290001Sglebius      res = parse_scaled_value (res, &pz, ps, 1);
339290001Sglebius      pz++;
340290001Sglebius    }
341290001Sglebius
342290001Sglebius  while (isspace ((unsigned char)*pz))
343290001Sglebius    pz++;
344290001Sglebius  if (*pz != NUL)
345290001Sglebius    {
346290001Sglebius      errno = EINVAL;
347290001Sglebius      return BAD_TIME;
348290001Sglebius    }
349290001Sglebius
350290001Sglebius  return res;
351290001Sglebius}
352290001Sglebius
353290001Sglebius/* Parses a time (hours, minutes, seconds) specification in either syntax.  */
354290001Sglebiusstatic time_t
355290001Sglebiusparse_time (cch_t * pz)
356290001Sglebius{
357290001Sglebius  cch_t * ps;
358290001Sglebius  time_t  res = 0;
359290001Sglebius
360290001Sglebius  /*
361290001Sglebius   *  Scan for a hyphen
362290001Sglebius   */
363290001Sglebius  ps = strchr (pz, ':');
364290001Sglebius  if (ps != NULL)
365290001Sglebius    {
366290001Sglebius      res = parse_hour_minute_second (pz, ps);
367290001Sglebius    }
368290001Sglebius
369290001Sglebius  /*
370290001Sglebius   *  Try for a 'H', 'M' or 'S' suffix
371290001Sglebius   */
372290001Sglebius  else if (ps = strpbrk (pz, "HMS"),
373290001Sglebius           ps == NULL)
374290001Sglebius    {
375290001Sglebius      /* Its a YYYYMMDD format: */
376290001Sglebius      res = parse_hourminutesecond (pz);
377290001Sglebius    }
378290001Sglebius
379290001Sglebius  else
380290001Sglebius    res = parse_HMS (pz);
381290001Sglebius
382290001Sglebius  return res;
383290001Sglebius}
384290001Sglebius
385290001Sglebius/* Returns a substring of the given string, with spaces at the beginning and at
386290001Sglebius   the end destructively removed, per SNOBOL.  */
387290001Sglebiusstatic char *
388290001Sglebiustrim (char * pz)
389290001Sglebius{
390290001Sglebius  /* trim leading white space */
391290001Sglebius  while (isspace ((unsigned char)*pz))
392290001Sglebius    pz++;
393290001Sglebius
394290001Sglebius  /* trim trailing white space */
395290001Sglebius  {
396290001Sglebius    char * pe = pz + strlen (pz);
397290001Sglebius    while ((pe > pz) && isspace ((unsigned char)pe[-1]))
398290001Sglebius      pe--;
399290001Sglebius    *pe = NUL;
400290001Sglebius  }
401290001Sglebius
402290001Sglebius  return pz;
403290001Sglebius}
404290001Sglebius
405290001Sglebius/*
406290001Sglebius *  Parse the year/months/days of a time period
407290001Sglebius */
408290001Sglebiusstatic time_t
409290001Sglebiusparse_period (cch_t * in_pz)
410290001Sglebius{
411290001Sglebius  char * pT;
412290001Sglebius  char * ps;
413290001Sglebius  char * pz   = strdup (in_pz);
414290001Sglebius  void * fptr = pz;
415290001Sglebius  time_t res  = 0;
416290001Sglebius
417290001Sglebius  if (pz == NULL)
418290001Sglebius    {
419290001Sglebius      errno = ENOMEM;
420290001Sglebius      return BAD_TIME;
421290001Sglebius    }
422290001Sglebius
423290001Sglebius  pT = strchr (pz, 'T');
424290001Sglebius  if (pT != NULL)
425290001Sglebius    {
426290001Sglebius      *(pT++) = NUL;
427290001Sglebius      pz = trim (pz);
428290001Sglebius      pT = trim (pT);
429290001Sglebius    }
430290001Sglebius
431290001Sglebius  /*
432290001Sglebius   *  Scan for a hyphen
433290001Sglebius   */
434290001Sglebius  ps = strchr (pz, '-');
435290001Sglebius  if (ps != NULL)
436290001Sglebius    {
437290001Sglebius      res = parse_year_month_day (pz, ps);
438290001Sglebius    }
439290001Sglebius
440290001Sglebius  /*
441290001Sglebius   *  Try for a 'Y', 'M' or 'D' suffix
442290001Sglebius   */
443290001Sglebius  else if (ps = strpbrk (pz, "YMWD"),
444290001Sglebius           ps == NULL)
445290001Sglebius    {
446290001Sglebius      /* Its a YYYYMMDD format: */
447290001Sglebius      res = parse_yearmonthday (pz);
448290001Sglebius    }
449290001Sglebius
450290001Sglebius  else
451290001Sglebius    res = parse_YMWD (pz);
452290001Sglebius
453290001Sglebius  if ((errno == 0) && (pT != NULL))
454290001Sglebius    {
455290001Sglebius      time_t val = parse_time (pT);
456290001Sglebius      res = scale_n_add (res, val, 1);
457290001Sglebius    }
458290001Sglebius
459290001Sglebius  free (fptr);
460290001Sglebius  return res;
461290001Sglebius}
462290001Sglebius
463290001Sglebiusstatic time_t
464290001Sglebiusparse_non_iso8601 (cch_t * pz)
465290001Sglebius{
466290001Sglebius  whats_done_t whatd_we_do = NOTHING_IS_DONE;
467290001Sglebius
468290001Sglebius  time_t res = 0;
469290001Sglebius
470290001Sglebius  do  {
471290001Sglebius    time_t val;
472290001Sglebius
473290001Sglebius    errno = 0;
474290001Sglebius    val = str_const_to_l (pz, &pz, 10);
475290001Sglebius    if (errno != 0)
476290001Sglebius      goto bad_time;
477290001Sglebius
478290001Sglebius    /*  IF we find a colon, then we're going to have a seconds value.
479290001Sglebius        We will not loop here any more.  We cannot already have parsed
480290001Sglebius        a minute value and if we've parsed an hour value, then the result
481290001Sglebius        value has to be less than an hour. */
482290001Sglebius    if (*pz == ':')
483290001Sglebius      {
484290001Sglebius        if (whatd_we_do >= MINUTE_IS_DONE)
485290001Sglebius          break;
486290001Sglebius
487290001Sglebius        val = parse_hr_min_sec (val, pz);
488290001Sglebius
489290001Sglebius        if ((whatd_we_do == HOUR_IS_DONE) && (val >= SEC_PER_HR))
490290001Sglebius          break;
491290001Sglebius
492290001Sglebius        return scale_n_add (res, val, 1);
493290001Sglebius      }
494290001Sglebius
495290001Sglebius    {
496290001Sglebius      unsigned int mult;
497290001Sglebius
498290001Sglebius      /*  Skip over white space following the number we just parsed. */
499290001Sglebius      while (isspace ((unsigned char)*pz))
500290001Sglebius        pz++;
501290001Sglebius
502290001Sglebius      switch (*pz)
503290001Sglebius        {
504290001Sglebius        default:  goto bad_time;
505290001Sglebius        case NUL:
506290001Sglebius          return scale_n_add (res, val, 1);
507290001Sglebius
508290001Sglebius        case 'y': case 'Y':
509290001Sglebius          if (whatd_we_do >= YEAR_IS_DONE)
510290001Sglebius            goto bad_time;
511290001Sglebius          mult = SEC_PER_YEAR;
512290001Sglebius          whatd_we_do = YEAR_IS_DONE;
513290001Sglebius          break;
514290001Sglebius
515290001Sglebius        case 'M':
516290001Sglebius          if (whatd_we_do >= MONTH_IS_DONE)
517290001Sglebius            goto bad_time;
518290001Sglebius          mult = SEC_PER_MONTH;
519290001Sglebius          whatd_we_do = MONTH_IS_DONE;
520290001Sglebius          break;
521290001Sglebius
522290001Sglebius        case 'W':
523290001Sglebius          if (whatd_we_do >= WEEK_IS_DONE)
524290001Sglebius            goto bad_time;
525290001Sglebius          mult = SEC_PER_WEEK;
526290001Sglebius          whatd_we_do = WEEK_IS_DONE;
527290001Sglebius          break;
528290001Sglebius
529290001Sglebius        case 'd': case 'D':
530290001Sglebius          if (whatd_we_do >= DAY_IS_DONE)
531290001Sglebius            goto bad_time;
532290001Sglebius          mult = SEC_PER_DAY;
533290001Sglebius          whatd_we_do = DAY_IS_DONE;
534290001Sglebius          break;
535290001Sglebius
536290001Sglebius        case 'h':
537290001Sglebius          if (whatd_we_do >= HOUR_IS_DONE)
538290001Sglebius            goto bad_time;
539290001Sglebius          mult = SEC_PER_HR;
540290001Sglebius          whatd_we_do = HOUR_IS_DONE;
541290001Sglebius          break;
542290001Sglebius
543290001Sglebius        case 'm':
544290001Sglebius          if (whatd_we_do >= MINUTE_IS_DONE)
545290001Sglebius            goto bad_time;
546290001Sglebius          mult = SEC_PER_MIN;
547290001Sglebius          whatd_we_do = MINUTE_IS_DONE;
548290001Sglebius          break;
549290001Sglebius
550290001Sglebius        case 's':
551290001Sglebius          mult = 1;
552290001Sglebius          whatd_we_do = SECOND_IS_DONE;
553290001Sglebius          break;
554290001Sglebius        }
555290001Sglebius
556290001Sglebius      res = scale_n_add (res, val, mult);
557290001Sglebius
558290001Sglebius      pz++;
559290001Sglebius      while (isspace ((unsigned char)*pz))
560290001Sglebius        pz++;
561290001Sglebius      if (*pz == NUL)
562290001Sglebius        return res;
563290001Sglebius
564290001Sglebius      if (! isdigit ((unsigned char)*pz))
565290001Sglebius        break;
566290001Sglebius    }
567290001Sglebius
568290001Sglebius  } while (whatd_we_do < SECOND_IS_DONE);
569290001Sglebius
570290001Sglebius bad_time:
571290001Sglebius  errno = EINVAL;
572290001Sglebius  return BAD_TIME;
573290001Sglebius}
574290001Sglebius
575290001Sglebiustime_t
576290001Sglebiusparse_duration (char const * pz)
577290001Sglebius{
578290001Sglebius  while (isspace ((unsigned char)*pz))
579290001Sglebius    pz++;
580290001Sglebius
581290001Sglebius  switch (*pz)
582290001Sglebius    {
583290001Sglebius    case 'P':
584290001Sglebius      return parse_period (pz + 1);
585290001Sglebius
586290001Sglebius    case 'T':
587290001Sglebius      return parse_time (pz + 1);
588290001Sglebius
589290001Sglebius    default:
590290001Sglebius      if (isdigit ((unsigned char)*pz))
591290001Sglebius        return parse_non_iso8601 (pz);
592290001Sglebius
593290001Sglebius      errno = EINVAL;
594290001Sglebius      return BAD_TIME;
595290001Sglebius    }
596290001Sglebius}
597290001Sglebius
598290001Sglebius/*
599290001Sglebius * Local Variables:
600290001Sglebius * mode: C
601290001Sglebius * c-file-style: "gnu"
602290001Sglebius * indent-tabs-mode: nil
603290001Sglebius * End:
604290001Sglebius * end of parse-duration.c */
605