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