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>
15SM_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
45LONGLONG_T
46sm_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
186ULONGLONG_T
187sm_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