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