• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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