show-error.md \
show-headers.md \
silent.md \
+ skip-existing.md \
socks4.md \
socks4a.md \
socks5-basic.md \
--- /dev/null
+---
+c: Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+SPDX-License-Identifier: curl
+Long: skip-existing
+Help: Skip download if local file already exists
+Category: curl output
+Added: 8.10.0
+Multi: boolean
+See-also:
+ - output
+ - remote-name
+ - no-clobber
+Example:
+ - --skip-existing --output local/dir/file $URL
+---
+
+# `--skip-existing`
+
+If there is a local file present when a download is requested, the operation
+is skipped. Note that curl cannot know if the local file was previously
+downloaded fine, or if it is incomplete etc, it just knows if there is a
+filename present in the file system or not and it skips the transfer if it is.
--show-error (-S) 5.9
--show-headers (-i) 4.8
--silent (-s) 4.0
+--skip-existing 8.10.0
--socks4 7.15.2
--socks4a 7.18.0
--socks5 7.18.0
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 */
{"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},
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;
C_SHOW_ERROR,
C_SHOW_HEADERS,
C_SILENT,
+ C_SKIP_EXISTING,
C_SOCKS4,
C_SOCKS4A,
C_SOCKS5,
{"-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},
if(per->infdopen)
close(per->infd);
+ if(per->skip)
+ goto skip;
+
#ifdef __VMS
if(is_vms_shell()) {
/* VMS DCL shell behavior */
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);
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 */
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();
+
+ 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(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->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) {
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;
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[]);
test961 test962 test963 test964 test965 test966 test967 test968 test969 \
test970 test971 test972 test973 test974 test975 test976 test977 test978 \
test979 test980 test981 test982 test983 test984 test985 test986 test987 \
-test988 test989 test990 test991 test992 test993 \
+test988 test989 test990 test991 test992 test993 test994 test995 test996 \
\
test1000 test1001 test1002 test1003 test1004 test1005 test1006 test1007 \
test1008 test1009 test1010 test1011 test1012 test1013 test1014 test1015 \
--- /dev/null
+<testcase>
+<info>
+<keywords>
+HTTP
+HTTP GET
+</keywords>
+</info>
+
+#
+# Server-side
+<reply>
+</reply>
+
+#
+# Client-side
+<client>
+<server>
+http
+</server>
+<name>
+--skip-existing with globbing
+</name>
+<command option="no-output">
+-o "%LOGDIR/#1" "http://%HOSTIP:%HTTPPORT/%TESTNUMBER/{hey,ho}" --skip-existing
+</command>
+<file name="%LOGDIR/hey">
+content
+</file>
+<file2 name="%LOGDIR/ho">
+content
+</file2>
+</client>
+
+#
+# Verify data after the test has been "shot"
+<verify>
+<stderr mode="text">
+Note: skips transfer, "%LOGDIR/hey" exists locally
+Note: skips transfer, "%LOGDIR/ho" exists locally
+</stderr>
+</verify>
+</testcase>
--- /dev/null
+<testcase>
+<info>
+<keywords>
+HTTP
+HTTP GET
+</keywords>
+</info>
+
+#
+# Server-side
+<reply>
+<data crlf="yes" nocheck="yes">
+HTTP/1.1 200 OK
+Date: Tue, 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>
+<name>
+--skip-existing without file present
+</name>
+<command option="no-output,no-include">
+-o %LOGDIR/there http://%HOSTIP:%HTTPPORT/%TESTNUMBER --skip-existing
+</command>
+</client>
+
+#
+# Verify data after the test has been "shot"
+<verify>
+<protocol crlf="yes">
+GET /%TESTNUMBER HTTP/1.1
+Host: %HOSTIP:%HTTPPORT
+User-Agent: curl/%VERSION
+Accept: */*
+
+</protocol>
+<file name="%LOGDIR/there">
+-foo-
+</file>
+</verify>
+</testcase>
--- /dev/null
+<testcase>
+<info>
+<keywords>
+HTTP
+HTTP GET
+</keywords>
+</info>
+
+#
+# Server-side
+<reply>
+</reply>
+
+#
+# Client-side
+<client>
+<server>
+http
+</server>
+<name>
+--skip-existing with file present
+</name>
+<command option="no-output">
+-o %LOGDIR/there http://%HOSTIP:%HTTPPORT/%TESTNUMBER --skip-existing
+</command>
+<file name="%LOGDIR/there">
+content
+</file>
+</client>
+
+#
+# Verify data after the test has been "shot"
+<verify>
+<stderr mode="text">
+Note: skips transfer, "%LOGDIR/there" exists locally
+</stderr>
+<file name="%LOGDIR/there">
+content
+</file>
+</verify>
+</testcase>