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