xref: /openbmc/linux/lib/kstrtox.c (revision 565d76cb)
1 /*
2  * Convert integer string representation to an integer.
3  * If an integer doesn't fit into specified type, -E is returned.
4  *
5  * Integer starts with optional sign.
6  * kstrtou*() functions do not accept sign "-".
7  *
8  * Radix 0 means autodetection: leading "0x" implies radix 16,
9  * leading "0" implies radix 8, otherwise radix is 10.
10  * Autodetection hints work after optional sign, but not before.
11  *
12  * If -E is returned, result is not touched.
13  */
14 #include <linux/ctype.h>
15 #include <linux/errno.h>
16 #include <linux/kernel.h>
17 #include <linux/math64.h>
18 #include <linux/module.h>
19 #include <linux/types.h>
20 
21 static inline char _tolower(const char c)
22 {
23 	return c | 0x20;
24 }
25 
26 static int _kstrtoull(const char *s, unsigned int base, unsigned long long *res)
27 {
28 	unsigned long long acc;
29 	int ok;
30 
31 	if (base == 0) {
32 		if (s[0] == '0') {
33 			if (_tolower(s[1]) == 'x' && isxdigit(s[2]))
34 				base = 16;
35 			else
36 				base = 8;
37 		} else
38 			base = 10;
39 	}
40 	if (base == 16 && s[0] == '0' && _tolower(s[1]) == 'x')
41 		s += 2;
42 
43 	acc = 0;
44 	ok = 0;
45 	while (*s) {
46 		unsigned int val;
47 
48 		if ('0' <= *s && *s <= '9')
49 			val = *s - '0';
50 		else if ('a' <= _tolower(*s) && _tolower(*s) <= 'f')
51 			val = _tolower(*s) - 'a' + 10;
52 		else if (*s == '\n') {
53 			if (*(s + 1) == '\0')
54 				break;
55 			else
56 				return -EINVAL;
57 		} else
58 			return -EINVAL;
59 
60 		if (val >= base)
61 			return -EINVAL;
62 		if (acc > div_u64(ULLONG_MAX - val, base))
63 			return -ERANGE;
64 		acc = acc * base + val;
65 		ok = 1;
66 
67 		s++;
68 	}
69 	if (!ok)
70 		return -EINVAL;
71 	*res = acc;
72 	return 0;
73 }
74 
75 int kstrtoull(const char *s, unsigned int base, unsigned long long *res)
76 {
77 	if (s[0] == '+')
78 		s++;
79 	return _kstrtoull(s, base, res);
80 }
81 EXPORT_SYMBOL(kstrtoull);
82 
83 int kstrtoll(const char *s, unsigned int base, long long *res)
84 {
85 	unsigned long long tmp;
86 	int rv;
87 
88 	if (s[0] == '-') {
89 		rv = _kstrtoull(s + 1, base, &tmp);
90 		if (rv < 0)
91 			return rv;
92 		if ((long long)(-tmp) >= 0)
93 			return -ERANGE;
94 		*res = -tmp;
95 	} else {
96 		rv = kstrtoull(s, base, &tmp);
97 		if (rv < 0)
98 			return rv;
99 		if ((long long)tmp < 0)
100 			return -ERANGE;
101 		*res = tmp;
102 	}
103 	return 0;
104 }
105 EXPORT_SYMBOL(kstrtoll);
106 
107 /* Internal, do not use. */
108 int _kstrtoul(const char *s, unsigned int base, unsigned long *res)
109 {
110 	unsigned long long tmp;
111 	int rv;
112 
113 	rv = kstrtoull(s, base, &tmp);
114 	if (rv < 0)
115 		return rv;
116 	if (tmp != (unsigned long long)(unsigned long)tmp)
117 		return -ERANGE;
118 	*res = tmp;
119 	return 0;
120 }
121 EXPORT_SYMBOL(_kstrtoul);
122 
123 /* Internal, do not use. */
124 int _kstrtol(const char *s, unsigned int base, long *res)
125 {
126 	long long tmp;
127 	int rv;
128 
129 	rv = kstrtoll(s, base, &tmp);
130 	if (rv < 0)
131 		return rv;
132 	if (tmp != (long long)(long)tmp)
133 		return -ERANGE;
134 	*res = tmp;
135 	return 0;
136 }
137 EXPORT_SYMBOL(_kstrtol);
138 
139 int kstrtouint(const char *s, unsigned int base, unsigned int *res)
140 {
141 	unsigned long long tmp;
142 	int rv;
143 
144 	rv = kstrtoull(s, base, &tmp);
145 	if (rv < 0)
146 		return rv;
147 	if (tmp != (unsigned long long)(unsigned int)tmp)
148 		return -ERANGE;
149 	*res = tmp;
150 	return 0;
151 }
152 EXPORT_SYMBOL(kstrtouint);
153 
154 int kstrtoint(const char *s, unsigned int base, int *res)
155 {
156 	long long tmp;
157 	int rv;
158 
159 	rv = kstrtoll(s, base, &tmp);
160 	if (rv < 0)
161 		return rv;
162 	if (tmp != (long long)(int)tmp)
163 		return -ERANGE;
164 	*res = tmp;
165 	return 0;
166 }
167 EXPORT_SYMBOL(kstrtoint);
168 
169 int kstrtou16(const char *s, unsigned int base, u16 *res)
170 {
171 	unsigned long long tmp;
172 	int rv;
173 
174 	rv = kstrtoull(s, base, &tmp);
175 	if (rv < 0)
176 		return rv;
177 	if (tmp != (unsigned long long)(u16)tmp)
178 		return -ERANGE;
179 	*res = tmp;
180 	return 0;
181 }
182 EXPORT_SYMBOL(kstrtou16);
183 
184 int kstrtos16(const char *s, unsigned int base, s16 *res)
185 {
186 	long long tmp;
187 	int rv;
188 
189 	rv = kstrtoll(s, base, &tmp);
190 	if (rv < 0)
191 		return rv;
192 	if (tmp != (long long)(s16)tmp)
193 		return -ERANGE;
194 	*res = tmp;
195 	return 0;
196 }
197 EXPORT_SYMBOL(kstrtos16);
198 
199 int kstrtou8(const char *s, unsigned int base, u8 *res)
200 {
201 	unsigned long long tmp;
202 	int rv;
203 
204 	rv = kstrtoull(s, base, &tmp);
205 	if (rv < 0)
206 		return rv;
207 	if (tmp != (unsigned long long)(u8)tmp)
208 		return -ERANGE;
209 	*res = tmp;
210 	return 0;
211 }
212 EXPORT_SYMBOL(kstrtou8);
213 
214 int kstrtos8(const char *s, unsigned int base, s8 *res)
215 {
216 	long long tmp;
217 	int rv;
218 
219 	rv = kstrtoll(s, base, &tmp);
220 	if (rv < 0)
221 		return rv;
222 	if (tmp != (long long)(s8)tmp)
223 		return -ERANGE;
224 	*res = tmp;
225 	return 0;
226 }
227 EXPORT_SYMBOL(kstrtos8);
228