From 6881d2e767d76e4dffb655463fde3c8cb9d257fa Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 24 Aug 2020 08:31:36 +0200 Subject: curl: add --output-dir Works with --create-dirs and with -J Add test 3008, 3009, 3011, 3012 and 3013 to verify. Closes #5637 --- docs/TODO | 7 ----- docs/cmdline-opts/Makefile.inc | 1 + docs/cmdline-opts/output-dir.d | 18 +++++++++++ docs/options-in-versions | 1 + src/tool_cb_wrt.c | 32 ++++++++++++++------ src/tool_cfgable.c | 1 + src/tool_cfgable.h | 1 + src/tool_getparam.c | 5 +++ src/tool_help.c | 2 ++ src/tool_operate.c | 9 ++++++ tests/data/Makefile.inc | 4 +-- tests/data/test3008 | 59 ++++++++++++++++++++++++++++++++++++ tests/data/test3009 | 59 ++++++++++++++++++++++++++++++++++++ tests/data/test3011 | 59 ++++++++++++++++++++++++++++++++++++ tests/data/test3012 | 62 +++++++++++++++++++++++++++++++++++++ tests/data/test3013 | 69 ++++++++++++++++++++++++++++++++++++++++++ tests/runtests.pl | 16 +++++++--- 17 files changed, 381 insertions(+), 24 deletions(-) create mode 100644 docs/cmdline-opts/output-dir.d create mode 100644 tests/data/test3008 create mode 100644 tests/data/test3009 create mode 100644 tests/data/test3011 create mode 100644 tests/data/test3012 create mode 100644 tests/data/test3013 diff --git a/docs/TODO b/docs/TODO index 4f9b57bf6..874dba874 100644 --- a/docs/TODO +++ b/docs/TODO @@ -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 diff --git a/docs/cmdline-opts/Makefile.inc b/docs/cmdline-opts/Makefile.inc index 6a7b953bc..aa1acabe0 100644 --- a/docs/cmdline-opts/Makefile.inc +++ b/docs/cmdline-opts/Makefile.inc @@ -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 \ diff --git a/docs/cmdline-opts/output-dir.d b/docs/cmdline-opts/output-dir.d new file mode 100644 index 000000000..40bcb78ee --- /dev/null +++ b/docs/cmdline-opts/output-dir.d @@ -0,0 +1,18 @@ +Long: output-dir +Arg: +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. diff --git a/docs/options-in-versions b/docs/options-in-versions index 1a27306ae..2945e1373 100644 --- a/docs/options-in-versions +++ b/docs/options-in-versions @@ -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 diff --git a/src/tool_cb_wrt.c b/src/tool_cb_wrt.c index 64b62fefd..6fc51f9a5 100644 --- a/src/tool_cb_wrt.c +++ b/src/tool_cb_wrt.c @@ -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 */ diff --git a/src/tool_cfgable.c b/src/tool_cfgable.c index 63bdeaa46..e99602c4f 100644 --- a/src/tool_cfgable.c +++ b/src/tool_cfgable.c @@ -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) { diff --git a/src/tool_cfgable.h b/src/tool_cfgable.h index 4a90d0b72..620bfef3e 100644 --- a/src/tool_cfgable.h +++ b/src/tool_cfgable.h @@ -80,6 +80,7 @@ struct OperationConfig { double connecttimeout; long maxredirs; curl_off_t max_filesize; + char *output_dir; char *headerfile; char *ftpport; char *iface; diff --git a/src/tool_getparam.c b/src/tool_getparam.c index 0648c29b9..74b6b7369 100644 --- a/src/tool_getparam.c +++ b/src/tool_getparam.c @@ -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 */ diff --git a/src/tool_help.c b/src/tool_help.c index 0fc818d3c..29680d05a 100644 --- a/src/tool_help.c +++ b/src/tool_help.c @@ -284,6 +284,8 @@ static const struct helptxt helptext[] = { "OAuth 2 Bearer Token"}, {"-o, --output ", "Write to file instead of stdout"}, + {" --output-dir ", + "Directory to save files in"}, {"-Z, --parallel", "Perform transfers in parallel"}, {" --parallel-immediate", diff --git a/src/tool_operate.c b/src/tool_operate.c index ad4e85dd8..cb7371280 100644 --- a/src/tool_operate.c +++ b/src/tool_operate.c @@ -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 */ diff --git a/tests/data/Makefile.inc b/tests/data/Makefile.inc index 3752502ef..fe6b003a2 100644 --- a/tests/data/Makefile.inc +++ b/tests/data/Makefile.inc @@ -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 diff --git a/tests/data/test3008 b/tests/data/test3008 new file mode 100644 index 000000000..154ce2056 --- /dev/null +++ b/tests/data/test3008 @@ -0,0 +1,59 @@ + + + +-O + + +# +# Server-side + + +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- + + + +# +# Client-side + + +http + + +http + + +--output-dir + + +http://%HOSTIP:%HTTPPORT/this/is/the/3008 -O --output-dir %PWD/log + + + +# +# Verify data after the test has been "shot" + + +^User-Agent:.* + + +GET /this/is/the/3008 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* + + + +-foo- + + + diff --git a/tests/data/test3009 b/tests/data/test3009 new file mode 100644 index 000000000..ec4bcea4d --- /dev/null +++ b/tests/data/test3009 @@ -0,0 +1,59 @@ + + + +-O + + +# +# Server-side + + +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- + + + +# +# Client-side + + +http + + +http + + +--output-dir a non-existing directory + + +http://%HOSTIP:%HTTPPORT/this/is/the/3009 -O --output-dir %PWD/not-there + + + +# +# Verify data after the test has been "shot" + + +^User-Agent:.* + + +GET /this/is/the/3009 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* + + + +23 + + + diff --git a/tests/data/test3011 b/tests/data/test3011 new file mode 100644 index 000000000..fcf761050 --- /dev/null +++ b/tests/data/test3011 @@ -0,0 +1,59 @@ + + + +-O + + +# +# Server-side + + +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- + + + +# +# Client-side + + +http + + +http + + +--output-dir with --create-dirs + + +http://%HOSTIP:%HTTPPORT/this/is/the/3011 -O --output-dir %PWD/log/tmp --create-dirs + + + +# +# Verify data after the test has been "shot" + + +^User-Agent:.* + + +GET /this/is/the/3011 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* + + + +-foo- + + + diff --git a/tests/data/test3012 b/tests/data/test3012 new file mode 100644 index 000000000..0a64faac4 --- /dev/null +++ b/tests/data/test3012 @@ -0,0 +1,62 @@ + + + +-O +-J +--output-dir + + +# +# Server-side + + +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- + + + +# +# Client-side + + +http + + +http + + +--output-dir with -J + + +http://%HOSTIP:%HTTPPORT/this/is/the/3012 -OJ --output-dir %PWD/log + + + +# +# Verify data after the test has been "shot" + + +^User-Agent:.* + + +GET /this/is/the/3012 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* + + + +-foo- + + + diff --git a/tests/data/test3013 b/tests/data/test3013 new file mode 100644 index 000000000..d2fcfa4c0 --- /dev/null +++ b/tests/data/test3013 @@ -0,0 +1,69 @@ + + + +-O +-J +--output-dir + + +# +# Server-side + + +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- + + + +# +# Client-side + + +http + + +http + + +Two --output-dir with --next in between + + +http://%HOSTIP:%HTTPPORT/this/is/the/3013 -O --output-dir %PWD/log http://%HOSTIP:%HTTPPORT/another/3013 -o second3013 --output-dir %PWD/log + + + +# +# Verify data after the test has been "shot" + + +^User-Agent:.* + + +GET /this/is/the/3013 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* + +GET /another/3013 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* + + + +-foo- + + +-foo- + + + diff --git a/tests/runtests.pl b/tests/runtests.pl index b0bddba1c..3985f7fde 100755 --- a/tests/runtests.pl +++ b/tests/runtests.pl @@ -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; } -- cgit v1.2.1