struct ProgressData progressbar;
struct getout *urlnode;
- struct OutStruct outs;
struct OutStruct heads;
CURL *curl = NULL;
int i;
errorbuffer[0] = '\0';
- memset(&outs, 0, sizeof(struct OutStruct));
+ /* default headers output stream is stdout */
memset(&heads, 0, sizeof(struct OutStruct));
+ heads.stream = stdout;
+ heads.config = config;
memory_tracking_init();
}
config->easy = curl;
- /* Store a pointer to our 'outs' struct used for output writing */
- config->outs = &outs;
-
/*
** Beyond this point no return'ing from this function allowed.
** Jump to label 'quit_curl' in order to abandon this function
/* Single header file for all URLs */
if(config->headerfile) {
/* open file for output: */
- if(strcmp(config->headerfile, "-")) {
+ if(!curlx_strequal(config->headerfile, "-")) {
FILE *newfile = fopen(config->headerfile, "wb");
if(!newfile) {
warnf(config, "Failed to open %s\n", config->headerfile);
}
else {
heads.filename = config->headerfile;
- heads.alloc_filename = FALSE;
+ heads.s_isreg = TRUE;
+ heads.fopened = TRUE;
heads.stream = newfile;
- heads.config = config;
}
}
- else {
- heads.filename = config->headerfile; /* "-" */
- heads.alloc_filename = FALSE;
- heads.stream = stdout;
- heads.config = config;
- }
}
/*
continue; /* next URL please */
}
- /* default output stream is stdout */
- outs.stream = stdout;
- outs.config = config;
- outs.bytes = 0; /* nothing written yet */
- outs.filename = NULL;
-
/* save outfile pattern before expansion */
if(urlnode->outfile) {
outfiles = strdup(urlnode->outfile);
int infd;
bool infdopen;
char *outfile;
+ struct OutStruct outs;
struct InStruct input;
struct timeval retrystart;
curl_off_t uploadfilesize;
infd = STDIN_FILENO;
uploadfilesize = -1; /* -1 means unknown */
+ /* default output stream is stdout */
+ memset(&outs, 0, sizeof(struct OutStruct));
+ outs.stream = stdout;
+ outs.config = config;
+
if(urls)
this_url = glob_next_url(urls);
else if(!i) {
}
}
- if(config->resume_from_current) {
- /* We're told to continue from where we are now. Get the
- size of the file as it is now and open it for append instead */
-
- struct_stat fileinfo;
-
- /* VMS -- Danger, the filesize is only valid for stream files */
- if(0 == stat(outfile, &fileinfo))
- /* set offset to current file size: */
- config->resume_from = fileinfo.st_size;
- else
- /* let offset be 0 */
- config->resume_from = 0;
+ if((urlnode->flags & GETOUT_USEREMOTE)
+ && config->content_disposition) {
+ /* Our header callback sets the filename */
+ DEBUGASSERT(!outs.filename);
}
+ else {
+ if(config->resume_from_current) {
+ /* We're told to continue from where we are now. Get the size
+ of the file as it is now and open it for append instead */
+ struct_stat fileinfo;
+ /* VMS -- Danger, the filesize is only valid for stream files */
+ if(0 == stat(outfile, &fileinfo))
+ /* set offset to current file size: */
+ config->resume_from = fileinfo.st_size;
+ else
+ /* let offset be 0 */
+ config->resume_from = 0;
+ }
- outs.filename = outfile;
- outs.alloc_filename = FALSE;
-
- if(config->resume_from) {
- outs.init = config->resume_from;
- /* open file for output: */
- outs.stream = fopen(outfile, config->resume_from?"ab":"wb");
- if(!outs.stream) {
- helpf(config->errors, "Can't open '%s'!\n", outfile);
- res = CURLE_WRITE_ERROR;
- goto quit_urls;
+ if(config->resume_from) {
+ /* open file for output: */
+ FILE *file = fopen(outfile, config->resume_from?"ab":"wb");
+ if(!file) {
+ helpf(config->errors, "Can't open '%s'!\n", outfile);
+ res = CURLE_WRITE_ERROR;
+ goto quit_urls;
+ }
+ outs.fopened = TRUE;
+ outs.stream = file;
+ outs.init = config->resume_from;
}
- }
- else {
- outs.stream = NULL; /* open when needed */
- outs.bytes = 0; /* reset byte counter */
+ else {
+ outs.stream = NULL; /* open when needed */
+ }
+ outs.filename = outfile;
+ outs.s_isreg = TRUE;
}
}
quit_urls:
/* Set file extended attributes */
- if(!res && config->xattr &&
- outfile && !curlx_strequal(outfile, "-") && outs.stream) {
+ if(!res && config->xattr && outs.fopened && outs.stream) {
int rc = fwrite_xattr(curl, fileno(outs.stream));
if(rc)
warnf(config, "Error setting extended attributes: %s\n",
}
/* Close the file */
- if(outfile && !curlx_strequal(outfile, "-") && outs.stream) {
+ if(outs.fopened && outs.stream) {
int rc = fclose(outs.stream);
if(!res && rc) {
/* something went wrong in the writing process */
res = CURLE_WRITE_ERROR;
fprintf(config->errors, "(%d) Failed writing body\n", res);
}
- if(outs.alloc_filename)
- Curl_safefree(outs.filename);
+ }
+ else if(!outs.s_isreg && outs.stream) {
+ /* Dump standard stream buffered data */
+ int rc = fflush(outs.stream);
+ if(!res && rc) {
+ /* something went wrong in the writing process */
+ res = CURLE_WRITE_ERROR;
+ fprintf(config->errors, "(%d) Failed writing body\n", res);
+ }
}
#ifdef __AMIGA__
- if(!res) {
+ if(!res && outs.s_isreg && outs.filename) {
/* Set the url (up to 80 chars) as comment for the file */
if(strlen(url) > 78)
url[79] = '\0';
- SetComment( outs.filename, url);
+ SetComment(outs.filename, url);
}
#endif
#ifdef HAVE_UTIME
/* File time can only be set _after_ the file has been closed */
- if(!res && config->remote_time &&
- outs.filename && !curlx_strequal("-", outs.filename)) {
+ if(!res && config->remote_time && outs.s_isreg && outs.filename) {
/* Ask libcurl if we got a remote file time */
long filetime = -1;
curl_easy_getinfo(curl, CURLINFO_FILETIME, &filetime);
}
}
#endif
+ /* No more business with this output struct */
+ if(outs.alloc_filename)
+ Curl_safefree(outs.filename);
+ memset(&outs, 0, sizeof(struct OutStruct));
/* Free loop-local allocated memory and close loop-local opened fd */
} /* loop to the next URL */
- /* Free loop-local allocated memory and close loop-local opened fd */
+ /* Free loop-local allocated memory */
Curl_safefree(uploadfile);
} /* loop to the next globbed upload file */
- /* Free loop-local allocated memory and close loop-local opened fd */
+ /* Free loop-local allocated memory */
Curl_safefree(outfiles);
/* Close function-local opened file descriptors */
- if(config->headerfile) {
- if(strcmp(heads.filename, "-") && heads.stream)
- fclose(heads.stream);
- if(heads.alloc_filename)
- Curl_safefree(heads.filename);
- }
+ if(heads.fopened && heads.stream)
+ fclose(heads.stream);
+ if(heads.alloc_filename)
+ Curl_safefree(heads.filename);
if(config->trace_fopened && config->trace_stream)
fclose(config->trace_stream);
size_t tool_write_cb(void *buffer, size_t sz, size_t nmemb, void *userdata)
{
size_t rc;
- struct OutStruct *out = userdata;
- struct Configurable *config = out->config;
+ struct OutStruct *outs = userdata;
+ struct Configurable *config = outs->config;
/*
* Once that libcurl has called back tool_write_cb() the returned value
* it does not match then it fails with CURLE_WRITE_ERROR. So at this
* point returning a value different from sz*nmemb indicates failure.
*/
- const size_t err_rc = (sz * nmemb) ? 0 : 1;
+ const size_t failure = (sz * nmemb) ? 0 : 1;
+
+ if(!config)
+ return failure;
#ifdef DEBUGBUILD
if(sz * nmemb > (size_t)CURL_MAX_WRITE_SIZE) {
warnf(config, "Data size exceeds single call write limit!\n");
- return err_rc; /* Failure */
+ return failure;
+ }
+
+ {
+ /* Some internal congruency checks on received OutStruct */
+ bool check_fails = FALSE;
+ if(outs->filename) {
+ /* regular file */
+ if(!*outs->filename)
+ check_fails = TRUE;
+ if(!outs->s_isreg)
+ check_fails = TRUE;
+ if(outs->fopened && !outs->stream)
+ check_fails = TRUE;
+ if(!outs->fopened && outs->stream)
+ check_fails = TRUE;
+ if(!outs->fopened && outs->bytes)
+ check_fails = TRUE;
+ }
+ else {
+ /* standard stream */
+ if(!outs->stream || outs->s_isreg || outs->fopened)
+ check_fails = TRUE;
+ if(outs->alloc_filename || outs->init)
+ check_fails = TRUE;
+ }
+ if(check_fails) {
+ warnf(config, "Invalid output struct data for write callback\n");
+ return failure;
+ }
}
#endif
- if(!out->stream) {
- out->bytes = 0; /* nothing written yet */
- if(!out->filename) {
+ if(!outs->stream) {
+ FILE *file;
+
+ if(!outs->filename || !*outs->filename) {
warnf(config, "Remote filename has no length!\n");
- return err_rc; /* Failure */
+ return failure;
}
if(config->content_disposition) {
/* don't overwrite existing files */
- FILE* f = fopen(out->filename, "r");
- if(f) {
- fclose(f);
- warnf(config, "Refusing to overwrite %s: %s\n", out->filename,
+ file = fopen(outs->filename, "rb");
+ if(file) {
+ fclose(file);
+ warnf(config, "Refusing to overwrite %s: %s\n", outs->filename,
strerror(EEXIST));
- return err_rc; /* Failure */
+ return failure;
}
}
/* open file for writing */
- out->stream = fopen(out->filename, "wb");
- if(!out->stream) {
- warnf(config, "Failed to create the file %s: %s\n", out->filename,
+ file = fopen(outs->filename, "wb");
+ if(!file) {
+ warnf(config, "Failed to create the file %s: %s\n", outs->filename,
strerror(errno));
- return err_rc; /* failure */
+ return failure;
}
+ outs->s_isreg = TRUE;
+ outs->fopened = TRUE;
+ outs->stream = file;
+ outs->bytes = 0;
+ outs->init = 0;
}
- rc = fwrite(buffer, sz, nmemb, out->stream);
+ rc = fwrite(buffer, sz, nmemb, outs->stream);
if((sz * nmemb) == rc)
/* we added this amount of data to the output */
- out->bytes += (sz * nmemb);
+ outs->bytes += (sz * nmemb);
if(config->readbusy) {
config->readbusy = FALSE;
}
if(config->nobuffer) {
- /* disable output buffering */
- int res = fflush(out->stream);
- if(res) {
- /* return a value that isn't the same as sz * nmemb */
- return err_rc; /* failure */
- }
+ /* output buffering disabled */
+ int res = fflush(outs->stream);
+ if(res)
+ return failure;
}
return rc;
/*
* OutStruct variables keep track of information relative to curl's
- * output writing, which may take place to stdout or to some file.
+ * output writing, which may take place to a standard stream or a file.
*
- * 'filename' member is a pointer to either a file name string or to
- * string "-" to indicate that output is written to stdout.
+ * 'filename' member is either a pointer to a file name string or NULL
+ * when dealing with a standard stream.
*
* 'alloc_filename' member is TRUE when string pointed by 'filename' has been
* dynamically allocated and 'belongs' to this OutStruct, otherwise FALSE.
*
+ * 's_isreg' member is TRUE when output goes to a regular file, this also
+ * implies that output is 'seekable' and 'appendable' and also that member
+ * 'filename' points to file name's string. For any standard stream member
+ * 's_isreg' will be FALSE.
+ *
+ * 'fopened' member is TRUE when output goes to a regular file and it
+ * has been fopen'ed, requiring it to be closed later on. In any other
+ * case this is FALSE.
+ *
* 'stream' member is a pointer to a stream controlling object as returned
- * from a 'fopen' call or stdout. When 'stdout' this shall not be closed.
+ * from a 'fopen' call or a standard stream.
+ *
+ * 'config' member is a pointer to associated 'Configurable' struct.
+ *
+ * 'bytes' member represents amount written so far.
*
- * 'bytes' member represents amount written, and 'init' initial file size.
+ * 'init' member holds original file size or offset at which truncation is
+ * taking place. Always zero unless appending to a non-empty regular file.
*/
struct OutStruct {
- char *filename; /* pointer to file name or "-" string */
- bool alloc_filename; /* allocated filename belongs to this */
- FILE *stream; /* stdout or stream controlling object */
- struct Configurable *config; /* pointer back to Configurable struct */
- curl_off_t bytes; /* amount written so far */
- curl_off_t init; /* original size (non-zero when appending) */
+ char *filename;
+ bool alloc_filename;
+ bool s_isreg;
+ bool fopened;
+ FILE *stream;
+ struct Configurable *config;
+ curl_off_t bytes;
+ curl_off_t init;
};