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