Coverage Report

Created: 2023-06-07 07:02

/src/curl/lib/strtoofft.c
Line
Count
Source (jump to first uncovered line)
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
0
#  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
  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
 */
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
 */
217
CURLofft curlx_strtoofft(const char *str, char **endp, int base,
218
                         curl_off_t *num)
219
0
{
220
0
  char *end;
221
0
  curl_off_t number;
222
0
  errno = 0;
223
0
  *num = 0; /* clear by default */
224
0
  DEBUGASSERT(base); /* starting now, avoid base zero */
225
226
0
  while(*str && ISBLANK(*str))
227
0
    str++;
228
0
  if(('-' == *str) || (ISSPACE(*str))) {
229
0
    if(endp)
230
0
      *endp = (char *)str; /* didn't actually move */
231
0
    return CURL_OFFT_INVAL; /* nothing parsed */
232
0
  }
233
0
  number = strtooff(str, &end, base);
234
0
  if(endp)
235
0
    *endp = end;
236
0
  if(errno == ERANGE)
237
    /* overflow/underflow */
238
0
    return CURL_OFFT_FLOW;
239
0
  else if(str == end)
240
    /* nothing parsed */
241
0
    return CURL_OFFT_INVAL;
242
243
0
  *num = number;
244
0
  return CURL_OFFT_OK;
245
0
}