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