tool: improve --stderr handling
- freopen stderr with the user-specified file (--stderr file) instead of using a separate 'errors' stream. - In tool_setup.h override stdio.h's stderr macro as global variable tool_stderr. Both freopen and overriding the stderr macro are necessary because if the user-specified filename is "-" then stdout is assigned to tool_stderr and no freopen takes place. See the PR for more information. Ref: https://github.com/curl/curl/issues/10491 Closes https://github.com/curl/curl/pull/10673
This commit is contained in:
parent
395b9175b7
commit
2f17a9b654
@ -82,6 +82,7 @@ CURL_CFILES = \
|
|||||||
tool_paramhlp.c \
|
tool_paramhlp.c \
|
||||||
tool_parsecfg.c \
|
tool_parsecfg.c \
|
||||||
tool_progress.c \
|
tool_progress.c \
|
||||||
|
tool_stderr.c \
|
||||||
tool_strdup.c \
|
tool_strdup.c \
|
||||||
tool_setopt.c \
|
tool_setopt.c \
|
||||||
tool_sleep.c \
|
tool_sleep.c \
|
||||||
@ -126,6 +127,7 @@ CURL_HFILES = \
|
|||||||
tool_setopt.h \
|
tool_setopt.h \
|
||||||
tool_setup.h \
|
tool_setup.h \
|
||||||
tool_sleep.h \
|
tool_sleep.h \
|
||||||
|
tool_stderr.h \
|
||||||
tool_strdup.h \
|
tool_strdup.h \
|
||||||
tool_urlglob.h \
|
tool_urlglob.h \
|
||||||
tool_util.h \
|
tool_util.h \
|
||||||
|
|||||||
@ -48,7 +48,7 @@ int tool_debug_cb(CURL *handle, curl_infotype type,
|
|||||||
{
|
{
|
||||||
struct OperationConfig *operation = userdata;
|
struct OperationConfig *operation = userdata;
|
||||||
struct GlobalConfig *config = operation->global;
|
struct GlobalConfig *config = operation->global;
|
||||||
FILE *output = config->errors;
|
FILE *output = stderr;
|
||||||
const char *text;
|
const char *text;
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
char timebuf[20];
|
char timebuf[20];
|
||||||
@ -80,7 +80,7 @@ int tool_debug_cb(CURL *handle, curl_infotype type,
|
|||||||
config->trace_stream = stdout;
|
config->trace_stream = stdout;
|
||||||
else if(!strcmp("%", config->trace_dump))
|
else if(!strcmp("%", config->trace_dump))
|
||||||
/* Ok, this is somewhat hackish but we do it undocumented for now */
|
/* Ok, this is somewhat hackish but we do it undocumented for now */
|
||||||
config->trace_stream = config->errors; /* aka stderr */
|
config->trace_stream = stderr;
|
||||||
else {
|
else {
|
||||||
config->trace_stream = fopen(config->trace_dump, FOPEN_WRITETEXT);
|
config->trace_stream = fopen(config->trace_dump, FOPEN_WRITETEXT);
|
||||||
config->trace_fopened = TRUE;
|
config->trace_fopened = TRUE;
|
||||||
|
|||||||
@ -274,7 +274,7 @@ void progressbarinit(struct ProgressData *bar,
|
|||||||
else if(bar->width > MAX_BARLENGTH)
|
else if(bar->width > MAX_BARLENGTH)
|
||||||
bar->width = MAX_BARLENGTH;
|
bar->width = MAX_BARLENGTH;
|
||||||
|
|
||||||
bar->out = config->global->errors;
|
bar->out = stderr;
|
||||||
bar->tick = 150;
|
bar->tick = 150;
|
||||||
bar->barmove = 1;
|
bar->barmove = 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -302,8 +302,6 @@ struct GlobalConfig {
|
|||||||
bool silent; /* don't show messages, --silent given */
|
bool silent; /* don't show messages, --silent given */
|
||||||
bool noprogress; /* don't show progress bar */
|
bool noprogress; /* don't show progress bar */
|
||||||
bool isatty; /* Updated internally if output is a tty */
|
bool isatty; /* Updated internally if output is a tty */
|
||||||
FILE *errors; /* Error stream, defaults to stderr */
|
|
||||||
bool errors_fopened; /* Whether error stream isn't stderr */
|
|
||||||
char *trace_dump; /* file to dump the network trace to */
|
char *trace_dump; /* file to dump the network trace to */
|
||||||
FILE *trace_stream;
|
FILE *trace_stream;
|
||||||
bool trace_fopened;
|
bool trace_fopened;
|
||||||
|
|||||||
@ -417,8 +417,7 @@ static int read_field_headers(struct OperationConfig *config,
|
|||||||
if(hdrlen) {
|
if(hdrlen) {
|
||||||
hdrbuf[hdrlen] = '\0';
|
hdrbuf[hdrlen] = '\0';
|
||||||
if(slist_append(pheaders, hdrbuf)) {
|
if(slist_append(pheaders, hdrbuf)) {
|
||||||
fprintf(config->global->errors,
|
fprintf(stderr, "Out of memory for field headers!\n");
|
||||||
"Out of memory for field headers!\n");
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
hdrlen = 0;
|
hdrlen = 0;
|
||||||
@ -428,8 +427,8 @@ static int read_field_headers(struct OperationConfig *config,
|
|||||||
switch(c) {
|
switch(c) {
|
||||||
case EOF:
|
case EOF:
|
||||||
if(ferror(fp)) {
|
if(ferror(fp)) {
|
||||||
fprintf(config->global->errors,
|
fprintf(stderr, "Header file %s read error: %s\n", filename,
|
||||||
"Header file %s read error: %s\n", filename, strerror(errno));
|
strerror(errno));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return 0; /* Done. */
|
return 0; /* Done. */
|
||||||
@ -585,7 +584,7 @@ static int get_param_part(struct OperationConfig *config, char endchar,
|
|||||||
sep = *p;
|
sep = *p;
|
||||||
*endpos = '\0';
|
*endpos = '\0';
|
||||||
if(slist_append(&headers, hdr)) {
|
if(slist_append(&headers, hdr)) {
|
||||||
fprintf(config->global->errors, "Out of memory for field header!\n");
|
fprintf(stderr, "Out of memory for field header!\n");
|
||||||
curl_slist_free_all(headers);
|
curl_slist_free_all(headers);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -42,6 +42,7 @@
|
|||||||
#include "tool_parsecfg.h"
|
#include "tool_parsecfg.h"
|
||||||
#include "tool_main.h"
|
#include "tool_main.h"
|
||||||
#include "dynbuf.h"
|
#include "dynbuf.h"
|
||||||
|
#include "tool_stderr.h"
|
||||||
|
|
||||||
#include "memdebug.h" /* keep this as LAST include */
|
#include "memdebug.h" /* keep this as LAST include */
|
||||||
|
|
||||||
@ -1036,19 +1037,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'v': /* --stderr */
|
case 'v': /* --stderr */
|
||||||
if(strcmp(nextarg, "-")) {
|
tool_set_stderr_file(nextarg);
|
||||||
FILE *newfile = fopen(nextarg, FOPEN_WRITETEXT);
|
|
||||||
if(!newfile)
|
|
||||||
warnf(global, "Failed to open %s!\n", nextarg);
|
|
||||||
else {
|
|
||||||
if(global->errors_fopened)
|
|
||||||
fclose(global->errors);
|
|
||||||
global->errors = newfile;
|
|
||||||
global->errors_fopened = TRUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
global->errors = stdout;
|
|
||||||
break;
|
break;
|
||||||
case 'w': /* --interface */
|
case 'w': /* --interface */
|
||||||
/* interface */
|
/* interface */
|
||||||
@ -2567,9 +2556,9 @@ ParameterError parse_args(struct GlobalConfig *global, int argc,
|
|||||||
const char *reason = param2text(result);
|
const char *reason = param2text(result);
|
||||||
|
|
||||||
if(orig_opt && strcmp(":", orig_opt))
|
if(orig_opt && strcmp(":", orig_opt))
|
||||||
helpf(global->errors, "option %s: %s\n", orig_opt, reason);
|
helpf(stderr, "option %s: %s\n", orig_opt, reason);
|
||||||
else
|
else
|
||||||
helpf(global->errors, "%s\n", reason);
|
helpf(stderr, "%s\n", reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
curlx_unicodefree(orig_opt);
|
curlx_unicodefree(orig_opt);
|
||||||
|
|||||||
@ -53,6 +53,7 @@
|
|||||||
#include "tool_vms.h"
|
#include "tool_vms.h"
|
||||||
#include "tool_main.h"
|
#include "tool_main.h"
|
||||||
#include "tool_libinfo.h"
|
#include "tool_libinfo.h"
|
||||||
|
#include "tool_stderr.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is low-level hard-hacking memory leak tracking and similar. Using
|
* This is low-level hard-hacking memory leak tracking and similar. Using
|
||||||
@ -156,7 +157,6 @@ static CURLcode main_init(struct GlobalConfig *config)
|
|||||||
|
|
||||||
/* Initialise the global config */
|
/* Initialise the global config */
|
||||||
config->showerror = FALSE; /* show errors when silent */
|
config->showerror = FALSE; /* show errors when silent */
|
||||||
config->errors = stderr; /* Default errors to stderr */
|
|
||||||
config->styled_output = TRUE; /* enable detection */
|
config->styled_output = TRUE; /* enable detection */
|
||||||
config->parallel_max = PARALLEL_DEFAULT;
|
config->parallel_max = PARALLEL_DEFAULT;
|
||||||
|
|
||||||
@ -196,10 +196,6 @@ static void free_globalconfig(struct GlobalConfig *config)
|
|||||||
{
|
{
|
||||||
Curl_safefree(config->trace_dump);
|
Curl_safefree(config->trace_dump);
|
||||||
|
|
||||||
if(config->errors_fopened && config->errors)
|
|
||||||
fclose(config->errors);
|
|
||||||
config->errors = NULL;
|
|
||||||
|
|
||||||
if(config->trace_fopened && config->trace_stream)
|
if(config->trace_fopened && config->trace_stream)
|
||||||
fclose(config->trace_stream);
|
fclose(config->trace_stream);
|
||||||
config->trace_stream = NULL;
|
config->trace_stream = NULL;
|
||||||
@ -250,6 +246,8 @@ int main(int argc, char *argv[])
|
|||||||
struct GlobalConfig global;
|
struct GlobalConfig global;
|
||||||
memset(&global, 0, sizeof(global));
|
memset(&global, 0, sizeof(global));
|
||||||
|
|
||||||
|
tool_init_stderr();
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
/* Undocumented diagnostic option to list the full paths of all loaded
|
/* Undocumented diagnostic option to list the full paths of all loaded
|
||||||
modules. This is purposely pre-init. */
|
modules. This is purposely pre-init. */
|
||||||
|
|||||||
@ -54,7 +54,7 @@ static void voutf(struct GlobalConfig *config,
|
|||||||
|
|
||||||
ptr = print_buffer;
|
ptr = print_buffer;
|
||||||
while(len > 0) {
|
while(len > 0) {
|
||||||
fputs(prefix, config->errors);
|
fputs(prefix, stderr);
|
||||||
|
|
||||||
if(len > width) {
|
if(len > width) {
|
||||||
size_t cut = width-1;
|
size_t cut = width-1;
|
||||||
@ -67,13 +67,13 @@ static void voutf(struct GlobalConfig *config,
|
|||||||
max text width then! */
|
max text width then! */
|
||||||
cut = width-1;
|
cut = width-1;
|
||||||
|
|
||||||
(void)fwrite(ptr, cut + 1, 1, config->errors);
|
(void)fwrite(ptr, cut + 1, 1, stderr);
|
||||||
fputs("\n", config->errors);
|
fputs("\n", stderr);
|
||||||
ptr += cut + 1; /* skip the space too */
|
ptr += cut + 1; /* skip the space too */
|
||||||
len -= cut + 1;
|
len -= cut + 1;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
fputs(ptr, config->errors);
|
fputs(ptr, stderr);
|
||||||
len = 0;
|
len = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -306,7 +306,7 @@ static CURLcode pre_transfer(struct GlobalConfig *global,
|
|||||||
if((per->infd == -1) || fstat(per->infd, &fileinfo))
|
if((per->infd == -1) || fstat(per->infd, &fileinfo))
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
helpf(global->errors, "Can't open '%s'!\n", per->uploadfile);
|
helpf(stderr, "Can't open '%s'!\n", per->uploadfile);
|
||||||
if(per->infd != -1) {
|
if(per->infd != -1) {
|
||||||
close(per->infd);
|
close(per->infd);
|
||||||
per->infd = STDIN_FILENO;
|
per->infd = STDIN_FILENO;
|
||||||
@ -404,10 +404,10 @@ static CURLcode post_per_transfer(struct GlobalConfig *global,
|
|||||||
if(!config->synthetic_error && result &&
|
if(!config->synthetic_error && result &&
|
||||||
(!global->silent || global->showerror)) {
|
(!global->silent || global->showerror)) {
|
||||||
const char *msg = per->errorbuffer;
|
const char *msg = per->errorbuffer;
|
||||||
fprintf(global->errors, "curl: (%d) %s\n", result,
|
fprintf(stderr, "curl: (%d) %s\n", result,
|
||||||
(msg && msg[0]) ? msg : curl_easy_strerror(result));
|
(msg && msg[0]) ? msg : curl_easy_strerror(result));
|
||||||
if(result == CURLE_PEER_FAILED_VERIFICATION)
|
if(result == CURLE_PEER_FAILED_VERIFICATION)
|
||||||
fputs(CURL_CA_CERT_ERRORMSG, global->errors);
|
fputs(CURL_CA_CERT_ERRORMSG, stderr);
|
||||||
}
|
}
|
||||||
else if(config->failwithbody) {
|
else if(config->failwithbody) {
|
||||||
/* if HTTP response >= 400, return error */
|
/* if HTTP response >= 400, return error */
|
||||||
@ -415,7 +415,7 @@ static CURLcode post_per_transfer(struct GlobalConfig *global,
|
|||||||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);
|
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);
|
||||||
if(code >= 400) {
|
if(code >= 400) {
|
||||||
if(!global->silent || global->showerror)
|
if(!global->silent || global->showerror)
|
||||||
fprintf(global->errors,
|
fprintf(stderr,
|
||||||
"curl: (%d) The requested URL returned error: %ld\n",
|
"curl: (%d) The requested URL returned error: %ld\n",
|
||||||
CURLE_HTTP_RETURNED_ERROR, code);
|
CURLE_HTTP_RETURNED_ERROR, code);
|
||||||
result = CURLE_HTTP_RETURNED_ERROR;
|
result = CURLE_HTTP_RETURNED_ERROR;
|
||||||
@ -448,7 +448,7 @@ static CURLcode post_per_transfer(struct GlobalConfig *global,
|
|||||||
/* something went wrong in the writing process */
|
/* something went wrong in the writing process */
|
||||||
result = CURLE_WRITE_ERROR;
|
result = CURLE_WRITE_ERROR;
|
||||||
if(!global->silent || global->showerror)
|
if(!global->silent || global->showerror)
|
||||||
fprintf(global->errors, "curl: (%d) Failed writing body\n", result);
|
fprintf(stderr, "curl: (%d) Failed writing body\n", result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -589,8 +589,7 @@ static CURLcode post_per_transfer(struct GlobalConfig *global,
|
|||||||
/* We have written data to an output file, we truncate file
|
/* We have written data to an output file, we truncate file
|
||||||
*/
|
*/
|
||||||
if(!global->silent)
|
if(!global->silent)
|
||||||
fprintf(global->errors, "Throwing away %"
|
fprintf(stderr, "Throwing away %" CURL_FORMAT_CURL_OFF_T " bytes\n",
|
||||||
CURL_FORMAT_CURL_OFF_T " bytes\n",
|
|
||||||
outs->bytes);
|
outs->bytes);
|
||||||
fflush(outs->stream);
|
fflush(outs->stream);
|
||||||
/* truncate file at the position where we started appending */
|
/* truncate file at the position where we started appending */
|
||||||
@ -599,8 +598,7 @@ static CURLcode post_per_transfer(struct GlobalConfig *global,
|
|||||||
/* when truncate fails, we can't just append as then we'll
|
/* when truncate fails, we can't just append as then we'll
|
||||||
create something strange, bail out */
|
create something strange, bail out */
|
||||||
if(!global->silent || global->showerror)
|
if(!global->silent || global->showerror)
|
||||||
fprintf(global->errors,
|
fprintf(stderr, "curl: (23) Failed to truncate file\n");
|
||||||
"curl: (23) Failed to truncate file\n");
|
|
||||||
return CURLE_WRITE_ERROR;
|
return CURLE_WRITE_ERROR;
|
||||||
}
|
}
|
||||||
/* now seek to the end of the file, the position where we
|
/* now seek to the end of the file, the position where we
|
||||||
@ -615,8 +613,7 @@ static CURLcode post_per_transfer(struct GlobalConfig *global,
|
|||||||
#endif
|
#endif
|
||||||
if(rc) {
|
if(rc) {
|
||||||
if(!global->silent || global->showerror)
|
if(!global->silent || global->showerror)
|
||||||
fprintf(global->errors,
|
fprintf(stderr, "curl: (23) Failed seeking to end of file\n");
|
||||||
"curl: (23) Failed seeking to end of file\n");
|
|
||||||
return CURLE_WRITE_ERROR;
|
return CURLE_WRITE_ERROR;
|
||||||
}
|
}
|
||||||
outs->bytes = 0; /* clear for next round */
|
outs->bytes = 0; /* clear for next round */
|
||||||
@ -641,7 +638,7 @@ static CURLcode post_per_transfer(struct GlobalConfig *global,
|
|||||||
/* something went wrong in the writing process */
|
/* something went wrong in the writing process */
|
||||||
result = CURLE_WRITE_ERROR;
|
result = CURLE_WRITE_ERROR;
|
||||||
if(!global->silent || global->showerror)
|
if(!global->silent || global->showerror)
|
||||||
fprintf(global->errors, "curl: (%d) Failed writing body\n", result);
|
fprintf(stderr, "curl: (%d) Failed writing body\n", result);
|
||||||
}
|
}
|
||||||
if(result && config->rm_partial) {
|
if(result && config->rm_partial) {
|
||||||
notef(global, "Removing output file: %s\n", outs->filename);
|
notef(global, "Removing output file: %s\n", outs->filename);
|
||||||
@ -801,7 +798,7 @@ static CURLcode single_transfer(struct GlobalConfig *global,
|
|||||||
/* Unless explicitly shut off */
|
/* Unless explicitly shut off */
|
||||||
result = glob_url(&inglob, infiles, &state->infilenum,
|
result = glob_url(&inglob, infiles, &state->infilenum,
|
||||||
(!global->silent || global->showerror)?
|
(!global->silent || global->showerror)?
|
||||||
global->errors:NULL);
|
stderr:NULL);
|
||||||
if(result)
|
if(result)
|
||||||
break;
|
break;
|
||||||
config->state.inglob = inglob;
|
config->state.inglob = inglob;
|
||||||
@ -837,7 +834,7 @@ static CURLcode single_transfer(struct GlobalConfig *global,
|
|||||||
expressions and return total number of URLs in pattern set */
|
expressions and return total number of URLs in pattern set */
|
||||||
result = glob_url(&state->urls, urlnode->url, &state->urlnum,
|
result = glob_url(&state->urls, urlnode->url, &state->urlnum,
|
||||||
(!global->silent || global->showerror)?
|
(!global->silent || global->showerror)?
|
||||||
global->errors:NULL);
|
stderr:NULL);
|
||||||
if(result)
|
if(result)
|
||||||
break;
|
break;
|
||||||
urlnum = state->urlnum;
|
urlnum = state->urlnum;
|
||||||
@ -1096,7 +1093,7 @@ static CURLcode single_transfer(struct GlobalConfig *global,
|
|||||||
file output call */
|
file output call */
|
||||||
|
|
||||||
if(config->create_dirs) {
|
if(config->create_dirs) {
|
||||||
result = create_dir_hierarchy(per->outfile, global->errors);
|
result = create_dir_hierarchy(per->outfile, stderr);
|
||||||
/* create_dir_hierarchy shows error upon CURLE_WRITE_ERROR */
|
/* create_dir_hierarchy shows error upon CURLE_WRITE_ERROR */
|
||||||
if(result)
|
if(result)
|
||||||
break;
|
break;
|
||||||
@ -1240,9 +1237,6 @@ static CURLcode single_transfer(struct GlobalConfig *global,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!global->errors)
|
|
||||||
global->errors = stderr;
|
|
||||||
|
|
||||||
if((!per->outfile || !strcmp(per->outfile, "-")) &&
|
if((!per->outfile || !strcmp(per->outfile, "-")) &&
|
||||||
!config->use_ascii) {
|
!config->use_ascii) {
|
||||||
/* We get the output to stdout and we have not got the ASCII/text
|
/* We get the output to stdout and we have not got the ASCII/text
|
||||||
@ -1851,7 +1845,7 @@ static CURLcode single_transfer(struct GlobalConfig *global,
|
|||||||
my_setopt(curl, CURLOPT_TIMEVALUE_LARGE, config->condtime);
|
my_setopt(curl, CURLOPT_TIMEVALUE_LARGE, config->condtime);
|
||||||
my_setopt_str(curl, CURLOPT_CUSTOMREQUEST, config->customrequest);
|
my_setopt_str(curl, CURLOPT_CUSTOMREQUEST, config->customrequest);
|
||||||
customrequest_helper(config, config->httpreq, config->customrequest);
|
customrequest_helper(config, config->httpreq, config->customrequest);
|
||||||
my_setopt(curl, CURLOPT_STDERR, global->errors);
|
my_setopt(curl, CURLOPT_STDERR, stderr);
|
||||||
|
|
||||||
/* three new ones in libcurl 7.3: */
|
/* three new ones in libcurl 7.3: */
|
||||||
my_setopt_str(curl, CURLOPT_INTERFACE, config->iface);
|
my_setopt_str(curl, CURLOPT_INTERFACE, config->iface);
|
||||||
@ -2517,8 +2511,7 @@ static CURLcode transfer_per_config(struct GlobalConfig *global,
|
|||||||
|
|
||||||
/* Check we have a url */
|
/* Check we have a url */
|
||||||
if(!config->url_list || !config->url_list->url) {
|
if(!config->url_list || !config->url_list->url) {
|
||||||
helpf(global->errors, "(%d) no URL specified!\n",
|
helpf(stderr, "(%d) no URL specified!\n", CURLE_FAILED_INIT);
|
||||||
CURLE_FAILED_INIT);
|
|
||||||
return CURLE_FAILED_INIT;
|
return CURLE_FAILED_INIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2576,7 +2569,7 @@ static CURLcode transfer_per_config(struct GlobalConfig *global,
|
|||||||
if(!config->capath) {
|
if(!config->capath) {
|
||||||
curl_free(env);
|
curl_free(env);
|
||||||
curl_easy_cleanup(curltls);
|
curl_easy_cleanup(curltls);
|
||||||
helpf(global->errors, "out of memory\n");
|
helpf(stderr, "out of memory\n");
|
||||||
return CURLE_OUT_OF_MEMORY;
|
return CURLE_OUT_OF_MEMORY;
|
||||||
}
|
}
|
||||||
capath_from_env = true;
|
capath_from_env = true;
|
||||||
@ -2694,7 +2687,7 @@ CURLcode operate(struct GlobalConfig *global, int argc, argv_item_t argv[])
|
|||||||
|
|
||||||
/* If we had no arguments then make sure a url was specified in .curlrc */
|
/* If we had no arguments then make sure a url was specified in .curlrc */
|
||||||
if((argc < 2) && (!global->first->url_list)) {
|
if((argc < 2) && (!global->first->url_list)) {
|
||||||
helpf(global->errors, NULL);
|
helpf(stderr, NULL);
|
||||||
result = CURLE_FAILED_INIT;
|
result = CURLE_FAILED_INIT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -173,7 +173,7 @@ bool progress_meter(struct GlobalConfig *global,
|
|||||||
header = TRUE;
|
header = TRUE;
|
||||||
fputs("DL% UL% Dled Uled Xfers Live "
|
fputs("DL% UL% Dled Uled Xfers Live "
|
||||||
"Total Current Left Speed\n",
|
"Total Current Left Speed\n",
|
||||||
global->errors);
|
stderr);
|
||||||
}
|
}
|
||||||
if(final || (diff > 500)) {
|
if(final || (diff > 500)) {
|
||||||
char time_left[10];
|
char time_left[10];
|
||||||
@ -275,7 +275,7 @@ bool progress_meter(struct GlobalConfig *global,
|
|||||||
}
|
}
|
||||||
time2str(time_spent, spent);
|
time2str(time_spent, spent);
|
||||||
|
|
||||||
fprintf(global->errors,
|
fprintf(stderr,
|
||||||
"\r"
|
"\r"
|
||||||
"%-3s " /* percent downloaded */
|
"%-3s " /* percent downloaded */
|
||||||
"%-3s " /* percent uploaded */
|
"%-3s " /* percent uploaded */
|
||||||
|
|||||||
@ -37,6 +37,15 @@
|
|||||||
|
|
||||||
#include "curl_setup.h" /* from the lib directory */
|
#include "curl_setup.h" /* from the lib directory */
|
||||||
|
|
||||||
|
extern FILE *tool_stderr;
|
||||||
|
|
||||||
|
#if !defined(CURL_DO_NOT_OVERRIDE_STDERR) && !defined(UNITTESTS)
|
||||||
|
#ifdef stderr
|
||||||
|
#undef stderr
|
||||||
|
#endif
|
||||||
|
#define stderr tool_stderr
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* curl tool certainly uses libcurl's external interface.
|
* curl tool certainly uses libcurl's external interface.
|
||||||
*/
|
*/
|
||||||
|
|||||||
72
src/tool_stderr.c
Normal file
72
src/tool_stderr.c
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
/***************************************************************************
|
||||||
|
* _ _ ____ _
|
||||||
|
* Project ___| | | | _ \| |
|
||||||
|
* / __| | | | |_) | |
|
||||||
|
* | (__| |_| | _ <| |___
|
||||||
|
* \___|\___/|_| \_\_____|
|
||||||
|
*
|
||||||
|
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||||
|
*
|
||||||
|
* This software is licensed as described in the file COPYING, which
|
||||||
|
* you should have received as part of this distribution. The terms
|
||||||
|
* are also available at https://curl.se/docs/copyright.html.
|
||||||
|
*
|
||||||
|
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||||
|
* copies of the Software, and permit persons to whom the Software is
|
||||||
|
* furnished to do so, under the terms of the COPYING file.
|
||||||
|
*
|
||||||
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||||
|
* KIND, either express or implied.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: curl
|
||||||
|
*
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
/* In this file, stdio.h's stderr macro is not overridden. */
|
||||||
|
#define CURL_DO_NOT_OVERRIDE_STDERR
|
||||||
|
|
||||||
|
#include "tool_setup.h"
|
||||||
|
|
||||||
|
#include "tool_stderr.h"
|
||||||
|
|
||||||
|
#include "memdebug.h" /* keep this as LAST include */
|
||||||
|
|
||||||
|
/* In other tool files stderr is defined as tool_stderr by tool_setup.h */
|
||||||
|
FILE *tool_stderr;
|
||||||
|
|
||||||
|
void tool_init_stderr(void)
|
||||||
|
{
|
||||||
|
tool_stderr = stderr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tool_set_stderr_file(char *filename)
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
if(!filename)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(!strcmp(filename, "-")) {
|
||||||
|
tool_stderr = stdout;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* precheck that filename is accessible to lessen the chance that the
|
||||||
|
subsequent freopen will fail. */
|
||||||
|
fp = fopen(filename, FOPEN_WRITETEXT);
|
||||||
|
if(!fp) {
|
||||||
|
fprintf(tool_stderr, "Warning: Failed to open %s!\n", filename);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
/* freopen the actual stderr (stdio.h stderr) instead of tool_stderr since
|
||||||
|
the latter may be set to stdout. */
|
||||||
|
fp = freopen(filename, FOPEN_WRITETEXT, stderr);
|
||||||
|
if(!fp) {
|
||||||
|
/* stderr may have been closed by freopen. there is nothing to be done. */
|
||||||
|
DEBUGASSERT(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tool_stderr = stderr;
|
||||||
|
}
|
||||||
31
src/tool_stderr.h
Normal file
31
src/tool_stderr.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#ifndef HEADER_CURL_TOOL_STDERR_H
|
||||||
|
#define HEADER_CURL_TOOL_STDERR_H
|
||||||
|
/***************************************************************************
|
||||||
|
* _ _ ____ _
|
||||||
|
* Project ___| | | | _ \| |
|
||||||
|
* / __| | | | |_) | |
|
||||||
|
* | (__| |_| | _ <| |___
|
||||||
|
* \___|\___/|_| \_\_____|
|
||||||
|
*
|
||||||
|
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||||
|
*
|
||||||
|
* This software is licensed as described in the file COPYING, which
|
||||||
|
* you should have received as part of this distribution. The terms
|
||||||
|
* are also available at https://curl.se/docs/copyright.html.
|
||||||
|
*
|
||||||
|
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||||
|
* copies of the Software, and permit persons to whom the Software is
|
||||||
|
* furnished to do so, under the terms of the COPYING file.
|
||||||
|
*
|
||||||
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||||
|
* KIND, either express or implied.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: curl
|
||||||
|
*
|
||||||
|
***************************************************************************/
|
||||||
|
#include "tool_setup.h"
|
||||||
|
|
||||||
|
void tool_init_stderr(void);
|
||||||
|
void tool_set_stderr_file(char *filename);
|
||||||
|
|
||||||
|
#endif /* HEADER_CURL_TOOL_STDERR_H */
|
||||||
@ -427,7 +427,7 @@ void ourWriteOut(struct OperationConfig *config, struct per_transfer *per,
|
|||||||
stream = stdout;
|
stream = stdout;
|
||||||
break;
|
break;
|
||||||
case VAR_STDERR:
|
case VAR_STDERR:
|
||||||
stream = config->global->errors;
|
stream = stderr;
|
||||||
break;
|
break;
|
||||||
case VAR_JSON:
|
case VAR_JSON:
|
||||||
ourWriteOutJSON(stream, variables, per, per_result);
|
ourWriteOutJSON(stream, variables, per, per_result);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user