diff options
Diffstat (limited to 'lib/kstrtox.c')
-rw-r--r-- | lib/kstrtox.c | 250 |
1 files changed, 250 insertions, 0 deletions
diff --git a/lib/kstrtox.c b/lib/kstrtox.c new file mode 100644 index 000000000000..2dbae88090ac --- /dev/null +++ b/lib/kstrtox.c | |||
@@ -0,0 +1,250 @@ | |||
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 | #include <asm/uaccess.h> | ||
21 | |||
22 | static inline char _tolower(const char c) | ||
23 | { | ||
24 | return c | 0x20; | ||
25 | } | ||
26 | |||
27 | static int _kstrtoull(const char *s, unsigned int base, unsigned long long *res) | ||
28 | { | ||
29 | unsigned long long acc; | ||
30 | int ok; | ||
31 | |||
32 | if (base == 0) { | ||
33 | if (s[0] == '0') { | ||
34 | if (_tolower(s[1]) == 'x' && isxdigit(s[2])) | ||
35 | base = 16; | ||
36 | else | ||
37 | base = 8; | ||
38 | } else | ||
39 | base = 10; | ||
40 | } | ||
41 | if (base == 16 && s[0] == '0' && _tolower(s[1]) == 'x') | ||
42 | s += 2; | ||
43 | |||
44 | acc = 0; | ||
45 | ok = 0; | ||
46 | while (*s) { | ||
47 | unsigned int val; | ||
48 | |||
49 | if ('0' <= *s && *s <= '9') | ||
50 | val = *s - '0'; | ||
51 | else if ('a' <= _tolower(*s) && _tolower(*s) <= 'f') | ||
52 | val = _tolower(*s) - 'a' + 10; | ||
53 | else if (*s == '\n' && *(s + 1) == '\0') | ||
54 | break; | ||
55 | else | ||
56 | return -EINVAL; | ||
57 | |||
58 | if (val >= base) | ||
59 | return -EINVAL; | ||
60 | if (acc > div_u64(ULLONG_MAX - val, base)) | ||
61 | return -ERANGE; | ||
62 | acc = acc * base + val; | ||
63 | ok = 1; | ||
64 | |||
65 | s++; | ||
66 | } | ||
67 | if (!ok) | ||
68 | return -EINVAL; | ||
69 | *res = acc; | ||
70 | return 0; | ||
71 | } | ||
72 | |||
73 | int kstrtoull(const char *s, unsigned int base, unsigned long long *res) | ||
74 | { | ||
75 | if (s[0] == '+') | ||
76 | s++; | ||
77 | return _kstrtoull(s, base, res); | ||
78 | } | ||
79 | EXPORT_SYMBOL(kstrtoull); | ||
80 | |||
81 | int kstrtoll(const char *s, unsigned int base, long long *res) | ||
82 | { | ||
83 | unsigned long long tmp; | ||
84 | int rv; | ||
85 | |||
86 | if (s[0] == '-') { | ||
87 | rv = _kstrtoull(s + 1, base, &tmp); | ||
88 | if (rv < 0) | ||
89 | return rv; | ||
90 | if ((long long)(-tmp) >= 0) | ||
91 | return -ERANGE; | ||
92 | *res = -tmp; | ||
93 | } else { | ||
94 | rv = kstrtoull(s, base, &tmp); | ||
95 | if (rv < 0) | ||
96 | return rv; | ||
97 | if ((long long)tmp < 0) | ||
98 | return -ERANGE; | ||
99 | *res = tmp; | ||
100 | } | ||
101 | return 0; | ||
102 | } | ||
103 | EXPORT_SYMBOL(kstrtoll); | ||
104 | |||
105 | /* Internal, do not use. */ | ||
106 | int _kstrtoul(const char *s, unsigned int base, unsigned long *res) | ||
107 | { | ||
108 | unsigned long long tmp; | ||
109 | int rv; | ||
110 | |||
111 | rv = kstrtoull(s, base, &tmp); | ||
112 | if (rv < 0) | ||
113 | return rv; | ||
114 | if (tmp != (unsigned long long)(unsigned long)tmp) | ||
115 | return -ERANGE; | ||
116 | *res = tmp; | ||
117 | return 0; | ||
118 | } | ||
119 | EXPORT_SYMBOL(_kstrtoul); | ||
120 | |||
121 | /* Internal, do not use. */ | ||
122 | int _kstrtol(const char *s, unsigned int base, long *res) | ||
123 | { | ||
124 | long long tmp; | ||
125 | int rv; | ||
126 | |||
127 | rv = kstrtoll(s, base, &tmp); | ||
128 | if (rv < 0) | ||
129 | return rv; | ||
130 | if (tmp != (long long)(long)tmp) | ||
131 | return -ERANGE; | ||
132 | *res = tmp; | ||
133 | return 0; | ||
134 | } | ||
135 | EXPORT_SYMBOL(_kstrtol); | ||
136 | |||
137 | int kstrtouint(const char *s, unsigned int base, unsigned int *res) | ||
138 | { | ||
139 | unsigned long long tmp; | ||
140 | int rv; | ||
141 | |||
142 | rv = kstrtoull(s, base, &tmp); | ||
143 | if (rv < 0) | ||
144 | return rv; | ||
145 | if (tmp != (unsigned long long)(unsigned int)tmp) | ||
146 | return -ERANGE; | ||
147 | *res = tmp; | ||
148 | return 0; | ||
149 | } | ||
150 | EXPORT_SYMBOL(kstrtouint); | ||
151 | |||
152 | int kstrtoint(const char *s, unsigned int base, int *res) | ||
153 | { | ||
154 | long long tmp; | ||
155 | int rv; | ||
156 | |||
157 | rv = kstrtoll(s, base, &tmp); | ||
158 | if (rv < 0) | ||
159 | return rv; | ||
160 | if (tmp != (long long)(int)tmp) | ||
161 | return -ERANGE; | ||
162 | *res = tmp; | ||
163 | return 0; | ||
164 | } | ||
165 | EXPORT_SYMBOL(kstrtoint); | ||
166 | |||
167 | int kstrtou16(const char *s, unsigned int base, u16 *res) | ||
168 | { | ||
169 | unsigned long long tmp; | ||
170 | int rv; | ||
171 | |||
172 | rv = kstrtoull(s, base, &tmp); | ||
173 | if (rv < 0) | ||
174 | return rv; | ||
175 | if (tmp != (unsigned long long)(u16)tmp) | ||
176 | return -ERANGE; | ||
177 | *res = tmp; | ||
178 | return 0; | ||
179 | } | ||
180 | EXPORT_SYMBOL(kstrtou16); | ||
181 | |||
182 | int kstrtos16(const char *s, unsigned int base, s16 *res) | ||
183 | { | ||
184 | long long tmp; | ||
185 | int rv; | ||
186 | |||
187 | rv = kstrtoll(s, base, &tmp); | ||
188 | if (rv < 0) | ||
189 | return rv; | ||
190 | if (tmp != (long long)(s16)tmp) | ||
191 | return -ERANGE; | ||
192 | *res = tmp; | ||
193 | return 0; | ||
194 | } | ||
195 | EXPORT_SYMBOL(kstrtos16); | ||
196 | |||
197 | int kstrtou8(const char *s, unsigned int base, u8 *res) | ||
198 | { | ||
199 | unsigned long long tmp; | ||
200 | int rv; | ||
201 | |||
202 | rv = kstrtoull(s, base, &tmp); | ||
203 | if (rv < 0) | ||
204 | return rv; | ||
205 | if (tmp != (unsigned long long)(u8)tmp) | ||
206 | return -ERANGE; | ||
207 | *res = tmp; | ||
208 | return 0; | ||
209 | } | ||
210 | EXPORT_SYMBOL(kstrtou8); | ||
211 | |||
212 | int kstrtos8(const char *s, unsigned int base, s8 *res) | ||
213 | { | ||
214 | long long tmp; | ||
215 | int rv; | ||
216 | |||
217 | rv = kstrtoll(s, base, &tmp); | ||
218 | if (rv < 0) | ||
219 | return rv; | ||
220 | if (tmp != (long long)(s8)tmp) | ||
221 | return -ERANGE; | ||
222 | *res = tmp; | ||
223 | return 0; | ||
224 | } | ||
225 | EXPORT_SYMBOL(kstrtos8); | ||
226 | |||
227 | #define kstrto_from_user(f, g, type) \ | ||
228 | int f(const char __user *s, size_t count, unsigned int base, type *res) \ | ||
229 | { \ | ||
230 | /* sign, base 2 representation, newline, terminator */ \ | ||
231 | char buf[1 + sizeof(type) * 8 + 1 + 1]; \ | ||
232 | \ | ||
233 | count = min(count, sizeof(buf) - 1); \ | ||
234 | if (copy_from_user(buf, s, count)) \ | ||
235 | return -EFAULT; \ | ||
236 | buf[count] = '\0'; \ | ||
237 | return g(buf, base, res); \ | ||
238 | } \ | ||
239 | EXPORT_SYMBOL(f) | ||
240 | |||
241 | kstrto_from_user(kstrtoull_from_user, kstrtoull, unsigned long long); | ||
242 | kstrto_from_user(kstrtoll_from_user, kstrtoll, long long); | ||
243 | kstrto_from_user(kstrtoul_from_user, kstrtoul, unsigned long); | ||
244 | kstrto_from_user(kstrtol_from_user, kstrtol, long); | ||
245 | kstrto_from_user(kstrtouint_from_user, kstrtouint, unsigned int); | ||
246 | kstrto_from_user(kstrtoint_from_user, kstrtoint, int); | ||
247 | kstrto_from_user(kstrtou16_from_user, kstrtou16, u16); | ||
248 | kstrto_from_user(kstrtos16_from_user, kstrtos16, s16); | ||
249 | kstrto_from_user(kstrtou8_from_user, kstrtou8, u8); | ||
250 | kstrto_from_user(kstrtos8_from_user, kstrtos8, s8); | ||