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