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