tool_writeout.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676
  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_writeout.h"
  30. #include "tool_writeout_json.h"
  31. #include "dynbuf.h"
  32. #include "memdebug.h" /* keep this as LAST include */
  33. static int writeTime(FILE *stream, const struct writeoutvar *wovar,
  34. struct per_transfer *per, CURLcode per_result,
  35. bool use_json);
  36. static int writeString(FILE *stream, const struct writeoutvar *wovar,
  37. struct per_transfer *per, CURLcode per_result,
  38. bool use_json);
  39. static int writeLong(FILE *stream, const struct writeoutvar *wovar,
  40. struct per_transfer *per, CURLcode per_result,
  41. bool use_json);
  42. static int writeOffset(FILE *stream, const struct writeoutvar *wovar,
  43. struct per_transfer *per, CURLcode per_result,
  44. bool use_json);
  45. struct httpmap {
  46. const char *str;
  47. int num;
  48. };
  49. static const struct httpmap http_version[] = {
  50. { "0", CURL_HTTP_VERSION_NONE},
  51. { "1", CURL_HTTP_VERSION_1_0},
  52. { "1.1", CURL_HTTP_VERSION_1_1},
  53. { "2", CURL_HTTP_VERSION_2},
  54. { "3", CURL_HTTP_VERSION_3},
  55. { NULL, 0} /* end of list */
  56. };
  57. /* The designated write function should be the same as the CURLINFO return type
  58. with exceptions special cased in the respective function. For example,
  59. http_version uses CURLINFO_HTTP_VERSION which returns the version as a long,
  60. however it is output as a string and therefore is handled in writeString.
  61. Yes: "http_version": "1.1"
  62. No: "http_version": 1.1
  63. Variable names should be in alphabetical order.
  64. */
  65. static const struct writeoutvar variables[] = {
  66. {"certs", VAR_CERT, CURLINFO_NONE, writeString},
  67. {"content_type", VAR_CONTENT_TYPE, CURLINFO_CONTENT_TYPE, writeString},
  68. {"conn_id", VAR_CONN_ID, CURLINFO_CONN_ID, writeOffset},
  69. {"errormsg", VAR_ERRORMSG, CURLINFO_NONE, writeString},
  70. {"exitcode", VAR_EXITCODE, CURLINFO_NONE, writeLong},
  71. {"filename_effective", VAR_EFFECTIVE_FILENAME, CURLINFO_NONE, writeString},
  72. {"ftp_entry_path", VAR_FTP_ENTRY_PATH, CURLINFO_FTP_ENTRY_PATH, writeString},
  73. {"header_json", VAR_HEADER_JSON, CURLINFO_NONE, NULL},
  74. {"http_code", VAR_HTTP_CODE, CURLINFO_RESPONSE_CODE, writeLong},
  75. {"http_connect", VAR_HTTP_CODE_PROXY, CURLINFO_HTTP_CONNECTCODE, writeLong},
  76. {"http_version", VAR_HTTP_VERSION, CURLINFO_HTTP_VERSION, writeString},
  77. {"json", VAR_JSON, CURLINFO_NONE, NULL},
  78. {"local_ip", VAR_LOCAL_IP, CURLINFO_LOCAL_IP, writeString},
  79. {"local_port", VAR_LOCAL_PORT, CURLINFO_LOCAL_PORT, writeLong},
  80. {"method", VAR_EFFECTIVE_METHOD, CURLINFO_EFFECTIVE_METHOD, writeString},
  81. {"num_certs", VAR_NUM_CERTS, CURLINFO_NONE, writeLong},
  82. {"num_connects", VAR_NUM_CONNECTS, CURLINFO_NUM_CONNECTS, writeLong},
  83. {"num_headers", VAR_NUM_HEADERS, CURLINFO_NONE, writeLong},
  84. {"num_redirects", VAR_REDIRECT_COUNT, CURLINFO_REDIRECT_COUNT, writeLong},
  85. {"onerror", VAR_ONERROR, CURLINFO_NONE, NULL},
  86. {"proxy_ssl_verify_result", VAR_PROXY_SSL_VERIFY_RESULT,
  87. CURLINFO_PROXY_SSL_VERIFYRESULT, writeLong},
  88. {"proxy_used", VAR_PROXY_USED, CURLINFO_USED_PROXY, writeLong},
  89. {"redirect_url", VAR_REDIRECT_URL, CURLINFO_REDIRECT_URL, writeString},
  90. {"referer", VAR_REFERER, CURLINFO_REFERER, writeString},
  91. {"remote_ip", VAR_PRIMARY_IP, CURLINFO_PRIMARY_IP, writeString},
  92. {"remote_port", VAR_PRIMARY_PORT, CURLINFO_PRIMARY_PORT, writeLong},
  93. {"response_code", VAR_HTTP_CODE, CURLINFO_RESPONSE_CODE, writeLong},
  94. {"scheme", VAR_SCHEME, CURLINFO_SCHEME, writeString},
  95. {"size_download", VAR_SIZE_DOWNLOAD, CURLINFO_SIZE_DOWNLOAD_T, writeOffset},
  96. {"size_header", VAR_HEADER_SIZE, CURLINFO_HEADER_SIZE, writeLong},
  97. {"size_request", VAR_REQUEST_SIZE, CURLINFO_REQUEST_SIZE, writeLong},
  98. {"size_upload", VAR_SIZE_UPLOAD, CURLINFO_SIZE_UPLOAD_T, writeOffset},
  99. {"speed_download", VAR_SPEED_DOWNLOAD, CURLINFO_SPEED_DOWNLOAD_T,
  100. writeOffset},
  101. {"speed_upload", VAR_SPEED_UPLOAD, CURLINFO_SPEED_UPLOAD_T, writeOffset},
  102. {"ssl_verify_result", VAR_SSL_VERIFY_RESULT, CURLINFO_SSL_VERIFYRESULT,
  103. writeLong},
  104. {"stderr", VAR_STDERR, CURLINFO_NONE, NULL},
  105. {"stdout", VAR_STDOUT, CURLINFO_NONE, NULL},
  106. {"time_appconnect", VAR_APPCONNECT_TIME, CURLINFO_APPCONNECT_TIME_T,
  107. writeTime},
  108. {"time_connect", VAR_CONNECT_TIME, CURLINFO_CONNECT_TIME_T, writeTime},
  109. {"time_namelookup", VAR_NAMELOOKUP_TIME, CURLINFO_NAMELOOKUP_TIME_T,
  110. writeTime},
  111. {"time_pretransfer", VAR_PRETRANSFER_TIME, CURLINFO_PRETRANSFER_TIME_T,
  112. writeTime},
  113. {"time_redirect", VAR_REDIRECT_TIME, CURLINFO_REDIRECT_TIME_T, writeTime},
  114. {"time_starttransfer", VAR_STARTTRANSFER_TIME, CURLINFO_STARTTRANSFER_TIME_T,
  115. writeTime},
  116. {"time_total", VAR_TOTAL_TIME, CURLINFO_TOTAL_TIME_T, writeTime},
  117. {"url", VAR_INPUT_URL, CURLINFO_NONE, writeString},
  118. {"url.scheme", VAR_INPUT_URLSCHEME, CURLINFO_NONE, writeString},
  119. {"url.user", VAR_INPUT_URLUSER, CURLINFO_NONE, writeString},
  120. {"url.password", VAR_INPUT_URLPASSWORD, CURLINFO_NONE, writeString},
  121. {"url.options", VAR_INPUT_URLOPTIONS, CURLINFO_NONE, writeString},
  122. {"url.host", VAR_INPUT_URLHOST, CURLINFO_NONE, writeString},
  123. {"url.port", VAR_INPUT_URLPORT, CURLINFO_NONE, writeString},
  124. {"url.path", VAR_INPUT_URLPATH, CURLINFO_NONE, writeString},
  125. {"url.query", VAR_INPUT_URLQUERY, CURLINFO_NONE, writeString},
  126. {"url.fragment", VAR_INPUT_URLFRAGMENT, CURLINFO_NONE, writeString},
  127. {"url.zoneid", VAR_INPUT_URLZONEID, CURLINFO_NONE, writeString},
  128. {"urle.scheme", VAR_INPUT_URLESCHEME, CURLINFO_NONE, writeString},
  129. {"urle.user", VAR_INPUT_URLEUSER, CURLINFO_NONE, writeString},
  130. {"urle.password", VAR_INPUT_URLEPASSWORD, CURLINFO_NONE, writeString},
  131. {"urle.options", VAR_INPUT_URLEOPTIONS, CURLINFO_NONE, writeString},
  132. {"urle.host", VAR_INPUT_URLEHOST, CURLINFO_NONE, writeString},
  133. {"urle.port", VAR_INPUT_URLEPORT, CURLINFO_NONE, writeString},
  134. {"urle.path", VAR_INPUT_URLEPATH, CURLINFO_NONE, writeString},
  135. {"urle.query", VAR_INPUT_URLEQUERY, CURLINFO_NONE, writeString},
  136. {"urle.fragment", VAR_INPUT_URLEFRAGMENT, CURLINFO_NONE, writeString},
  137. {"urle.zoneid", VAR_INPUT_URLEZONEID, CURLINFO_NONE, writeString},
  138. {"url_effective", VAR_EFFECTIVE_URL, CURLINFO_EFFECTIVE_URL, writeString},
  139. {"urlnum", VAR_URLNUM, CURLINFO_NONE, writeLong},
  140. {"xfer_id", VAR_EASY_ID, CURLINFO_XFER_ID, writeOffset},
  141. {NULL, VAR_NONE, CURLINFO_NONE, NULL}
  142. };
  143. static int writeTime(FILE *stream, const struct writeoutvar *wovar,
  144. struct per_transfer *per, CURLcode per_result,
  145. bool use_json)
  146. {
  147. bool valid = false;
  148. curl_off_t us = 0;
  149. (void)per;
  150. (void)per_result;
  151. DEBUGASSERT(wovar->writefunc == writeTime);
  152. if(wovar->ci) {
  153. if(!curl_easy_getinfo(per->curl, wovar->ci, &us))
  154. valid = true;
  155. }
  156. else {
  157. DEBUGASSERT(0);
  158. }
  159. if(valid) {
  160. curl_off_t secs = us / 1000000;
  161. us %= 1000000;
  162. if(use_json)
  163. fprintf(stream, "\"%s\":", wovar->name);
  164. fprintf(stream, "%" CURL_FORMAT_CURL_OFF_TU
  165. ".%06" CURL_FORMAT_CURL_OFF_TU, secs, us);
  166. }
  167. else {
  168. if(use_json)
  169. fprintf(stream, "\"%s\":null", wovar->name);
  170. }
  171. return 1; /* return 1 if anything was written */
  172. }
  173. static int urlpart(struct per_transfer *per, writeoutid vid,
  174. const char **contentp)
  175. {
  176. CURLU *uh = curl_url();
  177. int rc = 0;
  178. if(uh) {
  179. CURLUPart cpart = CURLUPART_HOST;
  180. char *part = NULL;
  181. const char *url = NULL;
  182. if(vid >= VAR_INPUT_URLEHOST) {
  183. if(curl_easy_getinfo(per->curl, CURLINFO_EFFECTIVE_URL, &url))
  184. rc = 5;
  185. }
  186. else
  187. url = per->this_url;
  188. if(!rc) {
  189. switch(vid) {
  190. case VAR_INPUT_URLSCHEME:
  191. case VAR_INPUT_URLESCHEME:
  192. cpart = CURLUPART_SCHEME;
  193. break;
  194. case VAR_INPUT_URLUSER:
  195. case VAR_INPUT_URLEUSER:
  196. cpart = CURLUPART_USER;
  197. break;
  198. case VAR_INPUT_URLPASSWORD:
  199. case VAR_INPUT_URLEPASSWORD:
  200. cpart = CURLUPART_PASSWORD;
  201. break;
  202. case VAR_INPUT_URLOPTIONS:
  203. case VAR_INPUT_URLEOPTIONS:
  204. cpart = CURLUPART_OPTIONS;
  205. break;
  206. case VAR_INPUT_URLHOST:
  207. case VAR_INPUT_URLEHOST:
  208. cpart = CURLUPART_HOST;
  209. break;
  210. case VAR_INPUT_URLPORT:
  211. case VAR_INPUT_URLEPORT:
  212. cpart = CURLUPART_PORT;
  213. break;
  214. case VAR_INPUT_URLPATH:
  215. case VAR_INPUT_URLEPATH:
  216. cpart = CURLUPART_PATH;
  217. break;
  218. case VAR_INPUT_URLQUERY:
  219. case VAR_INPUT_URLEQUERY:
  220. cpart = CURLUPART_QUERY;
  221. break;
  222. case VAR_INPUT_URLFRAGMENT:
  223. case VAR_INPUT_URLEFRAGMENT:
  224. cpart = CURLUPART_FRAGMENT;
  225. break;
  226. case VAR_INPUT_URLZONEID:
  227. case VAR_INPUT_URLEZONEID:
  228. cpart = CURLUPART_ZONEID;
  229. break;
  230. default:
  231. /* not implemented */
  232. rc = 4;
  233. break;
  234. }
  235. }
  236. if(!rc && curl_url_set(uh, CURLUPART_URL, url,
  237. CURLU_GUESS_SCHEME|CURLU_NON_SUPPORT_SCHEME))
  238. rc = 2;
  239. if(!rc && curl_url_get(uh, cpart, &part, CURLU_DEFAULT_PORT))
  240. rc = 3;
  241. if(!rc && part)
  242. *contentp = part;
  243. curl_url_cleanup(uh);
  244. }
  245. else
  246. return 1;
  247. return rc;
  248. }
  249. static int writeString(FILE *stream, const struct writeoutvar *wovar,
  250. struct per_transfer *per, CURLcode per_result,
  251. bool use_json)
  252. {
  253. bool valid = false;
  254. const char *strinfo = NULL;
  255. const char *freestr = NULL;
  256. struct dynbuf buf;
  257. curlx_dyn_init(&buf, 256*1024);
  258. DEBUGASSERT(wovar->writefunc == writeString);
  259. if(wovar->ci) {
  260. if(wovar->ci == CURLINFO_HTTP_VERSION) {
  261. long version = 0;
  262. if(!curl_easy_getinfo(per->curl, CURLINFO_HTTP_VERSION, &version)) {
  263. const struct httpmap *m = &http_version[0];
  264. while(m->str) {
  265. if(m->num == version) {
  266. strinfo = m->str;
  267. valid = true;
  268. break;
  269. }
  270. m++;
  271. }
  272. }
  273. }
  274. else {
  275. if(!curl_easy_getinfo(per->curl, wovar->ci, &strinfo) && strinfo)
  276. valid = true;
  277. }
  278. }
  279. else {
  280. switch(wovar->id) {
  281. case VAR_CERT:
  282. if(per->certinfo) {
  283. int i;
  284. bool error = FALSE;
  285. for(i = 0; (i < per->certinfo->num_of_certs) && !error; i++) {
  286. struct curl_slist *slist;
  287. for(slist = per->certinfo->certinfo[i]; slist; slist = slist->next) {
  288. size_t len;
  289. if(curl_strnequal(slist->data, "cert:", 5)) {
  290. if(curlx_dyn_add(&buf, &slist->data[5])) {
  291. error = TRUE;
  292. break;
  293. }
  294. }
  295. else {
  296. if(curlx_dyn_add(&buf, slist->data)) {
  297. error = TRUE;
  298. break;
  299. }
  300. }
  301. len = curlx_dyn_len(&buf);
  302. if(len) {
  303. char *ptr = curlx_dyn_ptr(&buf);
  304. if(ptr[len -1] != '\n') {
  305. /* add a newline to make things look better */
  306. if(curlx_dyn_addn(&buf, "\n", 1)) {
  307. error = TRUE;
  308. break;
  309. }
  310. }
  311. }
  312. }
  313. }
  314. if(!error) {
  315. strinfo = curlx_dyn_ptr(&buf);
  316. if(!strinfo)
  317. /* maybe not a TLS protocol */
  318. strinfo = "";
  319. valid = true;
  320. }
  321. }
  322. else
  323. strinfo = ""; /* no cert info */
  324. break;
  325. case VAR_ERRORMSG:
  326. if(per_result) {
  327. strinfo = (per->errorbuffer && per->errorbuffer[0]) ?
  328. per->errorbuffer : curl_easy_strerror(per_result);
  329. valid = true;
  330. }
  331. break;
  332. case VAR_EFFECTIVE_FILENAME:
  333. if(per->outs.filename) {
  334. strinfo = per->outs.filename;
  335. valid = true;
  336. }
  337. break;
  338. case VAR_INPUT_URL:
  339. if(per->this_url) {
  340. strinfo = per->this_url;
  341. valid = true;
  342. }
  343. break;
  344. case VAR_INPUT_URLSCHEME:
  345. case VAR_INPUT_URLUSER:
  346. case VAR_INPUT_URLPASSWORD:
  347. case VAR_INPUT_URLOPTIONS:
  348. case VAR_INPUT_URLHOST:
  349. case VAR_INPUT_URLPORT:
  350. case VAR_INPUT_URLPATH:
  351. case VAR_INPUT_URLQUERY:
  352. case VAR_INPUT_URLFRAGMENT:
  353. case VAR_INPUT_URLZONEID:
  354. case VAR_INPUT_URLESCHEME:
  355. case VAR_INPUT_URLEUSER:
  356. case VAR_INPUT_URLEPASSWORD:
  357. case VAR_INPUT_URLEOPTIONS:
  358. case VAR_INPUT_URLEHOST:
  359. case VAR_INPUT_URLEPORT:
  360. case VAR_INPUT_URLEPATH:
  361. case VAR_INPUT_URLEQUERY:
  362. case VAR_INPUT_URLEFRAGMENT:
  363. case VAR_INPUT_URLEZONEID:
  364. if(per->this_url) {
  365. if(!urlpart(per, wovar->id, &strinfo)) {
  366. freestr = strinfo;
  367. valid = true;
  368. }
  369. }
  370. break;
  371. default:
  372. DEBUGASSERT(0);
  373. break;
  374. }
  375. }
  376. if(valid) {
  377. DEBUGASSERT(strinfo);
  378. if(use_json) {
  379. fprintf(stream, "\"%s\":", wovar->name);
  380. jsonWriteString(stream, strinfo, FALSE);
  381. }
  382. else
  383. fputs(strinfo, stream);
  384. }
  385. else {
  386. if(use_json)
  387. fprintf(stream, "\"%s\":null", wovar->name);
  388. }
  389. curl_free((char *)freestr);
  390. curlx_dyn_free(&buf);
  391. return 1; /* return 1 if anything was written */
  392. }
  393. static int writeLong(FILE *stream, const struct writeoutvar *wovar,
  394. struct per_transfer *per, CURLcode per_result,
  395. bool use_json)
  396. {
  397. bool valid = false;
  398. long longinfo = 0;
  399. DEBUGASSERT(wovar->writefunc == writeLong);
  400. if(wovar->ci) {
  401. if(!curl_easy_getinfo(per->curl, wovar->ci, &longinfo))
  402. valid = true;
  403. }
  404. else {
  405. switch(wovar->id) {
  406. case VAR_NUM_CERTS:
  407. longinfo = per->certinfo ? per->certinfo->num_of_certs : 0;
  408. valid = true;
  409. break;
  410. case VAR_NUM_HEADERS:
  411. longinfo = per->num_headers;
  412. valid = true;
  413. break;
  414. case VAR_EXITCODE:
  415. longinfo = per_result;
  416. valid = true;
  417. break;
  418. case VAR_URLNUM:
  419. if(per->urlnum <= INT_MAX) {
  420. longinfo = (long)per->urlnum;
  421. valid = true;
  422. }
  423. break;
  424. default:
  425. DEBUGASSERT(0);
  426. break;
  427. }
  428. }
  429. if(valid) {
  430. if(use_json)
  431. fprintf(stream, "\"%s\":%ld", wovar->name, longinfo);
  432. else {
  433. if(wovar->id == VAR_HTTP_CODE || wovar->id == VAR_HTTP_CODE_PROXY)
  434. fprintf(stream, "%03ld", longinfo);
  435. else
  436. fprintf(stream, "%ld", longinfo);
  437. }
  438. }
  439. else {
  440. if(use_json)
  441. fprintf(stream, "\"%s\":null", wovar->name);
  442. }
  443. return 1; /* return 1 if anything was written */
  444. }
  445. static int writeOffset(FILE *stream, const struct writeoutvar *wovar,
  446. struct per_transfer *per, CURLcode per_result,
  447. bool use_json)
  448. {
  449. bool valid = false;
  450. curl_off_t offinfo = 0;
  451. (void)per;
  452. (void)per_result;
  453. DEBUGASSERT(wovar->writefunc == writeOffset);
  454. if(wovar->ci) {
  455. if(!curl_easy_getinfo(per->curl, wovar->ci, &offinfo))
  456. valid = true;
  457. }
  458. else {
  459. DEBUGASSERT(0);
  460. }
  461. if(valid) {
  462. if(use_json)
  463. fprintf(stream, "\"%s\":", wovar->name);
  464. fprintf(stream, "%" CURL_FORMAT_CURL_OFF_T, offinfo);
  465. }
  466. else {
  467. if(use_json)
  468. fprintf(stream, "\"%s\":null", wovar->name);
  469. }
  470. return 1; /* return 1 if anything was written */
  471. }
  472. void ourWriteOut(struct OperationConfig *config, struct per_transfer *per,
  473. CURLcode per_result)
  474. {
  475. FILE *stream = stdout;
  476. const char *writeinfo = config->writeout;
  477. const char *ptr = writeinfo;
  478. bool done = FALSE;
  479. struct curl_certinfo *certinfo;
  480. CURLcode res = curl_easy_getinfo(per->curl, CURLINFO_CERTINFO, &certinfo);
  481. bool fclose_stream = FALSE;
  482. if(!writeinfo)
  483. return;
  484. if(!res && certinfo)
  485. per->certinfo = certinfo;
  486. while(ptr && *ptr && !done) {
  487. if('%' == *ptr && ptr[1]) {
  488. if('%' == ptr[1]) {
  489. /* an escaped %-letter */
  490. fputc('%', stream);
  491. ptr += 2;
  492. }
  493. else {
  494. /* this is meant as a variable to output */
  495. char *end;
  496. size_t vlen;
  497. if('{' == ptr[1]) {
  498. int i;
  499. bool match = FALSE;
  500. end = strchr(ptr, '}');
  501. ptr += 2; /* pass the % and the { */
  502. if(!end) {
  503. fputs("%{", stream);
  504. continue;
  505. }
  506. vlen = end - ptr;
  507. for(i = 0; variables[i].name; i++) {
  508. if((strlen(variables[i].name) == vlen) &&
  509. curl_strnequal(ptr, variables[i].name, vlen)) {
  510. match = TRUE;
  511. switch(variables[i].id) {
  512. case VAR_ONERROR:
  513. if(per_result == CURLE_OK)
  514. /* this isn't error so skip the rest */
  515. done = TRUE;
  516. break;
  517. case VAR_STDOUT:
  518. if(fclose_stream)
  519. fclose(stream);
  520. fclose_stream = FALSE;
  521. stream = stdout;
  522. break;
  523. case VAR_STDERR:
  524. if(fclose_stream)
  525. fclose(stream);
  526. fclose_stream = FALSE;
  527. stream = tool_stderr;
  528. break;
  529. case VAR_JSON:
  530. ourWriteOutJSON(stream, variables, per, per_result);
  531. break;
  532. case VAR_HEADER_JSON:
  533. headerJSON(stream, per);
  534. break;
  535. default:
  536. (void)variables[i].writefunc(stream, &variables[i],
  537. per, per_result, false);
  538. break;
  539. }
  540. break;
  541. }
  542. }
  543. if(!match) {
  544. fprintf(tool_stderr,
  545. "curl: unknown --write-out variable: '%.*s'\n",
  546. (int)vlen, ptr);
  547. }
  548. ptr = end + 1; /* pass the end */
  549. }
  550. else if(!strncmp("header{", &ptr[1], 7)) {
  551. ptr += 8;
  552. end = strchr(ptr, '}');
  553. if(end) {
  554. char hname[256]; /* holds the longest header field name */
  555. struct curl_header *header;
  556. vlen = end - ptr;
  557. if(vlen < sizeof(hname)) {
  558. memcpy(hname, ptr, vlen);
  559. hname[vlen] = 0;
  560. if(CURLHE_OK == curl_easy_header(per->curl, hname, 0,
  561. CURLH_HEADER, -1, &header))
  562. fputs(header->value, stream);
  563. }
  564. ptr = end + 1;
  565. }
  566. else
  567. fputs("%header{", stream);
  568. }
  569. else if(!strncmp("output{", &ptr[1], 7)) {
  570. bool append = FALSE;
  571. ptr += 8;
  572. if((ptr[0] == '>') && (ptr[1] == '>')) {
  573. append = TRUE;
  574. ptr += 2;
  575. }
  576. end = strchr(ptr, '}');
  577. if(end) {
  578. char fname[512]; /* holds the longest file name */
  579. size_t flen = end - ptr;
  580. if(flen < sizeof(fname)) {
  581. FILE *stream2;
  582. memcpy(fname, ptr, flen);
  583. fname[flen] = 0;
  584. stream2 = fopen(fname, append? FOPEN_APPENDTEXT :
  585. FOPEN_WRITETEXT);
  586. if(stream2) {
  587. /* only change if the open worked */
  588. if(fclose_stream)
  589. fclose(stream);
  590. stream = stream2;
  591. fclose_stream = TRUE;
  592. }
  593. }
  594. ptr = end + 1;
  595. }
  596. else
  597. fputs("%output{", stream);
  598. }
  599. else {
  600. /* illegal syntax, then just output the characters that are used */
  601. fputc('%', stream);
  602. fputc(ptr[1], stream);
  603. ptr += 2;
  604. }
  605. }
  606. }
  607. else if('\\' == *ptr && ptr[1]) {
  608. switch(ptr[1]) {
  609. case 'r':
  610. fputc('\r', stream);
  611. break;
  612. case 'n':
  613. fputc('\n', stream);
  614. break;
  615. case 't':
  616. fputc('\t', stream);
  617. break;
  618. default:
  619. /* unknown, just output this */
  620. fputc(*ptr, stream);
  621. fputc(ptr[1], stream);
  622. break;
  623. }
  624. ptr += 2;
  625. }
  626. else {
  627. fputc(*ptr, stream);
  628. ptr++;
  629. }
  630. }
  631. if(fclose_stream)
  632. fclose(stream);
  633. }