tool_operate: split create_single into smaller sub functions

Reduces complexity from 58 => 32.

Closes #21389
This commit is contained in:
Daniel Stenberg 2026-04-20 16:55:38 +02:00
parent 4e0bfd8cf7
commit 19695e815c
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2

View file

@ -847,38 +847,40 @@ static CURLcode append2query(struct OperationConfig *config,
const char *q)
{
CURLcode result = CURLE_OK;
CURLU *uh = curl_url();
if(uh) {
CURLUcode uerr;
uerr = curl_url_set(uh, CURLUPART_URL, per->url, CURLU_GUESS_SCHEME);
if(uerr) {
result = urlerr_cvt(uerr);
errorf("(%d) Could not parse the URL, "
"failed to set query", result);
config->synthetic_error = TRUE;
}
else {
char *updated = NULL;
uerr = curl_url_set(uh, CURLUPART_QUERY, q, CURLU_APPENDQUERY);
if(!uerr)
uerr = curl_url_get(uh, CURLUPART_URL, &updated,
CURLU_NO_GUESS_SCHEME);
if(uerr)
if(q) {
CURLU *uh = curl_url();
if(uh) {
CURLUcode uerr;
uerr = curl_url_set(uh, CURLUPART_URL, per->url, CURLU_GUESS_SCHEME);
if(uerr) {
result = urlerr_cvt(uerr);
else {
/* use our new URL instead! */
curlx_free(per->url); /* free previous URL */
/* make it allocated by tool memory */
per->url = curlx_strdup(updated);
curl_free(updated);
if(!per->url)
result = CURLE_OUT_OF_MEMORY;
errorf("(%d) Could not parse the URL, "
"failed to set query", result);
config->synthetic_error = TRUE;
}
else {
char *updated = NULL;
uerr = curl_url_set(uh, CURLUPART_QUERY, q, CURLU_APPENDQUERY);
if(!uerr)
uerr = curl_url_get(uh, CURLUPART_URL, &updated,
CURLU_NO_GUESS_SCHEME);
if(uerr)
result = urlerr_cvt(uerr);
else {
/* use our new URL instead! */
curlx_free(per->url); /* free previous URL */
/* make it allocated by tool memory */
per->url = curlx_strdup(updated);
curl_free(updated);
if(!per->url)
result = CURLE_OUT_OF_MEMORY;
}
}
curl_url_cleanup(uh);
}
curl_url_cleanup(uh);
else
result = CURLE_OUT_OF_MEMORY;
}
else
result = CURLE_OUT_OF_MEMORY;
return result;
}
@ -965,67 +967,76 @@ static CURLcode setup_headerfile(struct OperationConfig *config,
struct per_transfer *per,
struct OutStruct *heads)
{
/* open file for output: */
if(!strcmp(config->headerfile, "%")) {
heads->stream = stderr;
/* use binary mode for protocol header output */
CURL_BINMODE(heads->stream);
}
else if(strcmp(config->headerfile, "-")) {
FILE *newfile;
/*
* Since every transfer has its own file handle for dumping
* the headers, we need to open it in append mode, since transfers
* might finish in any order.
* The first transfer clears the file.
*
* Consider placing the file handle inside the OperationConfig, so
* that it does not need to be opened/closed for every transfer.
*/
if(config->create_dirs) {
CURLcode result = create_dir_hierarchy(config->headerfile);
/* create_dir_hierarchy shows error upon CURLE_WRITE_ERROR */
if(result)
return result;
if(config->headerfile) {
/* open file for output: */
if(!strcmp(config->headerfile, "%")) {
heads->stream = stderr;
/* use binary mode for protocol header output */
CURL_BINMODE(heads->stream);
}
if(!per->prev || per->prev->config != config) {
newfile = curlx_fopen(config->headerfile, "wb");
if(newfile)
curlx_fclose(newfile);
}
newfile = curlx_fopen(config->headerfile, "ab");
else if(strcmp(config->headerfile, "-")) {
FILE *newfile;
if(!newfile) {
errorf("Failed to open %s", config->headerfile);
return CURLE_WRITE_ERROR;
/*
* Since every transfer has its own file handle for dumping
* the headers, we need to open it in append mode, since transfers
* might finish in any order.
* The first transfer clears the file.
*
* Consider placing the file handle inside the OperationConfig, so
* that it does not need to be opened/closed for every transfer.
*/
if(config->create_dirs) {
CURLcode result = create_dir_hierarchy(config->headerfile);
/* create_dir_hierarchy shows error upon CURLE_WRITE_ERROR */
if(result)
return result;
}
if(!per->prev || per->prev->config != config) {
newfile = curlx_fopen(config->headerfile, "wb");
if(newfile)
curlx_fclose(newfile);
}
newfile = curlx_fopen(config->headerfile, "ab");
if(!newfile) {
errorf("Failed to open %s", config->headerfile);
return CURLE_WRITE_ERROR;
}
else {
heads->filename = config->headerfile;
heads->regular_file = TRUE;
heads->fopened = TRUE;
heads->stream = newfile;
}
}
else {
heads->filename = config->headerfile;
heads->regular_file = TRUE;
heads->fopened = TRUE;
heads->stream = newfile;
/* always use binary mode for protocol header output */
CURL_BINMODE(heads->stream);
}
}
else {
/* always use binary mode for protocol header output */
CURL_BINMODE(heads->stream);
}
return CURLE_OK;
}
/* figure out and populate per->outfile */
static CURLcode setup_outfile(struct OperationConfig *config,
struct per_transfer *per,
struct getout *u,
struct OutStruct *outs,
bool *skipped)
{
/*
* We have specified a filename to store the result in, or we have
* decided we want to use the remote filename.
*/
struct State *state = &global->state;
if(!per->outfile) {
outs->out_null = u->out_null;
/* if --out-null or output to stdout, return */
if(outs->out_null ||
(!u->useremote && (!u->outfile || !strcmp("-", u->outfile)))) {
per->outfile = u->outfile;
u->outfile = NULL;
return CURLE_OK;
}
if(!u->outfile) {
SANITIZEcode sc;
/* extract the filename from the URL */
CURLcode result = get_url_file_name(&per->outfile, per->url, &sc);
@ -1037,18 +1048,16 @@ static CURLcode setup_outfile(struct OperationConfig *config,
return result;
}
else if(result) {
errorf("Failed to extract a filename"
" from the URL to use for storage");
errorf("Failed to extract a filename from the URL to use for storage");
return result;
}
}
else if(glob_inuse(&state->urlglob)) {
/* fill '#1' ... '#9' terms from URL pattern */
char *storefile = per->outfile;
SANITIZEcode sc;
CURLcode result =
glob_match_url(&per->outfile, storefile, &state->urlglob, &sc);
curlx_safefree(storefile);
glob_match_url(&per->outfile, u->outfile, &state->urlglob, &sc);
if(sc) {
if(sc == SANITIZE_ERR_OUT_OF_MEMORY)
return CURLE_OUT_OF_MEMORY;
@ -1066,6 +1075,11 @@ static CURLcode setup_outfile(struct OperationConfig *config,
return CURLE_WRITE_ERROR;
}
}
else {
per->outfile = u->outfile;
u->outfile = NULL;
}
DEBUGASSERT(per->outfile);
if(config->output_dir && *config->output_dir) {
@ -1211,6 +1225,110 @@ static void setup_header_cb(struct OperationConfig *config,
hdrcbdata->config = config;
}
static CURLcode setup_input_file(struct OperationConfig *config,
struct State *state, struct getout *u,
FILE *err)
{
CURLcode result = CURLE_OK;
if(u->infile) {
if(!config->globoff && !glob_inuse(&state->inglob))
result = glob_url(&state->inglob, u->infile, &state->upnum, err);
if(!result && !state->uploadfile) {
if(glob_inuse(&state->inglob))
result = glob_next_url(&state->uploadfile, &state->inglob);
else if(!state->upidx) {
/* copy the allocated string */
state->uploadfile = u->infile;
u->infile = NULL;
}
}
}
return result;
}
static CURLcode setup_url_pattern(struct OperationConfig *config,
struct State *state, struct getout *u,
FILE *err)
{
CURLcode result = CURLE_OK;
if(!state->urlnum) {
if(!config->globoff && !u->noglob) {
/* Unless explicitly shut off, we expand '{...}' and '[...]'
expressions and return total number of URLs in pattern set */
result = glob_url(&state->urlglob, u->url, &state->urlnum, err);
}
else
state->urlnum = 1; /* without globbing, this is a single URL */
}
return result;
}
static CURLcode setup_etag_files(struct OperationConfig *config,
struct OutStruct *etag_save,
bool *break_loop)
{
CURLcode result = CURLE_OK;
*break_loop = FALSE;
/* --etag-save */
memset(etag_save, 0, sizeof(*etag_save));
etag_save->stream = stdout;
/* --etag-compare */
if(config->etag_compare_file) {
result = etag_compare(config);
if(result)
return result;
}
if(config->etag_save_file) {
bool badetag = FALSE;
result = etag_store(config, etag_save, &badetag);
if(result || badetag) {
*break_loop = TRUE;
return result;
}
}
return result;
}
static CURLcode select_next_url(struct State *state,
struct getout *u,
char **url)
{
CURLcode result = CURLE_OK;
if(glob_inuse(&state->urlglob))
result = glob_next_url(url, &state->urlglob);
else if(!state->urlidx) {
*url = curlx_strdup(u->url);
if(!*url)
result = CURLE_OUT_OF_MEMORY;
}
else
*url = NULL;
return result;
}
static CURLcode setup_transfer_upload(struct OperationConfig *config,
struct per_transfer *per)
{
CURLcode result = CURLE_OK;
if(per->uploadfile) {
if(stdin_upload(per->uploadfile))
check_stdin_upload(config, per);
else {
/* We have specified a file to upload and it is not "-" */
result = add_file_name_to_url(per->curl, &per->url, per->uploadfile);
if(result)
return result;
}
if(config->resume_from_current)
config->resume_from = -1; /* -1 then forces get-it-yourself */
}
return result;
}
/* create a transfer */
static CURLcode create_single(struct OperationConfig *config,
CURLSH *share, struct State *state,
@ -1227,27 +1345,17 @@ static CURLcode create_single(struct OperationConfig *config,
CURL *curl;
struct getout *u = state->urlnode;
FILE *err = (!global->silent || global->showerror) ? tool_stderr : NULL;
bool break_loop = FALSE;
if(!u->url) {
/* This node has no URL. End of the road. */
warnf("Got more output options than URLs");
break;
}
if(u->infile) {
if(!config->globoff && !glob_inuse(&state->inglob))
result = glob_url(&state->inglob, u->infile, &state->upnum, err);
if(!result && !state->uploadfile) {
if(glob_inuse(&state->inglob))
result = glob_next_url(&state->uploadfile, &state->inglob);
else if(!state->upidx) {
/* copy the allocated string */
state->uploadfile = u->infile;
u->infile = NULL;
}
}
if(result)
return result;
}
result = setup_input_file(config, state, u, err);
if(result)
return result;
if(state->upidx >= state->upnum) {
state->urlnum = 0;
@ -1258,35 +1366,13 @@ static CURLcode create_single(struct OperationConfig *config,
continue;
}
if(!state->urlnum) {
if(!config->globoff && !u->noglob) {
/* Unless explicitly shut off, we expand '{...}' and '[...]'
expressions and return total number of URLs in pattern set */
result = glob_url(&state->urlglob, u->url, &state->urlnum, err);
if(result)
return result;
}
else
state->urlnum = 1; /* without globbing, this is a single URL */
}
result = setup_url_pattern(config, state, u, err);
if(result)
return result;
/* --etag-save */
memset(&etag_first, 0, sizeof(etag_first));
etag_first.stream = stdout;
/* --etag-compare */
if(config->etag_compare_file) {
result = etag_compare(config);
if(result)
return result;
}
if(config->etag_save_file) {
bool badetag = FALSE;
result = etag_store(config, &etag_first, &badetag);
if(result || badetag)
break;
}
result = setup_etag_files(config, &etag_first, &break_loop);
if(result || break_loop)
return result;
curl = curl_easy_init();
if(curl)
@ -1318,64 +1404,30 @@ static CURLcode create_single(struct OperationConfig *config,
heads->stream = stdout;
/* Single header file for all URLs */
if(config->headerfile) {
result = setup_headerfile(config, per, heads);
if(result)
return result;
}
result = setup_headerfile(config, per, heads);
if(result)
return result;
outs = &per->outs;
per->outfile = NULL;
per->infdopen = FALSE;
per->infd = STDIN_FILENO;
/* default output stream is stdout */
outs->stream = stdout;
if(glob_inuse(&state->urlglob))
result = glob_next_url(&per->url, &state->urlglob);
else if(!state->urlidx) {
per->url = curlx_strdup(u->url);
if(!per->url)
result = CURLE_OUT_OF_MEMORY;
}
else {
per->url = NULL;
result = select_next_url(state, u, &per->url);
if(result)
return result;
if(!per->url)
break;
}
result = setup_outfile(config, per, u, outs, skipped);
if(result)
return result;
if(u->outfile) {
per->outfile = curlx_strdup(u->outfile);
if(!per->outfile)
return CURLE_OUT_OF_MEMORY;
}
outs->out_null = u->out_null;
if(!outs->out_null &&
(u->useremote || (per->outfile && strcmp("-", per->outfile)))) {
result = setup_outfile(config, per, outs, skipped);
if(result)
return result;
}
if(per->uploadfile) {
if(stdin_upload(per->uploadfile))
check_stdin_upload(config, per);
else {
/*
* We have specified a file to upload and it is not "-".
*/
result = add_file_name_to_url(per->curl, &per->url, per->uploadfile);
if(result)
return result;
}
if(config->resume_from_current)
config->resume_from = -1; /* -1 then forces get-it-yourself */
}
result = setup_transfer_upload(config, per);
if(result)
return result;
if(!outs->out_null && output_expected(per->url, per->uploadfile) &&
outs->stream && isatty(fileno(outs->stream)))
@ -1383,17 +1435,14 @@ static CURLcode create_single(struct OperationConfig *config,
meter */
per->noprogress = global->noprogress = global->isatty = TRUE;
else {
/* progress meter is per download, so restore config
values */
/* progress meter is per download, so restore config values */
per->noprogress = global->noprogress = orig_noprogress;
global->isatty = orig_isatty;
}
if(config->httpgetfields) {
result = append2query(config, per, config->httpgetfields);
if(result)
return result;
}
result = append2query(config, per, config->httpgetfields);
if(result)
return result;
if((!per->outfile || !strcmp(per->outfile, "-")) &&
!config->use_ascii) {