Skip to content

Commit d357341

Browse files
jaybch
authored andcommitted
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: curl#10491 Closes curl#10673
1 parent d5d63f3 commit d357341

14 files changed

+151
-60
lines changed

src/Makefile.inc

+2
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ CURL_CFILES = \
8282
tool_paramhlp.c \
8383
tool_parsecfg.c \
8484
tool_progress.c \
85+
tool_stderr.c \
8586
tool_strdup.c \
8687
tool_setopt.c \
8788
tool_sleep.c \
@@ -126,6 +127,7 @@ CURL_HFILES = \
126127
tool_setopt.h \
127128
tool_setup.h \
128129
tool_sleep.h \
130+
tool_stderr.h \
129131
tool_strdup.h \
130132
tool_urlglob.h \
131133
tool_util.h \

src/tool_cb_dbg.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ int tool_debug_cb(CURL *handle, curl_infotype type,
4848
{
4949
struct OperationConfig *operation = userdata;
5050
struct GlobalConfig *config = operation->global;
51-
FILE *output = config->errors;
51+
FILE *output = stderr;
5252
const char *text;
5353
struct timeval tv;
5454
char timebuf[20];
@@ -80,7 +80,7 @@ int tool_debug_cb(CURL *handle, curl_infotype type,
8080
config->trace_stream = stdout;
8181
else if(!strcmp("%", config->trace_dump))
8282
/* Ok, this is somewhat hackish but we do it undocumented for now */
83-
config->trace_stream = config->errors; /* aka stderr */
83+
config->trace_stream = stderr;
8484
else {
8585
config->trace_stream = fopen(config->trace_dump, FOPEN_WRITETEXT);
8686
config->trace_fopened = TRUE;

src/tool_cb_prg.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ void progressbarinit(struct ProgressData *bar,
274274
else if(bar->width > MAX_BARLENGTH)
275275
bar->width = MAX_BARLENGTH;
276276

277-
bar->out = config->global->errors;
277+
bar->out = stderr;
278278
bar->tick = 150;
279279
bar->barmove = 1;
280280
}

src/tool_cfgable.h

-2
Original file line numberDiff line numberDiff line change
@@ -302,8 +302,6 @@ struct GlobalConfig {
302302
bool silent; /* don't show messages, --silent given */
303303
bool noprogress; /* don't show progress bar */
304304
bool isatty; /* Updated internally if output is a tty */
305-
FILE *errors; /* Error stream, defaults to stderr */
306-
bool errors_fopened; /* Whether error stream isn't stderr */
307305
char *trace_dump; /* file to dump the network trace to */
308306
FILE *trace_stream;
309307
bool trace_fopened;

src/tool_formparse.c

+4-5
Original file line numberDiff line numberDiff line change
@@ -417,8 +417,7 @@ static int read_field_headers(struct OperationConfig *config,
417417
if(hdrlen) {
418418
hdrbuf[hdrlen] = '\0';
419419
if(slist_append(pheaders, hdrbuf)) {
420-
fprintf(config->global->errors,
421-
"Out of memory for field headers!\n");
420+
fprintf(stderr, "Out of memory for field headers!\n");
422421
return -1;
423422
}
424423
hdrlen = 0;
@@ -428,8 +427,8 @@ static int read_field_headers(struct OperationConfig *config,
428427
switch(c) {
429428
case EOF:
430429
if(ferror(fp)) {
431-
fprintf(config->global->errors,
432-
"Header file %s read error: %s\n", filename, strerror(errno));
430+
fprintf(stderr, "Header file %s read error: %s\n", filename,
431+
strerror(errno));
433432
return -1;
434433
}
435434
return 0; /* Done. */
@@ -585,7 +584,7 @@ static int get_param_part(struct OperationConfig *config, char endchar,
585584
sep = *p;
586585
*endpos = '\0';
587586
if(slist_append(&headers, hdr)) {
588-
fprintf(config->global->errors, "Out of memory for field header!\n");
587+
fprintf(stderr, "Out of memory for field header!\n");
589588
curl_slist_free_all(headers);
590589
return -1;
591590
}

src/tool_getparam.c

+4-15
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#include "tool_parsecfg.h"
4343
#include "tool_main.h"
4444
#include "dynbuf.h"
45+
#include "tool_stderr.h"
4546

4647
#include "memdebug.h" /* keep this as LAST include */
4748

@@ -1036,19 +1037,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
10361037
break;
10371038

10381039
case 'v': /* --stderr */
1039-
if(strcmp(nextarg, "-")) {
1040-
FILE *newfile = fopen(nextarg, FOPEN_WRITETEXT);
1041-
if(!newfile)
1042-
warnf(global, "Failed to open %s!\n", nextarg);
1043-
else {
1044-
if(global->errors_fopened)
1045-
fclose(global->errors);
1046-
global->errors = newfile;
1047-
global->errors_fopened = TRUE;
1048-
}
1049-
}
1050-
else
1051-
global->errors = stdout;
1040+
tool_set_stderr_file(nextarg);
10521041
break;
10531042
case 'w': /* --interface */
10541043
/* interface */
@@ -2567,9 +2556,9 @@ ParameterError parse_args(struct GlobalConfig *global, int argc,
25672556
const char *reason = param2text(result);
25682557

25692558
if(orig_opt && strcmp(":", orig_opt))
2570-
helpf(global->errors, "option %s: %s\n", orig_opt, reason);
2559+
helpf(stderr, "option %s: %s\n", orig_opt, reason);
25712560
else
2572-
helpf(global->errors, "%s\n", reason);
2561+
helpf(stderr, "%s\n", reason);
25732562
}
25742563

25752564
curlx_unicodefree(orig_opt);

src/tool_main.c

+3-5
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
#include "tool_vms.h"
5454
#include "tool_main.h"
5555
#include "tool_libinfo.h"
56+
#include "tool_stderr.h"
5657

5758
/*
5859
* This is low-level hard-hacking memory leak tracking and similar. Using
@@ -156,7 +157,6 @@ static CURLcode main_init(struct GlobalConfig *config)
156157

157158
/* Initialise the global config */
158159
config->showerror = FALSE; /* show errors when silent */
159-
config->errors = stderr; /* Default errors to stderr */
160160
config->styled_output = TRUE; /* enable detection */
161161
config->parallel_max = PARALLEL_DEFAULT;
162162

@@ -196,10 +196,6 @@ static void free_globalconfig(struct GlobalConfig *config)
196196
{
197197
Curl_safefree(config->trace_dump);
198198

199-
if(config->errors_fopened && config->errors)
200-
fclose(config->errors);
201-
config->errors = NULL;
202-
203199
if(config->trace_fopened && config->trace_stream)
204200
fclose(config->trace_stream);
205201
config->trace_stream = NULL;
@@ -250,6 +246,8 @@ int main(int argc, char *argv[])
250246
struct GlobalConfig global;
251247
memset(&global, 0, sizeof(global));
252248

249+
tool_init_stderr();
250+
253251
#ifdef WIN32
254252
/* Undocumented diagnostic option to list the full paths of all loaded
255253
modules. This is purposely pre-init. */

src/tool_msgs.c

+4-4
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ static void voutf(struct GlobalConfig *config,
5454

5555
ptr = print_buffer;
5656
while(len > 0) {
57-
fputs(prefix, config->errors);
57+
fputs(prefix, stderr);
5858

5959
if(len > width) {
6060
size_t cut = width-1;
@@ -67,13 +67,13 @@ static void voutf(struct GlobalConfig *config,
6767
max text width then! */
6868
cut = width-1;
6969

70-
(void)fwrite(ptr, cut + 1, 1, config->errors);
71-
fputs("\n", config->errors);
70+
(void)fwrite(ptr, cut + 1, 1, stderr);
71+
fputs("\n", stderr);
7272
ptr += cut + 1; /* skip the space too */
7373
len -= cut + 1;
7474
}
7575
else {
76-
fputs(ptr, config->errors);
76+
fputs(ptr, stderr);
7777
len = 0;
7878
}
7979
}

src/tool_operate.c

+16-23
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ static CURLcode pre_transfer(struct GlobalConfig *global,
306306
if((per->infd == -1) || fstat(per->infd, &fileinfo))
307307
#endif
308308
{
309-
helpf(global->errors, "Can't open '%s'!\n", per->uploadfile);
309+
helpf(stderr, "Can't open '%s'!\n", per->uploadfile);
310310
if(per->infd != -1) {
311311
close(per->infd);
312312
per->infd = STDIN_FILENO;
@@ -404,18 +404,18 @@ static CURLcode post_per_transfer(struct GlobalConfig *global,
404404
if(!config->synthetic_error && result &&
405405
(!global->silent || global->showerror)) {
406406
const char *msg = per->errorbuffer;
407-
fprintf(global->errors, "curl: (%d) %s\n", result,
407+
fprintf(stderr, "curl: (%d) %s\n", result,
408408
(msg && msg[0]) ? msg : curl_easy_strerror(result));
409409
if(result == CURLE_PEER_FAILED_VERIFICATION)
410-
fputs(CURL_CA_CERT_ERRORMSG, global->errors);
410+
fputs(CURL_CA_CERT_ERRORMSG, stderr);
411411
}
412412
else if(config->failwithbody) {
413413
/* if HTTP response >= 400, return error */
414414
long code = 0;
415415
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);
416416
if(code >= 400) {
417417
if(!global->silent || global->showerror)
418-
fprintf(global->errors,
418+
fprintf(stderr,
419419
"curl: (%d) The requested URL returned error: %ld\n",
420420
CURLE_HTTP_RETURNED_ERROR, code);
421421
result = CURLE_HTTP_RETURNED_ERROR;
@@ -448,7 +448,7 @@ static CURLcode post_per_transfer(struct GlobalConfig *global,
448448
/* something went wrong in the writing process */
449449
result = CURLE_WRITE_ERROR;
450450
if(!global->silent || global->showerror)
451-
fprintf(global->errors, "curl: (%d) Failed writing body\n", result);
451+
fprintf(stderr, "curl: (%d) Failed writing body\n", result);
452452
}
453453
}
454454

@@ -589,8 +589,7 @@ static CURLcode post_per_transfer(struct GlobalConfig *global,
589589
/* We have written data to an output file, we truncate file
590590
*/
591591
if(!global->silent)
592-
fprintf(global->errors, "Throwing away %"
593-
CURL_FORMAT_CURL_OFF_T " bytes\n",
592+
fprintf(stderr, "Throwing away %" CURL_FORMAT_CURL_OFF_T " bytes\n",
594593
outs->bytes);
595594
fflush(outs->stream);
596595
/* truncate file at the position where we started appending */
@@ -599,8 +598,7 @@ static CURLcode post_per_transfer(struct GlobalConfig *global,
599598
/* when truncate fails, we can't just append as then we'll
600599
create something strange, bail out */
601600
if(!global->silent || global->showerror)
602-
fprintf(global->errors,
603-
"curl: (23) Failed to truncate file\n");
601+
fprintf(stderr, "curl: (23) Failed to truncate file\n");
604602
return CURLE_WRITE_ERROR;
605603
}
606604
/* now seek to the end of the file, the position where we
@@ -615,8 +613,7 @@ static CURLcode post_per_transfer(struct GlobalConfig *global,
615613
#endif
616614
if(rc) {
617615
if(!global->silent || global->showerror)
618-
fprintf(global->errors,
619-
"curl: (23) Failed seeking to end of file\n");
616+
fprintf(stderr, "curl: (23) Failed seeking to end of file\n");
620617
return CURLE_WRITE_ERROR;
621618
}
622619
outs->bytes = 0; /* clear for next round */
@@ -641,7 +638,7 @@ static CURLcode post_per_transfer(struct GlobalConfig *global,
641638
/* something went wrong in the writing process */
642639
result = CURLE_WRITE_ERROR;
643640
if(!global->silent || global->showerror)
644-
fprintf(global->errors, "curl: (%d) Failed writing body\n", result);
641+
fprintf(stderr, "curl: (%d) Failed writing body\n", result);
645642
}
646643
if(result && config->rm_partial) {
647644
notef(global, "Removing output file: %s\n", outs->filename);
@@ -801,7 +798,7 @@ static CURLcode single_transfer(struct GlobalConfig *global,
801798
/* Unless explicitly shut off */
802799
result = glob_url(&inglob, infiles, &state->infilenum,
803800
(!global->silent || global->showerror)?
804-
global->errors:NULL);
801+
stderr:NULL);
805802
if(result)
806803
break;
807804
config->state.inglob = inglob;
@@ -837,7 +834,7 @@ static CURLcode single_transfer(struct GlobalConfig *global,
837834
expressions and return total number of URLs in pattern set */
838835
result = glob_url(&state->urls, urlnode->url, &state->urlnum,
839836
(!global->silent || global->showerror)?
840-
global->errors:NULL);
837+
stderr:NULL);
841838
if(result)
842839
break;
843840
urlnum = state->urlnum;
@@ -1096,7 +1093,7 @@ static CURLcode single_transfer(struct GlobalConfig *global,
10961093
file output call */
10971094

10981095
if(config->create_dirs) {
1099-
result = create_dir_hierarchy(per->outfile, global->errors);
1096+
result = create_dir_hierarchy(per->outfile, stderr);
11001097
/* create_dir_hierarchy shows error upon CURLE_WRITE_ERROR */
11011098
if(result)
11021099
break;
@@ -1240,9 +1237,6 @@ static CURLcode single_transfer(struct GlobalConfig *global,
12401237
}
12411238
}
12421239

1243-
if(!global->errors)
1244-
global->errors = stderr;
1245-
12461240
if((!per->outfile || !strcmp(per->outfile, "-")) &&
12471241
!config->use_ascii) {
12481242
/* 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,
18511845
my_setopt(curl, CURLOPT_TIMEVALUE_LARGE, config->condtime);
18521846
my_setopt_str(curl, CURLOPT_CUSTOMREQUEST, config->customrequest);
18531847
customrequest_helper(config, config->httpreq, config->customrequest);
1854-
my_setopt(curl, CURLOPT_STDERR, global->errors);
1848+
my_setopt(curl, CURLOPT_STDERR, stderr);
18551849

18561850
/* three new ones in libcurl 7.3: */
18571851
my_setopt_str(curl, CURLOPT_INTERFACE, config->iface);
@@ -2517,8 +2511,7 @@ static CURLcode transfer_per_config(struct GlobalConfig *global,
25172511

25182512
/* Check we have a url */
25192513
if(!config->url_list || !config->url_list->url) {
2520-
helpf(global->errors, "(%d) no URL specified!\n",
2521-
CURLE_FAILED_INIT);
2514+
helpf(stderr, "(%d) no URL specified!\n", CURLE_FAILED_INIT);
25222515
return CURLE_FAILED_INIT;
25232516
}
25242517

@@ -2576,7 +2569,7 @@ static CURLcode transfer_per_config(struct GlobalConfig *global,
25762569
if(!config->capath) {
25772570
curl_free(env);
25782571
curl_easy_cleanup(curltls);
2579-
helpf(global->errors, "out of memory\n");
2572+
helpf(stderr, "out of memory\n");
25802573
return CURLE_OUT_OF_MEMORY;
25812574
}
25822575
capath_from_env = true;
@@ -2694,7 +2687,7 @@ CURLcode operate(struct GlobalConfig *global, int argc, argv_item_t argv[])
26942687

26952688
/* If we had no arguments then make sure a url was specified in .curlrc */
26962689
if((argc < 2) && (!global->first->url_list)) {
2697-
helpf(global->errors, NULL);
2690+
helpf(stderr, NULL);
26982691
result = CURLE_FAILED_INIT;
26992692
}
27002693
}

src/tool_progress.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ bool progress_meter(struct GlobalConfig *global,
173173
header = TRUE;
174174
fputs("DL% UL% Dled Uled Xfers Live "
175175
"Total Current Left Speed\n",
176-
global->errors);
176+
stderr);
177177
}
178178
if(final || (diff > 500)) {
179179
char time_left[10];
@@ -275,7 +275,7 @@ bool progress_meter(struct GlobalConfig *global,
275275
}
276276
time2str(time_spent, spent);
277277

278-
fprintf(global->errors,
278+
fprintf(stderr,
279279
"\r"
280280
"%-3s " /* percent downloaded */
281281
"%-3s " /* percent uploaded */

src/tool_setup.h

+9
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,15 @@
3737

3838
#include "curl_setup.h" /* from the lib directory */
3939

40+
extern FILE *tool_stderr;
41+
42+
#if !defined(CURL_DO_NOT_OVERRIDE_STDERR) && !defined(UNITTESTS)
43+
#ifdef stderr
44+
#undef stderr
45+
#endif
46+
#define stderr tool_stderr
47+
#endif
48+
4049
/*
4150
* curl tool certainly uses libcurl's external interface.
4251
*/

0 commit comments

Comments
 (0)