curl: add --skip-existing

With this option, the entire download is skipped if the selected target
filename already exists when the opertion is about to begin.

Test 994, 995 and 996 verify.

Ref: #11012
Closes #13993
This commit is contained in:
Daniel Stenberg 2024-08-04 16:14:24 +02:00
parent eec908bb6e
commit 732cb15b97
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
13 changed files with 213 additions and 25 deletions

View file

@ -302,6 +302,7 @@ struct OperationConfig {
struct State state; /* for create_transfer() */
bool rm_partial; /* on error, remove partially written output
files */
bool skip_existing;
#ifdef USE_ECH
char *ech; /* Config set by --ech keywords */
char *ech_config; /* Config set by "--ech esl:" option */

View file

@ -287,6 +287,7 @@ static const struct LongShort aliases[]= {
{"show-error", ARG_BOOL, 'S', C_SHOW_ERROR},
{"show-headers", ARG_BOOL, 'i', C_SHOW_HEADERS},
{"silent", ARG_BOOL, 's', C_SILENT},
{"skip-existing", ARG_BOOL, ' ', C_SKIP_EXISTING},
{"socks4", ARG_STRG, ' ', C_SOCKS4},
{"socks4a", ARG_STRG, ' ', C_SOCKS4A},
{"socks5", ARG_STRG, ' ', C_SOCKS5},
@ -2435,6 +2436,9 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
case C_SILENT: /* --silent */
global->silent = toggle;
break;
case C_SKIP_EXISTING: /* --skip-existing */
config->skip_existing = toggle;
break;
case C_SHOW_ERROR: /* --show-error */
global->showerror = toggle;
break;

View file

@ -242,6 +242,7 @@ typedef enum {
C_SHOW_ERROR,
C_SHOW_HEADERS,
C_SILENT,
C_SKIP_EXISTING,
C_SOCKS4,
C_SOCKS4A,
C_SOCKS5,

View file

@ -662,6 +662,9 @@ const struct helptxt helptext[] = {
{"-s, --silent",
"Silent mode",
CURLHELP_IMPORTANT | CURLHELP_VERBOSE},
{" --skip-existing",
"Skip download if local file already exists",
CURLHELP_CURL | CURLHELP_OUTPUT},
{" --socks4 <host[:port]>",
"SOCKS4 proxy on given host + port",
CURLHELP_PROXY},

View file

@ -460,6 +460,9 @@ static CURLcode post_per_transfer(struct GlobalConfig *global,
if(per->infdopen)
close(per->infd);
if(per->skip)
goto skip;
#ifdef __VMS
if(is_vms_shell()) {
/* VMS DCL shell behavior */
@ -731,7 +734,7 @@ noretry:
curl_easy_getinfo(curl, CURLINFO_FILETIME_T, &filetime);
setfiletime(filetime, outs->filename, global);
}
skip:
/* Write the --write-out data before cleanup but after result is final */
if(config->writeout)
ourWriteOut(config, per, result);
@ -1197,6 +1200,15 @@ static CURLcode single_transfer(struct GlobalConfig *global,
break;
}
if(per->outfile && config->skip_existing) {
struct_stat fileinfo;
if(!stat(per->outfile, &fileinfo)) {
/* file is present */
notef(global, "skips transfer, \"%s\" exists locally",
per->outfile);
per->skip = TRUE;
}
}
if((urlnode->flags & GETOUT_USEREMOTE)
&& config->content_disposition) {
/* Our header callback MIGHT set the filename */
@ -2611,25 +2623,29 @@ static CURLcode serial_transfers(struct GlobalConfig *global,
long delay_ms;
bool bailout = FALSE;
struct timeval start;
result = pre_transfer(global, per);
if(result)
break;
if(global->libcurl) {
result = easysrc_perform();
start = tvnow();
if(!per->skip) {
result = pre_transfer(global, per);
if(result)
break;
}
start = tvnow();
#ifdef DEBUGBUILD
if(getenv("CURL_FORBID_REUSE"))
(void)curl_easy_setopt(per->curl, CURLOPT_FORBID_REUSE, 1L);
if(global->test_event_based)
result = curl_easy_perform_ev(per->curl);
else
if(global->libcurl) {
result = easysrc_perform();
if(result)
break;
}
#ifdef DEBUGBUILD
if(getenv("CURL_FORBID_REUSE"))
(void)curl_easy_setopt(per->curl, CURLOPT_FORBID_REUSE, 1L);
if(global->test_event_based)
result = curl_easy_perform_ev(per->curl);
else
#endif
result = curl_easy_perform(per->curl);
result = curl_easy_perform(per->curl);
}
returncode = post_per_transfer(global, per, result, &retry, &delay_ms);
if(retry) {

View file

@ -44,25 +44,16 @@ struct per_transfer {
char *this_url;
unsigned int urlnum; /* the index of the given URL */
char *outfile;
bool infdopen; /* TRUE if infd needs closing */
int infd;
bool noprogress;
struct ProgressData progressbar;
struct OutStruct outs;
struct OutStruct heads;
struct OutStruct etag_save;
struct HdrCbData hdrcbdata;
long num_headers;
bool was_last_header_empty;
bool added; /* set TRUE when added to the multi handle */
time_t startat; /* when doing parallel transfers, this is a retry transfer
that has been set to sleep until this time before it
should get started (again) */
bool abort; /* when doing parallel transfers and this is TRUE then a critical
error (eg --fail-early) has occurred in another transfer and
this transfer will be aborted in the progress callback */
/* for parallel progress bar */
curl_off_t dltotal;
curl_off_t dlnow;
@ -77,6 +68,15 @@ struct per_transfer {
char *uploadfile;
char *errorbuffer; /* allocated and assigned while this is used for a
transfer */
bool infdopen; /* TRUE if infd needs closing */
bool noprogress;
bool was_last_header_empty;
bool added; /* set TRUE when added to the multi handle */
bool abort; /* when doing parallel transfers and this is TRUE then a critical
error (eg --fail-early) has occurred in another transfer and
this transfer will be aborted in the progress callback */
bool skip; /* considered already done */
};
CURLcode operate(struct GlobalConfig *config, int argc, argv_item_t argv[]);