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