strparse.c (8565B)
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 "strparse.h" 26 27 #ifndef WITHOUT_LIBCURL 28 #include <curl/curl.h> /* for curl_strnequal() */ 29 #endif 30 31 void curlx_str_init(struct Curl_str *out) 32 { 33 out->str = NULL; 34 out->len = 0; 35 } 36 37 void curlx_str_assign(struct Curl_str *out, const char *str, size_t len) 38 { 39 out->str = str; 40 out->len = len; 41 } 42 43 /* Get a word until the first DELIM or end of string. At least one byte long. 44 return non-zero on error */ 45 int curlx_str_until(const char **linep, struct Curl_str *out, 46 const size_t max, char delim) 47 { 48 const char *s = *linep; 49 size_t len = 0; 50 DEBUGASSERT(linep && *linep && out && max && delim); 51 52 curlx_str_init(out); 53 while(*s && (*s != delim)) { 54 s++; 55 if(++len > max) { 56 return STRE_BIG; 57 } 58 } 59 if(!len) 60 return STRE_SHORT; 61 out->str = *linep; 62 out->len = len; 63 *linep = s; /* point to the first byte after the word */ 64 return STRE_OK; 65 } 66 67 /* Get a word until the first space or end of string. At least one byte long. 68 return non-zero on error */ 69 int curlx_str_word(const char **linep, struct Curl_str *out, 70 const size_t max) 71 { 72 return curlx_str_until(linep, out, max, ' '); 73 } 74 75 /* Get a word until a newline byte or end of string. At least one byte long. 76 return non-zero on error */ 77 int curlx_str_untilnl(const char **linep, struct Curl_str *out, 78 const size_t max) 79 { 80 const char *s = *linep; 81 size_t len = 0; 82 DEBUGASSERT(linep && *linep && out && max); 83 84 curlx_str_init(out); 85 while(*s && !ISNEWLINE(*s)) { 86 s++; 87 if(++len > max) 88 return STRE_BIG; 89 } 90 if(!len) 91 return STRE_SHORT; 92 out->str = *linep; 93 out->len = len; 94 *linep = s; /* point to the first byte after the word */ 95 return STRE_OK; 96 } 97 98 99 /* Get a "quoted" word. No escaping possible. 100 return non-zero on error */ 101 int curlx_str_quotedword(const char **linep, struct Curl_str *out, 102 const size_t max) 103 { 104 const char *s = *linep; 105 size_t len = 0; 106 DEBUGASSERT(linep && *linep && out && max); 107 108 curlx_str_init(out); 109 if(*s != '\"') 110 return STRE_BEGQUOTE; 111 s++; 112 while(*s && (*s != '\"')) { 113 s++; 114 if(++len > max) 115 return STRE_BIG; 116 } 117 if(*s != '\"') 118 return STRE_ENDQUOTE; 119 out->str = (*linep) + 1; 120 out->len = len; 121 *linep = s + 1; 122 return STRE_OK; 123 } 124 125 /* Advance over a single character. 126 return non-zero on error */ 127 int curlx_str_single(const char **linep, char byte) 128 { 129 DEBUGASSERT(linep && *linep); 130 if(**linep != byte) 131 return STRE_BYTE; 132 (*linep)++; /* move over it */ 133 return STRE_OK; 134 } 135 136 /* Advance over a single space. 137 return non-zero on error */ 138 int curlx_str_singlespace(const char **linep) 139 { 140 return curlx_str_single(linep, ' '); 141 } 142 143 /* given an ASCII character and max ascii, return TRUE if valid */ 144 #define valid_digit(x,m) \ 145 (((x) >= '0') && ((x) <= m) && Curl_hexasciitable[(x)-'0']) 146 147 /* We use 16 for the zero index (and the necessary bitwise AND in the loop) 148 to be able to have a non-zero value there to make valid_digit() able to 149 use the info */ 150 const unsigned char Curl_hexasciitable[] = { 151 16, 1, 2, 3, 4, 5, 6, 7, 8, 9, /* 0x30: 0 - 9 */ 152 0, 0, 0, 0, 0, 0, 0, 153 10, 11, 12, 13, 14, 15, /* 0x41: A - F */ 154 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 155 10, 11, 12, 13, 14, 15 /* 0x61: a - f */ 156 }; 157 158 /* no support for 0x prefix nor leading spaces */ 159 static int str_num_base(const char **linep, curl_off_t *nump, curl_off_t max, 160 int base) /* 8, 10 or 16, nothing else */ 161 { 162 curl_off_t num = 0; 163 const char *p; 164 int m = (base == 10) ? '9' : /* the largest digit possible */ 165 (base == 16) ? 'f' : '7'; 166 DEBUGASSERT(linep && *linep && nump); 167 DEBUGASSERT((base == 8) || (base == 10) || (base == 16)); 168 DEBUGASSERT(max >= 0); /* mostly to catch SIZE_T_MAX, which is too large */ 169 *nump = 0; 170 p = *linep; 171 if(!valid_digit(*p, m)) 172 return STRE_NO_NUM; 173 if(max < base) { 174 /* special-case low max scenario because check needs to be different */ 175 do { 176 int n = Curl_hexval(*p++); 177 num = num * base + n; 178 if(num > max) 179 return STRE_OVERFLOW; 180 } while(valid_digit(*p, m)); 181 } 182 else { 183 do { 184 int n = Curl_hexval(*p++); 185 if(num > ((max - n) / base)) 186 return STRE_OVERFLOW; 187 num = num * base + n; 188 } while(valid_digit(*p, m)); 189 } 190 *nump = num; 191 *linep = p; 192 return STRE_OK; 193 } 194 195 /* Get an unsigned decimal number with no leading space or minus. Leading 196 zeroes are accepted. return non-zero on error */ 197 int curlx_str_number(const char **linep, curl_off_t *nump, curl_off_t max) 198 { 199 return str_num_base(linep, nump, max, 10); 200 } 201 202 /* Get an unsigned hexadecimal number with no leading space or minus and no 203 "0x" support. Leading zeroes are accepted. return non-zero on error */ 204 int curlx_str_hex(const char **linep, curl_off_t *nump, curl_off_t max) 205 { 206 return str_num_base(linep, nump, max, 16); 207 } 208 209 /* Get an unsigned octal number with no leading space or minus and no "0" 210 prefix support. Leading zeroes are accepted. return non-zero on error */ 211 int curlx_str_octal(const char **linep, curl_off_t *nump, curl_off_t max) 212 { 213 return str_num_base(linep, nump, max, 8); 214 } 215 216 /* 217 * Parse a positive number up to 63-bit number written in ASCII. Skip leading 218 * blanks. No support for prefixes. 219 */ 220 int curlx_str_numblanks(const char **str, curl_off_t *num) 221 { 222 curlx_str_passblanks(str); 223 return curlx_str_number(str, num, CURL_OFF_T_MAX); 224 } 225 226 /* CR or LF 227 return non-zero on error */ 228 int curlx_str_newline(const char **linep) 229 { 230 DEBUGASSERT(linep && *linep); 231 if(ISNEWLINE(**linep)) { 232 (*linep)++; 233 return STRE_OK; /* yessir */ 234 } 235 return STRE_NEWLINE; 236 } 237 238 #ifndef WITHOUT_LIBCURL 239 /* case insensitive compare that the parsed string matches the given string. 240 Returns non-zero on match. */ 241 int curlx_str_casecompare(struct Curl_str *str, const char *check) 242 { 243 size_t clen = check ? strlen(check) : 0; 244 return ((str->len == clen) && curl_strnequal(str->str, check, clen)); 245 } 246 #endif 247 248 /* case sensitive string compare. Returns non-zero on match. */ 249 int curlx_str_cmp(struct Curl_str *str, const char *check) 250 { 251 if(check) { 252 size_t clen = strlen(check); 253 return ((str->len == clen) && !strncmp(str->str, check, clen)); 254 } 255 return !!(str->len); 256 } 257 258 /* Trim off 'num' number of bytes from the beginning (left side) of the 259 string. If 'num' is larger than the string, return error. */ 260 int curlx_str_nudge(struct Curl_str *str, size_t num) 261 { 262 if(num <= str->len) { 263 str->str += num; 264 str->len -= num; 265 return STRE_OK; 266 } 267 return STRE_OVERFLOW; 268 } 269 270 /* Get the following character sequence that consists only of bytes not 271 present in the 'reject' string. Like strcspn(). */ 272 int curlx_str_cspn(const char **linep, struct Curl_str *out, 273 const char *reject) 274 { 275 const char *s = *linep; 276 size_t len; 277 DEBUGASSERT(linep && *linep); 278 279 len = strcspn(s, reject); 280 if(len) { 281 out->str = s; 282 out->len = len; 283 *linep = &s[len]; 284 return STRE_OK; 285 } 286 curlx_str_init(out); 287 return STRE_SHORT; 288 } 289 290 /* remove ISBLANK()s from both ends of the string */ 291 void curlx_str_trimblanks(struct Curl_str *out) 292 { 293 while(out->len && ISBLANK(*out->str)) 294 curlx_str_nudge(out, 1); 295 296 /* trim trailing spaces and tabs */ 297 while(out->len && ISBLANK(out->str[out->len - 1])) 298 out->len--; 299 } 300 301 /* increase the pointer until it has moved over all blanks */ 302 void curlx_str_passblanks(const char **linep) 303 { 304 while(ISBLANK(**linep)) 305 (*linep)++; /* move over it */ 306 }