xref: /csrg-svn/lib/libc/stdlib/strtoq.c (revision 54495)
1*54495Sbostic /*-
2*54495Sbostic  * Copyright (c) 1992 The Regents of the University of California.
3*54495Sbostic  * All rights reserved.
4*54495Sbostic  *
5*54495Sbostic  * %sccs.include.redist.c%
6*54495Sbostic  */
7*54495Sbostic 
8*54495Sbostic #if defined(LIBC_SCCS) && !defined(lint)
9*54495Sbostic static char sccsid[] = "@(#)strtoq.c	5.1 (Berkeley) 06/26/92";
10*54495Sbostic #endif /* LIBC_SCCS and not lint */
11*54495Sbostic 
12*54495Sbostic #include <sys/types.h>
13*54495Sbostic 
14*54495Sbostic #include <limits.h>
15*54495Sbostic #include <errno.h>
16*54495Sbostic #include <ctype.h>
17*54495Sbostic #include <stdlib.h>
18*54495Sbostic 
19*54495Sbostic /*
20*54495Sbostic  * Convert a string to a quad integer.
21*54495Sbostic  *
22*54495Sbostic  * Ignores `locale' stuff.  Assumes that the upper and lower case
23*54495Sbostic  * alphabets and digits are each contiguous.
24*54495Sbostic  */
25*54495Sbostic quad_t
26*54495Sbostic strtoq(nptr, endptr, base)
27*54495Sbostic 	const char *nptr;
28*54495Sbostic 	char **endptr;
29*54495Sbostic 	register int base;
30*54495Sbostic {
31*54495Sbostic 	register const char *s;
32*54495Sbostic 	register u_quad_t acc;
33*54495Sbostic 	register int c;
34*54495Sbostic 	register u_quad_t qbase, cutoff;
35*54495Sbostic 	register int neg, any, cutlim;
36*54495Sbostic 
37*54495Sbostic 	/*
38*54495Sbostic 	 * Skip white space and pick up leading +/- sign if any.
39*54495Sbostic 	 * If base is 0, allow 0x for hex and 0 for octal, else
40*54495Sbostic 	 * assume decimal; if base is already 16, allow 0x.
41*54495Sbostic 	 */
42*54495Sbostic 	s = nptr;
43*54495Sbostic 	do {
44*54495Sbostic 		c = *s++;
45*54495Sbostic 	} while (isspace(c));
46*54495Sbostic 	if (c == '-') {
47*54495Sbostic 		neg = 1;
48*54495Sbostic 		c = *s++;
49*54495Sbostic 	} else {
50*54495Sbostic 		neg = 0;
51*54495Sbostic 		if (c == '+')
52*54495Sbostic 			c = *s++;
53*54495Sbostic 	}
54*54495Sbostic 	if ((base == 0 || base == 16) &&
55*54495Sbostic 	    c == '0' && (*s == 'x' || *s == 'X')) {
56*54495Sbostic 		c = s[1];
57*54495Sbostic 		s += 2;
58*54495Sbostic 		base = 16;
59*54495Sbostic 	}
60*54495Sbostic 	if (base == 0)
61*54495Sbostic 		base = c == '0' ? 8 : 10;
62*54495Sbostic 
63*54495Sbostic 	/*
64*54495Sbostic 	 * Compute the cutoff value between legal numbers and illegal
65*54495Sbostic 	 * numbers.  That is the largest legal value, divided by the
66*54495Sbostic 	 * base.  An input number that is greater than this value, if
67*54495Sbostic 	 * followed by a legal input character, is too big.  One that
68*54495Sbostic 	 * is equal to this value may be valid or not; the limit
69*54495Sbostic 	 * between valid and invalid numbers is then based on the last
70*54495Sbostic 	 * digit.  For instance, if the range for quads is
71*54495Sbostic 	 * [-9223372036854775808..9223372036854775807] and the input base
72*54495Sbostic 	 * is 10, cutoff will be set to 922337203685477580 and cutlim to
73*54495Sbostic 	 * either 7 (neg==0) or 8 (neg==1), meaning that if we have
74*54495Sbostic 	 * accumulated a value > 922337203685477580, or equal but the
75*54495Sbostic 	 * next digit is > 7 (or 8), the number is too big, and we will
76*54495Sbostic 	 * return a range error.
77*54495Sbostic 	 *
78*54495Sbostic 	 * Set any if any `digits' consumed; make it negative to indicate
79*54495Sbostic 	 * overflow.
80*54495Sbostic 	 */
81*54495Sbostic 	qbase = (unsigned)base;
82*54495Sbostic 	cutoff = neg ? -(u_quad_t)QUAD_MIN : QUAD_MAX;
83*54495Sbostic 	cutlim = cutoff % qbase;
84*54495Sbostic 	cutoff /= qbase;
85*54495Sbostic 	for (acc = 0, any = 0;; c = *s++) {
86*54495Sbostic 		if (isdigit(c))
87*54495Sbostic 			c -= '0';
88*54495Sbostic 		else if (isalpha(c))
89*54495Sbostic 			c -= isupper(c) ? 'A' - 10 : 'a' - 10;
90*54495Sbostic 		else
91*54495Sbostic 			break;
92*54495Sbostic 		if (c >= base)
93*54495Sbostic 			break;
94*54495Sbostic 		if (any < 0 || acc > cutoff || acc == cutoff && c > cutlim)
95*54495Sbostic 			any = -1;
96*54495Sbostic 		else {
97*54495Sbostic 			any = 1;
98*54495Sbostic 			acc *= qbase;
99*54495Sbostic 			acc += c;
100*54495Sbostic 		}
101*54495Sbostic 	}
102*54495Sbostic 	if (any < 0) {
103*54495Sbostic 		acc = neg ? QUAD_MIN : QUAD_MAX;
104*54495Sbostic 		errno = ERANGE;
105*54495Sbostic 	} else if (neg)
106*54495Sbostic 		acc = -acc;
107*54495Sbostic 	if (endptr != 0)
108*54495Sbostic 		*endptr = any ? s - 1 : (char *)nptr;
109*54495Sbostic 	return (acc);
110*54495Sbostic }
111