1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 * SPDX-License-Identifier: curl
22 *
23 ***************************************************************************/
24
25 #include <errno.h>
26 #include "curl_setup.h"
27
28 #include "strtoofft.h"
29
30 /*
31 * NOTE:
32 *
33 * In the ISO C standard (IEEE Std 1003.1), there is a strtoimax() function we
34 * could use in case strtoll() doesn't exist... See
35 * https://www.opengroup.org/onlinepubs/009695399/functions/strtoimax.html
36 */
37
38 #if (SIZEOF_CURL_OFF_T > SIZEOF_LONG)
39 # ifdef HAVE_STRTOLL
40 # define strtooff strtoll
41 # else
42 # if defined(_MSC_VER) && (_MSC_VER >= 1300) && (_INTEGRAL_MAX_BITS >= 64)
43 # if defined(_SAL_VERSION)
44 _Check_return_ _CRTIMP __int64 __cdecl _strtoi64(
45 _In_z_ const char *_String,
46 _Out_opt_ _Deref_post_z_ char **_EndPtr, _In_ int _Radix);
47 # else
48 _CRTIMP __int64 __cdecl _strtoi64(const char *_String,
49 char **_EndPtr, int _Radix);
50 # endif
51 # define strtooff _strtoi64
52 # else
53 # define PRIVATE_STRTOOFF 1
54 # endif
55 # endif
56 #else
57 # define strtooff strtol
58 #endif
59
60 #ifdef PRIVATE_STRTOOFF
61
62 /* Range tests can be used for alphanum decoding if characters are consecutive,
63 like in ASCII. Else an array is scanned. Determine this condition now. */
64
65 #if('9' - '0') != 9 || ('Z' - 'A') != 25 || ('z' - 'a') != 25
66
67 #define NO_RANGE_TEST
68
69 static const char valchars[] =
70 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
71 #endif
72
73 static int get_char(char c, int base);
74
75 /**
76 * Custom version of the strtooff function. This extracts a curl_off_t
77 * value from the given input string and returns it.
78 */
strtooff(const char * nptr,char ** endptr,int base)79 static curl_off_t strtooff(const char *nptr, char **endptr, int base)
80 {
81 char *end;
82 int is_negative = 0;
83 int overflow;
84 int i;
85 curl_off_t value = 0;
86 curl_off_t newval;
87
88 /* Skip leading whitespace. */
89 end = (char *)nptr;
90 while(ISBLANK(end[0])) {
91 end++;
92 }
93
94 /* Handle the sign, if any. */
95 if(end[0] == '-') {
96 is_negative = 1;
97 end++;
98 }
99 else if(end[0] == '+') {
100 end++;
101 }
102 else if(end[0] == '\0') {
103 /* We had nothing but perhaps some whitespace -- there was no number. */
104 if(endptr) {
105 *endptr = end;
106 }
107 return 0;
108 }
109
110 /* Handle special beginnings, if present and allowed. */
111 if(end[0] == '0' && end[1] == 'x') {
112 if(base == 16 || base == 0) {
113 end += 2;
114 base = 16;
115 }
116 }
117 else if(end[0] == '0') {
118 if(base == 8 || base == 0) {
119 end++;
120 base = 8;
121 }
122 }
123
124 /* Matching strtol, if the base is 0 and it doesn't look like
125 * the number is octal or hex, we assume it's base 10.
126 */
127 if(base == 0) {
128 base = 10;
129 }
130
131 /* Loop handling digits. */
132 value = 0;
133 overflow = 0;
134 for(i = get_char(end[0], base);
135 i != -1;
136 end++, i = get_char(end[0], base)) {
137 newval = base * value + i;
138 if(newval < value) {
139 /* We've overflowed. */
140 overflow = 1;
141 break;
142 }
143 else
144 value = newval;
145 }
146
147 if(!overflow) {
148 if(is_negative) {
149 /* Fix the sign. */
150 value *= -1;
151 }
152 }
153 else {
154 if(is_negative)
155 value = CURL_OFF_T_MIN;
156 else
157 value = CURL_OFF_T_MAX;
158
159 errno = ERANGE;
160 }
161
162 if(endptr)
163 *endptr = end;
164
165 return value;
166 }
167
168 /**
169 * Returns the value of c in the given base, or -1 if c cannot
170 * be interpreted properly in that base (i.e., is out of range,
171 * is a null, etc.).
172 *
173 * @param c the character to interpret according to base
174 * @param base the base in which to interpret c
175 *
176 * @return the value of c in base, or -1 if c isn't in range
177 */
get_char(char c,int base)178 static int get_char(char c, int base)
179 {
180 #ifndef NO_RANGE_TEST
181 int value = -1;
182 if(c <= '9' && c >= '0') {
183 value = c - '0';
184 }
185 else if(c <= 'Z' && c >= 'A') {
186 value = c - 'A' + 10;
187 }
188 else if(c <= 'z' && c >= 'a') {
189 value = c - 'a' + 10;
190 }
191 #else
192 const char *cp;
193 int value;
194
195 cp = memchr(valchars, c, 10 + 26 + 26);
196
197 if(!cp)
198 return -1;
199
200 value = cp - valchars;
201
202 if(value >= 10 + 26)
203 value -= 26; /* Lowercase. */
204 #endif
205
206 if(value >= base) {
207 value = -1;
208 }
209
210 return value;
211 }
212 #endif /* Only present if we need strtoll, but don't have it. */
213
214 /*
215 * Parse a *positive* up to 64 bit number written in ascii.
216 */
curlx_strtoofft(const char * str,char ** endp,int base,curl_off_t * num)217 CURLofft curlx_strtoofft(const char *str, char **endp, int base,
218 curl_off_t *num)
219 {
220 char *end;
221 curl_off_t number;
222 errno = 0;
223 *num = 0; /* clear by default */
224 DEBUGASSERT(base); /* starting now, avoid base zero */
225
226 while(*str && ISBLANK(*str))
227 str++;
228 if(('-' == *str) || (ISSPACE(*str))) {
229 if(endp)
230 *endp = (char *)str; /* didn't actually move */
231 return CURL_OFFT_INVAL; /* nothing parsed */
232 }
233 number = strtooff(str, &end, base);
234 if(endp)
235 *endp = end;
236 if(errno == ERANGE)
237 /* overflow/underflow */
238 return CURL_OFFT_FLOW;
239 else if(str == end)
240 /* nothing parsed */
241 return CURL_OFFT_INVAL;
242
243 *num = number;
244 return CURL_OFFT_OK;
245 }
246