From 80b2a5dd37e8fbea3aa70229ab7685f535f1a6cc Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 8 Apr 2026 00:36:36 +0200 Subject: [PATCH] tool_getparam: reduce opt_string complexity - move arguments taking unsigned numbers into opt_num - move arugments taking seconds into opt_secs Closes #21261 --- src/tool_getparam.c | 223 ++++++++++++++++++++++++++------------------ src/tool_getparam.h | 4 +- 2 files changed, 136 insertions(+), 91 deletions(-) diff --git a/src/tool_getparam.c b/src/tool_getparam.c index 6e1e8ff31d..de4c3e90d2 100644 --- a/src/tool_getparam.c +++ b/src/tool_getparam.c @@ -97,7 +97,7 @@ static const struct LongShort aliases[]= { {"compressed", ARG_BOOL, ' ', C_COMPRESSED}, {"compressed-ssh", ARG_BOOL, ' ', C_COMPRESSED_SSH}, {"config", ARG_FILE, 'K', C_CONFIG}, - {"connect-timeout", ARG_STRG, ' ', C_CONNECT_TIMEOUT}, + {"connect-timeout", ARG_SECS, ' ', C_CONNECT_TIMEOUT}, {"connect-to", ARG_STRG, ' ', C_CONNECT_TO}, {"continue-at", ARG_STRG, 'C', C_CONTINUE_AT}, {"cookie", ARG_STRG, 'b', C_COOKIE}, @@ -134,7 +134,7 @@ static const struct LongShort aliases[]= { {"epsv", ARG_BOOL, ' ', C_EPSV}, {"etag-compare", ARG_FILE, ' ', C_ETAG_COMPARE}, {"etag-save", ARG_FILE, ' ', C_ETAG_SAVE}, - {"expect100-timeout", ARG_STRG, ' ', C_EXPECT100_TIMEOUT}, + {"expect100-timeout", ARG_SECS, ' ', C_EXPECT100_TIMEOUT}, {"fail", ARG_BOOL, 'f', C_FAIL}, {"fail-early", ARG_BOOL, ' ', C_FAIL_EARLY}, {"fail-with-body", ARG_BOOL, ' ', C_FAIL_WITH_BODY}, @@ -158,7 +158,7 @@ static const struct LongShort aliases[]= { {"ftp-ssl-reqd", ARG_BOOL|ARG_TLS, ' ', C_FTP_SSL_REQD}, {"get", ARG_BOOL, 'G', C_GET}, {"globoff", ARG_BOOL, 'g', C_GLOBOFF}, - {"happy-eyeballs-timeout-ms", ARG_STRG, ' ', C_HAPPY_EYEBALLS_TIMEOUT_MS}, + {"happy-eyeballs-timeout-ms", ARG_UNUM, ' ', C_HAPPY_EYEBALLS_TIMEOUT_MS}, {"haproxy-clientip", ARG_STRG, ' ', C_HAPROXY_CLIENTIP}, {"haproxy-protocol", ARG_BOOL, ' ', C_HAPROXY_PROTOCOL}, {"head", ARG_BOOL, 'I', C_HEAD}, @@ -187,8 +187,8 @@ static const struct LongShort aliases[]= { {"json", ARG_STRG, ' ', C_JSON}, {"junk-session-cookies", ARG_BOOL, 'j', C_JUNK_SESSION_COOKIES}, {"keepalive", ARG_BOOL|ARG_NO, ' ', C_KEEPALIVE}, - {"keepalive-cnt", ARG_STRG, ' ', C_KEEPALIVE_CNT}, - {"keepalive-time", ARG_STRG, ' ', C_KEEPALIVE_TIME}, + {"keepalive-cnt", ARG_UNUM, ' ', C_KEEPALIVE_CNT}, + {"keepalive-time", ARG_UNUM, ' ', C_KEEPALIVE_TIME}, {"key", ARG_FILE, ' ', C_KEY}, {"key-type", ARG_STRG|ARG_TLS, ' ', C_KEY_TYPE}, {"knownhosts", ARG_FILE, ' ', C_KNOWNHOSTS}, @@ -208,7 +208,7 @@ static const struct LongShort aliases[]= { {"manual", ARG_BOOL, 'M', C_MANUAL}, {"max-filesize", ARG_STRG, ' ', C_MAX_FILESIZE}, {"max-redirs", ARG_STRG, ' ', C_MAX_REDIRS}, - {"max-time", ARG_STRG, 'm', C_MAX_TIME}, + {"max-time", ARG_SECS, 'm', C_MAX_TIME}, {"metalink", ARG_BOOL|ARG_DEPR, ' ', C_METALINK}, {"mptcp", ARG_BOOL, ' ', C_MPTCP}, {"negotiate", ARG_BOOL, ' ', C_NEGOTIATE}, @@ -226,8 +226,8 @@ static const struct LongShort aliases[]= { {"output-dir", ARG_STRG, ' ', C_OUTPUT_DIR}, {"parallel", ARG_BOOL, 'Z', C_PARALLEL}, {"parallel-immediate", ARG_BOOL, ' ', C_PARALLEL_IMMEDIATE}, - {"parallel-max", ARG_STRG, ' ', C_PARALLEL_MAX}, - {"parallel-max-host", ARG_STRG, ' ', C_PARALLEL_HOST}, + {"parallel-max", ARG_UNUM, ' ', C_PARALLEL_MAX}, + {"parallel-max-host", ARG_UNUM, ' ', C_PARALLEL_HOST}, {"pass", ARG_STRG|ARG_CLEAR, ' ', C_PASS}, {"path-as-is", ARG_BOOL, ' ', C_PATH_AS_IS}, {"pinnedpubkey", ARG_STRG|ARG_TLS, ' ', C_PINNEDPUBKEY}, @@ -288,11 +288,11 @@ static const struct LongShort aliases[]= { {"request", ARG_STRG, 'X', C_REQUEST}, {"request-target", ARG_STRG, ' ', C_REQUEST_TARGET}, {"resolve", ARG_STRG, ' ', C_RESOLVE}, - {"retry", ARG_STRG, ' ', C_RETRY}, + {"retry", ARG_UNUM, ' ', C_RETRY}, {"retry-all-errors", ARG_BOOL, ' ', C_RETRY_ALL_ERRORS}, {"retry-connrefused", ARG_BOOL, ' ', C_RETRY_CONNREFUSED}, - {"retry-delay", ARG_STRG, ' ', C_RETRY_DELAY}, - {"retry-max-time", ARG_STRG, ' ', C_RETRY_MAX_TIME}, + {"retry-delay", ARG_SECS, ' ', C_RETRY_DELAY}, + {"retry-max-time", ARG_SECS, ' ', C_RETRY_MAX_TIME}, {"sasl-authzid", ARG_STRG, ' ', C_SASL_AUTHZID}, {"sasl-ir", ARG_BOOL, ' ', C_SASL_IR}, {"service-name", ARG_STRG, ' ', C_SERVICE_NAME}, @@ -311,8 +311,8 @@ static const struct LongShort aliases[]= { {"socks5-gssapi-nec", ARG_BOOL, ' ', C_SOCKS5_GSSAPI_NEC}, {"socks5-gssapi-service", ARG_STRG, ' ', C_SOCKS5_GSSAPI_SERVICE}, {"socks5-hostname", ARG_STRG, ' ', C_SOCKS5_HOSTNAME}, - {"speed-limit", ARG_STRG, 'Y', C_SPEED_LIMIT}, - {"speed-time", ARG_STRG, 'y', C_SPEED_TIME}, + {"speed-limit", ARG_UNUM, 'Y', C_SPEED_LIMIT}, + {"speed-time", ARG_UNUM, 'y', C_SPEED_TIME}, {"ssl", ARG_BOOL|ARG_TLS, ' ', C_SSL}, {"ssl-allow-beast", ARG_BOOL|ARG_TLS, ' ', C_SSL_ALLOW_BEAST}, {"ssl-auto-client-cert", ARG_BOOL|ARG_TLS, ' ', @@ -334,7 +334,7 @@ static const struct LongShort aliases[]= { {"test-duphandle", ARG_BOOL, ' ', C_TEST_DUPHANDLE}, {"test-event", ARG_BOOL, ' ', C_TEST_EVENT}, #endif - {"tftp-blksize", ARG_STRG, ' ', C_TFTP_BLKSIZE}, + {"tftp-blksize", ARG_UNUM, ' ', C_TFTP_BLKSIZE}, {"tftp-no-options", ARG_BOOL, ' ', C_TFTP_NO_OPTIONS}, {"time-cond", ARG_STRG, 'z', C_TIME_COND}, {"tls-earlydata", ARG_BOOL|ARG_TLS, ' ', C_TLS_EARLYDATA}, @@ -365,7 +365,7 @@ static const struct LongShort aliases[]= { {"variable", ARG_STRG, ' ', C_VARIABLE}, {"verbose", ARG_BOOL, 'v', C_VERBOSE}, {"version", ARG_BOOL, 'V', C_VERSION}, - {"vlan-priority", ARG_STRG, ' ', C_VLAN_PRIORITY}, + {"vlan-priority", ARG_UNUM, ' ', C_VLAN_PRIORITY}, #ifdef USE_WATT32 {"wdebug", ARG_BOOL, ' ', C_WDEBUG}, #endif @@ -2348,6 +2348,106 @@ static ParameterError opt_file(struct OperationConfig *config, return err; } +/* options that accept unsigned numbers */ +static ParameterError opt_unum(struct OperationConfig *config, + const struct LongShort *a, + const char *nextarg) +{ + ParameterError err = PARAM_OK; + long val; + err = str2unum(&val, nextarg); + if(err) + return err; + switch(a->cmd) { + case C_VLAN_PRIORITY: /* --vlan-priority */ + if(val > 7) + return PARAM_NUMBER_TOO_LARGE; + config->vlan_priority = val; + break; + case C_RETRY: /* --retry */ + config->req_retry = val; + break; + case C_KEEPALIVE_TIME: /* --keepalive-time */ + config->alivetime = val; + break; + case C_KEEPALIVE_CNT: /* --keepalive-cnt */ + config->alivecnt = val; + break; + case C_TFTP_BLKSIZE: /* --tftp-blksize */ + config->tftp_blksize = val; + break; + case C_HAPPY_EYEBALLS_TIMEOUT_MS: /* --happy-eyeballs-timeout-ms */ + config->happy_eyeballs_timeout_ms = val; + /* 0 is a valid value for this timeout */ + break; + case C_SPEED_TIME: /* --speed-time */ + /* low speed time */ + config->low_speed_time = val; + if(!config->low_speed_limit) + config->low_speed_limit = 1; + break; + case C_SPEED_LIMIT: /* --speed-limit */ + /* low speed limit */ + config->low_speed_limit = val; + if(!config->low_speed_time) + config->low_speed_time = 30; + break; + case C_PARALLEL_HOST: /* --parallel-max-host */ + if(val > MAX_PARALLEL_HOST) + global->parallel_host = MAX_PARALLEL_HOST; + else if(val < 1) + global->parallel_host = PARALLEL_HOST_DEFAULT; + else + global->parallel_host = (unsigned short)val; + break; + case C_PARALLEL_MAX: /* --parallel-max */ + if(val > MAX_PARALLEL) + global->parallel_max = MAX_PARALLEL; + else if(val < 1) + global->parallel_max = PARALLEL_DEFAULT; + else + global->parallel_max = (unsigned short)val; + break; + default: + DEBUGASSERT(0); + return PARAM_OPTION_UNKNOWN; + } + return err; +} + +/* handle options that accept seconds */ +static ParameterError opt_secs(struct OperationConfig *config, + const struct LongShort *a, + const char *nextarg) +{ + long val; + ParameterError err = secs2ms(&val, nextarg); + if(err) + return err; + switch(a->cmd) { + case C_CONNECT_TIMEOUT: /* --connect-timeout */ + config->connecttimeout_ms = val; + break; + case C_RETRY_DELAY: /* --retry-delay */ + config->retry_delay_ms = val; + break; + case C_RETRY_MAX_TIME: /* --retry-max-time */ + config->retry_maxtime_ms = val; + break; + case C_EXPECT100_TIMEOUT: /* --expect100-timeout */ + config->expect100timeout_ms = val; + break; + case C_MAX_TIME: /* --max-time */ + /* specified max time */ + config->timeout_ms = val; + break; + default: + DEBUGASSERT(0); + return PARAM_OPTION_UNKNOWN; + } + return err; +} + /* opt_string handles string options */ static ParameterError opt_string(struct OperationConfig *config, const struct LongShort *a, @@ -2355,7 +2455,6 @@ static ParameterError opt_string(struct OperationConfig *config, { ParameterError err = PARAM_OK; curl_off_t value; - long val; static const char *redir_protos[] = { "http", "https", @@ -2383,9 +2482,6 @@ static ParameterError opt_string(struct OperationConfig *config, config->authtype |= CURLAUTH_BEARER; return getstr(&config->oauth_bearer, nextarg, DENY_BLANK); - case C_CONNECT_TIMEOUT: /* --connect-timeout */ - return secs2ms(&config->connecttimeout_ms, nextarg); - case C_DOH_URL: /* --doh-url */ err = getstr(&config->doh_url, nextarg, ALLOW_BLANK); if(!err && config->doh_url && !config->doh_url[0]) @@ -2486,18 +2582,6 @@ static ParameterError opt_string(struct OperationConfig *config, err = str2unummax(&config->ip_tos, nextarg, 0xFF); break; } - case C_VLAN_PRIORITY: /* --vlan-priority */ - err = str2unummax(&config->vlan_priority, nextarg, 7); - break; - case C_RETRY: /* --retry */ - err = str2unum(&config->req_retry, nextarg); - break; - case C_RETRY_DELAY: /* --retry-delay */ - err = secs2ms(&config->retry_delay_ms, nextarg); - break; - case C_RETRY_MAX_TIME: /* --retry-max-time */ - err = secs2ms(&config->retry_maxtime_ms, nextarg); - break; case C_FTP_ACCOUNT: /* --ftp-account */ err = getstr(&config->ftp_account, nextarg, DENY_BLANK); break; @@ -2518,12 +2602,6 @@ static ParameterError opt_string(struct OperationConfig *config, err = getstr(&global->libcurl, nextarg, DENY_BLANK); #endif break; - case C_KEEPALIVE_TIME: /* --keepalive-time */ - err = str2unum(&config->alivetime, nextarg); - break; - case C_KEEPALIVE_CNT: /* --keepalive-cnt */ - err = str2unum(&config->alivecnt, nextarg); - break; case C_NOPROXY: /* --noproxy */ /* This specifies the noproxy list */ err = getstr(&config->noproxy, nextarg, ALLOW_BLANK); @@ -2533,9 +2611,6 @@ static ParameterError opt_string(struct OperationConfig *config, err = getstr(&config->proxy, nextarg, DENY_BLANK); config->proxyver = CURLPROXY_HTTP_1_0; break; - case C_TFTP_BLKSIZE: /* --tftp-blksize */ - err = str2unum(&config->tftp_blksize, nextarg); - break; case C_MAIL_FROM: /* --mail-from */ err = getstr(&config->mail_from, nextarg, DENY_BLANK); break; @@ -2575,9 +2650,6 @@ static ParameterError opt_string(struct OperationConfig *config, if(!err) err = check_protocol(config->proto_default); break; - case C_EXPECT100_TIMEOUT: /* --expect100-timeout */ - err = secs2ms(&config->expect100timeout_ms, nextarg); - break; case C_CONNECT_TO: /* --connect-to */ err = add2list(&config->connect_to, nextarg); break; @@ -2588,10 +2660,6 @@ static ParameterError opt_string(struct OperationConfig *config, err = PARAM_BAD_USE; } break; - case C_HAPPY_EYEBALLS_TIMEOUT_MS: /* --happy-eyeballs-timeout-ms */ - err = str2unum(&config->happy_eyeballs_timeout_ms, nextarg); - /* 0 is a valid value for this timeout */ - break; case C_TRACE_CONFIG: /* --trace-config */ global->trace_set = TRUE; if(set_trace_config(nextarg)) @@ -2789,10 +2857,6 @@ static ParameterError opt_string(struct OperationConfig *config, case C_PROXY_HEADER: /* --proxy-header */ err = parse_header(config, (cmdline_t)a->cmd, nextarg); break; - case C_MAX_TIME: /* --max-time */ - /* specified max time */ - err = secs2ms(&config->timeout_ms, nextarg); - break; case C_OUTPUT_DIR: /* --output-dir */ err = getstr(&config->output_dir, nextarg, DENY_BLANK); break; @@ -2842,40 +2906,6 @@ static ParameterError opt_string(struct OperationConfig *config, /* set custom request */ err = getstr(&config->customrequest, nextarg, DENY_BLANK); break; - case C_SPEED_TIME: /* --speed-time */ - /* low speed time */ - err = str2unum(&config->low_speed_time, nextarg); - if(!err && !config->low_speed_limit) - config->low_speed_limit = 1; - break; - case C_SPEED_LIMIT: /* --speed-limit */ - /* low speed limit */ - err = str2unum(&config->low_speed_limit, nextarg); - if(!err && !config->low_speed_time) - config->low_speed_time = 30; - break; - case C_PARALLEL_HOST: /* --parallel-max-host */ - err = str2unum(&val, nextarg); - if(err) - break; - if(val > MAX_PARALLEL_HOST) - global->parallel_host = MAX_PARALLEL_HOST; - else if(val < 1) - global->parallel_host = PARALLEL_HOST_DEFAULT; - else - global->parallel_host = (unsigned short)val; - break; - case C_PARALLEL_MAX: /* --parallel-max */ - err = str2unum(&val, nextarg); - if(err) - break; - if(val > MAX_PARALLEL) - global->parallel_max = MAX_PARALLEL; - else if(val < 1) - global->parallel_max = PARALLEL_DEFAULT; - else - global->parallel_max = (unsigned short)val; - break; case C_TIME_COND: /* --time-cond */ err = parse_time_cond(config, nextarg); break; @@ -2966,9 +2996,8 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ struct dynbuf nbuf; bool replaced; - if((ARGTYPE(a->desc) != ARG_STRG) && - (ARGTYPE(a->desc) != ARG_FILE)) { - /* --expand on an option that is not a string or a filename */ + if(ARGTYPE(a->desc) < ARG_STRG) { + /* --expand on an option that does not take an argument */ err = PARAM_EXPAND_ERROR; goto error; } @@ -3030,10 +3059,24 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ warnf("The argument '%s' starts with a Unicode character. " "Maybe ASCII was intended?", nextarg); } - if(ARGTYPE(a->desc) == ARG_FILE) + switch(ARGTYPE(a->desc)) { + case ARG_FILE: err = opt_file(config, a, nextarg, max_recursive); - else /* if(ARGTYPE(a->desc) == ARG_STRG) */ + break; + case ARG_STRG: err = opt_string(config, a, nextarg); + break; + case ARG_SECS: + err = opt_secs(config, a, nextarg); + break; + case ARG_UNUM: + err = opt_unum(config, a, nextarg); + break; + default: + DEBUGASSERT(0); + err = PARAM_OPTION_UNKNOWN; + break; + } if(a->desc & ARG_CLEAR) cleanarg(CURL_UNCONST(nextarg)); } diff --git a/src/tool_getparam.h b/src/tool_getparam.h index e3cbe5454a..e137cc322f 100644 --- a/src/tool_getparam.h +++ b/src/tool_getparam.h @@ -317,8 +317,10 @@ typedef enum { #define ARG_BOOL 1 /* accepts a --no-[name] prefix */ #define ARG_STRG 2 /* requires an argument */ #define ARG_FILE 3 /* requires an argument, usually a filename */ +#define ARG_SECS 4 /* requires a time in seconds */ +#define ARG_UNUM 5 /* requires a non-negative number */ -#define ARG_TYPEMASK 0x03 +#define ARG_TYPEMASK 0x07 #define ARGTYPE(x) ((x) & ARG_TYPEMASK) #define ARG_DEPR 0x10 /* deprecated option */