kstrto*: converting strings to integers done (hopefully) right
[linux-3.10.git] / lib / kstrtox.c
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);