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