upstream: Add convtime_double() that converts a string interval,

such as "3w2d4h5m10.5s", into a floating point number of seconds.

Reimplement the existing convtime() function using convtime_double()
(it just drops the fractional seconds)

lots of feedback deraadt@ / dtucker@; ok deraadt@

OpenBSD-Commit-ID: 053cdd0c72325a20efc6613caa847473fb89e36f
This commit is contained in:
djm@openbsd.org
2025-12-05 07:43:12 +00:00
committed by Damien Miller
parent b7dc1d95ee
commit f45cd249e4
2 changed files with 78 additions and 56 deletions

130
misc.c
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: misc.c,v 1.209 2025/11/06 01:31:11 djm Exp $ */
/* $OpenBSD: misc.c,v 1.210 2025/12/05 07:43:12 djm Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
* Copyright (c) 2005-2020 Damien Miller. All rights reserved.
@@ -622,25 +622,22 @@ a2tun(const char *s, int *remote)
return (tun);
}
#define SECONDS 1
#define SECONDS 1.0
#define MINUTES (SECONDS * 60)
#define HOURS (MINUTES * 60)
#define DAYS (HOURS * 24)
#define WEEKS (DAYS * 7)
static char *
scandigits(char *s)
{
while (isdigit((unsigned char)*s))
s++;
return s;
}
/*
* Convert a time string into seconds; format is
* a sequence of:
* Convert an interval/duration time string into seconds, which may include
* fractional seconds.
*
* The format is a sequence of:
* time[qualifier]
*
* This supports fractional values for the seconds value only. All other
* values must be integers.
*
* Valid time qualifiers are:
* <none> seconds
* s|S seconds
@@ -650,44 +647,46 @@ scandigits(char *s)
* w|W weeks
*
* Examples:
* 90m 90 minutes
* 1h30m 90 minutes
* 2d 2 days
* 1w 1 week
* 90m 90 minutes
* 1h30m 90 minutes
* 1.5s 1.5 seconds
* 2d 2 days
* 1w 1 week
*
* Return -1 if time string is invalid.
* Returns <0.0 if the time string is invalid.
*/
int
convtime(const char *s)
double
convtime_double(const char *s)
{
int secs, total = 0, multiplier;
char *p, *os, *np, c = 0;
const char *errstr;
double val, total_sec = 0.0, multiplier;
const char *p, *start_p;
char *endp;
int seen_seconds = 0;
if (s == NULL || *s == '\0')
return -1;
p = os = strdup(s); /* deal with const */
if (os == NULL)
return -1;
return -1.0;
while (*p) {
np = scandigits(p);
if (np) {
c = *np;
*np = '\0';
}
secs = (int)strtonum(p, 0, INT_MAX, &errstr);
if (errstr)
goto fail;
*np = c;
for (p = s; *p != '\0';) {
if (!isdigit((unsigned char)*p) && *p != '.')
return -1.0;
multiplier = 1;
switch (c) {
errno = 0;
if ((val = strtod(p, &endp)) < 0 || errno != 0 || p == endp)
return -1.0;
/* Allow only decimal forms */
if (p + strspn(p, "0123456789.") != endp)
return -1.0;
start_p = p;
p = endp;
switch (*p) {
case '\0':
np--; /* back up */
break;
/* FALLTHROUGH */
case 's':
case 'S':
if (seen_seconds++)
return -1.0;
multiplier = SECONDS;
break;
case 'm':
case 'M':
@@ -706,23 +705,44 @@ convtime(const char *s)
multiplier = WEEKS;
break;
default:
goto fail;
return -1.0;
}
if (secs > INT_MAX / multiplier)
goto fail;
secs *= multiplier;
if (total > INT_MAX - secs)
goto fail;
total += secs;
if (total < 0)
goto fail;
p = ++np;
/* Special handling if this was a decimal */
if (memchr(start_p, '.', endp - start_p) != NULL) {
/* Decimal point present */
if (multiplier > 1.0)
return -1.0; /* No fractionals for non-seconds */
/* For seconds, ensure digits follow */
if (!isdigit((unsigned char)*(endp - 1)))
return -1.0;
}
total_sec += val * multiplier;
if (*p != '\0')
p++;
}
free(os);
return total;
fail:
free(os);
return -1;
return total_sec;
}
/*
* Same as convtime_double() above but fractional seconds are ignored.
* Return -1 if time string is invalid.
*/
int
convtime(const char *s)
{
double sec_val;
if ((sec_val = convtime_double(s)) < 0.0)
return -1;
/* Check for overflow into int */
if (sec_val < 0 || sec_val > INT_MAX)
return -1;
return (int)sec_val;
}
#define TF_BUFS 8

4
misc.h
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: misc.h,v 1.113 2025/11/06 01:31:11 djm Exp $ */
/* $OpenBSD: misc.h,v 1.114 2025/12/05 07:43:12 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -81,7 +81,9 @@ char *colon(char *);
int parse_user_host_path(const char *, char **, char **, char **);
int parse_user_host_port(const char *, char **, char **, int *);
int parse_uri(const char *, const char *, char **, char **, int *, char **);
double convtime_double(const char *);
int convtime(const char *);
double convtime_double(const char *);
const char *fmt_timeframe(time_t t);
int tilde_expand(const char *, uid_t, char **);
char *tilde_expand_filename(const char *, uid_t);