uu_strtoint.c revision 168404
1169691Skan/*
2169691Skan * CDDL HEADER START
3169691Skan *
4169691Skan * The contents of this file are subject to the terms of the
5169691Skan * Common Development and Distribution License, Version 1.0 only
6169691Skan * (the "License").  You may not use this file except in compliance
7169691Skan * with the License.
8169691Skan *
9169691Skan * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10169691Skan * or http://www.opensolaris.org/os/licensing.
11169691Skan * See the License for the specific language governing permissions
12169691Skan * and limitations under the License.
13169691Skan *
14169691Skan * When distributing Covered Code, include this CDDL HEADER in each
15169691Skan * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16169691Skan * If applicable, add the following below this CDDL HEADER, with the
17169691Skan * fields enclosed by brackets "[]" replaced with your own identifying
18169691Skan * information: Portions Copyright [yyyy] [name of copyright owner]
19169691Skan *
20169691Skan * CDDL HEADER END
21169691Skan */
22169691Skan/*
23169691Skan * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24169691Skan * Use is subject to license terms.
25169691Skan */
26169691Skan
27169691Skan#pragma ident	"%Z%%M%	%I%	%E% SMI"
28169691Skan
29169691Skan#include "libuutil_common.h"
30169691Skan
31169691Skan#include <limits.h>
32169691Skan#include <ctype.h>
33169691Skan
34169691Skan#define	MAX_BASE	36
35169691Skan
36169691Skan#define	IS_DIGIT(x)	((x) >= '0' && (x) <= '9')
37169691Skan
38169691Skan#define	CTOI(x) (((x) >= '0' && (x) <= '9') ? (x) - '0' : \
39169691Skan	    ((x) >= 'a' && (x) <= 'z') ? (x) + 10 - 'a' : (x) + 10 - 'A')
40169691Skan
41169691Skanstatic int
42169691Skanstrtoint(const char *s_arg, uint64_t *out, uint32_t base, int sign)
43169691Skan{
44169691Skan	const unsigned char *s = (const unsigned char *)s_arg;
45169691Skan
46169691Skan	uint64_t val = 0;
47169691Skan	uint64_t multmax;
48169691Skan
49169691Skan	unsigned c, i;
50169691Skan
51169691Skan	int neg = 0;
52169691Skan
53169691Skan	int bad_digit = 0;
54169691Skan	int bad_char = 0;
55169691Skan	int overflow = 0;
56169691Skan
57169691Skan	if (s == NULL || base == 1 || base > MAX_BASE) {
58169691Skan		uu_set_error(UU_ERROR_INVALID_ARGUMENT);
59169691Skan		return (-1);
60169691Skan	}
61169691Skan
62169691Skan	while ((c = *s) != 0 && isspace(c))
63169691Skan		s++;
64169691Skan
65169691Skan	switch (c) {
66169691Skan	case '-':
67169691Skan		if (!sign)
68169691Skan			overflow = 1;		/* becomes underflow below */
69169691Skan		neg = 1;
70169691Skan		/*FALLTHRU*/
71169691Skan	case '+':
72169691Skan		c = *++s;
73169691Skan		break;
74169691Skan	default:
75169691Skan		break;
76169691Skan	}
77169691Skan
78169691Skan	if (c == '\0') {
79169691Skan		uu_set_error(UU_ERROR_EMPTY);
80169691Skan		return (-1);
81169691Skan	}
82169691Skan
83169691Skan	if (base == 0) {
84169691Skan		if (c != '0')
85169691Skan			base = 10;
86169691Skan		else if (s[1] == 'x' || s[1] == 'X')
87169691Skan			base = 16;
88169691Skan		else
89169691Skan			base = 8;
90169691Skan	}
91169691Skan
92169691Skan	if (base == 16 && c == '0' && (s[1] == 'x' || s[1] == 'X'))
93169691Skan		c = *(s += 2);
94169691Skan
95169691Skan	if ((val = CTOI(c)) >= base) {
96169691Skan		if (IS_DIGIT(c))
97169691Skan			bad_digit = 1;
98169691Skan		else
99169691Skan			bad_char = 1;
100169691Skan		val = 0;
101169691Skan	}
102169691Skan
103169691Skan	multmax = (uint64_t)UINT64_MAX / (uint64_t)base;
104169691Skan
105169691Skan	for (c = *++s; c != '\0'; c = *++s) {
106169691Skan		if ((i = CTOI(c)) >= base) {
107169691Skan			if (isspace(c))
108169691Skan				break;
109169691Skan			if (IS_DIGIT(c))
110169691Skan				bad_digit = 1;
111169691Skan			else
112169691Skan				bad_char = 1;
113169691Skan			i = 0;
114169691Skan		}
115169691Skan
116169691Skan		if (val > multmax)
117169691Skan			overflow = 1;
118169691Skan
119169691Skan		val *= base;
120169691Skan		if ((uint64_t)UINT64_MAX - val < (uint64_t)i)
121169691Skan			overflow = 1;
122169691Skan
123169691Skan		val += i;
124169691Skan	}
125169691Skan
126169691Skan	while ((c = *s) != 0) {
127169691Skan		if (!isspace(c))
128169691Skan			bad_char = 1;
129169691Skan		s++;
130169691Skan	}
131169691Skan
132169691Skan	if (sign) {
133169691Skan		if (neg) {
134169691Skan			if (val > -(uint64_t)INT64_MIN)
135169691Skan				overflow = 1;
136169691Skan		} else {
137169691Skan			if (val > INT64_MAX)
138169691Skan				overflow = 1;
139169691Skan		}
140169691Skan	}
141169691Skan
142169691Skan	if (neg)
143169691Skan		val = -val;
144169691Skan
145169691Skan	if (bad_char | bad_digit | overflow) {
146169691Skan		if (bad_char)
147169691Skan			uu_set_error(UU_ERROR_INVALID_CHAR);
148169691Skan		else if (bad_digit)
149169691Skan			uu_set_error(UU_ERROR_INVALID_DIGIT);
150169691Skan		else if (overflow) {
151169691Skan			if (neg)
152169691Skan				uu_set_error(UU_ERROR_UNDERFLOW);
153169691Skan			else
154169691Skan				uu_set_error(UU_ERROR_OVERFLOW);
155169691Skan		}
156169691Skan		return (-1);
157169691Skan	}
158169691Skan
159169691Skan	*out = val;
160169691Skan	return (0);
161169691Skan}
162169691Skan
163169691Skanint
164169691Skanuu_strtoint(const char *s, void *v, size_t sz, int base,
165169691Skan    int64_t min, int64_t max)
166169691Skan{
167169691Skan	uint64_t val_u;
168169691Skan	int64_t val;
169169691Skan
170169691Skan	if (min > max)
171169691Skan		goto bad_argument;
172169691Skan
173169691Skan	switch (sz) {
174169691Skan	case 1:
175169691Skan		if (max > INT8_MAX || min < INT8_MIN)
176169691Skan			goto bad_argument;
177169691Skan		break;
178169691Skan	case 2:
179169691Skan		if (max > INT16_MAX || min < INT16_MIN)
180169691Skan			goto bad_argument;
181169691Skan		break;
182169691Skan	case 4:
183169691Skan		if (max > INT32_MAX || min < INT32_MIN)
184169691Skan			goto bad_argument;
185169691Skan		break;
186169691Skan	case 8:
187169691Skan		if (max > INT64_MAX || min < INT64_MIN)
188169691Skan			goto bad_argument;
189169691Skan		break;
190169691Skan	default:
191169691Skan		goto bad_argument;
192169691Skan	}
193169691Skan
194169691Skan	if (min == 0 && max == 0) {
195169691Skan		min = -(1ULL << (8 * sz - 1));
196169691Skan		max = (1ULL << (8 * sz - 1)) - 1;
197169691Skan	}
198169691Skan
199169691Skan	if (strtoint(s, &val_u, base, 1) == -1)
200169691Skan		return (-1);
201169691Skan
202169691Skan	val = (int64_t)val_u;
203169691Skan
204169691Skan	if (val < min) {
205169691Skan		uu_set_error(UU_ERROR_UNDERFLOW);
206169691Skan		return (-1);
207169691Skan	} else if (val > max) {
208169691Skan		uu_set_error(UU_ERROR_OVERFLOW);
209169691Skan		return (-1);
210169691Skan	}
211169691Skan
212169691Skan	switch (sz) {
213169691Skan	case 1:
214169691Skan		*(int8_t *)v = val;
215169691Skan		return (0);
216169691Skan	case 2:
217169691Skan		*(int16_t *)v = val;
218169691Skan		return (0);
219169691Skan	case 4:
220169691Skan		*(int32_t *)v = val;
221169691Skan		return (0);
222169691Skan	case 8:
223169691Skan		*(int64_t *)v = val;
224169691Skan		return (0);
225169691Skan	default:
226169691Skan		break;		/* fall through to bad_argument */
227169691Skan	}
228169691Skan
229169691Skanbad_argument:
230169691Skan	uu_set_error(UU_ERROR_INVALID_ARGUMENT);
231169691Skan	return (-1);
232169691Skan}
233169691Skan
234169691Skanint
235169691Skanuu_strtouint(const char *s, void *v, size_t sz, int base,
236169691Skan    uint64_t min, uint64_t max)
237169691Skan{
238169691Skan	uint64_t val;
239169691Skan
240169691Skan	if (min > max)
241169691Skan		goto bad_argument;
242169691Skan
243169691Skan	switch (sz) {
244169691Skan	case 1:
245169691Skan		if (max > UINT8_MAX)
246169691Skan			goto bad_argument;
247169691Skan		break;
248169691Skan	case 2:
249169691Skan		if (max > UINT16_MAX)
250169691Skan			goto bad_argument;
251169691Skan		break;
252169691Skan	case 4:
253169691Skan		if (max > UINT32_MAX)
254169691Skan			goto bad_argument;
255169691Skan		break;
256169691Skan	case 8:
257169691Skan		if (max > UINT64_MAX)
258169691Skan			goto bad_argument;
259169691Skan		break;
260169691Skan	default:
261169691Skan		goto bad_argument;
262169691Skan	}
263169691Skan
264169691Skan	if (min == 0 && max == 0) {
265169691Skan		/* we have to be careful, since << can overflow */
266169691Skan		max = (1ULL << (8 * sz - 1)) * 2 - 1;
267169691Skan	}
268169691Skan
269169691Skan	if (strtoint(s, &val, base, 0) == -1)
270169691Skan		return (-1);
271169691Skan
272169691Skan	if (val < min) {
273169691Skan		uu_set_error(UU_ERROR_UNDERFLOW);
274169691Skan		return (-1);
275169691Skan	} else if (val > max) {
276169691Skan		uu_set_error(UU_ERROR_OVERFLOW);
277169691Skan		return (-1);
278169691Skan	}
279169691Skan
280169691Skan	switch (sz) {
281169691Skan	case 1:
282169691Skan		*(uint8_t *)v = val;
283169691Skan		return (0);
284169691Skan	case 2:
285169691Skan		*(uint16_t *)v = val;
286169691Skan		return (0);
287169691Skan	case 4:
288169691Skan		*(uint32_t *)v = val;
289169691Skan		return (0);
290169691Skan	case 8:
291169691Skan		*(uint64_t *)v = val;
292169691Skan		return (0);
293169691Skan	default:
294169691Skan		break;		/* shouldn't happen, fall through */
295169691Skan	}
296169691Skan
297169691Skanbad_argument:
298169691Skan	uu_set_error(UU_ERROR_INVALID_ARGUMENT);
299169691Skan	return (-1);
300169691Skan}
301