diff options
| author | Paul Eggert | 2011-04-21 11:57:37 -0700 |
|---|---|---|
| committer | Paul Eggert | 2011-04-21 11:57:37 -0700 |
| commit | d78050d6bab1f1add4284163b8fc474495ac6580 (patch) | |
| tree | 19bafdabc58b1758fa4f240bd63a2da402502546 | |
| parent | 452f4150134e4ba7bbd2bad9ce87d19c200505de (diff) | |
| download | emacs-d78050d6bab1f1add4284163b8fc474495ac6580.tar.gz emacs-d78050d6bab1f1add4284163b8fc474495ac6580.zip | |
* lread.c (string_to_number): Use strtoumax, to convert more integers without overflow.
| -rw-r--r-- | src/ChangeLog | 7 | ||||
| -rw-r--r-- | 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 @@ | |||
| 11 | parsing non-base-10 numbers, as the documentation specifies. | 11 | parsing non-base-10 numbers, as the documentation specifies. |
| 12 | * lisp.h (string_to_number): New decl, replacing ... | 12 | * lisp.h (string_to_number): New decl, replacing ... |
| 13 | (isfloat_string): Remove. | 13 | (isfloat_string): Remove. |
| 14 | * lread.c (read1): Do not accept +. and -. as integers; this | 14 | * lread.c: Include <inttypes.h>, for uintmax_t and strtoimax. |
| 15 | (read1): Do not accept +. and -. as integers; this | ||
| 15 | appears to have been a coding error. Similarly, do not accept | 16 | appears to have been a coding error. Similarly, do not accept |
| 16 | strings like +-1e0 as floating point numbers. Do not report | 17 | strings like +-1e0 as floating point numbers. Do not report |
| 17 | overflow for integer overflows unless the base is not 10 which | 18 | overflow for integer overflows unless the base is not 10 which |
| @@ -25,7 +26,9 @@ | |||
| 25 | (string_to_number): New function, replacing isfloat_string. | 26 | (string_to_number): New function, replacing isfloat_string. |
| 26 | This function checks for valid syntax and produces the resulting | 27 | This function checks for valid syntax and produces the resulting |
| 27 | Lisp float number too. Rework it so that string-to-number | 28 | Lisp float number too. Rework it so that string-to-number |
| 28 | no longer mishandles examples like "1.0e+". | 29 | no longer mishandles examples like "1.0e+". Use strtoimax, |
| 30 | so that overflow for non-base-10 numbers is reported only when | ||
| 31 | there's no portable and simple way to convert to floating point. | ||
| 29 | 32 | ||
| 30 | 2011-04-20 Paul Eggert <eggert@cs.ucla.edu> | 33 | 2011-04-20 Paul Eggert <eggert@cs.ucla.edu> |
| 31 | 34 | ||
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 <http://www.gnu.org/licenses/>. */ | |||
| 19 | 19 | ||
| 20 | 20 | ||
| 21 | #include <config.h> | 21 | #include <config.h> |
| 22 | #include <inttypes.h> | ||
| 22 | #include <stdio.h> | 23 | #include <stdio.h> |
| 23 | #include <sys/types.h> | 24 | #include <sys/types.h> |
| 24 | #include <sys/stat.h> | 25 | #include <sys/stat.h> |
| @@ -3226,7 +3227,6 @@ string_to_number (char const *string, int base, int ignore_trailing) | |||
| 3226 | ++cp; | 3227 | ++cp; |
| 3227 | while (0 <= digit_to_number (*cp, base)); | 3228 | while (0 <= digit_to_number (*cp, base)); |
| 3228 | } | 3229 | } |
| 3229 | |||
| 3230 | if (*cp == '.') | 3230 | if (*cp == '.') |
| 3231 | { | 3231 | { |
| 3232 | state |= DOT_CHAR; | 3232 | state |= DOT_CHAR; |
| @@ -3300,47 +3300,45 @@ string_to_number (char const *string, int base, int ignore_trailing) | |||
| 3300 | : (!*cp && ((state & ~DOT_CHAR) == LEAD_INT || float_syntax)))) | 3300 | : (!*cp && ((state & ~DOT_CHAR) == LEAD_INT || float_syntax)))) |
| 3301 | return Qnil; | 3301 | return Qnil; |
| 3302 | 3302 | ||
| 3303 | /* If the number does not use float syntax, and fits into a fixnum, return | 3303 | /* If the number uses integer and not float syntax, and is in C-language |
| 3304 | the fixnum. */ | 3304 | range, use its value, preferably as a fixnum. */ |
| 3305 | if (0 <= leading_digit && ! float_syntax) | 3305 | if (0 <= leading_digit && ! float_syntax) |
| 3306 | { | 3306 | { |
| 3307 | /* Convert string to EMACS_INT. Do not use strtol, to avoid assuming | 3307 | uintmax_t n; |
| 3308 | that EMACS_INT is no wider than 'long', and because when BASE is 16 | 3308 | |
| 3309 | strtol might accept numbers like "0x1" that are not allowed here. */ | 3309 | /* Fast special case for single-digit integers. This also avoids a |
| 3310 | EMACS_INT n = leading_digit; | 3310 | glitch when BASE is 16 and IGNORE_TRAILING is nonzero, because in that |
| 3311 | EMACS_INT abs_bound = | 3311 | case some versions of strtoumax accept numbers like "0x1" that Emacs |
| 3312 | (negative ? -MOST_NEGATIVE_FIXNUM : MOST_POSITIVE_FIXNUM); | 3312 | does not allow. */ |
| 3313 | EMACS_INT abs_bound_over_base = abs_bound / base; | 3313 | if (digit_to_number (string[signedp + 1], base) < 0) |
| 3314 | 3314 | return make_number (negative ? -leading_digit : leading_digit); | |
| 3315 | for (cp = string + signedp + 1; ; cp++) | 3315 | |
| 3316 | errno = 0; | ||
| 3317 | n = strtoumax (string + signedp, NULL, base); | ||
| 3318 | if (errno == ERANGE) | ||
| 3316 | { | 3319 | { |
| 3317 | int d = digit_to_number (*cp, base); | 3320 | /* Unfortunately there's no simple and accurate way to convert |
| 3318 | if (d < 0) | 3321 | non-base-10 numbers that are out of C-language range. */ |
| 3319 | { | 3322 | if (base != 10) |
| 3320 | if (n <= abs_bound) | 3323 | xsignal (Qoverflow_error, list1 (build_string (string))); |
| 3321 | return make_number (negative ? -n : n); | ||
| 3322 | break; | ||
| 3323 | } | ||
| 3324 | if (abs_bound_over_base < n) | ||
| 3325 | break; | ||
| 3326 | n = base * n + d; | ||
| 3327 | } | 3324 | } |
| 3328 | 3325 | else if (n <= (negative ? -MOST_NEGATIVE_FIXNUM : MOST_POSITIVE_FIXNUM)) | |
| 3329 | /* Unfortunately there's no simple and reliable way to convert | 3326 | { |
| 3330 | non-base-10 to floating point. */ | 3327 | EMACS_INT signed_n = n; |
| 3331 | if (base != 10) | 3328 | return make_number (negative ? -signed_n : signed_n); |
| 3332 | xsignal (Qoverflow_error, list1 (build_string (string))); | 3329 | } |
| 3330 | else | ||
| 3331 | value = n; | ||
| 3333 | } | 3332 | } |
| 3334 | 3333 | ||
| 3335 | /* Either the number uses float syntax, or it does not fit into a fixnum. | 3334 | /* Either the number uses float syntax, or it does not fit into a fixnum. |
| 3336 | Convert it from string to floating point, unless the value is already | 3335 | Convert it from string to floating point, unless the value is already |
| 3337 | known because it is an infinity or a NAN. */ | 3336 | known because it is an infinity, a NAN, or its absolute value fits in |
| 3337 | uintmax_t. */ | ||
| 3338 | if (! value) | 3338 | if (! value) |
| 3339 | value = atof (string + signedp); | 3339 | value = atof (string + signedp); |
| 3340 | 3340 | ||
| 3341 | if (negative) | 3341 | return make_float (negative ? -value : value); |
| 3342 | value = -value; | ||
| 3343 | return make_float (value); | ||
| 3344 | } | 3342 | } |
| 3345 | 3343 | ||
| 3346 | 3344 | ||