87cd744a72765cd277c10f3f21d4859ebcfc3f6b
[external/busybox.git] / util-linux / 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 tarball for details.
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, char *endptr)
40 {
41         if (endp) *endp = endptr;
42
43         /* errno is already set to ERANGE by strtoXXX if value overflowed */
44         if (endptr[0]) {
45                 /* "1234abcg" or out-of-range? */
46                 if (isalnum(endptr[0]) || 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         /* strtoul("  -4200000000") returns 94967296, errno 0 (!) */
61         /* I don't think that this is right. Preventing this... */
62         if (!isalnum(arg[0])) return ret_ERANGE();
63
64         /* not 100% correct for lib func, but convenient for the caller */
65         errno = 0;
66         v = strtoull(arg, &endptr, base);
67         return handle_errors(v, endp, endptr);
68 }
69
70 long long FAST_FUNC bb_strtoll(const char *arg, char **endp, int base)
71 {
72         unsigned long long v;
73         char *endptr;
74
75         /* Check for the weird "feature":
76          * a "-" string is apparently a valid "number" for strto[u]l[l]!
77          * It returns zero and errno is 0! :( */
78         char first = (arg[0] != '-' ? arg[0] : arg[1]);
79         if (!isalnum(first)) return ret_ERANGE();
80
81         errno = 0;
82         v = strtoll(arg, &endptr, base);
83         return handle_errors(v, endp, endptr);
84 }
85
86 #if ULONG_MAX != ULLONG_MAX
87 unsigned long FAST_FUNC bb_strtoul(const char *arg, char **endp, int base)
88 {
89         unsigned long v;
90         char *endptr;
91
92         if (!isalnum(arg[0])) return ret_ERANGE();
93         errno = 0;
94         v = strtoul(arg, &endptr, base);
95         return handle_errors(v, endp, endptr);
96 }
97
98 long FAST_FUNC bb_strtol(const char *arg, char **endp, int base)
99 {
100         long v;
101         char *endptr;
102
103         char first = (arg[0] != '-' ? arg[0] : arg[1]);
104         if (!isalnum(first)) return ret_ERANGE();
105
106         errno = 0;
107         v = strtol(arg, &endptr, base);
108         return handle_errors(v, endp, endptr);
109 }
110 #endif
111
112 #if UINT_MAX != ULONG_MAX
113 unsigned FAST_FUNC bb_strtou(const char *arg, char **endp, int base)
114 {
115         unsigned long v;
116         char *endptr;
117
118         if (!isalnum(arg[0])) return ret_ERANGE();
119         errno = 0;
120         v = strtoul(arg, &endptr, base);
121         if (v > UINT_MAX) return ret_ERANGE();
122         return handle_errors(v, endp, endptr);
123 }
124
125 int FAST_FUNC bb_strtoi(const char *arg, char **endp, int base)
126 {
127         long v;
128         char *endptr;
129
130         char first = (arg[0] != '-' ? arg[0] : arg[1]);
131         if (!isalnum(first)) return ret_ERANGE();
132
133         errno = 0;
134         v = strtol(arg, &endptr, base);
135         if (v > INT_MAX) return ret_ERANGE();
136         if (v < INT_MIN) return ret_ERANGE();
137         return handle_errors(v, endp, endptr);
138 }
139 #endif