Coverage Report

Created: 2024-05-21 06:52

/src/curl/lib/strtoofft.c
Line
Count
Source
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
39.7k
#  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
 */
79
static curl_off_t strtooff(const char *nptr, char **endptr, int base)
80
{
81
  char *end;
82
  bool is_negative = FALSE;
83
  bool overflow = FALSE;
84
  int i;
85
  curl_off_t value = 0;
86
87
  /* Skip leading whitespace. */
88
  end = (char *)nptr;
89
  while(ISBLANK(end[0])) {
90
    end++;
91
  }
92
93
  /* Handle the sign, if any. */
94
  if(end[0] == '-') {
95
    is_negative = TRUE;
96
    end++;
97
  }
98
  else if(end[0] == '+') {
99
    end++;
100
  }
101
  else if(end[0] == '\0') {
102
    /* We had nothing but perhaps some whitespace -- there was no number. */
103
    if(endptr) {
104
      *endptr = end;
105
    }
106
    return 0;
107
  }
108
109
  /* Handle special beginnings, if present and allowed. */
110
  if(end[0] == '0' && end[1] == 'x') {
111
    if(base == 16 || base == 0) {
112
      end += 2;
113
      base = 16;
114
    }
115
  }
116
  else if(end[0] == '0') {
117
    if(base == 8 || base == 0) {
118
      end++;
119
      base = 8;
120
    }
121
  }
122
123
  /* Matching strtol, if the base is 0 and it doesn't look like
124
   * the number is octal or hex, we assume it's base 10.
125
   */
126
  if(base == 0) {
127
    base = 10;
128
  }
129
130
  /* Loop handling digits. */
131
  for(i = get_char(end[0], base);
132
      i != -1;
133
      end++, i = get_char(end[0], base)) {
134
135
    if(value > (CURL_OFF_T_MAX - i) / base) {
136
      overflow = TRUE;
137
      break;
138
    }
139
    value = base * value + i;
140
  }
141
142
  if(!overflow) {
143
    if(is_negative) {
144
      /* Fix the sign. */
145
      value *= -1;
146
    }
147
  }
148
  else {
149
    if(is_negative)
150
      value = CURL_OFF_T_MIN;
151
    else
152
      value = CURL_OFF_T_MAX;
153
154
    errno = ERANGE;
155
  }
156
157
  if(endptr)
158
    *endptr = end;
159
160
  return value;
161
}
162
163
/**
164
 * Returns the value of c in the given base, or -1 if c cannot
165
 * be interpreted properly in that base (i.e., is out of range,
166
 * is a null, etc.).
167
 *
168
 * @param c     the character to interpret according to base
169
 * @param base  the base in which to interpret c
170
 *
171
 * @return  the value of c in base, or -1 if c isn't in range
172
 */
173
static int get_char(char c, int base)
174
{
175
#ifndef NO_RANGE_TEST
176
  int value = -1;
177
  if(c <= '9' && c >= '0') {
178
    value = c - '0';
179
  }
180
  else if(c <= 'Z' && c >= 'A') {
181
    value = c - 'A' + 10;
182
  }
183
  else if(c <= 'z' && c >= 'a') {
184
    value = c - 'a' + 10;
185
  }
186
#else
187
  const char *cp;
188
  int value;
189
190
  cp = memchr(valchars, c, 10 + 26 + 26);
191
192
  if(!cp)
193
    return -1;
194
195
  value = cp - valchars;
196
197
  if(value >= 10 + 26)
198
    value -= 26;                /* Lowercase. */
199
#endif
200
201
  if(value >= base) {
202
    value = -1;
203
  }
204
205
  return value;
206
}
207
#endif  /* Only present if we need strtoll, but don't have it. */
208
209
/*
210
 * Parse a *positive* up to 64 bit number written in ascii.
211
 */
212
CURLofft curlx_strtoofft(const char *str, char **endp, int base,
213
                         curl_off_t *num)
214
42.7k
{
215
42.7k
  char *end = NULL;
216
42.7k
  curl_off_t number;
217
42.7k
  errno = 0;
218
42.7k
  *num = 0; /* clear by default */
219
42.7k
  DEBUGASSERT(base); /* starting now, avoid base zero */
220
221
74.5k
  while(*str && ISBLANK(*str))
222
31.7k
    str++;
223
42.7k
  if(('-' == *str) || (ISSPACE(*str))) {
224
3.02k
    if(endp)
225
931
      *endp = (char *)str; /* didn't actually move */
226
3.02k
    return CURL_OFFT_INVAL; /* nothing parsed */
227
3.02k
  }
228
39.7k
  number = strtooff(str, &end, base);
229
39.7k
  if(endp)
230
9.13k
    *endp = end;
231
39.7k
  if(errno == ERANGE)
232
    /* overflow/underflow */
233
2.15k
    return CURL_OFFT_FLOW;
234
37.5k
  else if(str == end)
235
    /* nothing parsed */
236
20.1k
    return CURL_OFFT_INVAL;
237
238
17.4k
  *num = number;
239
17.4k
  return CURL_OFFT_OK;
240
39.7k
}