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