xref: /third_party/curl/lib/strtoofft.c (revision 13498266)
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
69static const char valchars[] =
70            "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
71#endif
72
73static 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 */
79static 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 */
178static 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 */
217CURLofft 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