xref: /illumos-gate/usr/src/cmd/sendmail/libsm/strto.c (revision 7c478bd9)
1 /*
2  * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
3  *      All rights reserved.
4  * Copyright (c) 1992
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * By using this file, you agree to the terms and conditions set
8  * forth in the LICENSE file which can be found at the top level of
9  * the sendmail distribution.
10  */
11 
12 #pragma ident	"%Z%%M%	%I%	%E% SMI"
13 
14 #include <sm/gen.h>
15 SM_IDSTR(id, "@(#)$Id: strto.c,v 1.18 2001/12/30 04:59:37 gshapiro Exp $")
16 
17 #include <sys/param.h>
18 #include <sys/types.h>
19 #include <stdlib.h>
20 #include <ctype.h>
21 #include <errno.h>
22 #include <sm/limits.h>
23 #include <sm/conf.h>
24 #include <sm/string.h>
25 
26 /*
27 **  SM_STRTOLL --  Convert a string to a (signed) long long integer.
28 **
29 **  Ignores `locale' stuff.  Assumes that the upper and lower case
30 **  alphabets and digits are each contiguous.
31 **
32 **	Parameters:
33 **		nptr -- string containing number
34 **		endptr -- location of first invalid character
35 **		base -- numeric base that 'nptr' number is based in
36 **
37 **	Returns:
38 **		Failure: on underflow LLONG_MIN is returned; on overflow
39 **			LLONG_MAX is returned and errno is set.
40 **			When 'endptr' == '\0' then the entire string 'nptr'
41 **			was valid.
42 **		Success: returns the converted number
43 */
44 
45 LONGLONG_T
46 sm_strtoll(nptr, endptr, base)
47 	const char *nptr;
48 	char **endptr;
49 	register int base;
50 {
51 	register bool neg;
52 	register const char *s;
53 	register LONGLONG_T acc, cutoff;
54 	register int c;
55 	register int any, cutlim;
56 
57 	/*
58 	**  Skip white space and pick up leading +/- sign if any.
59 	**  If base is 0, allow 0x for hex and 0 for octal, else
60 	**  assume decimal; if base is already 16, allow 0x.
61 	*/
62 
63 	s = nptr;
64 	do
65 	{
66 		c = (unsigned char) *s++;
67 	} while (isascii(c) && isspace(c));
68 	if (c == '-')
69 	{
70 		neg = true;
71 		c = *s++;
72 	}
73 	else
74 	{
75 		neg = false;
76 		if (c == '+')
77 			c = *s++;
78 	}
79 	if ((base == 0 || base == 16) &&
80 	    c == '0' && (*s == 'x' || *s == 'X'))
81 	{
82 		c = s[1];
83 		s += 2;
84 		base = 16;
85 	}
86 	if (base == 0)
87 		base = c == '0' ? 8 : 10;
88 
89 	/*
90 	**  Compute the cutoff value between legal numbers and illegal
91 	**  numbers.  That is the largest legal value, divided by the
92 	**  base.  An input number that is greater than this value, if
93 	**  followed by a legal input character, is too big.  One that
94 	**  is equal to this value may be valid or not; the limit
95 	**  between valid and invalid numbers is then based on the last
96 	**  digit.  For instance, if the range for long-long's is
97 	**  [-9223372036854775808..9223372036854775807] and the input base
98 	**  is 10, cutoff will be set to 922337203685477580 and cutlim to
99 	**  either 7 (!neg) or 8 (neg), meaning that if we have
100 	**  accumulated a value > 922337203685477580, or equal but the
101 	**  next digit is > 7 (or 8), the number is too big, and we will
102 	**  return a range error.
103 	**
104 	**  Set any if any `digits' consumed; make it negative to indicate
105 	**  overflow.
106 	*/
107 
108 	cutoff = neg ? LLONG_MIN : LLONG_MAX;
109 	cutlim = cutoff % base;
110 	cutoff /= base;
111 	if (neg)
112 	{
113 		if (cutlim > 0)
114 		{
115 			cutlim -= base;
116 			cutoff += 1;
117 		}
118 		cutlim = -cutlim;
119 	}
120 	for (acc = 0, any = 0;; c = (unsigned char) *s++)
121 	{
122 		if (isascii(c) && isdigit(c))
123 			c -= '0';
124 		else if (isascii(c) && isalpha(c))
125 			c -= isupper(c) ? 'A' - 10 : 'a' - 10;
126 		else
127 			break;
128 		if (c >= base)
129 			break;
130 		if (any < 0)
131 			continue;
132 		if (neg)
133 		{
134 			if (acc < cutoff || (acc == cutoff && c > cutlim))
135 			{
136 				any = -1;
137 				acc = LLONG_MIN;
138 				errno = ERANGE;
139 			}
140 			else
141 			{
142 				any = 1;
143 				acc *= base;
144 				acc -= c;
145 			}
146 		}
147 		else
148 		{
149 			if (acc > cutoff || (acc == cutoff && c > cutlim))
150 			{
151 				any = -1;
152 				acc = LLONG_MAX;
153 				errno = ERANGE;
154 			}
155 			else
156 			{
157 				any = 1;
158 				acc *= base;
159 				acc += c;
160 			}
161 		}
162 	}
163 	if (endptr != 0)
164 		*endptr = (char *) (any ? s - 1 : nptr);
165 	return acc;
166 }
167 
168 /*
169 **  SM_STRTOULL --  Convert a string to an unsigned long long integer.
170 **
171 **  Ignores `locale' stuff.  Assumes that the upper and lower case
172 **  alphabets and digits are each contiguous.
173 **
174 **	Parameters:
175 **		nptr -- string containing (unsigned) number
176 **		endptr -- location of first invalid character
177 **		base -- numeric base that 'nptr' number is based in
178 **
179 **	Returns:
180 **		Failure: on overflow ULLONG_MAX is returned and errno is set.
181 **			When 'endptr' == '\0' then the entire string 'nptr'
182 **			was valid.
183 **		Success: returns the converted number
184 */
185 
186 ULONGLONG_T
187 sm_strtoull(nptr, endptr, base)
188 	const char *nptr;
189 	char **endptr;
190 	register int base;
191 {
192 	register const char *s;
193 	register ULONGLONG_T acc, cutoff;
194 	register int c;
195 	register bool neg;
196 	register int any, cutlim;
197 
198 	/* See sm_strtoll for comments as to the logic used. */
199 	s = nptr;
200 	do
201 	{
202 		c = (unsigned char) *s++;
203 	} while (isascii(c) && isspace(c));
204 	neg = (c == '-');
205 	if (neg)
206 	{
207 		c = *s++;
208 	}
209 	else
210 	{
211 		if (c == '+')
212 			c = *s++;
213 	}
214 	if ((base == 0 || base == 16) &&
215 	    c == '0' && (*s == 'x' || *s == 'X'))
216 	{
217 		c = s[1];
218 		s += 2;
219 		base = 16;
220 	}
221 	if (base == 0)
222 		base = c == '0' ? 8 : 10;
223 
224 	cutoff = ULLONG_MAX / (ULONGLONG_T)base;
225 	cutlim = ULLONG_MAX % (ULONGLONG_T)base;
226 	for (acc = 0, any = 0;; c = (unsigned char) *s++)
227 	{
228 		if (isascii(c) && isdigit(c))
229 			c -= '0';
230 		else if (isascii(c) && isalpha(c))
231 			c -= isupper(c) ? 'A' - 10 : 'a' - 10;
232 		else
233 			break;
234 		if (c >= base)
235 			break;
236 		if (any < 0)
237 			continue;
238 		if (acc > cutoff || (acc == cutoff && c > cutlim))
239 		{
240 			any = -1;
241 			acc = ULLONG_MAX;
242 			errno = ERANGE;
243 		}
244 		else
245 		{
246 			any = 1;
247 			acc *= (ULONGLONG_T)base;
248 			acc += c;
249 		}
250 	}
251 	if (neg && any > 0)
252 		acc = -((LONGLONG_T) acc);
253 	if (endptr != 0)
254 		*endptr = (char *) (any ? s - 1 : nptr);
255 	return acc;
256 }
257