1 /* @(#)astoul.c	1.2 18/08/31 Copyright 1985, 2000-2018 J. Schilling */
2 /*
3  *	astoul() converts a string to unsigned long
4  *
5  *	Leading tabs and spaces are ignored.
6  *	Both return pointer to the first char that has not been used.
7  *	Caller must check if this means a bad conversion.
8  *
9  *	leading "+" is ignored
10  *	leading "0"  makes conversion octal (base 8)
11  *	leading "0x" makes conversion hex   (base 16)
12  *
13  *	Copyright (c) 1985, 2000-2018 J. Schilling
14  */
15 /*
16  * The contents of this file are subject to the terms of the
17  * Common Development and Distribution License, Version 1.0 only
18  * (the "License").  You may not use this file except in compliance
19  * with the License.
20  *
21  * See the file CDDL.Schily.txt in this distribution for details.
22  * A copy of the CDDL is also available via the Internet at
23  * http://www.opensource.org/licenses/cddl1.txt
24  *
25  * When distributing Covered Code, include this CDDL HEADER in each
26  * file and include the License file CDDL.Schily.txt from this distribution.
27  */
28 
29 #include <schily/mconfig.h>
30 #include <schily/standard.h>
31 #include <schily/utypes.h>
32 #include <schily/schily.h>
33 #include <schily/errno.h>
34 
35 #ifndef	NO_SIGNED_ULONG
36 #define	DO_SIGNED
37 #endif
38 
39 #define	is_space(c)	 ((c) == ' ' || (c) == '\t')
40 #define	is_digit(c)	 ((c) >= '0' && (c) <= '9')
41 #define	is_hex(c)	(\
42 			((c) >= 'a' && (c) <= 'f') || \
43 			((c) >= 'A' && (c) <= 'F'))
44 
45 #define	is_lower(c)	((c) >= 'a' && (c) <= 'z')
46 #define	is_upper(c)	((c) >= 'A' && (c) <= 'Z')
47 #define	to_lower(c)	(((c) >= 'A' && (c) <= 'Z') ? (c) - 'A'+'a' : (c))
48 
49 #if	('i' + 1) < 'j'
50 #define	BASE_MAX	('i' - 'a' + 10 + 1)	/* This is EBCDIC */
51 #else
52 #define	BASE_MAX	('z' - 'a' + 10 + 1)	/* This is ASCII */
53 #endif
54 
55 
56 
57 EXPORT	char *astoul	__PR((const char *s, unsigned long *l));
58 EXPORT	char *astoulb	__PR((const char *s, unsigned long *l, int base));
59 
60 EXPORT char *
astoul(s,l)61 astoul(s, l)
62 	register const char *s;
63 	unsigned long *l;
64 {
65 	return (astoulb(s, l, 0));
66 }
67 
68 EXPORT char *
astoulb(s,l,base)69 astoulb(s, l, base)
70 	register const char *s;
71 	unsigned long *l;
72 	register int base;
73 {
74 #ifdef	DO_SIGNED
75 	int neg = 0;
76 #endif
77 	register unsigned long ret = (unsigned long)0;
78 		unsigned long maxmult;
79 	register int digit;
80 	register char c;
81 
82 	if (base > BASE_MAX || base == 1 || base < 0) {
83 		seterrno(EINVAL);
84 		return ((char *)s);
85 	}
86 
87 	while (is_space(*s))
88 		s++;
89 
90 	if (*s == '+') {
91 		s++;
92 	} else if (*s == '-') {
93 #ifndef	DO_SIGNED
94 		seterrno(EINVAL);
95 		return ((char *)s);
96 #else
97 		s++;
98 		neg++;
99 #endif
100 	}
101 
102 	if (base == 0) {
103 		if (*s == '0') {
104 			base = 8;
105 			s++;
106 			if (*s == 'x' || *s == 'X') {
107 				s++;
108 				base = 16;
109 			}
110 		} else {
111 			base = 10;
112 		}
113 	}
114 	maxmult = TYPE_MAXVAL(unsigned long) / base;
115 	for (; (c = *s) != 0; s++) {
116 
117 		if (is_digit(c)) {
118 			digit = c - '0';
119 		} else if (is_lower(c)) {
120 			digit = c - 'a' + 10;
121 		} else if (is_upper(c)) {
122 			digit = c - 'A' + 10;
123 		} else {
124 			break;
125 		}
126 
127 		if (digit < base) {
128 			if (ret > maxmult)
129 				goto overflow;
130 			ret *= base;
131 			if (TYPE_MAXVAL(unsigned long) - ret < digit)
132 				goto overflow;
133 			ret += digit;
134 		} else {
135 			break;
136 		}
137 	}
138 #ifdef	DO_SIGNED
139 	if (neg)
140 		ret = -ret;
141 #endif
142 	*l = ret;
143 	return ((char *)s);
144 overflow:
145 	for (; (c = *s) != 0; s++) {
146 
147 		if (is_digit(c)) {
148 			digit = c - '0';
149 		} else if (is_lower(c)) {
150 			digit = c - 'a' + 10;
151 		} else if (is_upper(c)) {
152 			digit = c - 'A' + 10;
153 		} else {
154 			break;
155 		}
156 		if (digit >= base)
157 			break;
158 	}
159 	*l = TYPE_MAXVAL(unsigned long);
160 	seterrno(ERANGE);
161 	return ((char *)s);
162 }
163