xref: /linux/lib/kstrtox.c (revision 834f0c353ae430c1a6ce023c9b77bbd3ff9241a7)
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' && *(s + 1) == '\0')
53 			break;
54 		else
55 			return -EINVAL;
56 
57 		if (val >= base)
58 			return -EINVAL;
59 		if (acc > div_u64(ULLONG_MAX - val, base))
60 			return -ERANGE;
61 		acc = acc * base + val;
62 		ok = 1;
63 
64 		s++;
65 	}
66 	if (!ok)
67 		return -EINVAL;
68 	*res = acc;
69 	return 0;
70 }
71 
72 int kstrtoull(const char *s, unsigned int base, unsigned long long *res)
73 {
74 	if (s[0] == '+')
75 		s++;
76 	return _kstrtoull(s, base, res);
77 }
78 EXPORT_SYMBOL(kstrtoull);
79 
80 int kstrtoll(const char *s, unsigned int base, long long *res)
81 {
82 	unsigned long long tmp;
83 	int rv;
84 
85 	if (s[0] == '-') {
86 		rv = _kstrtoull(s + 1, base, &tmp);
87 		if (rv < 0)
88 			return rv;
89 		if ((long long)(-tmp) >= 0)
90 			return -ERANGE;
91 		*res = -tmp;
92 	} else {
93 		rv = kstrtoull(s, base, &tmp);
94 		if (rv < 0)
95 			return rv;
96 		if ((long long)tmp < 0)
97 			return -ERANGE;
98 		*res = tmp;
99 	}
100 	return 0;
101 }
102 EXPORT_SYMBOL(kstrtoll);
103 
104 /* Internal, do not use. */
105 int _kstrtoul(const char *s, unsigned int base, unsigned long *res)
106 {
107 	unsigned long long tmp;
108 	int rv;
109 
110 	rv = kstrtoull(s, base, &tmp);
111 	if (rv < 0)
112 		return rv;
113 	if (tmp != (unsigned long long)(unsigned long)tmp)
114 		return -ERANGE;
115 	*res = tmp;
116 	return 0;
117 }
118 EXPORT_SYMBOL(_kstrtoul);
119 
120 /* Internal, do not use. */
121 int _kstrtol(const char *s, unsigned int base, long *res)
122 {
123 	long long tmp;
124 	int rv;
125 
126 	rv = kstrtoll(s, base, &tmp);
127 	if (rv < 0)
128 		return rv;
129 	if (tmp != (long long)(long)tmp)
130 		return -ERANGE;
131 	*res = tmp;
132 	return 0;
133 }
134 EXPORT_SYMBOL(_kstrtol);
135 
136 int kstrtouint(const char *s, unsigned int base, unsigned int *res)
137 {
138 	unsigned long long tmp;
139 	int rv;
140 
141 	rv = kstrtoull(s, base, &tmp);
142 	if (rv < 0)
143 		return rv;
144 	if (tmp != (unsigned long long)(unsigned int)tmp)
145 		return -ERANGE;
146 	*res = tmp;
147 	return 0;
148 }
149 EXPORT_SYMBOL(kstrtouint);
150 
151 int kstrtoint(const char *s, unsigned int base, int *res)
152 {
153 	long long tmp;
154 	int rv;
155 
156 	rv = kstrtoll(s, base, &tmp);
157 	if (rv < 0)
158 		return rv;
159 	if (tmp != (long long)(int)tmp)
160 		return -ERANGE;
161 	*res = tmp;
162 	return 0;
163 }
164 EXPORT_SYMBOL(kstrtoint);
165 
166 int kstrtou16(const char *s, unsigned int base, u16 *res)
167 {
168 	unsigned long long tmp;
169 	int rv;
170 
171 	rv = kstrtoull(s, base, &tmp);
172 	if (rv < 0)
173 		return rv;
174 	if (tmp != (unsigned long long)(u16)tmp)
175 		return -ERANGE;
176 	*res = tmp;
177 	return 0;
178 }
179 EXPORT_SYMBOL(kstrtou16);
180 
181 int kstrtos16(const char *s, unsigned int base, s16 *res)
182 {
183 	long long tmp;
184 	int rv;
185 
186 	rv = kstrtoll(s, base, &tmp);
187 	if (rv < 0)
188 		return rv;
189 	if (tmp != (long long)(s16)tmp)
190 		return -ERANGE;
191 	*res = tmp;
192 	return 0;
193 }
194 EXPORT_SYMBOL(kstrtos16);
195 
196 int kstrtou8(const char *s, unsigned int base, u8 *res)
197 {
198 	unsigned long long tmp;
199 	int rv;
200 
201 	rv = kstrtoull(s, base, &tmp);
202 	if (rv < 0)
203 		return rv;
204 	if (tmp != (unsigned long long)(u8)tmp)
205 		return -ERANGE;
206 	*res = tmp;
207 	return 0;
208 }
209 EXPORT_SYMBOL(kstrtou8);
210 
211 int kstrtos8(const char *s, unsigned int base, s8 *res)
212 {
213 	long long tmp;
214 	int rv;
215 
216 	rv = kstrtoll(s, base, &tmp);
217 	if (rv < 0)
218 		return rv;
219 	if (tmp != (long long)(s8)tmp)
220 		return -ERANGE;
221 	*res = tmp;
222 	return 0;
223 }
224 EXPORT_SYMBOL(kstrtos8);
225