Bump to version 1.22.1
[platform/upstream/busybox.git] / libbb / bb_strtonum.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Utility routines.
4  *
5  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6  *
7  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
8  */
9
10 #include "libbb.h"
11
12 /* On exit: errno = 0 only if there was non-empty, '\0' terminated value
13  * errno = EINVAL if value was not '\0' terminated, but otherwise ok
14  *    Return value is still valid, caller should just check whether end[0]
15  *    is a valid terminating char for particular case. OTOH, if caller
16  *    requires '\0' terminated input, [s]he can just check errno == 0.
17  * errno = ERANGE if value had alphanumeric terminating char ("1234abcg").
18  * errno = ERANGE if value is out of range, missing, etc.
19  * errno = ERANGE if value had minus sign for strtouXX (even "-0" is not ok )
20  *    return value is all-ones in this case.
21  *
22  * Test code:
23  * char *endptr;
24  * const char *minus = "-";
25  * errno = 0;
26  * bb_strtoi(minus, &endptr, 0); // must set ERANGE
27  * printf("minus:%p endptr:%p errno:%d EINVAL:%d\n", minus, endptr, errno, EINVAL);
28  * errno = 0;
29  * bb_strtoi("-0-", &endptr, 0); // must set EINVAL and point to second '-'
30  * printf("endptr[0]:%c errno:%d EINVAL:%d\n", endptr[0], errno, EINVAL);
31  */
32
33 static unsigned long long ret_ERANGE(void)
34 {
35         errno = ERANGE; /* this ain't as small as it looks (on glibc) */
36         return ULLONG_MAX;
37 }
38
39 static unsigned long long handle_errors(unsigned long long v, char **endp)
40 {
41         char next_ch = **endp;
42
43         /* errno is already set to ERANGE by strtoXXX if value overflowed */
44         if (next_ch) {
45                 /* "1234abcg" or out-of-range? */
46                 if (isalnum(next_ch) || errno)
47                         return ret_ERANGE();
48                 /* good number, just suspicious terminator */
49                 errno = EINVAL;
50         }
51         return v;
52 }
53
54
55 unsigned long long FAST_FUNC bb_strtoull(const char *arg, char **endp, int base)
56 {
57         unsigned long long v;
58         char *endptr;
59
60         if (!endp) endp = &endptr;
61         *endp = (char*) arg;
62
63         /* strtoul("  -4200000000") returns 94967296, errno 0 (!) */
64         /* I don't think that this is right. Preventing this... */
65         if (!isalnum(arg[0])) return ret_ERANGE();
66
67         /* not 100% correct for lib func, but convenient for the caller */
68         errno = 0;
69         v = strtoull(arg, endp, base);
70         return handle_errors(v, endp);
71 }
72
73 long long FAST_FUNC bb_strtoll(const char *arg, char **endp, int base)
74 {
75         unsigned long long v;
76         char *endptr;
77         char first;
78
79         if (!endp) endp = &endptr;
80         *endp = (char*) arg;
81
82         /* Check for the weird "feature":
83          * a "-" string is apparently a valid "number" for strto[u]l[l]!
84          * It returns zero and errno is 0! :( */
85         first = (arg[0] != '-' ? arg[0] : arg[1]);
86         if (!isalnum(first)) return ret_ERANGE();
87
88         errno = 0;
89         v = strtoll(arg, endp, base);
90         return handle_errors(v, endp);
91 }
92
93 #if ULONG_MAX != ULLONG_MAX
94 unsigned long FAST_FUNC bb_strtoul(const char *arg, char **endp, int base)
95 {
96         unsigned long v;
97         char *endptr;
98
99         if (!endp) endp = &endptr;
100         *endp = (char*) arg;
101
102         if (!isalnum(arg[0])) return ret_ERANGE();
103         errno = 0;
104         v = strtoul(arg, endp, base);
105         return handle_errors(v, endp);
106 }
107
108 long FAST_FUNC bb_strtol(const char *arg, char **endp, int base)
109 {
110         long v;
111         char *endptr;
112         char first;
113
114         if (!endp) endp = &endptr;
115         *endp = (char*) arg;
116
117         first = (arg[0] != '-' ? arg[0] : arg[1]);
118         if (!isalnum(first)) return ret_ERANGE();
119
120         errno = 0;
121         v = strtol(arg, endp, base);
122         return handle_errors(v, endp);
123 }
124 #endif
125
126 #if UINT_MAX != ULONG_MAX
127 unsigned FAST_FUNC bb_strtou(const char *arg, char **endp, int base)
128 {
129         unsigned long v;
130         char *endptr;
131
132         if (!endp) endp = &endptr;
133         *endp = (char*) arg;
134
135         if (!isalnum(arg[0])) return ret_ERANGE();
136         errno = 0;
137         v = strtoul(arg, endp, base);
138         if (v > UINT_MAX) return ret_ERANGE();
139         return handle_errors(v, endp);
140 }
141
142 int FAST_FUNC bb_strtoi(const char *arg, char **endp, int base)
143 {
144         long v;
145         char *endptr;
146         char first;
147
148         if (!endp) endp = &endptr;
149         *endp = (char*) arg;
150
151         first = (arg[0] != '-' ? arg[0] : arg[1]);
152         if (!isalnum(first)) return ret_ERANGE();
153
154         errno = 0;
155         v = strtol(arg, endp, base);
156         if (v > INT_MAX) return ret_ERANGE();
157         if (v < INT_MIN) return ret_ERANGE();
158         return handle_errors(v, endp);
159 }
160 #endif