mirror of
https://github.com/curl/curl.git
synced 2024-12-09 06:30:06 +08:00
curl: add --output-dir
Works with --create-dirs and with -J Add test 3008, 3009, 3011, 3012 and 3013 to verify. Closes #5637
This commit is contained in:
parent
510d98157f
commit
5620d2cc78
@ -159,7 +159,6 @@
|
||||
18.19 expand ~/ in config files
|
||||
18.20 host name sections in config files
|
||||
18.21 retry on the redirected-to URL
|
||||
18.22 Add flag to specify download directory
|
||||
18.23 Set the modification date on an uploaded file
|
||||
18.24 Use multiple parallel transfers for a single download
|
||||
|
||||
@ -1122,12 +1121,6 @@ that doesn't exist on the server, just like --ftp-create-dirs.
|
||||
|
||||
See https://github.com/curl/curl/issues/5462
|
||||
|
||||
18.22 Add flag to specify download directory
|
||||
|
||||
A directory name to basically prepend to the file name -O and -o use. Saves
|
||||
user from having to manually "cd" to the directory. Especially useful for
|
||||
command lines with multiple -O and different download directories.
|
||||
|
||||
18.23 Set the modification date on an uploaded file
|
||||
|
||||
For SFTP and posssibly FTP, curl could offer an option to set the
|
||||
|
@ -127,6 +127,7 @@ DPAGES = \
|
||||
ntlm.d ntlm-wb.d \
|
||||
oauth2-bearer.d \
|
||||
output.d \
|
||||
output-dir.d \
|
||||
parallel-immediate.d \
|
||||
parallel-max.d \
|
||||
parallel.d \
|
||||
|
18
docs/cmdline-opts/output-dir.d
Normal file
18
docs/cmdline-opts/output-dir.d
Normal file
@ -0,0 +1,18 @@
|
||||
Long: output-dir
|
||||
Arg: <dir>
|
||||
Help: Directory to save files in
|
||||
Added: 7.72.0
|
||||
See-also: remote-name remote-header-name
|
||||
---
|
||||
|
||||
This option specifies the directory in which files should be stored, when
|
||||
--remote-name or --output are used.
|
||||
|
||||
The given output directory is used for all URLs and output options on the
|
||||
command line, up until the first --next.
|
||||
|
||||
If the specified target directory doesn't exist, the operation will fail
|
||||
unless --create-dirs is also used.
|
||||
|
||||
If this option is used multiple times, the last specified directory will be
|
||||
used.
|
@ -127,6 +127,7 @@
|
||||
--ntlm-wb 7.22.0
|
||||
--oauth2-bearer 7.33.0
|
||||
--output (-o) 4.0
|
||||
--output-dir 7.72.0
|
||||
--parallel (-Z) 7.66.0
|
||||
--parallel-immediate 7.68.0
|
||||
--parallel-max 7.66.0
|
||||
|
@ -39,6 +39,15 @@
|
||||
|
||||
#include "memdebug.h" /* keep this as LAST include */
|
||||
|
||||
#ifndef O_BINARY
|
||||
#define O_BINARY 0
|
||||
#endif
|
||||
#ifdef WIN32
|
||||
#define OPENMODE S_IREAD | S_IWRITE
|
||||
#else
|
||||
#define OPENMODE S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH
|
||||
#endif
|
||||
|
||||
/* create a local file for writing, return TRUE on success */
|
||||
bool tool_create_output_file(struct OutStruct *outs,
|
||||
struct OperationConfig *config)
|
||||
@ -55,21 +64,24 @@ bool tool_create_output_file(struct OutStruct *outs,
|
||||
|
||||
if(outs->is_cd_filename) {
|
||||
/* don't overwrite existing files */
|
||||
#ifndef O_BINARY
|
||||
#define O_BINARY 0
|
||||
#endif
|
||||
int fd = open(outs->filename, O_CREAT | O_WRONLY | O_EXCL | O_BINARY,
|
||||
#ifdef WIN32
|
||||
S_IREAD | S_IWRITE
|
||||
#else
|
||||
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH
|
||||
#endif
|
||||
);
|
||||
int fd;
|
||||
char *name = outs->filename;
|
||||
char *aname = NULL;
|
||||
if(config->output_dir) {
|
||||
aname = aprintf("%s/%s", config->output_dir, name);
|
||||
if(!aname) {
|
||||
errorf(global, "out of memory\n");
|
||||
return FALSE;
|
||||
}
|
||||
name = aname;
|
||||
}
|
||||
fd = open(name, O_CREAT | O_WRONLY | O_EXCL | O_BINARY, OPENMODE);
|
||||
if(fd != -1) {
|
||||
file = fdopen(fd, "wb");
|
||||
if(!file)
|
||||
close(fd);
|
||||
}
|
||||
free(aname);
|
||||
}
|
||||
else
|
||||
/* open file for writing */
|
||||
|
@ -89,6 +89,7 @@ static void free_config_fields(struct OperationConfig *config)
|
||||
Curl_safefree(config->mail_auth);
|
||||
|
||||
Curl_safefree(config->netrc_file);
|
||||
Curl_safefree(config->output_dir);
|
||||
|
||||
urlnode = config->url_list;
|
||||
while(urlnode) {
|
||||
|
@ -80,6 +80,7 @@ struct OperationConfig {
|
||||
double connecttimeout;
|
||||
long maxredirs;
|
||||
curl_off_t max_filesize;
|
||||
char *output_dir;
|
||||
char *headerfile;
|
||||
char *ftpport;
|
||||
char *iface;
|
||||
|
@ -303,6 +303,7 @@ static const struct LongShort aliases[]= {
|
||||
{"o", "output", ARG_FILENAME},
|
||||
{"O", "remote-name", ARG_NONE},
|
||||
{"Oa", "remote-name-all", ARG_BOOL},
|
||||
{"Ob", "output-dir", ARG_STRING},
|
||||
{"p", "proxytunnel", ARG_BOOL},
|
||||
{"P", "ftp-port", ARG_STRING},
|
||||
{"q", "disable", ARG_BOOL},
|
||||
@ -1911,6 +1912,10 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
|
||||
config->default_node_flags = toggle?GETOUT_USEREMOTE:0;
|
||||
break;
|
||||
}
|
||||
else if(subletter == 'b') { /* --output-dir */
|
||||
GetStr(&config->output_dir, nextarg);
|
||||
break;
|
||||
}
|
||||
/* FALLTHROUGH */
|
||||
case 'o': /* --output */
|
||||
/* output file */
|
||||
|
@ -284,6 +284,8 @@ static const struct helptxt helptext[] = {
|
||||
"OAuth 2 Bearer Token"},
|
||||
{"-o, --output <file>",
|
||||
"Write to file instead of stdout"},
|
||||
{" --output-dir <dir>",
|
||||
"Directory to save files in"},
|
||||
{"-Z, --parallel",
|
||||
"Perform transfers in parallel"},
|
||||
{" --parallel-immediate",
|
||||
|
@ -1050,6 +1050,15 @@ static CURLcode single_transfer(struct GlobalConfig *global,
|
||||
}
|
||||
}
|
||||
|
||||
if(config->output_dir) {
|
||||
char *d = aprintf("%s/%s", config->output_dir, per->outfile);
|
||||
if(!d) {
|
||||
result = CURLE_WRITE_ERROR;
|
||||
break;
|
||||
}
|
||||
free(per->outfile);
|
||||
per->outfile = d;
|
||||
}
|
||||
/* Create the directory hierarchy, if not pre-existent to a multiple
|
||||
file output call */
|
||||
|
||||
|
@ -224,5 +224,5 @@ test2078 \
|
||||
test2080 \
|
||||
test2100 \
|
||||
\
|
||||
test3000 test3001 \
|
||||
test3002 test3003 test3004 test3005 test3006 test3007 test3010
|
||||
test3000 test3001 test3002 test3003 test3004 test3005 test3006 test3007 \
|
||||
test3008 test3009 test3010 test3011 test3012 test3013
|
||||
|
59
tests/data/test3008
Normal file
59
tests/data/test3008
Normal file
@ -0,0 +1,59 @@
|
||||
<testcase>
|
||||
<info>
|
||||
<keywords>
|
||||
-O
|
||||
</keywords>
|
||||
</info>
|
||||
#
|
||||
# Server-side
|
||||
<reply>
|
||||
<data nocheck="yes">
|
||||
HTTP/1.1 200 OK
|
||||
Date: Thu, 09 Nov 2010 14:49:00 GMT
|
||||
Server: test-server/fake
|
||||
Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT
|
||||
ETag: "21025-dc7-39462498"
|
||||
Accept-Ranges: bytes
|
||||
Content-Length: 6
|
||||
Connection: close
|
||||
Content-Type: text/html
|
||||
Funny-head: yesyes
|
||||
|
||||
-foo-
|
||||
</data>
|
||||
</reply>
|
||||
|
||||
#
|
||||
# Client-side
|
||||
<client>
|
||||
<server>
|
||||
http
|
||||
</server>
|
||||
<features>
|
||||
http
|
||||
</features>
|
||||
<name>
|
||||
--output-dir
|
||||
</name>
|
||||
<command option="no-output,no-include">
|
||||
http://%HOSTIP:%HTTPPORT/this/is/the/3008 -O --output-dir %PWD/log
|
||||
</command>
|
||||
</client>
|
||||
|
||||
#
|
||||
# Verify data after the test has been "shot"
|
||||
<verify>
|
||||
<strip>
|
||||
^User-Agent:.*
|
||||
</strip>
|
||||
<protocol>
|
||||
GET /this/is/the/3008 HTTP/1.1
|
||||
Host: %HOSTIP:%HTTPPORT
|
||||
Accept: */*
|
||||
|
||||
</protocol>
|
||||
<file name="log/3008">
|
||||
-foo-
|
||||
</file>
|
||||
</verify>
|
||||
</testcase>
|
59
tests/data/test3009
Normal file
59
tests/data/test3009
Normal file
@ -0,0 +1,59 @@
|
||||
<testcase>
|
||||
<info>
|
||||
<keywords>
|
||||
-O
|
||||
</keywords>
|
||||
</info>
|
||||
#
|
||||
# Server-side
|
||||
<reply>
|
||||
<data nocheck="yes">
|
||||
HTTP/1.1 200 OK
|
||||
Date: Thu, 09 Nov 2010 14:49:00 GMT
|
||||
Server: test-server/fake
|
||||
Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT
|
||||
ETag: "21025-dc7-39462498"
|
||||
Accept-Ranges: bytes
|
||||
Content-Length: 6
|
||||
Connection: close
|
||||
Content-Type: text/html
|
||||
Funny-head: yesyes
|
||||
|
||||
-foo-
|
||||
</data>
|
||||
</reply>
|
||||
|
||||
#
|
||||
# Client-side
|
||||
<client>
|
||||
<server>
|
||||
http
|
||||
</server>
|
||||
<features>
|
||||
http
|
||||
</features>
|
||||
<name>
|
||||
--output-dir a non-existing directory
|
||||
</name>
|
||||
<command option="no-output,no-include">
|
||||
http://%HOSTIP:%HTTPPORT/this/is/the/3009 -O --output-dir %PWD/not-there
|
||||
</command>
|
||||
</client>
|
||||
|
||||
#
|
||||
# Verify data after the test has been "shot"
|
||||
<verify>
|
||||
<strip>
|
||||
^User-Agent:.*
|
||||
</strip>
|
||||
<protocol>
|
||||
GET /this/is/the/3009 HTTP/1.1
|
||||
Host: %HOSTIP:%HTTPPORT
|
||||
Accept: */*
|
||||
|
||||
</protocol>
|
||||
<errorcode>
|
||||
23
|
||||
</errorcode>
|
||||
</verify>
|
||||
</testcase>
|
59
tests/data/test3011
Normal file
59
tests/data/test3011
Normal file
@ -0,0 +1,59 @@
|
||||
<testcase>
|
||||
<info>
|
||||
<keywords>
|
||||
-O
|
||||
</keywords>
|
||||
</info>
|
||||
#
|
||||
# Server-side
|
||||
<reply>
|
||||
<data nocheck="yes">
|
||||
HTTP/1.1 200 OK
|
||||
Date: Thu, 09 Nov 2010 14:49:00 GMT
|
||||
Server: test-server/fake
|
||||
Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT
|
||||
ETag: "21025-dc7-39462498"
|
||||
Accept-Ranges: bytes
|
||||
Content-Length: 6
|
||||
Connection: close
|
||||
Content-Type: text/html
|
||||
Funny-head: yesyes
|
||||
|
||||
-foo-
|
||||
</data>
|
||||
</reply>
|
||||
|
||||
#
|
||||
# Client-side
|
||||
<client>
|
||||
<server>
|
||||
http
|
||||
</server>
|
||||
<features>
|
||||
http
|
||||
</features>
|
||||
<name>
|
||||
--output-dir with --create-dirs
|
||||
</name>
|
||||
<command option="no-output,no-include">
|
||||
http://%HOSTIP:%HTTPPORT/this/is/the/3011 -O --output-dir %PWD/log/tmp --create-dirs
|
||||
</command>
|
||||
</client>
|
||||
|
||||
#
|
||||
# Verify data after the test has been "shot"
|
||||
<verify>
|
||||
<strip>
|
||||
^User-Agent:.*
|
||||
</strip>
|
||||
<protocol>
|
||||
GET /this/is/the/3011 HTTP/1.1
|
||||
Host: %HOSTIP:%HTTPPORT
|
||||
Accept: */*
|
||||
|
||||
</protocol>
|
||||
<file name="log/tmp/3011">
|
||||
-foo-
|
||||
</file>
|
||||
</verify>
|
||||
</testcase>
|
62
tests/data/test3012
Normal file
62
tests/data/test3012
Normal file
@ -0,0 +1,62 @@
|
||||
<testcase>
|
||||
<info>
|
||||
<keywords>
|
||||
-O
|
||||
-J
|
||||
--output-dir
|
||||
</keywords>
|
||||
</info>
|
||||
#
|
||||
# Server-side
|
||||
<reply>
|
||||
<data nocheck="yes">
|
||||
HTTP/1.1 200 OK
|
||||
Date: Thu, 09 Nov 2010 14:49:00 GMT
|
||||
Server: test-server/fake
|
||||
Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT
|
||||
ETag: "21025-dc7-39462498"
|
||||
Accept-Ranges: bytes
|
||||
Content-Length: 6
|
||||
Connection: close
|
||||
Content-Disposition: inline; filename="MMM3012MMM"
|
||||
Content-Type: text/html
|
||||
Funny-head: yesyes
|
||||
|
||||
-foo-
|
||||
</data>
|
||||
</reply>
|
||||
|
||||
#
|
||||
# Client-side
|
||||
<client>
|
||||
<server>
|
||||
http
|
||||
</server>
|
||||
<features>
|
||||
http
|
||||
</features>
|
||||
<name>
|
||||
--output-dir with -J
|
||||
</name>
|
||||
<command option="no-output,no-include">
|
||||
http://%HOSTIP:%HTTPPORT/this/is/the/3012 -OJ --output-dir %PWD/log
|
||||
</command>
|
||||
</client>
|
||||
|
||||
#
|
||||
# Verify data after the test has been "shot"
|
||||
<verify>
|
||||
<strip>
|
||||
^User-Agent:.*
|
||||
</strip>
|
||||
<protocol>
|
||||
GET /this/is/the/3012 HTTP/1.1
|
||||
Host: %HOSTIP:%HTTPPORT
|
||||
Accept: */*
|
||||
|
||||
</protocol>
|
||||
<file name="log/MMM3012MMM">
|
||||
-foo-
|
||||
</file>
|
||||
</verify>
|
||||
</testcase>
|
69
tests/data/test3013
Normal file
69
tests/data/test3013
Normal file
@ -0,0 +1,69 @@
|
||||
<testcase>
|
||||
<info>
|
||||
<keywords>
|
||||
-O
|
||||
-J
|
||||
--output-dir
|
||||
</keywords>
|
||||
</info>
|
||||
#
|
||||
# Server-side
|
||||
<reply>
|
||||
<data nocheck="yes">
|
||||
HTTP/1.1 200 OK
|
||||
Date: Thu, 09 Nov 2010 14:49:00 GMT
|
||||
Server: test-server/fake
|
||||
Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT
|
||||
ETag: "21025-dc7-39462498"
|
||||
Accept-Ranges: bytes
|
||||
Content-Length: 6
|
||||
Connection: close
|
||||
Content-Disposition: inline; filename="MMM3013MMM"
|
||||
Content-Type: text/html
|
||||
Funny-head: yesyes
|
||||
|
||||
-foo-
|
||||
</data>
|
||||
</reply>
|
||||
|
||||
#
|
||||
# Client-side
|
||||
<client>
|
||||
<server>
|
||||
http
|
||||
</server>
|
||||
<features>
|
||||
http
|
||||
</features>
|
||||
<name>
|
||||
Two --output-dir with --next in between
|
||||
</name>
|
||||
<command option="no-output,no-include">
|
||||
http://%HOSTIP:%HTTPPORT/this/is/the/3013 -O --output-dir %PWD/log http://%HOSTIP:%HTTPPORT/another/3013 -o second3013 --output-dir %PWD/log
|
||||
</command>
|
||||
</client>
|
||||
|
||||
#
|
||||
# Verify data after the test has been "shot"
|
||||
<verify>
|
||||
<strip>
|
||||
^User-Agent:.*
|
||||
</strip>
|
||||
<protocol>
|
||||
GET /this/is/the/3013 HTTP/1.1
|
||||
Host: %HOSTIP:%HTTPPORT
|
||||
Accept: */*
|
||||
|
||||
GET /another/3013 HTTP/1.1
|
||||
Host: %HOSTIP:%HTTPPORT
|
||||
Accept: */*
|
||||
|
||||
</protocol>
|
||||
<file name="log/3013">
|
||||
-foo-
|
||||
</file>
|
||||
<file2 name="log/second3013">
|
||||
-foo-
|
||||
</file2>
|
||||
</verify>
|
||||
</testcase>
|
@ -2713,15 +2713,21 @@ sub cleardir {
|
||||
my $file;
|
||||
|
||||
# Get all files
|
||||
opendir(DIR, $dir) ||
|
||||
opendir(my $dh, $dir) ||
|
||||
return 0; # can't open dir
|
||||
while($file = readdir(DIR)) {
|
||||
if(($file !~ /^\.(|\.)$/)) {
|
||||
unlink("$dir/$file");
|
||||
while($file = readdir($dh)) {
|
||||
if(($file !~ /^(\.|\.\.)\z/)) {
|
||||
if(-d "$dir/$file") {
|
||||
cleardir("$dir/$file");
|
||||
rmdir("$dir/$file");
|
||||
}
|
||||
else {
|
||||
unlink("$dir/$file");
|
||||
}
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
closedir DIR;
|
||||
closedir $dh;
|
||||
return $count;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user