tool_urlglob.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705
  1. /***************************************************************************
  2. * _ _ ____ _
  3. * Project ___| | | | _ \| |
  4. * / __| | | | |_) | |
  5. * | (__| |_| | _ <| |___
  6. * \___|\___/|_| \_\_____|
  7. *
  8. * Copyright (C) 1998 - 2020, 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. ***************************************************************************/
  22. #include "tool_setup.h"
  23. #define ENABLE_CURLX_PRINTF
  24. /* use our own printf() functions */
  25. #include "curlx.h"
  26. #include "tool_cfgable.h"
  27. #include "tool_doswin.h"
  28. #include "tool_urlglob.h"
  29. #include "tool_vms.h"
  30. #include "dynbuf.h"
  31. #include "memdebug.h" /* keep this as LAST include */
  32. #define GLOBERROR(string, column, code) \
  33. glob->error = string, glob->pos = column, code
  34. static CURLcode glob_fixed(struct URLGlob *glob, char *fixed, size_t len)
  35. {
  36. struct URLPattern *pat = &glob->pattern[glob->size];
  37. pat->type = UPTSet;
  38. pat->content.Set.size = 1;
  39. pat->content.Set.ptr_s = 0;
  40. pat->globindex = -1;
  41. pat->content.Set.elements = malloc(sizeof(char *));
  42. if(!pat->content.Set.elements)
  43. return GLOBERROR("out of memory", 0, CURLE_OUT_OF_MEMORY);
  44. pat->content.Set.elements[0] = malloc(len + 1);
  45. if(!pat->content.Set.elements[0])
  46. return GLOBERROR("out of memory", 0, CURLE_OUT_OF_MEMORY);
  47. memcpy(pat->content.Set.elements[0], fixed, len);
  48. pat->content.Set.elements[0][len] = 0;
  49. return CURLE_OK;
  50. }
  51. /* multiply
  52. *
  53. * Multiplies and checks for overflow.
  54. */
  55. static int multiply(unsigned long *amount, long with)
  56. {
  57. unsigned long sum = *amount * with;
  58. if(!with) {
  59. *amount = 0;
  60. return 0;
  61. }
  62. if(sum/with != *amount)
  63. return 1; /* didn't fit, bail out */
  64. *amount = sum;
  65. return 0;
  66. }
  67. static CURLcode glob_set(struct URLGlob *glob, char **patternp,
  68. size_t *posp, unsigned long *amount,
  69. int globindex)
  70. {
  71. /* processes a set expression with the point behind the opening '{'
  72. ','-separated elements are collected until the next closing '}'
  73. */
  74. struct URLPattern *pat;
  75. bool done = FALSE;
  76. char *buf = glob->glob_buffer;
  77. char *pattern = *patternp;
  78. char *opattern = pattern;
  79. size_t opos = *posp-1;
  80. pat = &glob->pattern[glob->size];
  81. /* patterns 0,1,2,... correspond to size=1,3,5,... */
  82. pat->type = UPTSet;
  83. pat->content.Set.size = 0;
  84. pat->content.Set.ptr_s = 0;
  85. pat->content.Set.elements = NULL;
  86. pat->globindex = globindex;
  87. while(!done) {
  88. switch (*pattern) {
  89. case '\0': /* URL ended while set was still open */
  90. return GLOBERROR("unmatched brace", opos, CURLE_URL_MALFORMAT);
  91. case '{':
  92. case '[': /* no nested expressions at this time */
  93. return GLOBERROR("nested brace", *posp, CURLE_URL_MALFORMAT);
  94. case '}': /* set element completed */
  95. if(opattern == pattern)
  96. return GLOBERROR("empty string within braces", *posp,
  97. CURLE_URL_MALFORMAT);
  98. /* add 1 to size since it'll be incremented below */
  99. if(multiply(amount, pat->content.Set.size + 1))
  100. return GLOBERROR("range overflow", 0, CURLE_URL_MALFORMAT);
  101. /* FALLTHROUGH */
  102. case ',':
  103. *buf = '\0';
  104. if(pat->content.Set.elements) {
  105. char **new_arr = realloc(pat->content.Set.elements,
  106. (pat->content.Set.size + 1) * sizeof(char *));
  107. if(!new_arr)
  108. return GLOBERROR("out of memory", 0, CURLE_OUT_OF_MEMORY);
  109. pat->content.Set.elements = new_arr;
  110. }
  111. else
  112. pat->content.Set.elements = malloc(sizeof(char *));
  113. if(!pat->content.Set.elements)
  114. return GLOBERROR("out of memory", 0, CURLE_OUT_OF_MEMORY);
  115. pat->content.Set.elements[pat->content.Set.size] =
  116. strdup(glob->glob_buffer);
  117. if(!pat->content.Set.elements[pat->content.Set.size])
  118. return GLOBERROR("out of memory", 0, CURLE_OUT_OF_MEMORY);
  119. ++pat->content.Set.size;
  120. if(*pattern == '}') {
  121. pattern++; /* pass the closing brace */
  122. done = TRUE;
  123. continue;
  124. }
  125. buf = glob->glob_buffer;
  126. ++pattern;
  127. ++(*posp);
  128. break;
  129. case ']': /* illegal closing bracket */
  130. return GLOBERROR("unexpected close bracket", *posp, CURLE_URL_MALFORMAT);
  131. case '\\': /* escaped character, skip '\' */
  132. if(pattern[1]) {
  133. ++pattern;
  134. ++(*posp);
  135. }
  136. /* FALLTHROUGH */
  137. default:
  138. *buf++ = *pattern++; /* copy character to set element */
  139. ++(*posp);
  140. }
  141. }
  142. *patternp = pattern; /* return with the new position */
  143. return CURLE_OK;
  144. }
  145. static CURLcode glob_range(struct URLGlob *glob, char **patternp,
  146. size_t *posp, unsigned long *amount,
  147. int globindex)
  148. {
  149. /* processes a range expression with the point behind the opening '['
  150. - char range: e.g. "a-z]", "B-Q]"
  151. - num range: e.g. "0-9]", "17-2000]"
  152. - num range with leading zeros: e.g. "001-999]"
  153. expression is checked for well-formedness and collected until the next ']'
  154. */
  155. struct URLPattern *pat;
  156. int rc;
  157. char *pattern = *patternp;
  158. char *c;
  159. pat = &glob->pattern[glob->size];
  160. pat->globindex = globindex;
  161. if(ISALPHA(*pattern)) {
  162. /* character range detected */
  163. char min_c;
  164. char max_c;
  165. char end_c;
  166. unsigned long step = 1;
  167. pat->type = UPTCharRange;
  168. rc = sscanf(pattern, "%c-%c%c", &min_c, &max_c, &end_c);
  169. if(rc == 3) {
  170. if(end_c == ':') {
  171. char *endp;
  172. errno = 0;
  173. step = strtoul(&pattern[4], &endp, 10);
  174. if(errno || &pattern[4] == endp || *endp != ']')
  175. step = 0;
  176. else
  177. pattern = endp + 1;
  178. }
  179. else if(end_c != ']')
  180. /* then this is wrong */
  181. rc = 0;
  182. else
  183. /* end_c == ']' */
  184. pattern += 4;
  185. }
  186. *posp += (pattern - *patternp);
  187. if(rc != 3 || !step || step > (unsigned)INT_MAX ||
  188. (min_c == max_c && step != 1) ||
  189. (min_c != max_c && (min_c > max_c || step > (unsigned)(max_c - min_c) ||
  190. (max_c - min_c) > ('z' - 'a'))))
  191. /* the pattern is not well-formed */
  192. return GLOBERROR("bad range", *posp, CURLE_URL_MALFORMAT);
  193. /* if there was a ":[num]" thing, use that as step or else use 1 */
  194. pat->content.CharRange.step = (int)step;
  195. pat->content.CharRange.ptr_c = pat->content.CharRange.min_c = min_c;
  196. pat->content.CharRange.max_c = max_c;
  197. if(multiply(amount, ((pat->content.CharRange.max_c -
  198. pat->content.CharRange.min_c) /
  199. pat->content.CharRange.step + 1)))
  200. return GLOBERROR("range overflow", *posp, CURLE_URL_MALFORMAT);
  201. }
  202. else if(ISDIGIT(*pattern)) {
  203. /* numeric range detected */
  204. unsigned long min_n;
  205. unsigned long max_n = 0;
  206. unsigned long step_n = 0;
  207. char *endp;
  208. pat->type = UPTNumRange;
  209. pat->content.NumRange.padlength = 0;
  210. if(*pattern == '0') {
  211. /* leading zero specified, count them! */
  212. c = pattern;
  213. while(ISDIGIT(*c)) {
  214. c++;
  215. ++pat->content.NumRange.padlength; /* padding length is set for all
  216. instances of this pattern */
  217. }
  218. }
  219. errno = 0;
  220. min_n = strtoul(pattern, &endp, 10);
  221. if(errno || (endp == pattern))
  222. endp = NULL;
  223. else {
  224. if(*endp != '-')
  225. endp = NULL;
  226. else {
  227. pattern = endp + 1;
  228. while(*pattern && ISBLANK(*pattern))
  229. pattern++;
  230. if(!ISDIGIT(*pattern)) {
  231. endp = NULL;
  232. goto fail;
  233. }
  234. errno = 0;
  235. max_n = strtoul(pattern, &endp, 10);
  236. if(errno)
  237. /* overflow */
  238. endp = NULL;
  239. else if(*endp == ':') {
  240. pattern = endp + 1;
  241. errno = 0;
  242. step_n = strtoul(pattern, &endp, 10);
  243. if(errno)
  244. /* over/underflow situation */
  245. endp = NULL;
  246. }
  247. else
  248. step_n = 1;
  249. if(endp && (*endp == ']')) {
  250. pattern = endp + 1;
  251. }
  252. else
  253. endp = NULL;
  254. }
  255. }
  256. fail:
  257. *posp += (pattern - *patternp);
  258. if(!endp || !step_n ||
  259. (min_n == max_n && step_n != 1) ||
  260. (min_n != max_n && (min_n > max_n || step_n > (max_n - min_n))))
  261. /* the pattern is not well-formed */
  262. return GLOBERROR("bad range", *posp, CURLE_URL_MALFORMAT);
  263. /* typecasting to ints are fine here since we make sure above that we
  264. are within 31 bits */
  265. pat->content.NumRange.ptr_n = pat->content.NumRange.min_n = min_n;
  266. pat->content.NumRange.max_n = max_n;
  267. pat->content.NumRange.step = step_n;
  268. if(multiply(amount, ((pat->content.NumRange.max_n -
  269. pat->content.NumRange.min_n) /
  270. pat->content.NumRange.step + 1)))
  271. return GLOBERROR("range overflow", *posp, CURLE_URL_MALFORMAT);
  272. }
  273. else
  274. return GLOBERROR("bad range specification", *posp, CURLE_URL_MALFORMAT);
  275. *patternp = pattern;
  276. return CURLE_OK;
  277. }
  278. #define MAX_IP6LEN 128
  279. static bool peek_ipv6(const char *str, size_t *skip)
  280. {
  281. /*
  282. * Scan for a potential IPv6 literal.
  283. * - Valid globs contain a hyphen and <= 1 colon.
  284. * - IPv6 literals contain no hyphens and >= 2 colons.
  285. */
  286. char hostname[MAX_IP6LEN];
  287. CURLU *u;
  288. char *endbr = strchr(str, ']');
  289. size_t hlen;
  290. CURLUcode rc;
  291. if(!endbr)
  292. return FALSE;
  293. hlen = endbr - str + 1;
  294. if(hlen >= MAX_IP6LEN)
  295. return FALSE;
  296. u = curl_url();
  297. if(!u)
  298. return FALSE;
  299. memcpy(hostname, str, hlen);
  300. hostname[hlen] = 0;
  301. /* ask to "guess scheme" as then it works without a https:// prefix */
  302. rc = curl_url_set(u, CURLUPART_URL, hostname, CURLU_GUESS_SCHEME);
  303. curl_url_cleanup(u);
  304. if(!rc)
  305. *skip = hlen;
  306. return rc ? FALSE : TRUE;
  307. }
  308. static CURLcode glob_parse(struct URLGlob *glob, char *pattern,
  309. size_t pos, unsigned long *amount)
  310. {
  311. /* processes a literal string component of a URL
  312. special characters '{' and '[' branch to set/range processing functions
  313. */
  314. CURLcode res = CURLE_OK;
  315. int globindex = 0; /* count "actual" globs */
  316. *amount = 1;
  317. while(*pattern && !res) {
  318. char *buf = glob->glob_buffer;
  319. size_t sublen = 0;
  320. while(*pattern && *pattern != '{') {
  321. if(*pattern == '[') {
  322. /* skip over IPv6 literals and [] */
  323. size_t skip = 0;
  324. if(!peek_ipv6(pattern, &skip) && (pattern[1] == ']'))
  325. skip = 2;
  326. if(skip) {
  327. memcpy(buf, pattern, skip);
  328. buf += skip;
  329. pattern += skip;
  330. sublen += skip;
  331. continue;
  332. }
  333. break;
  334. }
  335. if(*pattern == '}' || *pattern == ']')
  336. return GLOBERROR("unmatched close brace/bracket", pos,
  337. CURLE_URL_MALFORMAT);
  338. /* only allow \ to escape known "special letters" */
  339. if(*pattern == '\\' &&
  340. (*(pattern + 1) == '{' || *(pattern + 1) == '[' ||
  341. *(pattern + 1) == '}' || *(pattern + 1) == ']') ) {
  342. /* escape character, skip '\' */
  343. ++pattern;
  344. ++pos;
  345. }
  346. *buf++ = *pattern++; /* copy character to literal */
  347. ++pos;
  348. sublen++;
  349. }
  350. if(sublen) {
  351. /* we got a literal string, add it as a single-item list */
  352. *buf = '\0';
  353. res = glob_fixed(glob, glob->glob_buffer, sublen);
  354. }
  355. else {
  356. switch (*pattern) {
  357. case '\0': /* done */
  358. break;
  359. case '{':
  360. /* process set pattern */
  361. pattern++;
  362. pos++;
  363. res = glob_set(glob, &pattern, &pos, amount, globindex++);
  364. break;
  365. case '[':
  366. /* process range pattern */
  367. pattern++;
  368. pos++;
  369. res = glob_range(glob, &pattern, &pos, amount, globindex++);
  370. break;
  371. }
  372. }
  373. if(++glob->size >= GLOB_PATTERN_NUM)
  374. return GLOBERROR("too many globs", pos, CURLE_URL_MALFORMAT);
  375. }
  376. return res;
  377. }
  378. CURLcode glob_url(struct URLGlob **glob, char *url, unsigned long *urlnum,
  379. FILE *error)
  380. {
  381. /*
  382. * We can deal with any-size, just make a buffer with the same length
  383. * as the specified URL!
  384. */
  385. struct URLGlob *glob_expand;
  386. unsigned long amount = 0;
  387. char *glob_buffer;
  388. CURLcode res;
  389. *glob = NULL;
  390. glob_buffer = malloc(strlen(url) + 1);
  391. if(!glob_buffer)
  392. return CURLE_OUT_OF_MEMORY;
  393. glob_buffer[0] = 0;
  394. glob_expand = calloc(1, sizeof(struct URLGlob));
  395. if(!glob_expand) {
  396. Curl_safefree(glob_buffer);
  397. return CURLE_OUT_OF_MEMORY;
  398. }
  399. glob_expand->urllen = strlen(url);
  400. glob_expand->glob_buffer = glob_buffer;
  401. res = glob_parse(glob_expand, url, 1, &amount);
  402. if(!res)
  403. *urlnum = amount;
  404. else {
  405. if(error && glob_expand->error) {
  406. char text[512];
  407. const char *t;
  408. if(glob_expand->pos) {
  409. msnprintf(text, sizeof(text), "%s in URL position %zu:\n%s\n%*s^",
  410. glob_expand->error,
  411. glob_expand->pos, url, glob_expand->pos - 1, " ");
  412. t = text;
  413. }
  414. else
  415. t = glob_expand->error;
  416. /* send error description to the error-stream */
  417. fprintf(error, "curl: (%d) %s\n", res, t);
  418. }
  419. /* it failed, we cleanup */
  420. glob_cleanup(glob_expand);
  421. *urlnum = 1;
  422. return res;
  423. }
  424. *glob = glob_expand;
  425. return CURLE_OK;
  426. }
  427. void glob_cleanup(struct URLGlob *glob)
  428. {
  429. size_t i;
  430. int elem;
  431. if(!glob)
  432. return;
  433. for(i = 0; i < glob->size; i++) {
  434. if((glob->pattern[i].type == UPTSet) &&
  435. (glob->pattern[i].content.Set.elements)) {
  436. for(elem = glob->pattern[i].content.Set.size - 1;
  437. elem >= 0;
  438. --elem) {
  439. Curl_safefree(glob->pattern[i].content.Set.elements[elem]);
  440. }
  441. Curl_safefree(glob->pattern[i].content.Set.elements);
  442. }
  443. }
  444. Curl_safefree(glob->glob_buffer);
  445. Curl_safefree(glob);
  446. }
  447. CURLcode glob_next_url(char **globbed, struct URLGlob *glob)
  448. {
  449. struct URLPattern *pat;
  450. size_t i;
  451. size_t len;
  452. size_t buflen = glob->urllen + 1;
  453. char *buf = glob->glob_buffer;
  454. *globbed = NULL;
  455. if(!glob->beenhere)
  456. glob->beenhere = 1;
  457. else {
  458. bool carry = TRUE;
  459. /* implement a counter over the index ranges of all patterns, starting
  460. with the rightmost pattern */
  461. for(i = 0; carry && (i < glob->size); i++) {
  462. carry = FALSE;
  463. pat = &glob->pattern[glob->size - 1 - i];
  464. switch(pat->type) {
  465. case UPTSet:
  466. if((pat->content.Set.elements) &&
  467. (++pat->content.Set.ptr_s == pat->content.Set.size)) {
  468. pat->content.Set.ptr_s = 0;
  469. carry = TRUE;
  470. }
  471. break;
  472. case UPTCharRange:
  473. pat->content.CharRange.ptr_c =
  474. (char)(pat->content.CharRange.step +
  475. (int)((unsigned char)pat->content.CharRange.ptr_c));
  476. if(pat->content.CharRange.ptr_c > pat->content.CharRange.max_c) {
  477. pat->content.CharRange.ptr_c = pat->content.CharRange.min_c;
  478. carry = TRUE;
  479. }
  480. break;
  481. case UPTNumRange:
  482. pat->content.NumRange.ptr_n += pat->content.NumRange.step;
  483. if(pat->content.NumRange.ptr_n > pat->content.NumRange.max_n) {
  484. pat->content.NumRange.ptr_n = pat->content.NumRange.min_n;
  485. carry = TRUE;
  486. }
  487. break;
  488. default:
  489. printf("internal error: invalid pattern type (%d)\n", (int)pat->type);
  490. return CURLE_FAILED_INIT;
  491. }
  492. }
  493. if(carry) { /* first pattern ptr has run into overflow, done! */
  494. return CURLE_OK;
  495. }
  496. }
  497. for(i = 0; i < glob->size; ++i) {
  498. pat = &glob->pattern[i];
  499. switch(pat->type) {
  500. case UPTSet:
  501. if(pat->content.Set.elements) {
  502. msnprintf(buf, buflen, "%s",
  503. pat->content.Set.elements[pat->content.Set.ptr_s]);
  504. len = strlen(buf);
  505. buf += len;
  506. buflen -= len;
  507. }
  508. break;
  509. case UPTCharRange:
  510. if(buflen) {
  511. *buf++ = pat->content.CharRange.ptr_c;
  512. *buf = '\0';
  513. buflen--;
  514. }
  515. break;
  516. case UPTNumRange:
  517. msnprintf(buf, buflen, "%0*lu",
  518. pat->content.NumRange.padlength,
  519. pat->content.NumRange.ptr_n);
  520. len = strlen(buf);
  521. buf += len;
  522. buflen -= len;
  523. break;
  524. default:
  525. printf("internal error: invalid pattern type (%d)\n", (int)pat->type);
  526. return CURLE_FAILED_INIT;
  527. }
  528. }
  529. *globbed = strdup(glob->glob_buffer);
  530. if(!*globbed)
  531. return CURLE_OUT_OF_MEMORY;
  532. return CURLE_OK;
  533. }
  534. #define MAX_OUTPUT_GLOB_LENGTH (10*1024)
  535. CURLcode glob_match_url(char **result, char *filename, struct URLGlob *glob)
  536. {
  537. char numbuf[18];
  538. char *appendthis = (char *)"";
  539. size_t appendlen = 0;
  540. struct curlx_dynbuf dyn;
  541. *result = NULL;
  542. /* We cannot use the glob_buffer for storage since the filename may be
  543. * longer than the URL we use.
  544. */
  545. curlx_dyn_init(&dyn, MAX_OUTPUT_GLOB_LENGTH);
  546. while(*filename) {
  547. if(*filename == '#' && ISDIGIT(filename[1])) {
  548. char *ptr = filename;
  549. unsigned long num = strtoul(&filename[1], &filename, 10);
  550. struct URLPattern *pat = NULL;
  551. if(num && (num < glob->size)) {
  552. unsigned long i;
  553. num--; /* make it zero based */
  554. /* find the correct glob entry */
  555. for(i = 0; i<glob->size; i++) {
  556. if(glob->pattern[i].globindex == (int)num) {
  557. pat = &glob->pattern[i];
  558. break;
  559. }
  560. }
  561. }
  562. if(pat) {
  563. switch(pat->type) {
  564. case UPTSet:
  565. if(pat->content.Set.elements) {
  566. appendthis = pat->content.Set.elements[pat->content.Set.ptr_s];
  567. appendlen =
  568. strlen(pat->content.Set.elements[pat->content.Set.ptr_s]);
  569. }
  570. break;
  571. case UPTCharRange:
  572. numbuf[0] = pat->content.CharRange.ptr_c;
  573. numbuf[1] = 0;
  574. appendthis = numbuf;
  575. appendlen = 1;
  576. break;
  577. case UPTNumRange:
  578. msnprintf(numbuf, sizeof(numbuf), "%0*lu",
  579. pat->content.NumRange.padlength,
  580. pat->content.NumRange.ptr_n);
  581. appendthis = numbuf;
  582. appendlen = strlen(numbuf);
  583. break;
  584. default:
  585. fprintf(stderr, "internal error: invalid pattern type (%d)\n",
  586. (int)pat->type);
  587. curlx_dyn_free(&dyn);
  588. return CURLE_FAILED_INIT;
  589. }
  590. }
  591. else {
  592. /* #[num] out of range, use the #[num] in the output */
  593. filename = ptr;
  594. appendthis = filename++;
  595. appendlen = 1;
  596. }
  597. }
  598. else {
  599. appendthis = filename++;
  600. appendlen = 1;
  601. }
  602. if(curlx_dyn_addn(&dyn, appendthis, appendlen))
  603. return CURLE_OUT_OF_MEMORY;
  604. }
  605. #if defined(MSDOS) || defined(WIN32)
  606. {
  607. char *sanitized;
  608. SANITIZEcode sc = sanitize_file_name(&sanitized, curlx_dyn_ptr(&dyn),
  609. (SANITIZE_ALLOW_PATH |
  610. SANITIZE_ALLOW_RESERVED));
  611. curlx_dyn_free(&dyn);
  612. if(sc)
  613. return CURLE_URL_MALFORMAT;
  614. *result = sanitized;
  615. return CURLE_OK;
  616. }
  617. #else
  618. *result = curlx_dyn_ptr(&dyn);
  619. return CURLE_OK;
  620. #endif /* MSDOS || WIN32 */
  621. }