strparse.c 8.4 KB

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