curl/lib/strparse.c
Daniel Stenberg b4538ec522
strparse: switch to curl_off_t as base data type
- add hex and octal parsers to the Curl_str_* family
- make curlx_strtoofft use these parsers
- remove all use of strtol() and strtoul() in library code
- generally use Curl_str_* more than strtoofft, for stricter parsing
- supports 64-bit universally, instead of 'long' which differs in size
  between platforms

Extended the unit test 1664 to verify hex and octal parsing.

Closes #16336
2025-02-15 21:58:48 +01:00

176 lines
5.2 KiB
C

/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "strparse.h"
#include "strcase.h"
/* Get a word until the first DELIM or end of string. At least one byte long.
return non-zero on error */
int Curl_str_until(const char **linep, struct Curl_str *out,
const size_t max, char delim)
{
const char *s = *linep;
size_t len = 0;
DEBUGASSERT(linep && *linep && out && max && delim);
out->str = NULL;
out->len = 0;
while(*s && (*s != delim)) {
s++;
if(++len > max) {
return STRE_BIG;
}
}
if(!len)
return STRE_SHORT;
out->str = *linep;
out->len = len;
*linep = s; /* point to the first byte after the word */
return STRE_OK;
}
/* Get a word until the first space or end of string. At least one byte long.
return non-zero on error */
int Curl_str_word(const char **linep, struct Curl_str *out,
const size_t max)
{
return Curl_str_until(linep, out, max, ' ');
}
/* Get a "quoted" word. No escaping possible.
return non-zero on error */
int Curl_str_quotedword(const char **linep, struct Curl_str *out,
const size_t max)
{
const char *s = *linep;
size_t len = 0;
DEBUGASSERT(linep && *linep && out && max);
out->str = NULL;
out->len = 0;
if(*s != '\"')
return STRE_BEGQUOTE;
s++;
while(*s && (*s != '\"')) {
s++;
if(++len > max)
return STRE_BIG;
}
if(*s != '\"')
return STRE_ENDQUOTE;
out->str = (*linep) + 1;
out->len = len;
*linep = s + 1;
return STRE_OK;
}
/* Advance over a single character.
return non-zero on error */
int Curl_str_single(const char **linep, char byte)
{
DEBUGASSERT(linep && *linep);
if(**linep != byte)
return STRE_BYTE;
(*linep)++; /* move over it */
return STRE_OK;
}
/* Advance over a single space.
return non-zero on error */
int Curl_str_singlespace(const char **linep)
{
return Curl_str_single(linep, ' ');
}
/* given an ASCII hexadecimal character, return the value */
#define HEXDIGIT2NUM(x) \
(((x) > '9') ? Curl_raw_tolower(x) - 'a' + 10 : x - '0')
/* given an ASCII character and a given base, return TRUE if valid */
#define valid_digit(digit, base) \
(((base == 10) && ISDIGIT(digit)) || \
((base == 16) && ISXDIGIT(digit)) || \
((base == 8) && ISODIGIT(digit)))
/* given an ASCII character and a given base, return the value */
#define num_digit(digit, base) \
((base != 16) ? digit - '0' : HEXDIGIT2NUM(digit))
/* no support for 0x prefix nor leading spaces */
static int str_num_base(const char **linep, curl_off_t *nump, curl_off_t max,
int base) /* 8, 10 or 16, nothing else */
{
curl_off_t num = 0;
DEBUGASSERT(linep && *linep && nump);
DEBUGASSERT((base == 8) || (base == 10) || (base == 16));
*nump = 0;
if(!valid_digit(**linep, base))
return STRE_NO_NUM;
do {
int n = num_digit(**linep, base);
if(num > ((CURL_OFF_T_MAX - n) / base))
return STRE_OVERFLOW;
num = num * base + n;
if(num > max)
return STRE_BIG; /** too big */
(*linep)++;
} while(valid_digit(**linep, base));
*nump = num;
return STRE_OK;
}
/* Get an unsigned decimal number with no leading space or minus. Leading
zeroes are accepted. return non-zero on error */
int Curl_str_number(const char **linep, curl_off_t *nump, curl_off_t max)
{
return str_num_base(linep, nump, max, 10);
}
/* Get an unsigned hexadecimal number with no leading space or minus and no
"0x" support. Leading zeroes are accepted. return non-zero on error */
int Curl_str_hex(const char **linep, curl_off_t *nump, curl_off_t max)
{
return str_num_base(linep, nump, max, 16);
}
/* Get an unsigned octal number with no leading space or minus and no "0"
prefix support. Leading zeroes are accepted. return non-zero on error */
int Curl_str_octal(const char **linep, curl_off_t *nump, curl_off_t max)
{
return str_num_base(linep, nump, max, 8);
}
/* CR or LF
return non-zero on error */
int Curl_str_newline(const char **linep)
{
DEBUGASSERT(linep && *linep);
if(ISNEWLINE(**linep)) {
(*linep)++;
return STRE_OK; /* yessir */
}
return STRE_NEWLINE;
}