commit 0677399ecf8b2f20d87cf8bb4a29bc9574545917 from: Omar Polo date: Sat Mar 19 14:24:56 2022 UTC readd scan_scaled even if unused reduces the diff with /usr/src. (scan_scaled was updated since the initial import of fmt_scaled, hence the file revision bump.) commit - e445acd17274f019c87c81b8feea8ca1e8b32c8e commit + 0677399ecf8b2f20d87cf8bb4a29bc9574545917 blob - 1b2d5f59da6911eb007cd5a1fae0d58fc2fce889 blob + af7750a2238f1de720ab98665c30b017ac37c489 --- compat/fmt_scaled.c +++ compat/fmt_scaled.c @@ -1,4 +1,4 @@ -/* $OpenBSD: fmt_scaled.c,v 1.19 2020/10/12 22:08:34 deraadt Exp $ */ +/* $OpenBSD: fmt_scaled.c,v 1.22 2022/03/11 09:04:59 dtucker Exp $ */ /* * Copyright (c) 2001, 2002, 2003 Ian F. Darwin. All rights reserved. @@ -65,6 +65,149 @@ static const long long scale_factors[] = { #define MAX_DIGITS (SCALE_LENGTH * 3) /* XXX strlen(sprintf("%lld", -1)? */ +/* Convert the given input string "scaled" into numeric in "result". + * Return 0 on success, -1 and errno set on error. + */ +int +scan_scaled(char *scaled, long long *result) +{ + char *p = scaled; + int sign = 0; + unsigned int i, ndigits = 0, fract_digits = 0; + long long scale_fact = 1, whole = 0, fpart = 0; + + /* Skip leading whitespace */ + while (isascii((unsigned char)*p) && isspace((unsigned char)*p)) + ++p; + + /* Then at most one leading + or - */ + while (*p == '-' || *p == '+') { + if (*p == '-') { + if (sign) { + errno = EINVAL; + return -1; + } + sign = -1; + ++p; + } else if (*p == '+') { + if (sign) { + errno = EINVAL; + return -1; + } + sign = +1; + ++p; + } + } + + /* Main loop: Scan digits, find decimal point, if present. + * We don't allow exponentials, so no scientific notation + * (but note that E for Exa might look like e to some!). + * Advance 'p' to end, to get scale factor. + */ + for (; isascii((unsigned char)*p) && + (isdigit((unsigned char)*p) || *p=='.'); ++p) { + if (*p == '.') { + if (fract_digits > 0) { /* oops, more than one '.' */ + errno = EINVAL; + return -1; + } + fract_digits = 1; + continue; + } + + i = (*p) - '0'; /* whew! finally a digit we can use */ + if (fract_digits > 0) { + if (fract_digits >= MAX_DIGITS-1) + /* ignore extra fractional digits */ + continue; + fract_digits++; /* for later scaling */ + if (fpart > LLONG_MAX / 10) { + errno = ERANGE; + return -1; + } + fpart *= 10; + if (i > LLONG_MAX - fpart) { + errno = ERANGE; + return -1; + } + fpart += i; + } else { /* normal digit */ + if (++ndigits >= MAX_DIGITS) { + errno = ERANGE; + return -1; + } + if (whole > LLONG_MAX / 10) { + errno = ERANGE; + return -1; + } + whole *= 10; + if (i > LLONG_MAX - whole) { + errno = ERANGE; + return -1; + } + whole += i; + } + } + + if (sign) + whole *= sign; + + /* If no scale factor given, we're done. fraction is discarded. */ + if (!*p) { + *result = whole; + return 0; + } + + /* Validate scale factor, and scale whole and fraction by it. */ + for (i = 0; i < SCALE_LENGTH; i++) { + + /* Are we there yet? */ + if (*p == scale_chars[i] || + *p == tolower((unsigned char)scale_chars[i])) { + + /* If it ends with alphanumerics after the scale char, bad. */ + if (isalnum((unsigned char)*(p+1))) { + errno = EINVAL; + return -1; + } + scale_fact = scale_factors[i]; + + /* check for overflow and underflow after scaling */ + if (whole > LLONG_MAX / scale_fact || + whole < LLONG_MIN / scale_fact) { + errno = ERANGE; + return -1; + } + + /* scale whole part */ + whole *= scale_fact; + + /* truncate fpart so it does't overflow. + * then scale fractional part. + */ + while (fpart >= LLONG_MAX / scale_fact) { + fpart /= 10; + fract_digits--; + } + fpart *= scale_fact; + if (fract_digits > 0) { + for (i = 0; i < fract_digits -1; i++) + fpart /= 10; + } + if (sign == -1) + whole -= fpart; + else + whole += fpart; + *result = whole; + return 0; + } + } + + /* Invalid unit or character */ + errno = EINVAL; + return -1; +} + /* Format the given "number" into human-readable form in "result". * Result must point to an allocated buffer of length FMT_SCALED_STRSIZE. * Return 0 on success, -1 and errno set if error.