From d78050d6bab1f1add4284163b8fc474495ac6580 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Thu, 21 Apr 2011 11:57:37 -0700 Subject: [PATCH] * lread.c (string_to_number): Use strtoumax, to convert more integers without overflow. --- src/ChangeLog | 7 ++++-- src/lread.c | 60 +++++++++++++++++++++++++-------------------------- 2 files changed, 34 insertions(+), 33 deletions(-) diff --git a/src/ChangeLog b/src/ChangeLog index 2b9978f3d6a..0ea90a4d8e5 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -11,7 +11,8 @@ parsing non-base-10 numbers, as the documentation specifies. * lisp.h (string_to_number): New decl, replacing ... (isfloat_string): Remove. - * lread.c (read1): Do not accept +. and -. as integers; this + * lread.c: Include , for uintmax_t and strtoimax. + (read1): Do not accept +. and -. as integers; this appears to have been a coding error. Similarly, do not accept strings like +-1e0 as floating point numbers. Do not report overflow for integer overflows unless the base is not 10 which @@ -25,7 +26,9 @@ (string_to_number): New function, replacing isfloat_string. This function checks for valid syntax and produces the resulting Lisp float number too. Rework it so that string-to-number - no longer mishandles examples like "1.0e+". + no longer mishandles examples like "1.0e+". Use strtoimax, + so that overflow for non-base-10 numbers is reported only when + there's no portable and simple way to convert to floating point. 2011-04-20 Paul Eggert diff --git a/src/lread.c b/src/lread.c index 390c57d1678..531fee3fa3b 100644 --- a/src/lread.c +++ b/src/lread.c @@ -19,6 +19,7 @@ along with GNU Emacs. If not, see . */ #include +#include #include #include #include @@ -3226,7 +3227,6 @@ string_to_number (char const *string, int base, int ignore_trailing) ++cp; while (0 <= digit_to_number (*cp, base)); } - if (*cp == '.') { state |= DOT_CHAR; @@ -3300,47 +3300,45 @@ string_to_number (char const *string, int base, int ignore_trailing) : (!*cp && ((state & ~DOT_CHAR) == LEAD_INT || float_syntax)))) return Qnil; - /* If the number does not use float syntax, and fits into a fixnum, return - the fixnum. */ + /* If the number uses integer and not float syntax, and is in C-language + range, use its value, preferably as a fixnum. */ if (0 <= leading_digit && ! float_syntax) { - /* Convert string to EMACS_INT. Do not use strtol, to avoid assuming - that EMACS_INT is no wider than 'long', and because when BASE is 16 - strtol might accept numbers like "0x1" that are not allowed here. */ - EMACS_INT n = leading_digit; - EMACS_INT abs_bound = - (negative ? -MOST_NEGATIVE_FIXNUM : MOST_POSITIVE_FIXNUM); - EMACS_INT abs_bound_over_base = abs_bound / base; - - for (cp = string + signedp + 1; ; cp++) + uintmax_t n; + + /* Fast special case for single-digit integers. This also avoids a + glitch when BASE is 16 and IGNORE_TRAILING is nonzero, because in that + case some versions of strtoumax accept numbers like "0x1" that Emacs + does not allow. */ + if (digit_to_number (string[signedp + 1], base) < 0) + return make_number (negative ? -leading_digit : leading_digit); + + errno = 0; + n = strtoumax (string + signedp, NULL, base); + if (errno == ERANGE) { - int d = digit_to_number (*cp, base); - if (d < 0) - { - if (n <= abs_bound) - return make_number (negative ? -n : n); - break; - } - if (abs_bound_over_base < n) - break; - n = base * n + d; + /* Unfortunately there's no simple and accurate way to convert + non-base-10 numbers that are out of C-language range. */ + if (base != 10) + xsignal (Qoverflow_error, list1 (build_string (string))); } - - /* Unfortunately there's no simple and reliable way to convert - non-base-10 to floating point. */ - if (base != 10) - xsignal (Qoverflow_error, list1 (build_string (string))); + else if (n <= (negative ? -MOST_NEGATIVE_FIXNUM : MOST_POSITIVE_FIXNUM)) + { + EMACS_INT signed_n = n; + return make_number (negative ? -signed_n : signed_n); + } + else + value = n; } /* Either the number uses float syntax, or it does not fit into a fixnum. Convert it from string to floating point, unless the value is already - known because it is an infinity or a NAN. */ + known because it is an infinity, a NAN, or its absolute value fits in + uintmax_t. */ if (! value) value = atof (string + signedp); - if (negative) - value = -value; - return make_float (value); + return make_float (negative ? -value : value); } -- 2.39.2