-/* $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.
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
* 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;
-
- 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;
-
- multiplier = 1;
- switch (c) {
+ return -1.0;
+
+ for (p = s; *p != '\0';) {
+ if (!isdigit((unsigned char)*p) && *p != '.')
+ return -1.0;
+
+ 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':
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;
- }
- free(os);
- return total;
-fail:
- free(os);
- return -1;
+
+ /* 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++;
+ }
+ 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
-/* $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>
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);