diff options
author | dkf <donal.k.fellows@manchester.ac.uk> | 2001-10-19 12:52:33 (GMT) |
---|---|---|
committer | dkf <donal.k.fellows@manchester.ac.uk> | 2001-10-19 12:52:33 (GMT) |
commit | 58278ae8a82801c12eaee78e44dc3806e3b67f44 (patch) | |
tree | 0de22fa9f25e7f70d3ab17a15a5002b59c0361e4 /compat | |
parent | c3994da9d4a7d77880f2c554fddf8effbc03d697 (diff) | |
download | tcl-58278ae8a82801c12eaee78e44dc3806e3b67f44.zip tcl-58278ae8a82801c12eaee78e44dc3806e3b67f44.tar.gz tcl-58278ae8a82801c12eaee78e44dc3806e3b67f44.tar.bz2 |
Added range-checking to strtoll and strtoull
Diffstat (limited to 'compat')
-rw-r--r-- | compat/strtoll.c | 28 | ||||
-rw-r--r-- | compat/strtoull.c | 85 |
2 files changed, 98 insertions, 15 deletions
diff --git a/compat/strtoll.c b/compat/strtoll.c index 240677a..0d3f44a 100644 --- a/compat/strtoll.c +++ b/compat/strtoll.c @@ -9,12 +9,15 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: strtoll.c,v 1.1.2.1 2001/10/15 10:48:49 dkf Exp $ + * RCS: @(#) $Id: strtoll.c,v 1.1.2.2 2001/10/19 12:52:33 dkf Exp $ */ #include "tcl.h" +#include "tclPort.h" #include <ctype.h> +#define TCL_WIDEINT_MAX (((Tcl_WideUInt)Tcl_LongAsWide(-1))>>1) + /* *---------------------------------------------------------------------- @@ -54,6 +57,7 @@ strtoll(string, endPtr, base) { register char *p; Tcl_WideInt result; + Tcl_WideUInt uwResult; /* * Skip any leading blanks. @@ -70,12 +74,30 @@ strtoll(string, endPtr, base) if (*p == '-') { p += 1; - result = -(strtoull(p, endPtr, base)); + uwResult = strtoull(p, endPtr, base); + if (errno != ERANGE) { + if (uwResult > TCL_WIDEINT_MAX+1) { + errno = ERANGE; + return Tcl_LongAsWide(-1); + } else if (uwResult > TCL_WIDEINT_MAX) { + return ~((Tcl_WideInt)TCL_WIDEINT_MAX); + } else { + result = -uwResult; + } + } } else { if (*p == '+') { p += 1; } - result = strtoull(p, endPtr, base); + uwResult = strtoull(p, endPtr, base); + if (errno != ERANGE) { + if (uwResult > TCL_WIDEINT_MAX) { + errno = ERANGE; + return Tcl_LongAsWide(-1); + } else { + result = uwResult; + } + } } if ((result == 0) && (endPtr != 0) && (*endPtr == p)) { *endPtr = string; diff --git a/compat/strtoull.c b/compat/strtoull.c index dd2d7d6..71254f7 100644 --- a/compat/strtoull.c +++ b/compat/strtoull.c @@ -9,10 +9,11 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: strtoull.c,v 1.1.2.2 2001/10/18 09:03:58 dkf Exp $ + * RCS: @(#) $Id: strtoull.c,v 1.1.2.3 2001/10/19 12:52:33 dkf Exp $ */ #include "tcl.h" +#include "tclPort.h" #include <ctype.h> /* @@ -31,6 +32,7 @@ static char cvtIn[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, /* 'a' - 'z' */ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35}; + /* *---------------------------------------------------------------------- @@ -71,27 +73,40 @@ strtoull(string, endPtr, base) register char *p; register Tcl_WideUInt result = 0; register unsigned digit; - int anyDigits = 0; + register Tcl_WideUInt shifted; + int anyDigits = 0, negative = 0; /* * Skip any leading blanks. */ p = string; - while (isspace(*p)) { + while (isspace(*p)) { /* INTL: locale-dependent */ p += 1; } /* + * Check for a sign. + */ + + if (*p == '-') { + p += 1; + negative = 1; + } else { + if (*p == '+') { + p += 1; + } + } + + /* * If no base was provided, pick one from the leading characters * of the string. */ - if (base == 0) - { + if (base == 0) { if (*p == '0') { p += 1; - if (*p == 'x') { + if (*p == 'x' || *p == 'X') { p += 1; base = 16; } else { @@ -104,15 +119,16 @@ strtoull(string, endPtr, base) anyDigits = 1; base = 8; } + } else { + base = 10; } - else base = 10; } else if (base == 16) { /* * Skip a leading "0x" from hex numbers. */ - if ((p[0] == '0') && (p[1] == 'x')) { + if ((p[0] == '0') && (p[1] == 'x' || *p == 'X')) { p += 2; } } @@ -128,7 +144,11 @@ strtoull(string, endPtr, base) if (digit > 7) { break; } - result = (result << 3) + digit; + shifted = result << 3; + if ((shifted >> 3) != result) { + goto overflow; + } + result = shifted + digit; anyDigits = 1; } } else if (base == 10) { @@ -137,7 +157,11 @@ strtoull(string, endPtr, base) if (digit > 9) { break; } - result = (10*result) + digit; + shifted = 10 * result; + if ((shifted / 10) != result) { + goto overflow; + } + result = shifted + digit; anyDigits = 1; } } else if (base == 16) { @@ -150,7 +174,11 @@ strtoull(string, endPtr, base) if (digit > 15) { break; } - result = (result << 4) + digit; + shifted = result << 4; + if ((shifted >> 4) != result) { + goto overflow; + } + result = shifted + digit; anyDigits = 1; } } else { @@ -163,12 +191,24 @@ strtoull(string, endPtr, base) if (digit >= base) { break; } - result = result*base + digit; + shifted = result * base; + if ((shifted/base) != result) { + goto overflow; + } + result = shifted + digit; anyDigits = 1; } } /* + * Negate if we found a '-' earlier. + */ + + if (negative) { + result = (Tcl_WideUInt)(-((Tcl_WideInt)result)); + } + + /* * See if there were any digits at all. */ @@ -181,4 +221,25 @@ strtoull(string, endPtr, base) } return result; + + /* + * On overflow generate the right output + */ + + overflow: + errno = ERANGE; + if (endPtr != 0) { + for ( ; ; p += 1) { + digit = *p - '0'; + if (digit > ('z' - '0')) { + break; + } + digit = cvtIn[digit]; + if (digit >= base) { + break; + } + } + *endPtr = p; + } + return (Tcl_WideUInt)Tcl_LongAsWide(-1); } |