diff options
Diffstat (limited to 'src/string.c')
-rw-r--r-- | src/string.c | 68 |
1 files changed, 58 insertions, 10 deletions
diff --git a/src/string.c b/src/string.c index 4abd694..06066ce 100644 --- a/src/string.c +++ b/src/string.c @@ -980,6 +980,7 @@ EXPORT_SYMBOL double HX_strtod_unit(const char *s, char **out_end, unsigned int if (end == s) { if (out_end != nullptr) *out_end = end; + errno = 0; return q; } while (HX_isspace(*end)) @@ -988,10 +989,12 @@ EXPORT_SYMBOL double HX_strtod_unit(const char *s, char **out_end, unsigned int if (pwr == 0) { if (out_end != nullptr) *out_end = const_cast(char *, end); + errno = 0; return q; } if (out_end != nullptr) *out_end = const_cast(char *, end + 1); + errno = 0; return q * pow(exponent, pwr); } @@ -1001,17 +1004,45 @@ EXPORT_SYMBOL unsigned long long HX_strtoull_unit(const char *s, char *end; unsigned long long ipart; unsigned int pwr = 0; + bool neg = false; while (HX_isspace(*s)) ++s; + if (*s == '-') { + /* + * "-5k": While (-5ULL) * 1000 is the same as (-5000ULL) under + * modulo arithmetic, the expression `ipart >= ULLONG_MAX / + * exponent` depends on seeing the true value (5 rather than + * (-5ULL).) + */ + neg = true; + ++s; + } + errno = 0; ipart = strtoull(s, &end, 10); + if (ipart == ULLONG_MAX && errno == ERANGE) + return ipart; if (*end == '.') { double q = HX_strtod_unit(s, out_end, exponent); bool lo_ok = q >= nextafter(-static_cast(double, ULLONG_MAX), 0); bool hi_ok = q <= nextafter(static_cast(double, ULLONG_MAX), 0); - if (!hi_ok || !lo_ok) + if (!hi_ok || !lo_ok) { + errno = ERANGE; return ULLONG_MAX; - return q; + } + /* + * https://eel.is/c++draft/conv.fpint: values unrepresentable + * in the target type (such as forcing -5.2f into a uint) is + * UB. Thus check for range and apply the negation after the + * conversion to ULL. + */ + if (q > ULLONG_MAX) { + errno = ERANGE; + return ULLONG_MAX; + } + unsigned long long r = q; + errno = 0; + return neg ? -r : r; } if (exponent == 0) exponent = 1000; @@ -1021,7 +1052,8 @@ EXPORT_SYMBOL unsigned long long HX_strtoull_unit(const char *s, if (pwr == 0) { if (out_end != nullptr) *out_end = end; - return ipart; + errno = 0; + return neg ? -ipart: ipart; } if (out_end != nullptr) *out_end = const_cast(char *, end + 1); @@ -1032,11 +1064,13 @@ EXPORT_SYMBOL unsigned long long HX_strtoull_unit(const char *s, } ipart *= exponent; } - return ipart; + errno = 0; + return neg ? -ipart : ipart; } -#define SECONDS_PER_YEAR 31557600 -#define SECONDS_PER_MONTH 2629800 +/* Numbers also used by systemd — the focus is on longterm averages */ +#define SECONDS_PER_YEAR 31557600 /* 365.25 days */ +#define SECONDS_PER_MONTH 2629800 /* 1/12th of that year = 30.4375 days */ #define NSEC_PER_SECOND 1000000000ULL static const struct { @@ -1071,7 +1105,6 @@ static const struct { {"µs", 3, 0, 1000}, {"nsec", 4, 0, 1}, {"ns", 2, 0, 1}, - {"", 0, 1, NSEC_PER_SECOND}, }; static unsigned long long HX_strtoull_time(const char *s, char **out_end, bool nsec) @@ -1081,10 +1114,13 @@ static unsigned long long HX_strtoull_time(const char *s, char **out_end, bool n while (*s != '\0') { while (HX_isspace(*s)) ++s; + const char *numbegin = s; if (*s == '-') break; char *end = nullptr; unsigned long long num = strtoull(s, &end, 10); + if (num == ULLONG_MAX && errno == ERANGE) + return num; double frac = 0; bool have_frac = *end == '.'; if (have_frac) @@ -1100,13 +1136,25 @@ static unsigned long long HX_strtoull_time(const char *s, char **out_end, bool n time_multiplier[i].len) == 0 && !HX_isalpha(s[time_multiplier[i].len])) break; - if (i == ARRAY_SIZE(time_multiplier)) + if (i == ARRAY_SIZE(time_multiplier)) { + if ((!have_frac && num == 0) || (have_frac && frac == 0)) + /* 0 is the same no matter what unit, take it */ + continue; + s = numbegin; break; + } unsigned long long mult = nsec ? time_multiplier[i].ns_mult : time_multiplier[i].s_mult; - if (have_frac) + if (have_frac) { quant += frac * mult; - else + } else { + if (mult > 0 && num >= ULLONG_MAX / mult) { + if (out_end != nullptr) + *out_end = const_cast(char *, numbegin); + errno = ERANGE; + return ULLONG_MAX; + } quant += num * mult; + } s += time_multiplier[i].len; } if (out_end != nullptr) |