1/* 2 * strtoul.c -- 3 * 4 * Source code for the "strtoul" library procedure. 5 * 6 * Copyright (c) 1988 The Regents of the University of California. 7 * Copyright (c) 1994 Sun Microsystems, Inc. 8 * 9 * See the file "license.terms" for information on usage and redistribution 10 * of this file, and for a DISCLAIMER OF ALL WARRANTIES. 11 * 12 * RCS: @(#) $Id: strtoul.c,v 1.5 2002/02/25 10:36:32 dkf Exp $ 13 */ 14 15#include "tclInt.h" 16#include "tclPort.h" 17 18/* 19 * The table below is used to convert from ASCII digits to a 20 * numerical equivalent. It maps from '0' through 'z' to integers 21 * (100 for non-digit characters). 22 */ 23 24static char cvtIn[] = { 25 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, /* '0' - '9' */ 26 100, 100, 100, 100, 100, 100, 100, /* punctuation */ 27 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, /* 'A' - 'Z' */ 28 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 29 30, 31, 32, 33, 34, 35, 30 100, 100, 100, 100, 100, 100, /* punctuation */ 31 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, /* 'a' - 'z' */ 32 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 33 30, 31, 32, 33, 34, 35}; 34 35/* 36 *---------------------------------------------------------------------- 37 * 38 * strtoul -- 39 * 40 * Convert an ASCII string into an integer. 41 * 42 * Results: 43 * The return value is the integer equivalent of string. If endPtr 44 * is non-NULL, then *endPtr is filled in with the character 45 * after the last one that was part of the integer. If string 46 * doesn't contain a valid integer value, then zero is returned 47 * and *endPtr is set to string. 48 * 49 * Side effects: 50 * None. 51 * 52 *---------------------------------------------------------------------- 53 */ 54 55unsigned long int 56strtoul(string, endPtr, base) 57 CONST char *string; /* String of ASCII digits, possibly 58 * preceded by white space. For bases 59 * greater than 10, either lower- or 60 * upper-case digits may be used. 61 */ 62 char **endPtr; /* Where to store address of terminating 63 * character, or NULL. */ 64 int base; /* Base for conversion. Must be less 65 * than 37. If 0, then the base is chosen 66 * from the leading characters of string: 67 * "0x" means hex, "0" means octal, anything 68 * else means decimal. 69 */ 70{ 71 register CONST char *p; 72 register unsigned long int result = 0; 73 register unsigned digit; 74 int anyDigits = 0; 75 int negative=0; 76 int overflow=0; 77 78 /* 79 * Skip any leading blanks. 80 */ 81 82 p = string; 83 while (isspace(UCHAR(*p))) { 84 p += 1; 85 } 86 if (*p == '-') { 87 negative = 1; 88 p += 1; 89 } else { 90 if (*p == '+') { 91 p += 1; 92 } 93 } 94 95 /* 96 * If no base was provided, pick one from the leading characters 97 * of the string. 98 */ 99 100 if (base == 0) 101 { 102 if (*p == '0') { 103 p += 1; 104 if ((*p == 'x') || (*p == 'X')) { 105 p += 1; 106 base = 16; 107 } else { 108 109 /* 110 * Must set anyDigits here, otherwise "0" produces a 111 * "no digits" error. 112 */ 113 114 anyDigits = 1; 115 base = 8; 116 } 117 } 118 else base = 10; 119 } else if (base == 16) { 120 121 /* 122 * Skip a leading "0x" from hex numbers. 123 */ 124 125 if ((p[0] == '0') && ((p[1] == 'x') || (p[1] == 'X'))) { 126 p += 2; 127 } 128 } 129 130 /* 131 * Sorry this code is so messy, but speed seems important. Do 132 * different things for base 8, 10, 16, and other. 133 */ 134 135 if (base == 8) { 136 unsigned long maxres = ULONG_MAX >> 3; 137 for ( ; ; p += 1) { 138 digit = *p - '0'; 139 if (digit > 7) { 140 break; 141 } 142 if (result > maxres) { overflow = 1; } 143 result = (result << 3); 144 if (digit > (ULONG_MAX - result)) { overflow = 1; } 145 result += digit; 146 anyDigits = 1; 147 } 148 } else if (base == 10) { 149 unsigned long maxres = ULONG_MAX / 10; 150 for ( ; ; p += 1) { 151 digit = *p - '0'; 152 if (digit > 9) { 153 break; 154 } 155 if (result > maxres) { overflow = 1; } 156 result *= 10; 157 if (digit > (ULONG_MAX - result)) { overflow = 1; } 158 result += digit; 159 anyDigits = 1; 160 } 161 } else if (base == 16) { 162 unsigned long maxres = ULONG_MAX >> 4; 163 for ( ; ; p += 1) { 164 digit = *p - '0'; 165 if (digit > ('z' - '0')) { 166 break; 167 } 168 digit = cvtIn[digit]; 169 if (digit > 15) { 170 break; 171 } 172 if (result > maxres) { overflow = 1; } 173 result = (result << 4); 174 if (digit > (ULONG_MAX - result)) { overflow = 1; } 175 result += digit; 176 anyDigits = 1; 177 } 178 } else if ( base >= 2 && base <= 36 ) { 179 unsigned long maxres = ULONG_MAX / base; 180 for ( ; ; p += 1) { 181 digit = *p - '0'; 182 if (digit > ('z' - '0')) { 183 break; 184 } 185 digit = cvtIn[digit]; 186 if (digit >= ( (unsigned) base )) { 187 break; 188 } 189 if (result > maxres) { overflow = 1; } 190 result *= base; 191 if (digit > (ULONG_MAX - result)) { overflow = 1; } 192 result += digit; 193 anyDigits = 1; 194 } 195 } 196 197 /* 198 * See if there were any digits at all. 199 */ 200 201 if (!anyDigits) { 202 p = string; 203 } 204 205 if (endPtr != 0) { 206 /* unsafe, but required by the strtoul prototype */ 207 *endPtr = (char *) p; 208 } 209 210 if (overflow) { 211 errno = ERANGE; 212 return ULONG_MAX; 213 } 214 if (negative) { 215 return -result; 216 } 217 return result; 218} 219