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