passwd: made smaller by ~130 bytes. size can go negative
[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 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 othervise 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  */
21
22 static unsigned long long ret_ERANGE(void)
23 {
24         errno = ERANGE; /* this ain't as small as it looks (on glibc) */
25         return ULLONG_MAX;
26 }
27
28 static unsigned long long handle_errors(unsigned long long v, char **endp, char *endptr)
29 {
30         if (endp) *endp = endptr;
31
32         /* Check for the weird "feature":
33          * a "-" string is apparently a valid "number" for strto[u]l[l]!
34          * It returns zero and errno is 0! :( */
35         if (endptr[-1] == '-')
36                 return ret_ERANGE();
37
38         /* errno is already set to ERANGE by strtoXXX if value overflowed */
39         if (endptr[0]) {
40                 /* "1234abcg" or out-of-range? */
41                 if (isalnum(endptr[0]) || errno)
42                         return ret_ERANGE();
43                 /* good number, just suspicious terminator */
44                 errno = EINVAL;
45         }
46         return v;
47 }
48
49
50 unsigned long long bb_strtoull(const char *arg, char **endp, int base)
51 {
52         unsigned long long v;
53         char *endptr;
54
55         /* strtoul("  -4200000000") returns 94967296, errno 0 (!) */
56         /* I don't think that this is right. Preventing this... */
57         if (!isalnum(arg[0])) return ret_ERANGE();
58
59         /* not 100% correct for lib func, but convenient for the caller */
60         errno = 0;
61         v = strtoull(arg, &endptr, base);
62         return handle_errors(v, endp, endptr);
63 }
64
65 long long bb_strtoll(const char *arg, char **endp, int base)
66 {
67         unsigned long long v;
68         char *endptr;
69
70         if (arg[0] != '-' && !isalnum(arg[0])) return ret_ERANGE();
71         errno = 0;
72         v = strtoll(arg, &endptr, base);
73         return handle_errors(v, endp, endptr);
74 }
75
76 #if ULONG_MAX != ULLONG_MAX
77 unsigned long bb_strtoul(const char *arg, char **endp, int base)
78 {
79         unsigned long v;
80         char *endptr;
81
82         if (!isalnum(arg[0])) return ret_ERANGE();
83         errno = 0;
84         v = strtoul(arg, &endptr, base);
85         return handle_errors(v, endp, endptr);
86 }
87
88 long bb_strtol(const char *arg, char **endp, int base)
89 {
90         long v;
91         char *endptr;
92
93         if (arg[0] != '-' && !isalnum(arg[0])) return ret_ERANGE();
94         errno = 0;
95         v = strtol(arg, &endptr, base);
96         return handle_errors(v, endp, endptr);
97 }
98 #endif
99
100 #if UINT_MAX != ULONG_MAX
101 unsigned bb_strtou(const char *arg, char **endp, int base)
102 {
103         unsigned long v;
104         char *endptr;
105
106         if (!isalnum(arg[0])) return ret_ERANGE();
107         errno = 0;
108         v = strtoul(arg, &endptr, base);
109         if (v > UINT_MAX) return ret_ERANGE();
110         return handle_errors(v, endp, endptr);
111 }
112
113 int bb_strtoi(const char *arg, char **endp, int base)
114 {
115         long v;
116         char *endptr;
117
118         if (arg[0] != '-' && !isalnum(arg[0])) return ret_ERANGE();
119         errno = 0;
120         v = strtol(arg, &endptr, base);
121         if (v > INT_MAX) return ret_ERANGE();
122         if (v < INT_MIN) return ret_ERANGE();
123         return handle_errors(v, endp, endptr);
124 }
125 #endif
126
127 /* Floating point */
128
129 #if 0
130
131 #include <math.h>  /* just for HUGE_VAL */
132 #define NOT_DIGIT(a) (((unsigned char)(a-'0')) > 9)
133 double bb_strtod(const char *arg, char **endp)
134 {
135         double v;
136         char *endptr;
137
138         if (arg[0] != '-' && NOT_DIGIT(arg[0])) goto err;
139         errno = 0;
140         v = strtod(arg, &endptr);
141         if (endp) *endp = endptr;
142         if (endptr[0]) {
143                 /* "1234abcg" or out-of-range? */
144                 if (isalnum(endptr[0]) || errno) {
145  err:
146                         errno = ERANGE;
147                         return HUGE_VAL;
148                 }
149                 /* good number, just suspicious terminator */
150                 errno = EINVAL;
151         }
152         return v;
153 }
154
155 #endif