Browse Source

curl: add variables to --write-out

In particular, these ones can help a user to create its own error
message when one or transfers fail.

writeout: add 'onerror', 'url', 'urlnum', 'exitcode', 'errormsg'

onerror - lets a user only show the rest on non-zero exit codes

url - the input URL used for this transfer

urlnum - the numerical URL counter (0 indexed) for this transfer

exitcode - the numerical exit code for the transfer

errormsg - obvious

Reported-by: Earnestly on github
Fixes #6199
Closes #6207
Daniel Stenberg 3 years ago
parent
commit
7a90ddf88f
7 changed files with 128 additions and 104 deletions
  1. 16 0
      docs/cmdline-opts/write-out.d
  2. 2 1
      src/tool_operate.c
  3. 1 0
      src/tool_operate.h
  4. 2 0
      src/tool_paramhlp.c
  5. 1 0
      src/tool_sdecls.h
  6. 72 75
      src/tool_writeout.c
  7. 34 28
      src/tool_writeout.h

+ 16 - 0
docs/cmdline-opts/write-out.d

@@ -29,6 +29,12 @@ The variables available are:
 .B content_type
 .B content_type
 The Content-Type of the requested document, if there was any.
 The Content-Type of the requested document, if there was any.
 .TP
 .TP
+.B errormsg
+The error message. (Added in 7.75.0)
+.TP
+.B exitcode
+The numerical exitcode. (Added in 7.75.0)
+.TP
 .B filename_effective
 .B filename_effective
 The ultimate filename that curl writes out to. This is only meaningful if curl
 The ultimate filename that curl writes out to. This is only meaningful if curl
 is told to write to a file with the --remote-name or --output
 is told to write to a file with the --remote-name or --output
@@ -74,6 +80,10 @@ The number of response headers in the most recent request (restarted at each
 .B num_redirects
 .B num_redirects
 Number of redirects that were followed in the request. (Added in 7.12.3)
 Number of redirects that were followed in the request. (Added in 7.12.3)
 .TP
 .TP
+.B onerror
+The rest of the output is only shown if the transfer returned a non-zero error
+(Added in 7.75.0)
+.TP
 .B proxy_ssl_verify_result
 .B proxy_ssl_verify_result
 The result of the HTTPS proxy's SSL peer certificate verification that was
 The result of the HTTPS proxy's SSL peer certificate verification that was
 requested. 0 means the verification was successful. (Added in 7.52.0)
 requested. 0 means the verification was successful. (Added in 7.52.0)
@@ -161,6 +171,12 @@ server needed to calculate the result.
 .B time_total
 .B time_total
 The total time, in seconds, that the full operation lasted.
 The total time, in seconds, that the full operation lasted.
 .TP
 .TP
+.B url
+The URL that was fetched. (Added in 7.75.0)
+.TP
+.B urlnum
+The URL index number of this transfer, 0-indexed. (Added in 7.75.0)
+.TP
 .B url_effective
 .B url_effective
 The URL that was fetched last. This is most meaningful if you've told curl
 The URL that was fetched last. This is most meaningful if you've told curl
 to follow location: headers.
 to follow location: headers.

+ 2 - 1
src/tool_operate.c

@@ -628,7 +628,7 @@ static CURLcode post_per_transfer(struct GlobalConfig *global,
     fputs("\n", per->progressbar.out);
     fputs("\n", per->progressbar.out);
 
 
   if(config->writeout)
   if(config->writeout)
-    ourWriteOut(per->curl, per, config->writeout);
+    ourWriteOut(per->curl, per, config->writeout, result);
 
 
   /* Close the outs file */
   /* Close the outs file */
   if(outs->fopened && outs->stream) {
   if(outs->fopened && outs->stream) {
@@ -873,6 +873,7 @@ static CURLcode single_transfer(struct GlobalConfig *global,
         *added = TRUE;
         *added = TRUE;
         per->config = config;
         per->config = config;
         per->curl = curl;
         per->curl = curl;
+        per->urlnum = urlnode->num;
 
 
         /* default headers output stream is stdout */
         /* default headers output stream is stdout */
         heads = &per->heads;
         heads = &per->heads;

+ 1 - 0
src/tool_operate.h

@@ -41,6 +41,7 @@ struct per_transfer {
   struct metalinkfile *mlfile;
   struct metalinkfile *mlfile;
   struct metalink_resource *mlres;
   struct metalink_resource *mlres;
   char *this_url;
   char *this_url;
+  unsigned int urlnum; /* the index of the given URL */
   char *outfile;
   char *outfile;
   bool infdopen; /* TRUE if infd needs closing */
   bool infdopen; /* TRUE if infd needs closing */
   int infd;
   int infd;

+ 2 - 0
src/tool_paramhlp.c

@@ -40,6 +40,7 @@
 
 
 struct getout *new_getout(struct OperationConfig *config)
 struct getout *new_getout(struct OperationConfig *config)
 {
 {
+  static int outnum = 0;
   struct getout *node = calloc(1, sizeof(struct getout));
   struct getout *node = calloc(1, sizeof(struct getout));
   struct getout *last = config->url_last;
   struct getout *last = config->url_last;
   if(node) {
   if(node) {
@@ -53,6 +54,7 @@ struct getout *new_getout(struct OperationConfig *config)
     config->url_last = node;
     config->url_last = node;
 
 
     node->flags = config->default_node_flags;
     node->flags = config->default_node_flags;
+    node->num = outnum++;
   }
   }
   return node;
   return node;
 }
 }

+ 1 - 0
src/tool_sdecls.h

@@ -105,6 +105,7 @@ struct getout {
   char          *outfile;   /* where to store the output */
   char          *outfile;   /* where to store the output */
   char          *infile;    /* file to upload, if GETOUT_UPLOAD is set */
   char          *infile;    /* file to upload, if GETOUT_UPLOAD is set */
   int            flags;     /* options - composed of GETOUT_* bits */
   int            flags;     /* options - composed of GETOUT_* bits */
+  int            num;       /* which URL number in an invocation */
 };
 };
 
 
 #define GETOUT_OUTFILE    (1<<0)  /* set when outfile is deemed done */
 #define GETOUT_OUTFILE    (1<<0)  /* set when outfile is deemed done */

+ 72 - 75
src/tool_writeout.c

@@ -30,80 +30,58 @@
 #include "memdebug.h" /* keep this as LAST include */
 #include "memdebug.h" /* keep this as LAST include */
 
 
 static const struct writeoutvar variables[] = {
 static const struct writeoutvar variables[] = {
-  {"url_effective", VAR_EFFECTIVE_URL, 0,
-   CURLINFO_EFFECTIVE_URL, JSON_STRING},
-  {"method", VAR_EFFECTIVE_METHOD, 0,
-   CURLINFO_EFFECTIVE_METHOD, JSON_STRING},
-  {"http_code", VAR_HTTP_CODE, 0,
-   CURLINFO_RESPONSE_CODE, JSON_LONG},
-  {"response_code", VAR_HTTP_CODE, 0,
-   CURLINFO_RESPONSE_CODE, JSON_LONG},
-  {"num_headers", VAR_NUM_HEADERS, 0,
-   0, JSON_LONG},
-  {"http_connect", VAR_HTTP_CODE_PROXY, 0,
-   CURLINFO_HTTP_CONNECTCODE, JSON_LONG},
-  {"time_total", VAR_TOTAL_TIME, 0,
-   CURLINFO_TOTAL_TIME_T, JSON_TIME},
-  {"time_namelookup", VAR_NAMELOOKUP_TIME, 0,
-   CURLINFO_NAMELOOKUP_TIME_T, JSON_TIME},
-  {"time_connect", VAR_CONNECT_TIME, 0,
-   CURLINFO_CONNECT_TIME_T, JSON_TIME},
-  {"time_appconnect", VAR_APPCONNECT_TIME, 0,
-   CURLINFO_APPCONNECT_TIME_T, JSON_TIME},
-  {"time_pretransfer", VAR_PRETRANSFER_TIME, 0,
-   CURLINFO_PRETRANSFER_TIME_T, JSON_TIME},
-  {"time_starttransfer", VAR_STARTTRANSFER_TIME, 0,
-   CURLINFO_STARTTRANSFER_TIME_T, JSON_TIME},
-  {"size_header", VAR_HEADER_SIZE, 0,
-   CURLINFO_HEADER_SIZE, JSON_LONG},
-  {"size_request", VAR_REQUEST_SIZE, 0,
-   CURLINFO_REQUEST_SIZE, JSON_LONG},
-  {"size_download", VAR_SIZE_DOWNLOAD, 0,
-   CURLINFO_SIZE_DOWNLOAD_T, JSON_OFFSET},
-  {"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},
-  {"content_type", VAR_CONTENT_TYPE, 0,
-   CURLINFO_CONTENT_TYPE, JSON_STRING},
-  {"num_connects", VAR_NUM_CONNECTS, 0,
-   CURLINFO_NUM_CONNECTS, JSON_LONG},
-  {"time_redirect", VAR_REDIRECT_TIME, 0,
-   CURLINFO_REDIRECT_TIME_T, JSON_TIME},
-  {"num_redirects", VAR_REDIRECT_COUNT, 0,
-   CURLINFO_REDIRECT_COUNT, JSON_LONG},
-  {"ftp_entry_path", VAR_FTP_ENTRY_PATH, 0,
-   CURLINFO_FTP_ENTRY_PATH, JSON_STRING},
-  {"redirect_url", VAR_REDIRECT_URL, 0,
-   CURLINFO_REDIRECT_URL, JSON_STRING},
-  {"ssl_verify_result", VAR_SSL_VERIFY_RESULT, 0,
-   CURLINFO_SSL_VERIFYRESULT, JSON_LONG},
+  {"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,
   {"proxy_ssl_verify_result", VAR_PROXY_SSL_VERIFY_RESULT, 0,
    CURLINFO_PROXY_SSL_VERIFYRESULT, JSON_LONG},
    CURLINFO_PROXY_SSL_VERIFYRESULT, JSON_LONG},
-  {"filename_effective", VAR_EFFECTIVE_FILENAME, 0,
-   0, JSON_FILENAME},
-  {"remote_ip", VAR_PRIMARY_IP, 0,
-   CURLINFO_PRIMARY_IP, JSON_STRING},
-  {"remote_port", VAR_PRIMARY_PORT, 0,
-   CURLINFO_PRIMARY_PORT, JSON_LONG},
-  {"local_ip", VAR_LOCAL_IP, 0,
-   CURLINFO_LOCAL_IP, JSON_STRING},
-  {"local_port", VAR_LOCAL_PORT, 0,
-   CURLINFO_LOCAL_PORT, JSON_LONG},
-  {"http_version", VAR_HTTP_VERSION, 0,
-   CURLINFO_HTTP_VERSION, JSON_VERSION},
-  {"scheme", VAR_SCHEME, 0,
-   CURLINFO_SCHEME, JSON_STRING},
-  {"stdout", VAR_STDOUT, 1,
-   0, JSON_NONE},
-  {"stderr", VAR_STDERR, 1,
-   0, JSON_NONE},
-  {"json", VAR_JSON, 1,
-   0, JSON_NONE},
-  {NULL, VAR_NONE, 1,
-   0, JSON_NONE}
+  {"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}
 };
 };
 
 
 static void us2sec(FILE *stream, curl_off_t us)
 static void us2sec(FILE *stream, curl_off_t us)
@@ -114,15 +92,17 @@ static void us2sec(FILE *stream, curl_off_t us)
           secs, us);
           secs, us);
 }
 }
 
 
-void ourWriteOut(CURL *curl, struct per_transfer *per, const char *writeinfo)
+void ourWriteOut(CURL *curl, struct per_transfer *per, const char *writeinfo,
+                 CURLcode result)
 {
 {
   FILE *stream = stdout;
   FILE *stream = stdout;
   const char *ptr = writeinfo;
   const char *ptr = writeinfo;
   char *stringp = NULL;
   char *stringp = NULL;
   long longinfo;
   long longinfo;
   curl_off_t offinfo;
   curl_off_t offinfo;
+  bool done = FALSE;
 
 
-  while(ptr && *ptr) {
+  while(ptr && *ptr && !done) {
     if('%' == *ptr && ptr[1]) {
     if('%' == *ptr && ptr[1]) {
       if('%' == ptr[1]) {
       if('%' == ptr[1]) {
         /* an escaped %-letter */
         /* an escaped %-letter */
@@ -148,6 +128,24 @@ void ourWriteOut(CURL *curl, struct per_transfer *per, const char *writeinfo)
             if(curl_strequal(ptr, variables[i].name)) {
             if(curl_strequal(ptr, variables[i].name)) {
               match = TRUE;
               match = TRUE;
               switch(variables[i].id) {
               switch(variables[i].id) {
+              case VAR_ONERROR:
+                if(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:
+                fputs(per->errorbuffer[0] ? per->errorbuffer :
+                      curl_easy_strerror(result), stream);
+                break;
+              case VAR_INPUT_URL:
+                fputs(per->this_url, stream);
+                break;
+              case VAR_URLNUM:
+                fprintf(stream, "%u", per->urlnum);
+                break;
               case VAR_EFFECTIVE_URL:
               case VAR_EFFECTIVE_URL:
                 if((CURLE_OK ==
                 if((CURLE_OK ==
                     curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &stringp))
                     curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &stringp))
@@ -392,5 +390,4 @@ void ourWriteOut(CURL *curl, struct per_transfer *per, const char *writeinfo)
       ptr++;
       ptr++;
     }
     }
   }
   }
-
 }
 }

+ 34 - 28
src/tool_writeout.h

@@ -26,41 +26,46 @@
 
 
 typedef enum {
 typedef enum {
   VAR_NONE,       /* must be the first */
   VAR_NONE,       /* must be the first */
-  VAR_TOTAL_TIME,
-  VAR_NAMELOOKUP_TIME,
-  VAR_CONNECT_TIME,
   VAR_APPCONNECT_TIME,
   VAR_APPCONNECT_TIME,
-  VAR_PRETRANSFER_TIME,
-  VAR_STARTTRANSFER_TIME,
-  VAR_SIZE_DOWNLOAD,
-  VAR_SIZE_UPLOAD,
-  VAR_SPEED_DOWNLOAD,
-  VAR_SPEED_UPLOAD,
-  VAR_HTTP_CODE,
-  VAR_HTTP_CODE_PROXY,
-  VAR_HEADER_SIZE,
-  VAR_NUM_HEADERS,
-  VAR_REQUEST_SIZE,
+  VAR_CONNECT_TIME,
+  VAR_CONTENT_TYPE,
+  VAR_EFFECTIVE_FILENAME,
   VAR_EFFECTIVE_METHOD,
   VAR_EFFECTIVE_METHOD,
   VAR_EFFECTIVE_URL,
   VAR_EFFECTIVE_URL,
-  VAR_CONTENT_TYPE,
-  VAR_NUM_CONNECTS,
-  VAR_REDIRECT_TIME,
-  VAR_REDIRECT_COUNT,
+  VAR_ERRORMSG,
+  VAR_EXITCODE,
   VAR_FTP_ENTRY_PATH,
   VAR_FTP_ENTRY_PATH,
-  VAR_REDIRECT_URL,
-  VAR_SSL_VERIFY_RESULT,
-  VAR_PROXY_SSL_VERIFY_RESULT,
-  VAR_EFFECTIVE_FILENAME,
-  VAR_PRIMARY_IP,
-  VAR_PRIMARY_PORT,
+  VAR_HEADER_SIZE,
+  VAR_HTTP_CODE,
+  VAR_HTTP_CODE_PROXY,
+  VAR_HTTP_VERSION,
+  VAR_INPUT_URL,
+  VAR_JSON,
   VAR_LOCAL_IP,
   VAR_LOCAL_IP,
   VAR_LOCAL_PORT,
   VAR_LOCAL_PORT,
-  VAR_HTTP_VERSION,
+  VAR_NAMELOOKUP_TIME,
+  VAR_NUM_CONNECTS,
+  VAR_NUM_HEADERS,
+  VAR_ONERROR,
+  VAR_PRETRANSFER_TIME,
+  VAR_PRIMARY_IP,
+  VAR_PRIMARY_PORT,
+  VAR_PROXY_SSL_VERIFY_RESULT,
+  VAR_REDIRECT_COUNT,
+  VAR_REDIRECT_TIME,
+  VAR_REDIRECT_URL,
+  VAR_REQUEST_SIZE,
   VAR_SCHEME,
   VAR_SCHEME,
-  VAR_STDOUT,
+  VAR_SIZE_DOWNLOAD,
+  VAR_SIZE_UPLOAD,
+  VAR_SPEED_DOWNLOAD,
+  VAR_SPEED_UPLOAD,
+  VAR_SSL_VERIFY_RESULT,
+  VAR_STARTTRANSFER_TIME,
   VAR_STDERR,
   VAR_STDERR,
-  VAR_JSON,
+  VAR_STDOUT,
+  VAR_TOTAL_TIME,
+  VAR_URLNUM,
   VAR_NUM_OF_VARS /* must be the last */
   VAR_NUM_OF_VARS /* must be the last */
 } writeoutid;
 } writeoutid;
 
 
@@ -82,6 +87,7 @@ struct writeoutvar {
   jsontype jsontype;
   jsontype jsontype;
 };
 };
 
 
-void ourWriteOut(CURL *curl, struct per_transfer *per, const char *writeinfo);
+void ourWriteOut(CURL *curl, struct per_transfer *per, const char *writeinfo,
+                 CURLcode exitcode);
 
 
 #endif /* HEADER_CURL_TOOL_WRITEOUT_H */
 #endif /* HEADER_CURL_TOOL_WRITEOUT_H */