1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29#include "libuutil_common.h"
30
31#include <limits.h>
32#include <ctype.h>
33
34#define	MAX_BASE	36
35
36#define	IS_DIGIT(x)	((x) >= '0' && (x) <= '9')
37
38#define	CTOI(x) (((x) >= '0' && (x) <= '9') ? (x) - '0' : \
39	    ((x) >= 'a' && (x) <= 'z') ? (x) + 10 - 'a' : (x) + 10 - 'A')
40
41static int
42strtoint(const char *s_arg, uint64_t *out, uint32_t base, int sign)
43{
44	const unsigned char *s = (const unsigned char *)s_arg;
45
46	uint64_t val = 0;
47	uint64_t multmax;
48
49	unsigned c, i;
50
51	int neg = 0;
52
53	int bad_digit = 0;
54	int bad_char = 0;
55	int overflow = 0;
56
57	if (s == NULL || base == 1 || base > MAX_BASE) {
58		uu_set_error(UU_ERROR_INVALID_ARGUMENT);
59		return (-1);
60	}
61
62	while ((c = *s) != 0 && isspace(c))
63		s++;
64
65	switch (c) {
66	case '-':
67		if (!sign)
68			overflow = 1;		/* becomes underflow below */
69		neg = 1;
70		/*FALLTHRU*/
71	case '+':
72		c = *++s;
73		break;
74	default:
75		break;
76	}
77
78	if (c == '\0') {
79		uu_set_error(UU_ERROR_EMPTY);
80		return (-1);
81	}
82
83	if (base == 0) {
84		if (c != '0')
85			base = 10;
86		else if (s[1] == 'x' || s[1] == 'X')
87			base = 16;
88		else
89			base = 8;
90	}
91
92	if (base == 16 && c == '0' && (s[1] == 'x' || s[1] == 'X'))
93		c = *(s += 2);
94
95	if ((val = CTOI(c)) >= base) {
96		if (IS_DIGIT(c))
97			bad_digit = 1;
98		else
99			bad_char = 1;
100		val = 0;
101	}
102
103	multmax = (uint64_t)UINT64_MAX / (uint64_t)base;
104
105	for (c = *++s; c != '\0'; c = *++s) {
106		if ((i = CTOI(c)) >= base) {
107			if (isspace(c))
108				break;
109			if (IS_DIGIT(c))
110				bad_digit = 1;
111			else
112				bad_char = 1;
113			i = 0;
114		}
115
116		if (val > multmax)
117			overflow = 1;
118
119		val *= base;
120		if ((uint64_t)UINT64_MAX - val < (uint64_t)i)
121			overflow = 1;
122
123		val += i;
124	}
125
126	while ((c = *s) != 0) {
127		if (!isspace(c))
128			bad_char = 1;
129		s++;
130	}
131
132	if (sign) {
133		if (neg) {
134			if (val > -(uint64_t)INT64_MIN)
135				overflow = 1;
136		} else {
137			if (val > INT64_MAX)
138				overflow = 1;
139		}
140	}
141
142	if (neg)
143		val = -val;
144
145	if (bad_char | bad_digit | overflow) {
146		if (bad_char)
147			uu_set_error(UU_ERROR_INVALID_CHAR);
148		else if (bad_digit)
149			uu_set_error(UU_ERROR_INVALID_DIGIT);
150		else if (overflow) {
151			if (neg)
152				uu_set_error(UU_ERROR_UNDERFLOW);
153			else
154				uu_set_error(UU_ERROR_OVERFLOW);
155		}
156		return (-1);
157	}
158
159	*out = val;
160	return (0);
161}
162
163int
164uu_strtoint(const char *s, void *v, size_t sz, int base,
165    int64_t min, int64_t max)
166{
167	uint64_t val_u;
168	int64_t val;
169
170	if (min > max)
171		goto bad_argument;
172
173	switch (sz) {
174	case 1:
175		if (max > INT8_MAX || min < INT8_MIN)
176			goto bad_argument;
177		break;
178	case 2:
179		if (max > INT16_MAX || min < INT16_MIN)
180			goto bad_argument;
181		break;
182	case 4:
183		if (max > INT32_MAX || min < INT32_MIN)
184			goto bad_argument;
185		break;
186	case 8:
187		if (max > INT64_MAX || min < INT64_MIN)
188			goto bad_argument;
189		break;
190	default:
191		goto bad_argument;
192	}
193
194	if (min == 0 && max == 0) {
195		min = -(1ULL << (8 * sz - 1));
196		max = (1ULL << (8 * sz - 1)) - 1;
197	}
198
199	if (strtoint(s, &val_u, base, 1) == -1)
200		return (-1);
201
202	val = (int64_t)val_u;
203
204	if (val < min) {
205		uu_set_error(UU_ERROR_UNDERFLOW);
206		return (-1);
207	} else if (val > max) {
208		uu_set_error(UU_ERROR_OVERFLOW);
209		return (-1);
210	}
211
212	switch (sz) {
213	case 1:
214		*(int8_t *)v = val;
215		return (0);
216	case 2:
217		*(int16_t *)v = val;
218		return (0);
219	case 4:
220		*(int32_t *)v = val;
221		return (0);
222	case 8:
223		*(int64_t *)v = val;
224		return (0);
225	default:
226		break;		/* fall through to bad_argument */
227	}
228
229bad_argument:
230	uu_set_error(UU_ERROR_INVALID_ARGUMENT);
231	return (-1);
232}
233
234int
235uu_strtouint(const char *s, void *v, size_t sz, int base,
236    uint64_t min, uint64_t max)
237{
238	uint64_t val;
239
240	if (min > max)
241		goto bad_argument;
242
243	switch (sz) {
244	case 1:
245		if (max > UINT8_MAX)
246			goto bad_argument;
247		break;
248	case 2:
249		if (max > UINT16_MAX)
250			goto bad_argument;
251		break;
252	case 4:
253		if (max > UINT32_MAX)
254			goto bad_argument;
255		break;
256	case 8:
257		if (max > UINT64_MAX)
258			goto bad_argument;
259		break;
260	default:
261		goto bad_argument;
262	}
263
264	if (min == 0 && max == 0) {
265		/* we have to be careful, since << can overflow */
266		max = (1ULL << (8 * sz - 1)) * 2 - 1;
267	}
268
269	if (strtoint(s, &val, base, 0) == -1)
270		return (-1);
271
272	if (val < min) {
273		uu_set_error(UU_ERROR_UNDERFLOW);
274		return (-1);
275	} else if (val > max) {
276		uu_set_error(UU_ERROR_OVERFLOW);
277		return (-1);
278	}
279
280	switch (sz) {
281	case 1:
282		*(uint8_t *)v = val;
283		return (0);
284	case 2:
285		*(uint16_t *)v = val;
286		return (0);
287	case 4:
288		*(uint32_t *)v = val;
289		return (0);
290	case 8:
291		*(uint64_t *)v = val;
292		return (0);
293	default:
294		break;		/* shouldn't happen, fall through */
295	}
296
297bad_argument:
298	uu_set_error(UU_ERROR_INVALID_ARGUMENT);
299	return (-1);
300}
301