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
sm_strtoull(nptr,endptr,base)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