diff options
Diffstat (limited to 'compat/strtoul.c')
| -rw-r--r-- | compat/strtoul.c | 129 |
1 files changed, 80 insertions, 49 deletions
diff --git a/compat/strtoul.c b/compat/strtoul.c index 3b7918c..d572c2b 100644 --- a/compat/strtoul.c +++ b/compat/strtoul.c @@ -6,21 +6,19 @@ * Copyright (c) 1988 The Regents of the University of California. * Copyright (c) 1994 Sun Microsystems, Inc. * - * See the file "license.terms" for information on usage and redistribution - * of this file, and for a DISCLAIMER OF ALL WARRANTIES. - * - * RCS: @(#) $Id: strtoul.c,v 1.2 1998/09/14 18:39:45 stanton Exp $ + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. */ -#include <ctype.h> +#include "tclInt.h" /* - * The table below is used to convert from ASCII digits to a - * numerical equivalent. It maps from '0' through 'z' to integers - * (100 for non-digit characters). + * The table below is used to convert from ASCII digits to a numerical + * equivalent. It maps from '0' through 'z' to integers (100 for non-digit + * characters). */ -static char cvtIn[] = { +static const char cvtIn[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, /* '0' - '9' */ 100, 100, 100, 100, 100, 100, 100, /* punctuation */ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, /* 'A' - 'Z' */ @@ -39,11 +37,10 @@ static char cvtIn[] = { * Convert an ASCII string into an integer. * * Results: - * The return value is the integer equivalent of string. If endPtr - * is non-NULL, then *endPtr is filled in with the character - * after the last one that was part of the integer. If string - * doesn't contain a valid integer value, then zero is returned - * and *endPtr is set to string. + * The return value is the integer equivalent of string. If endPtr is + * non-NULL, then *endPtr is filled in with the character after the last + * one that was part of the integer. If string doesn't contain a valid + * integer value, then zero is returned and *endPtr is set to string. * * Side effects: * None. @@ -52,94 +49,112 @@ static char cvtIn[] = { */ unsigned long int -strtoul(string, endPtr, base) - char *string; /* String of ASCII digits, possibly - * preceded by white space. For bases - * greater than 10, either lower- or - * upper-case digits may be used. - */ - char **endPtr; /* Where to store address of terminating +strtoul( + const char *string, /* String of ASCII digits, possibly preceded + * by white space. For bases greater than 10, + * either lower- or upper-case digits may be + * used. */ + char **endPtr, /* Where to store address of terminating * character, or NULL. */ - int base; /* Base for conversion. Must be less - * than 37. If 0, then the base is chosen - * from the leading characters of string: - * "0x" means hex, "0" means octal, anything - * else means decimal. - */ + int base) /* Base for conversion. Must be less than 37. + * If 0, then the base is chosen from the + * leading characters of string: "0x" means + * hex, "0" means octal, anything else means + * decimal. */ { - register char *p; + register const char *p; register unsigned long int result = 0; register unsigned digit; int anyDigits = 0; + int negative=0; + int overflow=0; /* * Skip any leading blanks. */ p = string; - while (isspace(*p)) { + while (isspace(UCHAR(*p))) { p += 1; } + if (*p == '-') { + negative = 1; + p += 1; + } else { + if (*p == '+') { + p += 1; + } + } /* - * If no base was provided, pick one from the leading characters - * of the string. + * 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 { - /* - * Must set anyDigits here, otherwise "0" produces a - * "no digits" error. + * Must set anyDigits here, otherwise "0" produces a "no + * digits" error. */ 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[1] == 'X'))) { p += 2; } } /* - * Sorry this code is so messy, but speed seems important. Do - * different things for base 8, 10, 16, and other. + * Sorry this code is so messy, but speed seems important. Do different + * things for base 8, 10, 16, and other. */ if (base == 8) { + unsigned long maxres = ULONG_MAX >> 3; + for ( ; ; p += 1) { digit = *p - '0'; if (digit > 7) { break; } - result = (result << 3) + digit; + if (result > maxres) { overflow = 1; } + result = (result << 3); + if (digit > (ULONG_MAX - result)) { overflow = 1; } + result += digit; anyDigits = 1; } } else if (base == 10) { + unsigned long maxres = ULONG_MAX / 10; + for ( ; ; p += 1) { digit = *p - '0'; if (digit > 9) { break; } - result = (10*result) + digit; + if (result > maxres) { overflow = 1; } + result *= 10; + if (digit > (ULONG_MAX - result)) { overflow = 1; } + result += digit; anyDigits = 1; } } else if (base == 16) { + unsigned long maxres = ULONG_MAX >> 4; + for ( ; ; p += 1) { digit = *p - '0'; if (digit > ('z' - '0')) { @@ -149,20 +164,28 @@ strtoul(string, endPtr, base) if (digit > 15) { break; } - result = (result << 4) + digit; + if (result > maxres) { overflow = 1; } + result = (result << 4); + if (digit > (ULONG_MAX - result)) { overflow = 1; } + result += digit; anyDigits = 1; } - } else { + } else if (base >= 2 && base <= 36) { + unsigned long maxres = ULONG_MAX / base; + for ( ; ; p += 1) { digit = *p - '0'; if (digit > ('z' - '0')) { break; } digit = cvtIn[digit]; - if (digit >= base) { + if (digit >= ( (unsigned) base )) { break; } - result = result*base + digit; + if (result > maxres) { overflow = 1; } + result *= base; + if (digit > (ULONG_MAX - result)) { overflow = 1; } + result += digit; anyDigits = 1; } } @@ -176,8 +199,16 @@ strtoul(string, endPtr, base) } if (endPtr != 0) { - *endPtr = p; + /* unsafe, but required by the strtoul prototype */ + *endPtr = (char *) p; } + if (overflow) { + errno = ERANGE; + return ULONG_MAX; + } + if (negative) { + return -result; + } return result; } |
