Skip to content

Commit 1032f56

Browse files
committed
curl: make %output{} in -w specify a file to write to
It can be used multiple times. Use %output{>>name} to append. Add docs. Test 990 and 991 verify. Idea: #11400 Suggested-by: ed0d2b2ce19451f2 Closes #11416
1 parent 92ac5a8 commit 1032f56

File tree

6 files changed

+175
-5
lines changed

6 files changed

+175
-5
lines changed

docs/cmdline-opts/write-out.d

+12-4
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,18 @@ output a newline by using \\n, a carriage return with \\r and a tab space with
2525
The output will be written to standard output, but this can be switched to
2626
standard error by using %{stderr}.
2727

28-
Output HTTP headers from the most recent request by using \fB%header{name}\fP
29-
where \fBname\fP is the case insensitive name of the header (without the
30-
trailing colon). The header contents are exactly as sent over the network,
31-
with leading and trailing whitespace trimmed. Added in curl 7.84.0.
28+
Output HTTP headers from the most recent request by using *%header{name}*
29+
where *name* is the case insensitive name of the header (without the trailing
30+
colon). The header contents are exactly as sent over the network, with leading
31+
and trailing whitespace trimmed. Added in curl 7.84.0.
32+
33+
Select a specific target destination file to write the output to, by using
34+
*%output{name}* where *name* is the full file name. The output following that
35+
instruction is then written to that file. More than one *%output{}* instruction
36+
can be specified in the same write-out argument. If the file name cannot be
37+
created, curl will leave the output to the one used prior to the *%output{}*
38+
instruction. Use *%output{>>name}* to append data to an existing file. Added in
39+
curl 8.3.0.
3240

3341
.B NOTE:
3442
In Windows the %-symbol is a special symbol used to expand environment

src/tool_writeout.c

+39
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,7 @@ void ourWriteOut(struct OperationConfig *config, struct per_transfer *per,
517517
bool done = FALSE;
518518
struct curl_certinfo *certinfo;
519519
CURLcode res = curl_easy_getinfo(per->curl, CURLINFO_CERTINFO, &certinfo);
520+
bool fclose_stream = FALSE;
520521

521522
if(!writeinfo)
522523
return;
@@ -556,9 +557,15 @@ void ourWriteOut(struct OperationConfig *config, struct per_transfer *per,
556557
done = TRUE;
557558
break;
558559
case VAR_STDOUT:
560+
if(fclose_stream)
561+
fclose(stream);
562+
fclose_stream = FALSE;
559563
stream = stdout;
560564
break;
561565
case VAR_STDERR:
566+
if(fclose_stream)
567+
fclose(stream);
568+
fclose_stream = FALSE;
562569
stream = stderr;
563570
break;
564571
case VAR_JSON:
@@ -600,6 +607,36 @@ void ourWriteOut(struct OperationConfig *config, struct per_transfer *per,
600607
else
601608
fputs("%header{", stream);
602609
}
610+
else if(!strncmp("output{", &ptr[1], 7)) {
611+
bool append = FALSE;
612+
ptr += 8;
613+
if((ptr[0] == '>') && (ptr[1] == '>')) {
614+
append = TRUE;
615+
ptr += 2;
616+
}
617+
end = strchr(ptr, '}');
618+
if(end) {
619+
char fname[512]; /* holds the longest file name */
620+
size_t flen = end - ptr;
621+
if(flen < sizeof(fname)) {
622+
FILE *stream2;
623+
memcpy(fname, ptr, flen);
624+
fname[flen] = 0;
625+
stream2 = fopen(fname, append? FOPEN_APPENDTEXT :
626+
FOPEN_WRITETEXT);
627+
if(stream2) {
628+
/* only change if the open worked */
629+
if(fclose_stream)
630+
fclose(stream);
631+
stream = stream2;
632+
fclose_stream = TRUE;
633+
}
634+
}
635+
ptr = end + 1;
636+
}
637+
else
638+
fputs("%output{", stream);
639+
}
603640
else {
604641
/* illegal syntax, then just output the characters that are used */
605642
fputc('%', stream);
@@ -632,4 +669,6 @@ void ourWriteOut(struct OperationConfig *config, struct per_transfer *per,
632669
ptr++;
633670
}
634671
}
672+
if(fclose_stream)
673+
fclose(stream);
635674
}

tests/data/Makefile.inc

+1-1
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ test952 test953 test954 test955 test956 test957 test958 test959 test960 \
124124
test961 test962 test963 test964 test965 test966 test967 test968 test969 \
125125
test970 test971 test972 test973 test974 test975 test976 test977 test978 \
126126
test979 test980 test981 test982 test983 test984 test985 test986 test987 \
127-
test988 test989 \
127+
test988 test989 test990 test991 \
128128
\
129129
test1000 test1001 test1002 test1003 test1004 test1005 test1006 test1007 \
130130
test1008 test1009 test1010 test1011 test1012 test1013 test1014 test1015 \

tests/data/test990

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<testcase>
2+
<info>
3+
<keywords>
4+
HTTP
5+
HTTP GET
6+
-w
7+
</keywords>
8+
</info>
9+
10+
#
11+
# Server-side
12+
<reply>
13+
<data crlf="yes">
14+
HTTP/1.1 200 OK
15+
Date: Tue, 09 Nov 2010 14:49:00 GMT
16+
Server: test-server/fake
17+
Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT
18+
ETag: "21025-dc7-39462498"
19+
Accept-Ranges: bytes
20+
Content-Length: 6
21+
Connection: close
22+
Content-Type: text/html
23+
Funny-head: yesyes
24+
25+
-foo-
26+
</data>
27+
</reply>
28+
29+
#
30+
# Client-side
31+
<client>
32+
<server>
33+
http
34+
</server>
35+
<name>
36+
use -w %output{}
37+
</name>
38+
<command>
39+
http://%HOSTIP:%HTTPPORT/%TESTNUMBER -w '%output{%LOGDIR/output}%{http_code}\n'
40+
</command>
41+
</client>
42+
43+
#
44+
# Verify data after the test has been "shot"
45+
<verify>
46+
<protocol crlf="yes">
47+
GET /%TESTNUMBER HTTP/1.1
48+
Host: %HOSTIP:%HTTPPORT
49+
User-Agent: curl/%VERSION
50+
Accept: */*
51+
52+
</protocol>
53+
<file name="%LOGDIR/output" mode="text">
54+
200
55+
</file>
56+
</verify>
57+
</testcase>

tests/data/test991

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<testcase>
2+
<info>
3+
<keywords>
4+
HTTP
5+
HTTP GET
6+
-w
7+
</keywords>
8+
</info>
9+
10+
#
11+
# Server-side
12+
<reply>
13+
<data crlf="yes">
14+
HTTP/1.1 200 OK
15+
Date: Tue, 09 Nov 2010 14:49:00 GMT
16+
Server: test-server/fake
17+
Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT
18+
ETag: "21025-dc7-39462498"
19+
Accept-Ranges: bytes
20+
Content-Length: 6
21+
Connection: close
22+
Content-Type: text/html
23+
Funny-head: yesyes
24+
25+
-foo-
26+
</data>
27+
</reply>
28+
29+
#
30+
# Client-side
31+
<client>
32+
<server>
33+
http
34+
</server>
35+
<name>
36+
use -w %output{} append
37+
</name>
38+
<file name="%LOGDIR/output" nonewline="yes">
39+
line one
40+
</file>
41+
<command>
42+
http://%HOSTIP:%HTTPPORT/%TESTNUMBER -w '%output{>>%LOGDIR/output}%{http_code}'
43+
</command>
44+
</client>
45+
46+
#
47+
# Verify data after the test has been "shot"
48+
<verify>
49+
<protocol crlf="yes">
50+
GET /%TESTNUMBER HTTP/1.1
51+
Host: %HOSTIP:%HTTPPORT
52+
User-Agent: curl/%VERSION
53+
Accept: */*
54+
55+
</protocol>
56+
<file name="%LOGDIR/output" nonewline="yes">
57+
line one200
58+
</file>
59+
</verify>
60+
</testcase>

tests/runtests.pl

+6
Original file line numberDiff line numberDiff line change
@@ -1558,6 +1558,12 @@ sub singletest_check {
15581558
@generated = @newgen;
15591559
}
15601560

1561+
if($hash{'nonewline'}) {
1562+
# cut off the final newline from the final line of the
1563+
# output data
1564+
chomp($outfile[-1]);
1565+
}
1566+
15611567
$res = compare($runnerid, $testnum, $testname, "output ($filename)",
15621568
\@generated, \@outfile);
15631569
if($res) {

0 commit comments

Comments
 (0)