kstrtox: simpler code in _kstrtoull()
[pandora-kernel.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' && *(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);