commit - e445acd17274f019c87c81b8feea8ca1e8b32c8e
commit + 0677399ecf8b2f20d87cf8bb4a29bc9574545917
blob - 1b2d5f59da6911eb007cd5a1fae0d58fc2fce889
blob + af7750a2238f1de720ab98665c30b017ac37c489
--- compat/fmt_scaled.c
+++ compat/fmt_scaled.c
-/* $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.
#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.