tool_urlglob.c 20 KB

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