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