]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
tool_writeout: refactor write-out and write-out json
authorJay Satiro <raysatiro@yahoo.com>
Thu, 28 Jan 2021 23:56:50 +0000 (18:56 -0500)
committerJay Satiro <raysatiro@yahoo.com>
Tue, 9 Feb 2021 07:48:27 +0000 (02:48 -0500)
- Deduplicate the logic used by write-out and write-out json.

Rather than have separate writeLong, writeString, etc, logic for
each of write-out and write-out json instead have respective shared
functions that can output either format and a 'use_json' parameter to
indicate whether it is json that is output.

This will make it easier to maintain. Rather than have to go through
two sets of logic now we only have to go through one.

- Support write-out %{errormsg} and %{exitcode} in json.

- Clarify in the doc that %{exitcode} is the exit code of the transfer.

Prior to this change it just said "The numerical exitcode" which
implies it's the exit code of the tool, and it's not necessarily that.

Closes https://github.com/curl/curl/pull/6544

docs/cmdline-opts/write-out.d
src/tool_operate.c
src/tool_writeout.c
src/tool_writeout.h
src/tool_writeout_json.c
src/tool_writeout_json.h
tests/data/test970

index af5d0cf22d71d0d88d87ebeaf70dc418b8bb35e7..be8f75ad51eaaf82709062d424eda51f3f607f01 100644 (file)
@@ -33,7 +33,7 @@ The Content-Type of the requested document, if there was any.
 The error message. (Added in 7.75.0)
 .TP
 .B exitcode
-The numerical exitcode. (Added in 7.75.0)
+The numerical exitcode of the transfer. (Added in 7.75.0)
 .TP
 .B filename_effective
 The ultimate filename that curl writes out to. This is only meaningful if curl
index 140142a323b8a01ce70caea53b4972db61e6b404..2b23680f2b3fafabdca4f79723c8a8f999eaaabf 100644 (file)
@@ -625,9 +625,6 @@ static CURLcode post_per_transfer(struct GlobalConfig *global,
        newline here */
     fputs("\n", per->progressbar.out);
 
-  if(config->writeout)
-    ourWriteOut(per->curl, per, config->writeout, result);
-
   /* Close the outs file */
   if(outs->fopened && outs->stream) {
     int rc = fclose(outs->stream);
@@ -647,6 +644,10 @@ static CURLcode post_per_transfer(struct GlobalConfig *global,
     setfiletime(filetime, outs->filename, global);
   }
 
+  /* Write the --write-out data before cleanup but after result is final */
+  if(config->writeout)
+    ourWriteOut(config->writeout, per, result);
+
   /* Close function-local opened file descriptors */
   if(per->heads.fopened && per->heads.stream)
     fclose(per->heads.stream);
index c548bd8f2d455d0637380e9235ed35ceb3ea2901..e914dfa7b39a062b6c87a17c54b060ea8fdef77b 100644 (file)
 
 #include "memdebug.h" /* keep this as LAST include */
 
+static int writeTime(FILE *stream, const struct writeoutvar *wovar,
+                     struct per_transfer *per, CURLcode per_result,
+                     bool use_json);
+
+static int writeString(FILE *stream, const struct writeoutvar *wovar,
+                       struct per_transfer *per, CURLcode per_result,
+                       bool use_json);
+
+static int writeLong(FILE *stream, const struct writeoutvar *wovar,
+                     struct per_transfer *per, CURLcode per_result,
+                     bool use_json);
+
+static int writeOffset(FILE *stream, const struct writeoutvar *wovar,
+                       struct per_transfer *per, CURLcode per_result,
+                       bool use_json);
+
+static const char *http_version[] = {
+  "0",   /* CURL_HTTP_VERSION_NONE */
+  "1",   /* CURL_HTTP_VERSION_1_0 */
+  "1.1", /* CURL_HTTP_VERSION_1_1 */
+  "2",   /* CURL_HTTP_VERSION_2 */
+  "3"    /* CURL_HTTP_VERSION_3 */
+};
+
+/* The designated write function should be the same as the CURLINFO return type
+   with exceptions special cased in the respective function. For example,
+   http_version uses CURLINFO_HTTP_VERSION which returns the version as a long,
+   however it is output as a string and therefore is handled in writeString.
+
+   Yes: "http_version": "1.1"
+   No:  "http_version": 1.1
+
+   Variable names should be in alphabetical order.
+   */
 static const struct writeoutvar variables[] = {
-  {"content_type", VAR_CONTENT_TYPE, 0, CURLINFO_CONTENT_TYPE, JSON_STRING},
-  {"filename_effective", VAR_EFFECTIVE_FILENAME, 0, 0, JSON_FILENAME},
-  {"exitcode", VAR_EXITCODE, 0, 0, JSON_LONG},
-  {"errormsg", VAR_ERRORMSG, 0, 0, JSON_STRING},
-  {"ftp_entry_path", VAR_FTP_ENTRY_PATH, 0, CURLINFO_FTP_ENTRY_PATH,
-   JSON_STRING},
-  {"http_code", VAR_HTTP_CODE, 0, CURLINFO_RESPONSE_CODE, JSON_LONG},
-  {"http_connect", VAR_HTTP_CODE_PROXY, 0, CURLINFO_HTTP_CONNECTCODE,
-   JSON_LONG},
-  {"http_version", VAR_HTTP_VERSION, 0, CURLINFO_HTTP_VERSION, JSON_VERSION},
-  {"json", VAR_JSON, 1, 0, JSON_NONE},
-  {"local_ip", VAR_LOCAL_IP, 0, CURLINFO_LOCAL_IP, JSON_STRING},
-  {"local_port", VAR_LOCAL_PORT, 0, CURLINFO_LOCAL_PORT, JSON_LONG},
-  {"method", VAR_EFFECTIVE_METHOD, 0, CURLINFO_EFFECTIVE_METHOD, JSON_STRING},
-  {"num_connects", VAR_NUM_CONNECTS, 0, CURLINFO_NUM_CONNECTS, JSON_LONG},
-  {"num_headers", VAR_NUM_HEADERS, 0, 0, JSON_LONG},
-  {"num_redirects", VAR_REDIRECT_COUNT, 0, CURLINFO_REDIRECT_COUNT, JSON_LONG},
-  {"onerror", VAR_ONERROR, 1, 0, JSON_NONE},
-  {"proxy_ssl_verify_result", VAR_PROXY_SSL_VERIFY_RESULT, 0,
-   CURLINFO_PROXY_SSL_VERIFYRESULT, JSON_LONG},
-  {"redirect_url", VAR_REDIRECT_URL, 0, CURLINFO_REDIRECT_URL, JSON_STRING},
-  {"remote_ip", VAR_PRIMARY_IP, 0, CURLINFO_PRIMARY_IP, JSON_STRING},
-  {"remote_port", VAR_PRIMARY_PORT, 0, CURLINFO_PRIMARY_PORT, JSON_LONG},
-  {"response_code", VAR_HTTP_CODE, 0, CURLINFO_RESPONSE_CODE, JSON_LONG},
-  {"scheme", VAR_SCHEME, 0, CURLINFO_SCHEME, JSON_STRING},
-  {"size_download", VAR_SIZE_DOWNLOAD, 0, CURLINFO_SIZE_DOWNLOAD_T,
-   JSON_OFFSET},
-  {"size_header", VAR_HEADER_SIZE, 0, CURLINFO_HEADER_SIZE, JSON_LONG},
-  {"size_request", VAR_REQUEST_SIZE, 0, CURLINFO_REQUEST_SIZE, JSON_LONG},
-  {"size_upload", VAR_SIZE_UPLOAD, 0, CURLINFO_SIZE_UPLOAD_T, JSON_OFFSET},
-  {"speed_download", VAR_SPEED_DOWNLOAD, 0, CURLINFO_SPEED_DOWNLOAD_T,
-   JSON_OFFSET},
-  {"speed_upload", VAR_SPEED_UPLOAD, 0, CURLINFO_SPEED_UPLOAD_T, JSON_OFFSET},
-  {"ssl_verify_result", VAR_SSL_VERIFY_RESULT, 0, CURLINFO_SSL_VERIFYRESULT,
-   JSON_LONG},
-  {"stderr", VAR_STDERR, 1, 0, JSON_NONE},
-  {"stdout", VAR_STDOUT, 1, 0, JSON_NONE},
-  {"time_appconnect", VAR_APPCONNECT_TIME, 0, CURLINFO_APPCONNECT_TIME_T,
-   JSON_TIME},
-  {"time_connect", VAR_CONNECT_TIME, 0, CURLINFO_CONNECT_TIME_T, JSON_TIME},
-  {"time_namelookup", VAR_NAMELOOKUP_TIME, 0, CURLINFO_NAMELOOKUP_TIME_T,
-   JSON_TIME},
-  {"time_pretransfer", VAR_PRETRANSFER_TIME, 0, CURLINFO_PRETRANSFER_TIME_T,
-   JSON_TIME},
-  {"time_redirect", VAR_REDIRECT_TIME, 0, CURLINFO_REDIRECT_TIME_T, JSON_TIME},
-  {"time_starttransfer", VAR_STARTTRANSFER_TIME, 0,
-   CURLINFO_STARTTRANSFER_TIME_T, JSON_TIME},
-  {"time_total", VAR_TOTAL_TIME, 0, CURLINFO_TOTAL_TIME_T, JSON_TIME},
-  {"url", VAR_INPUT_URL, 0, 0, JSON_STRING},
-  {"url_effective", VAR_EFFECTIVE_URL, 0, CURLINFO_EFFECTIVE_URL, JSON_STRING},
-  {"urlnum", VAR_URLNUM, 0, 0, JSON_LONG},
-  {NULL, VAR_NONE, 1, 0, JSON_NONE}
+  {"content_type", VAR_CONTENT_TYPE, CURLINFO_CONTENT_TYPE, writeString},
+  {"errormsg", VAR_ERRORMSG, 0, writeString},
+  {"exitcode", VAR_EXITCODE, 0, writeLong},
+  {"filename_effective", VAR_EFFECTIVE_FILENAME, 0, writeString},
+  {"ftp_entry_path", VAR_FTP_ENTRY_PATH, CURLINFO_FTP_ENTRY_PATH, writeString},
+  {"http_code", VAR_HTTP_CODE, CURLINFO_RESPONSE_CODE, writeLong},
+  {"http_connect", VAR_HTTP_CODE_PROXY, CURLINFO_HTTP_CONNECTCODE, writeLong},
+  {"http_version", VAR_HTTP_VERSION, CURLINFO_HTTP_VERSION, writeString},
+  {"json", VAR_JSON, 0, NULL},
+  {"local_ip", VAR_LOCAL_IP, CURLINFO_LOCAL_IP, writeString},
+  {"local_port", VAR_LOCAL_PORT, CURLINFO_LOCAL_PORT, writeLong},
+  {"method", VAR_EFFECTIVE_METHOD, CURLINFO_EFFECTIVE_METHOD, writeString},
+  {"num_connects", VAR_NUM_CONNECTS, CURLINFO_NUM_CONNECTS, writeLong},
+  {"num_headers", VAR_NUM_HEADERS, 0, writeLong},
+  {"num_redirects", VAR_REDIRECT_COUNT, CURLINFO_REDIRECT_COUNT, writeLong},
+  {"onerror", VAR_ONERROR, 0, NULL},
+  {"proxy_ssl_verify_result", VAR_PROXY_SSL_VERIFY_RESULT,
+   CURLINFO_PROXY_SSL_VERIFYRESULT, writeLong},
+  {"redirect_url", VAR_REDIRECT_URL, CURLINFO_REDIRECT_URL, writeString},
+  {"remote_ip", VAR_PRIMARY_IP, CURLINFO_PRIMARY_IP, writeString},
+  {"remote_port", VAR_PRIMARY_PORT, CURLINFO_PRIMARY_PORT, writeLong},
+  {"response_code", VAR_HTTP_CODE, CURLINFO_RESPONSE_CODE, writeLong},
+  {"scheme", VAR_SCHEME, CURLINFO_SCHEME, writeString},
+  {"size_download", VAR_SIZE_DOWNLOAD, CURLINFO_SIZE_DOWNLOAD_T, writeOffset},
+  {"size_header", VAR_HEADER_SIZE, CURLINFO_HEADER_SIZE, writeLong},
+  {"size_request", VAR_REQUEST_SIZE, CURLINFO_REQUEST_SIZE, writeLong},
+  {"size_upload", VAR_SIZE_UPLOAD, CURLINFO_SIZE_UPLOAD_T, writeOffset},
+  {"speed_download", VAR_SPEED_DOWNLOAD, CURLINFO_SPEED_DOWNLOAD_T,
+   writeOffset},
+  {"speed_upload", VAR_SPEED_UPLOAD, CURLINFO_SPEED_UPLOAD_T, writeOffset},
+  {"ssl_verify_result", VAR_SSL_VERIFY_RESULT, CURLINFO_SSL_VERIFYRESULT,
+   writeLong},
+  {"stderr", VAR_STDERR, 0, NULL},
+  {"stdout", VAR_STDOUT, 0, NULL},
+  {"time_appconnect", VAR_APPCONNECT_TIME, CURLINFO_APPCONNECT_TIME_T,
+   writeTime},
+  {"time_connect", VAR_CONNECT_TIME, CURLINFO_CONNECT_TIME_T, writeTime},
+  {"time_namelookup", VAR_NAMELOOKUP_TIME, CURLINFO_NAMELOOKUP_TIME_T,
+   writeTime},
+  {"time_pretransfer", VAR_PRETRANSFER_TIME, CURLINFO_PRETRANSFER_TIME_T,
+   writeTime},
+  {"time_redirect", VAR_REDIRECT_TIME, CURLINFO_REDIRECT_TIME_T, writeTime},
+  {"time_starttransfer", VAR_STARTTRANSFER_TIME, CURLINFO_STARTTRANSFER_TIME_T,
+   writeTime},
+  {"time_total", VAR_TOTAL_TIME, CURLINFO_TOTAL_TIME_T, writeTime},
+  {"url", VAR_INPUT_URL, 0, writeString},
+  {"url_effective", VAR_EFFECTIVE_URL, CURLINFO_EFFECTIVE_URL, writeString},
+  {"urlnum", VAR_URLNUM, 0, writeLong},
+  {NULL, VAR_NONE, 0, NULL}
 };
 
-static void us2sec(FILE *stream, curl_off_t us)
+static int writeTime(FILE *stream, const struct writeoutvar *wovar,
+                     struct per_transfer *per, CURLcode per_result,
+                     bool use_json)
 {
-  curl_off_t secs = us / 1000000;
-  us %= 1000000;
-  fprintf(stream, "%" CURL_FORMAT_CURL_OFF_TU ".%06" CURL_FORMAT_CURL_OFF_TU,
-          secs, us);
+  bool valid = false;
+  curl_off_t us = 0;
+
+  (void)per;
+  (void)per_result;
+  DEBUGASSERT(wovar->writefunc == writeTime);
+
+  if(wovar->ci) {
+    if(!curl_easy_getinfo(per->curl, wovar->ci, &us))
+      valid = true;
+  }
+  else {
+    DEBUGASSERT(0);
+  }
+
+  if(valid) {
+    curl_off_t secs = us / 1000000;
+    us %= 1000000;
+
+    if(use_json)
+      fprintf(stream, "\"%s\":", wovar->name);
+
+    fprintf(stream, "%" CURL_FORMAT_CURL_OFF_TU
+            ".%06" CURL_FORMAT_CURL_OFF_TU, secs, us);
+  }
+  else {
+    if(use_json)
+      fprintf(stream, "\"%s\":null", wovar->name);
+  }
+
+  return 1; /* return 1 if anything was written */
 }
 
-void ourWriteOut(CURL *curl, struct per_transfer *per, const char *writeinfo,
-                 CURLcode result)
+static int writeString(FILE *stream, const struct writeoutvar *wovar,
+                       struct per_transfer *per, CURLcode per_result,
+                       bool use_json)
+{
+  bool valid = false;
+  const char *strinfo = NULL;
+
+  DEBUGASSERT(wovar->writefunc == writeString);
+
+  if(wovar->ci) {
+    if(wovar->ci == CURLINFO_HTTP_VERSION) {
+      long version = 0;
+      if(!curl_easy_getinfo(per->curl, CURLINFO_HTTP_VERSION, &version) &&
+         (version >= 0) &&
+         (version < (long)(sizeof(http_version)/sizeof(http_version[0])))) {
+        strinfo = http_version[version];
+        valid = true;
+      }
+    }
+    else {
+      if(!curl_easy_getinfo(per->curl, wovar->ci, &strinfo) && strinfo)
+        valid = true;
+    }
+  }
+  else {
+    switch(wovar->id) {
+    case VAR_ERRORMSG:
+      if(per_result) {
+        strinfo = per->errorbuffer[0] ? per->errorbuffer :
+                  curl_easy_strerror(per_result);
+        valid = true;
+      }
+      break;
+    case VAR_EFFECTIVE_FILENAME:
+      if(per->outs.filename) {
+        strinfo = per->outs.filename;
+        valid = true;
+      }
+      break;
+    case VAR_INPUT_URL:
+      if(per->this_url) {
+        strinfo = per->this_url;
+        valid = true;
+      }
+      break;
+    default:
+      DEBUGASSERT(0);
+      break;
+    }
+  }
+
+  if(valid) {
+    DEBUGASSERT(strinfo);
+    if(use_json) {
+      fprintf(stream, "\"%s\":\"", wovar->name);
+      jsonWriteString(stream, strinfo);
+      fputs("\"", stream);
+    }
+    else
+      fputs(strinfo, stream);
+  }
+  else {
+    if(use_json)
+      fprintf(stream, "\"%s\":null", wovar->name);
+  }
+
+  return 1; /* return 1 if anything was written */
+}
+
+static int writeLong(FILE *stream, const struct writeoutvar *wovar,
+                     struct per_transfer *per, CURLcode per_result,
+                     bool use_json)
+{
+  bool valid = false;
+  long longinfo = 0;
+
+  DEBUGASSERT(wovar->writefunc == writeLong);
+
+  if(wovar->ci) {
+    if(!curl_easy_getinfo(per->curl, wovar->ci, &longinfo))
+      valid = true;
+  }
+  else {
+    switch(wovar->id) {
+    case VAR_NUM_HEADERS:
+      longinfo = per->num_headers;
+      valid = true;
+      break;
+    case VAR_EXITCODE:
+      longinfo = per_result;
+      valid = true;
+      break;
+    case VAR_URLNUM:
+      if(per->urlnum <= INT_MAX) {
+        longinfo = (long)per->urlnum;
+        valid = true;
+      }
+      break;
+    default:
+      DEBUGASSERT(0);
+      break;
+    }
+  }
+
+  if(valid) {
+    if(use_json)
+      fprintf(stream, "\"%s\":", wovar->name);
+
+    if(wovar->id == VAR_HTTP_CODE || wovar->id == VAR_HTTP_CODE_PROXY)
+      fprintf(stream, "%03ld", longinfo);
+    else
+      fprintf(stream, "%ld", longinfo);
+  }
+  else {
+    if(use_json)
+      fprintf(stream, "\"%s\":null", wovar->name);
+  }
+
+  return 1; /* return 1 if anything was written */
+}
+
+static int writeOffset(FILE *stream, const struct writeoutvar *wovar,
+                       struct per_transfer *per, CURLcode per_result,
+                       bool use_json)
+{
+  bool valid = false;
+  curl_off_t offinfo = 0;
+
+  (void)per;
+  (void)per_result;
+  DEBUGASSERT(wovar->writefunc == writeOffset);
+
+  if(wovar->ci) {
+    if(!curl_easy_getinfo(per->curl, wovar->ci, &offinfo))
+      valid = true;
+  }
+  else {
+    DEBUGASSERT(0);
+  }
+
+  if(valid) {
+    if(use_json)
+      fprintf(stream, "\"%s\":", wovar->name);
+
+    fprintf(stream, "%" CURL_FORMAT_CURL_OFF_T, offinfo);
+  }
+  else {
+    if(use_json)
+      fprintf(stream, "\"%s\":null", wovar->name);
+  }
+
+  return 1; /* return 1 if anything was written */
+}
+
+void ourWriteOut(const char *writeinfo, struct per_transfer *per,
+                 CURLcode per_result)
 {
   FILE *stream = stdout;
   const char *ptr = writeinfo;
-  char *stringp = NULL;
-  long longinfo;
-  curl_off_t offinfo;
   bool done = FALSE;
 
   while(ptr && *ptr && !done) {
@@ -129,217 +340,10 @@ void ourWriteOut(CURL *curl, struct per_transfer *per, const char *writeinfo,
               match = TRUE;
               switch(variables[i].id) {
               case VAR_ONERROR:
-                if(result == CURLE_OK)
+                if(per_result == CURLE_OK)
                   /* this isn't error so skip the rest */
                   done = TRUE;
                 break;
-              case VAR_EXITCODE:
-                fprintf(stream, "%d", (int)result);
-                break;
-              case VAR_ERRORMSG:
-                if(result)
-                  fputs(per->errorbuffer[0] ? per->errorbuffer :
-                        curl_easy_strerror(result), stream);
-                break;
-              case VAR_INPUT_URL:
-                if(per->this_url)
-                  fputs(per->this_url, stream);
-                break;
-              case VAR_URLNUM:
-                fprintf(stream, "%u", per->urlnum);
-                break;
-              case VAR_EFFECTIVE_URL:
-                if((CURLE_OK ==
-                    curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &stringp))
-                   && stringp)
-                  fputs(stringp, stream);
-                break;
-              case VAR_EFFECTIVE_METHOD:
-                if((CURLE_OK == curl_easy_getinfo(curl,
-                                                  CURLINFO_EFFECTIVE_METHOD,
-                                                  &stringp))
-                   && stringp)
-                  fputs(stringp, stream);
-                break;
-              case VAR_HTTP_CODE:
-                if(CURLE_OK ==
-                   curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &longinfo))
-                  fprintf(stream, "%03ld", longinfo);
-                break;
-              case VAR_NUM_HEADERS:
-                fprintf(stream, "%ld", per->num_headers);
-                break;
-              case VAR_HTTP_CODE_PROXY:
-                if(CURLE_OK ==
-                   curl_easy_getinfo(curl, CURLINFO_HTTP_CONNECTCODE,
-                                     &longinfo))
-                  fprintf(stream, "%03ld", longinfo);
-                break;
-              case VAR_HEADER_SIZE:
-                if(CURLE_OK ==
-                   curl_easy_getinfo(curl, CURLINFO_HEADER_SIZE, &longinfo))
-                  fprintf(stream, "%ld", longinfo);
-                break;
-              case VAR_REQUEST_SIZE:
-                if(CURLE_OK ==
-                   curl_easy_getinfo(curl, CURLINFO_REQUEST_SIZE, &longinfo))
-                  fprintf(stream, "%ld", longinfo);
-                break;
-              case VAR_NUM_CONNECTS:
-                if(CURLE_OK ==
-                   curl_easy_getinfo(curl, CURLINFO_NUM_CONNECTS, &longinfo))
-                  fprintf(stream, "%ld", longinfo);
-                break;
-              case VAR_REDIRECT_COUNT:
-                if(CURLE_OK ==
-                   curl_easy_getinfo(curl, CURLINFO_REDIRECT_COUNT, &longinfo))
-                  fprintf(stream, "%ld", longinfo);
-                break;
-              case VAR_REDIRECT_TIME:
-                if(CURLE_OK ==
-                   curl_easy_getinfo(curl, CURLINFO_REDIRECT_TIME_T, &offinfo))
-                  us2sec(stream, offinfo);
-                break;
-              case VAR_TOTAL_TIME:
-                if(CURLE_OK ==
-                   curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME_T, &offinfo))
-                  us2sec(stream, offinfo);
-                break;
-              case VAR_NAMELOOKUP_TIME:
-                if(CURLE_OK ==
-                   curl_easy_getinfo(curl, CURLINFO_NAMELOOKUP_TIME_T,
-                                     &offinfo))
-                  us2sec(stream, offinfo);
-                break;
-              case VAR_CONNECT_TIME:
-                if(CURLE_OK ==
-                   curl_easy_getinfo(curl, CURLINFO_CONNECT_TIME_T, &offinfo))
-                  us2sec(stream, offinfo);
-                break;
-              case VAR_APPCONNECT_TIME:
-                if(CURLE_OK ==
-                   curl_easy_getinfo(curl, CURLINFO_APPCONNECT_TIME_T,
-                                     &offinfo))
-                  us2sec(stream, offinfo);
-                break;
-              case VAR_PRETRANSFER_TIME:
-                if(CURLE_OK ==
-                   curl_easy_getinfo(curl, CURLINFO_PRETRANSFER_TIME_T,
-                                     &offinfo))
-                  us2sec(stream, offinfo);
-                break;
-              case VAR_STARTTRANSFER_TIME:
-                if(CURLE_OK ==
-                   curl_easy_getinfo(curl, CURLINFO_STARTTRANSFER_TIME_T,
-                                     &offinfo))
-                  us2sec(stream, offinfo);
-                break;
-              case VAR_SIZE_UPLOAD:
-                if(CURLE_OK ==
-                   curl_easy_getinfo(curl, CURLINFO_SIZE_UPLOAD_T, &offinfo))
-                  fprintf(stream, "%" CURL_FORMAT_CURL_OFF_TU, offinfo);
-                break;
-              case VAR_SIZE_DOWNLOAD:
-                if(CURLE_OK ==
-                   curl_easy_getinfo(curl, CURLINFO_SIZE_DOWNLOAD_T,
-                                     &offinfo))
-                  fprintf(stream, "%" CURL_FORMAT_CURL_OFF_TU, offinfo);
-                break;
-              case VAR_SPEED_DOWNLOAD:
-                if(CURLE_OK ==
-                   curl_easy_getinfo(curl, CURLINFO_SPEED_DOWNLOAD_T,
-                                     &offinfo))
-                  fprintf(stream, "%" CURL_FORMAT_CURL_OFF_TU, offinfo);
-                break;
-              case VAR_SPEED_UPLOAD:
-                if(CURLE_OK ==
-                   curl_easy_getinfo(curl, CURLINFO_SPEED_UPLOAD_T, &offinfo))
-                  fprintf(stream, "%" CURL_FORMAT_CURL_OFF_TU, offinfo);
-                break;
-              case VAR_CONTENT_TYPE:
-                if((CURLE_OK ==
-                    curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &stringp))
-                   && stringp)
-                  fputs(stringp, stream);
-                break;
-              case VAR_FTP_ENTRY_PATH:
-                if((CURLE_OK ==
-                    curl_easy_getinfo(curl, CURLINFO_FTP_ENTRY_PATH, &stringp))
-                   && stringp)
-                  fputs(stringp, stream);
-                break;
-              case VAR_REDIRECT_URL:
-                if((CURLE_OK ==
-                    curl_easy_getinfo(curl, CURLINFO_REDIRECT_URL, &stringp))
-                   && stringp)
-                  fputs(stringp, stream);
-                break;
-              case VAR_SSL_VERIFY_RESULT:
-                if(CURLE_OK ==
-                   curl_easy_getinfo(curl, CURLINFO_SSL_VERIFYRESULT,
-                                     &longinfo))
-                  fprintf(stream, "%ld", longinfo);
-                break;
-              case VAR_PROXY_SSL_VERIFY_RESULT:
-                if(CURLE_OK ==
-                   curl_easy_getinfo(curl, CURLINFO_PROXY_SSL_VERIFYRESULT,
-                                     &longinfo))
-                  fprintf(stream, "%ld", longinfo);
-                break;
-              case VAR_EFFECTIVE_FILENAME:
-                if(per->outs.filename)
-                  fputs(per->outs.filename, stream);
-                break;
-              case VAR_PRIMARY_IP:
-                if((CURLE_OK == curl_easy_getinfo(curl, CURLINFO_PRIMARY_IP,
-                                                  &stringp)) && stringp)
-                  fputs(stringp, stream);
-                break;
-              case VAR_PRIMARY_PORT:
-                if(CURLE_OK ==
-                   curl_easy_getinfo(curl, CURLINFO_PRIMARY_PORT,
-                                     &longinfo))
-                  fprintf(stream, "%ld", longinfo);
-                break;
-              case VAR_LOCAL_IP:
-                if((CURLE_OK == curl_easy_getinfo(curl, CURLINFO_LOCAL_IP,
-                                                  &stringp)) && stringp)
-                  fputs(stringp, stream);
-                break;
-              case VAR_LOCAL_PORT:
-                if(CURLE_OK ==
-                   curl_easy_getinfo(curl, CURLINFO_LOCAL_PORT,
-                                     &longinfo))
-                  fprintf(stream, "%ld", longinfo);
-                break;
-              case VAR_HTTP_VERSION:
-                if(CURLE_OK ==
-                   curl_easy_getinfo(curl, CURLINFO_HTTP_VERSION,
-                                     &longinfo)) {
-                  const char *version = "0";
-                  switch(longinfo) {
-                  case CURL_HTTP_VERSION_1_0:
-                    version = "1.0";
-                    break;
-                  case CURL_HTTP_VERSION_1_1:
-                    version = "1.1";
-                    break;
-                  case CURL_HTTP_VERSION_2_0:
-                    version = "2";
-                    break;
-                  case CURL_HTTP_VERSION_3:
-                    version = "3";
-                    break;
-                  }
-
-                  fprintf(stream, version);
-                }
-                break;
-              case VAR_SCHEME:
-                if((CURLE_OK == curl_easy_getinfo(curl, CURLINFO_SCHEME,
-                                                  &stringp)) && stringp)
-                  fputs(stringp, stream);
-                break;
               case VAR_STDOUT:
                 stream = stdout;
                 break;
@@ -347,8 +351,11 @@ void ourWriteOut(CURL *curl, struct per_transfer *per, const char *writeinfo,
                 stream = stderr;
                 break;
               case VAR_JSON:
-                ourWriteOutJSON(variables, curl, per, stream);
+                ourWriteOutJSON(stream, variables, per, per_result);
+                break;
               default:
+                (void)variables[i].writefunc(stream, &variables[i],
+                                             per, per_result, false);
                 break;
               }
               break;
index 02858d8c1716b5e3a20aab57c41615db73a507c2..2cf7d012ab0b383e719c3de0ea803aea99894095 100644 (file)
@@ -69,25 +69,16 @@ typedef enum {
   VAR_NUM_OF_VARS /* must be the last */
 } writeoutid;
 
-typedef enum {
-  JSON_NONE,
-  JSON_STRING,
-  JSON_LONG,
-  JSON_OFFSET,
-  JSON_TIME,
-  JSON_VERSION,
-  JSON_FILENAME
-} jsontype;
-
 struct writeoutvar {
   const char *name;
   writeoutid id;
-  int is_ctrl;
-  CURLINFO cinfo;
-  jsontype jsontype;
+  CURLINFO ci;
+  int (*writefunc)(FILE *stream, const struct writeoutvar *wovar,
+                   struct per_transfer *per, CURLcode per_result,
+                   bool use_json);
 };
 
-void ourWriteOut(CURL *curl, struct per_transfer *per, const char *writeinfo,
-                 CURLcode exitcode);
+void ourWriteOut(const char *writeinfo, struct per_transfer *per,
+                 CURLcode per_result);
 
 #endif /* HEADER_CURL_TOOL_WRITEOUT_H */
index 186ed685129e96e7acbd3134406644b4accd3944..ca1e708686908bb52852416e24fce7c74198fc16 100644 (file)
 #include "tool_writeout.h"
 
 
-static const char *http_version[] = {
-  "0",   /* CURL_HTTP_VERSION_NONE */
-  "1",   /* CURL_HTTP_VERSION_1_0 */
-  "1.1", /* CURL_HTTP_VERSION_1_1 */
-  "2",   /* CURL_HTTP_VERSION_2 */
-  "3"    /* CURL_HTTP_VERSION_3 */
-};
-
-static void jsonEscape(FILE *stream, const char *in)
+void jsonWriteString(FILE *stream, const char *in)
 {
   const char *i = in;
   const char *in_end = in + strlen(in);
@@ -78,126 +70,22 @@ static void jsonEscape(FILE *stream, const char *in)
   }
 }
 
-static int writeTime(FILE *str, CURL *curl, const char *key, CURLINFO ci)
-{
-  curl_off_t val = 0;
-  if(CURLE_OK == curl_easy_getinfo(curl, ci, &val)) {
-    curl_off_t s = val / 1000000l;
-    curl_off_t ms = val % 1000000l;
-    fprintf(str, "\"%s\":%" CURL_FORMAT_CURL_OFF_T
-            ".%06" CURL_FORMAT_CURL_OFF_T, key, s, ms);
-    return 1;
-  }
-  return 0;
-}
-
-static int writeString(FILE *str, CURL *curl, const char *key, CURLINFO ci)
-{
-  char *valp = NULL;
-  if((CURLE_OK == curl_easy_getinfo(curl, ci, &valp)) && valp) {
-    fprintf(str, "\"%s\":\"", key);
-    jsonEscape(str, valp);
-    fprintf(str, "\"");
-    return 1;
-  }
-  return 0;
-}
-
-static int writeLong(FILE *str, CURL *curl, const char *key, CURLINFO ci,
-                     struct per_transfer *per, const struct writeoutvar *wovar)
-{
-  if(wovar->id == VAR_NUM_HEADERS) {
-    fprintf(str, "\"%s\":%ld", key, per->num_headers);
-    return 1;
-  }
-  else {
-    long val = 0;
-    if(CURLE_OK == curl_easy_getinfo(curl, ci, &val)) {
-      fprintf(str, "\"%s\":%ld", key, val);
-      return 1;
-    }
-  }
-  return 0;
-}
-
-static int writeOffset(FILE *str, CURL *curl, const char *key, CURLINFO ci)
-{
-  curl_off_t val = 0;
-  if(CURLE_OK == curl_easy_getinfo(curl, ci, &val)) {
-    fprintf(str, "\"%s\":%" CURL_FORMAT_CURL_OFF_T, key, val);
-    return 1;
-  }
-  return 0;
-}
-
-static int writeFilename(FILE *str, const char *key, const char *filename)
-{
-  if(filename) {
-    fprintf(str, "\"%s\":\"", key);
-    jsonEscape(str, filename);
-    fprintf(str, "\"");
-  }
-  else {
-    fprintf(str, "\"%s\":null", key);
-  }
-  return 1;
-}
-
-static int writeVersion(FILE *str, CURL *curl, const char *key, CURLINFO ci)
-{
-  long version = 0;
-  if(CURLE_OK == curl_easy_getinfo(curl, ci, &version) &&
-     (version >= 0) &&
-     (version < (long)(sizeof(http_version)/sizeof(char *)))) {
-    fprintf(str, "\"%s\":\"%s\"", key, http_version[version]);
-    return 1;
-  }
-  return 0;
-}
-
-void ourWriteOutJSON(const struct writeoutvar mappings[], CURL *curl,
-                     struct per_transfer *per, FILE *stream)
+void ourWriteOutJSON(FILE *stream, const struct writeoutvar mappings[],
+                     struct per_transfer *per, CURLcode per_result)
 {
   int i;
 
   fputs("{", stream);
-  for(i = 0; mappings[i].name != NULL; i++) {
-    const struct writeoutvar *wovar = &mappings[i];
-    const char *name = mappings[i].name;
-    CURLINFO cinfo = mappings[i].cinfo;
-    int ok = 0;
-
-    if(mappings[i].is_ctrl == 1) {
-      continue;
-    }
 
-    switch(mappings[i].jsontype) {
-    case JSON_STRING:
-      ok = writeString(stream, curl, name, cinfo);
-      break;
-    case JSON_LONG:
-      ok = writeLong(stream, curl, name, cinfo, per, wovar);
-      break;
-    case JSON_OFFSET:
-      ok = writeOffset(stream, curl, name, cinfo);
-      break;
-    case JSON_TIME:
-      ok = writeTime(stream, curl, name, cinfo);
-      break;
-    case JSON_FILENAME:
-      ok = writeFilename(stream, name, per->outs.filename);
-      break;
-    case JSON_VERSION:
-      ok = writeVersion(stream, curl, name, cinfo);
-      break;
-    default:
-      break;
-    }
-
-    if(ok) {
+  for(i = 0; mappings[i].name != NULL; i++) {
+    if(mappings[i].writefunc &&
+       mappings[i].writefunc(stream, &mappings[i], per, per_result, true))
       fputs(",", stream);
-    }
   }
 
-  fprintf(stream, "\"curl_version\":\"%s\"}", curl_version());
+  /* The variables are sorted in alphabetical order but as a special case
+     curl_version (which is not actually a --write-out variable) is last. */
+  fprintf(stream, "\"curl_version\":\"");
+  jsonWriteString(stream, curl_version());
+  fprintf(stream, "\"}");
 }
index cc8c4e5851f0de2ddc0bce81392ff47da9958f60..3eb75243cb91eae7b056975649c013944009c5b6 100644 (file)
@@ -24,7 +24,9 @@
 #include "tool_setup.h"
 #include "tool_writeout.h"
 
-void ourWriteOutJSON(const struct writeoutvar mappings[], CURL *curl,
-                     struct per_transfer *per, FILE *stream);
+void ourWriteOutJSON(FILE *stream, const struct writeoutvar mappings[],
+                     struct per_transfer *per, CURLcode per_result);
+
+void jsonWriteString(FILE *stream, const char *in);
 
 #endif /* HEADER_CURL_TOOL_WRITEOUT_H */
index d2f9aa7b0422bb4373cc99eb4f93e455d91b1db0..6a89a54551cb1f9964ec839e8b671c50c2d52c2f 100644 (file)
@@ -59,7 +59,7 @@ Accept: */*
 \r
 </protocol>
 <stdout nonewline="yes">
-{"content_type":"text/html","filename_effective":"log/out970","http_code":200,"http_connect":0,"http_version":"1.1","local_ip":"127.0.0.1","local_port":13,"method":"GET","num_connects":1,"num_headers":9,"num_redirects":0,"proxy_ssl_verify_result":0,"remote_ip":"%HOSTIP","remote_port":%HTTPPORT,"response_code":200,"scheme":"HTTP","size_download":445,"size_header":4019,"size_request":4019,"size_upload":0,"speed_download":13,"speed_upload":13,"ssl_verify_result":0,"time_appconnect":0.000013,"time_connect":0.000013,"time_namelookup":0.000013,"time_pretransfer":0.000013,"time_redirect":0.000013,"time_starttransfer":0.000013,"time_total":0.000013,"url_effective":"http://%HOSTIP:%HTTPPORT/970","curl_version":"curl-unit-test-fake-version"}
+{"content_type":"text/html","errormsg":null,"exitcode":0,"filename_effective":"log/out970","ftp_entry_path":null,"http_code":200,"http_connect":000,"http_version":"1.1","local_ip":"127.0.0.1","local_port":13,"method":"GET","num_connects":1,"num_headers":9,"num_redirects":0,"proxy_ssl_verify_result":0,"redirect_url":null,"remote_ip":"%HOSTIP","remote_port":%HTTPPORT,"response_code":200,"scheme":"HTTP","size_download":445,"size_header":4019,"size_request":4019,"size_upload":0,"speed_download":13,"speed_upload":13,"ssl_verify_result":0,"time_appconnect":0.000013,"time_connect":0.000013,"time_namelookup":0.000013,"time_pretransfer":0.000013,"time_redirect":0.000013,"time_starttransfer":0.000013,"time_total":0.000013,"url":"http://%HOSTIP:%HTTPPORT/970","url_effective":"http://%HOSTIP:%HTTPPORT/970","urlnum":0,"curl_version":"curl-unit-test-fake-version"}
 </stdout>
 </verify>
 </testcase>