curl/lib/strparse.c
Daniel Stenberg d5c738c608
strparse: string parsing helper functions
Designed to aid converting off from sscanf parsers. sscanf is hard to
use right, easy to mess up and often makes for sloppy error checking.

The new parsers allow more exact and pedandic parsing.

This new set of functions should be possible to use (and extend) and
switch over other libcurl parser code to use going forward.

Adapts the following to use the new functions:

- altsvc.c
- hsts.c
- http_aws_sigv4.c

Bonus: fewer memory copies, fewer stack buffers.

Test: Unit test1664

Docs: docs/internals/STRPARSE.md

Closes #15692
2024-12-12 16:00:52 +01:00

137 lines
3.5 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"
/* 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(char **linep, struct Curl_str *out,
const size_t max, char delim)
{
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(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(char **linep, struct Curl_str *out,
const size_t max)
{
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(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(char **linep)
{
return Curl_str_single(linep, ' ');
}
/* Get an unsigned number. Leading zeroes are accepted.
return non-zero on error */
int Curl_str_number(char **linep, size_t *nump, size_t max)
{
size_t num = 0;
DEBUGASSERT(linep && *linep && nump);
*nump = 0;
while(ISDIGIT(**linep)) {
int n = **linep - '0';
if(num > ((SIZE_T_MAX - n) / 10))
return STRE_OVERFLOW;
num = num * 10 + n;
if(num > max)
return STRE_BIG; /** too big */
(*linep)++;
}
*nump = num;
return STRE_OK;
}
/* CR or LF
return non-zero on error */
int Curl_str_newline(char **linep)
{
DEBUGASSERT(linep && *linep);
if(ISNEWLINE(**linep)) {
(*linep)++;
return STRE_OK; /* yessir */
}
return STRE_NEWLINE;
}