1 /*
2  * This file has been modified for the cdrkit suite.
3  *
4  * The behaviour and appearence of the program code below can differ to a major
5  * extent from the version distributed by the original author(s).
6  *
7  * For details, see Changelog file distributed with the cdrkit package. If you
8  * received this file from another source then ask the distributing person for
9  * a log of modifications.
10  *
11  */
12 
13 /* @(#)astoll.c	1.3 03/06/15 Copyright 1985, 2000-2005 J. Schilling */
14 /*
15  *	astoll() converts a string to long long
16  *
17  *	Leading tabs and spaces are ignored.
18  *	Both return pointer to the first char that has not been used.
19  *	Caller must check if this means a bad conversion.
20  *
21  *	leading "+" is ignored
22  *	leading "0"  makes conversion octal (base 8)
23  *	leading "0x" makes conversion hex   (base 16)
24  *
25  *	Llong is silently reverted to long if the compiler does not
26  *	support long long.
27  *
28  *	Copyright (c) 1985, 2000-2005 J. Schilling
29  */
30 /*
31  * This program is free software; you can redistribute it and/or modify
32  * it under the terms of the GNU General Public License version 2
33  * as published by the Free Software Foundation.
34  *
35  * This program is distributed in the hope that it will be useful,
36  * but WITHOUT ANY WARRANTY; without even the implied warranty of
37  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
38  * GNU General Public License for more details.
39  *
40  * You should have received a copy of the GNU General Public License along with
41  * this program; see the file COPYING.  If not, write to the Free Software
42  * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
43  */
44 
45 #include <mconfig.h>
46 #include <standard.h>
47 #include <utypes.h>
48 #include <schily.h>
49 #include <errno.h>
50 #ifndef	HAVE_ERRNO_DEF
51 extern	int	errno;
52 #endif
53 
54 #define	is_space(c)	 ((c) == ' ' || (c) == '\t')
55 #define	is_digit(c)	 ((c) >= '0' && (c) <= '9')
56 #define	is_hex(c)	(\
57 			((c) >= 'a' && (c) <= 'f') || \
58 			((c) >= 'A' && (c) <= 'F'))
59 
60 #define	is_lower(c)	((c) >= 'a' && (c) <= 'z')
61 #define	is_upper(c)	((c) >= 'A' && (c) <= 'Z')
62 #define	to_lower(c)	(((c) >= 'A' && (c) <= 'Z') ? (c) - 'A'+'a' : (c))
63 
64 #if	('i' + 1) < 'j'
65 #define	BASE_MAX	('i' - 'a' + 10 + 1)	/* This is EBCDIC */
66 #else
67 #define	BASE_MAX	('z' - 'a' + 10 + 1)	/* This is ASCII */
68 #endif
69 
70 
71 
72 EXPORT	char *astoull	__PR((const char *s, Ullong *l));
73 EXPORT	char *astoullb	__PR((const char *s, Ullong *l, int base));
74 
75 EXPORT char *
astoull(s,l)76 astoull(s, l)
77 	register const char *s;
78 	Ullong *l;
79 {
80 	return (astoullb(s, l, 0));
81 }
82 
83 EXPORT char *
astoullb(s,l,base)84 astoullb(s, l, base)
85 	register const char *s;
86 	Ullong *l;
87 	register int base;
88 {
89 #ifdef	DO_SIGNED
90 	int neg = 0;
91 #endif
92 	register Ullong ret = (Ullong)0;
93 		Ullong maxmult;
94 	register int digit;
95 	register char c;
96 
97 	if (base > BASE_MAX || base == 1 || base < 0) {
98 		errno = EINVAL;
99 		return ((char *)s);
100 	}
101 
102 	while (is_space(*s))
103 		s++;
104 
105 	if (*s == '+') {
106 		s++;
107 	} else if (*s == '-') {
108 #ifndef	DO_SIGNED
109 		errno = EINVAL;
110 		return ((char *)s);
111 #else
112 		s++;
113 		neg++;
114 #endif
115 	}
116 
117 	if (base == 0) {
118 		if (*s == '0') {
119 			base = 8;
120 			s++;
121 			if (*s == 'x' || *s == 'X') {
122 				s++;
123 				base = 16;
124 			}
125 		} else {
126 			base = 10;
127 		}
128 	}
129 	maxmult = TYPE_MAXVAL(Ullong) / base;
130 	for (; (c = *s) != 0; s++) {
131 
132 		if (is_digit(c)) {
133 			digit = c - '0';
134 #ifdef	OLD
135 		} else if (is_hex(c)) {
136 			digit = to_lower(c) - 'a' + 10;
137 #else
138 		} else if (is_lower(c)) {
139 			digit = c - 'a' + 10;
140 		} else if (is_upper(c)) {
141 			digit = c - 'A' + 10;
142 #endif
143 		} else {
144 			break;
145 		}
146 
147 		if (digit < base) {
148 			if (ret > maxmult)
149 				goto overflow;
150 			ret *= base;
151 			if (TYPE_MAXVAL(Ullong) - ret < digit)
152 				goto overflow;
153 			ret += digit;
154 		} else {
155 			break;
156 		}
157 	}
158 #ifdef	DO_SIGNED
159 	if (neg)
160 		ret = -ret;
161 #endif
162 	*l = ret;
163 	return ((char *)s);
164 overflow:
165 	for (; (c = *s) != 0; s++) {
166 
167 		if (is_digit(c)) {
168 			digit = c - '0';
169 		} else if (is_lower(c)) {
170 			digit = c - 'a' + 10;
171 		} else if (is_upper(c)) {
172 			digit = c - 'A' + 10;
173 		} else {
174 			break;
175 		}
176 		if (digit >= base)
177 			break;
178 	}
179 	*l = TYPE_MAXVAL(Ullong);
180 	errno = ERANGE;
181 	return ((char *)s);
182 }
183