From: Daniel Stenberg Date: Sun, 25 May 2025 10:24:49 +0000 (+0200) Subject: tool_getparam: refactored, simplified X-Git-Tag: curl-8_14_1~46 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=698491f444947c89822c8776ab3eefe28259aedf;p=thirdparty%2Fcurl.git tool_getparam: refactored, simplified - add dedicated option functions for bools/none/strings - simplify clearing (some) arguments, use '*' instead of ' ' - scripts/top-complexity: remove getparameter from whitelist - handle --help separately - getstr and getstrn do not allow a NULL input - improve the ;auto check, it needs to be trailing - add toggle bit helper function - unify an error message for bad --no- use - introduce generic handling of deprecated options: ARG_DEPR - handle --no- prefix on more booleans: --cert-status --doh-cert-status --false-start --mptcp --ssl-no-revoke --ssl-revoke-best-effort --tcp-fastopen Closes #17448 --- diff --git a/scripts/top-complexity b/scripts/top-complexity index 675d83b185..9bd771048f 100755 --- a/scripts/top-complexity +++ b/scripts/top-complexity @@ -75,7 +75,7 @@ my @output=`$cmd`; # these functions can have these scores, but not higher my %whitelist = ( - 'getparameter' => 142 + ); # functions with complexity above this level causes the function to return error diff --git a/src/tool_cfgable.h b/src/tool_cfgable.h index 804e2c4a1a..c4f1d6b234 100644 --- a/src/tool_cfgable.h +++ b/src/tool_cfgable.h @@ -218,7 +218,7 @@ struct OperationConfig { long retry_delay; /* delay between retries (in seconds) */ long retry_maxtime; /* maximum time to keep retrying */ - long mime_options; /* Mime option flags. */ + unsigned long mime_options; /* Mime option flags. */ long tftp_blksize; /* TFTP BLKSIZE option */ long alivetime; /* keepalive-time */ long alivecnt; /* keepalive-cnt */ diff --git a/src/tool_getparam.c b/src/tool_getparam.c index 7354c059be..08b1167f19 100644 --- a/src/tool_getparam.c +++ b/src/tool_getparam.c @@ -51,14 +51,14 @@ static ParameterError getstr(char **str, const char *val, bool allowblank) free(*str); *str = NULL; } - if(val) { - if(!allowblank && !val[0]) - return PARAM_BLANK_STRING; + DEBUGASSERT(val); + if(!allowblank && !val[0]) + return PARAM_BLANK_STRING; + + *str = strdup(val); + if(!*str) + return PARAM_NO_MEM; - *str = strdup(val); - if(!*str) - return PARAM_NO_MEM; - } return PARAM_OK; } @@ -69,17 +69,17 @@ static ParameterError getstrn(char **str, const char *val, free(*str); *str = NULL; } - if(val) { - if(!allowblank && !val[0]) - return PARAM_BLANK_STRING; + DEBUGASSERT(val); + if(!allowblank && !val[0]) + return PARAM_BLANK_STRING; - *str = malloc(len + 1); - if(!*str) - return PARAM_NO_MEM; + *str = malloc(len + 1); + if(!*str) + return PARAM_NO_MEM; + + memcpy(*str, val, len); + (*str)[len] = 0; /* null terminate */ - memcpy(*str, val, len); - (*str)[len] = 0; /* null terminate */ - } return PARAM_OK; } @@ -96,7 +96,7 @@ static const struct LongShort aliases[]= { {"ca-native", ARG_BOOL|ARG_TLS, ' ', C_CA_NATIVE}, {"cacert", ARG_FILE|ARG_TLS, ' ', C_CACERT}, {"capath", ARG_FILE|ARG_TLS, ' ', C_CAPATH}, - {"cert", ARG_FILE|ARG_TLS, 'E', C_CERT}, + {"cert", ARG_FILE|ARG_TLS|ARG_CLEAR, 'E', C_CERT}, {"cert-status", ARG_BOOL|ARG_TLS, ' ', C_CERT_STATUS}, {"cert-type", ARG_STRG|ARG_TLS, ' ', C_CERT_TYPE}, {"ciphers", ARG_STRG|ARG_TLS, ' ', C_CIPHERS}, @@ -131,11 +131,11 @@ static const struct LongShort aliases[]= { {"dns-servers", ARG_STRG, ' ', C_DNS_SERVERS}, {"doh-cert-status", ARG_BOOL|ARG_TLS, ' ', C_DOH_CERT_STATUS}, {"doh-insecure", ARG_BOOL|ARG_TLS, ' ', C_DOH_INSECURE}, - {"doh-url" , ARG_STRG|ARG_TLS, ' ', C_DOH_URL}, + {"doh-url" , ARG_STRG, ' ', C_DOH_URL}, {"dump-ca-embed", ARG_NONE|ARG_TLS, ' ', C_DUMP_CA_EMBED}, {"dump-header", ARG_FILE, 'D', C_DUMP_HEADER}, {"ech", ARG_STRG|ARG_TLS, ' ', C_ECH}, - {"egd-file", ARG_STRG, ' ', C_EGD_FILE}, + {"egd-file", ARG_STRG|ARG_DEPR, ' ', C_EGD_FILE}, {"engine", ARG_STRG|ARG_TLS, ' ', C_ENGINE}, {"eprt", ARG_BOOL, ' ', C_EPRT}, {"epsv", ARG_BOOL, ' ', C_EPSV}, @@ -169,7 +169,7 @@ static const struct LongShort aliases[]= { {"haproxy-protocol", ARG_BOOL, ' ', C_HAPROXY_PROTOCOL}, {"head", ARG_BOOL, 'I', C_HEAD}, {"header", ARG_STRG, 'H', C_HEADER}, - {"help", ARG_BOOL, 'h', C_HELP}, + {"help", ARG_STRG, 'h', C_HELP}, {"hostpubmd5", ARG_STRG, ' ', C_HOSTPUBMD5}, {"hostpubsha256", ARG_STRG, ' ', C_HOSTPUBSHA256}, {"hsts", ARG_STRG|ARG_TLS, ' ', C_HSTS}, @@ -195,7 +195,7 @@ static const struct LongShort aliases[]= { {"keepalive", ARG_BOOL|ARG_NO, ' ', C_KEEPALIVE}, {"keepalive-cnt", ARG_STRG, ' ', C_KEEPALIVE_CNT}, {"keepalive-time", ARG_STRG, ' ', C_KEEPALIVE_TIME}, - {"key", ARG_FILE|ARG_TLS, ' ', C_KEY}, + {"key", ARG_FILE, ' ', C_KEY}, {"key-type", ARG_STRG|ARG_TLS, ' ', C_KEY_TYPE}, {"krb", ARG_STRG, ' ', C_KRB}, {"krb4", ARG_STRG, ' ', C_KRB4}, @@ -214,7 +214,7 @@ static const struct LongShort aliases[]= { {"max-filesize", ARG_STRG, ' ', C_MAX_FILESIZE}, {"max-redirs", ARG_STRG, ' ', C_MAX_REDIRS}, {"max-time", ARG_STRG, 'm', C_MAX_TIME}, - {"metalink", ARG_BOOL, ' ', C_METALINK}, + {"metalink", ARG_BOOL|ARG_DEPR, ' ', C_METALINK}, {"mptcp", ARG_BOOL, ' ', C_MPTCP}, {"negotiate", ARG_BOOL, ' ', C_NEGOTIATE}, {"netrc", ARG_BOOL, 'n', C_NETRC}, @@ -222,16 +222,16 @@ static const struct LongShort aliases[]= { {"netrc-optional", ARG_BOOL, ' ', C_NETRC_OPTIONAL}, {"next", ARG_NONE, ':', C_NEXT}, {"noproxy", ARG_STRG, ' ', C_NOPROXY}, - {"npn", ARG_BOOL|ARG_NO, ' ', C_NPN}, + {"npn", ARG_BOOL|ARG_DEPR, ' ', C_NPN}, {"ntlm", ARG_BOOL, ' ', C_NTLM}, - {"ntlm-wb", ARG_BOOL, ' ', C_NTLM_WB}, - {"oauth2-bearer", ARG_STRG, ' ', C_OAUTH2_BEARER}, + {"ntlm-wb", ARG_BOOL|ARG_DEPR, ' ', C_NTLM_WB}, + {"oauth2-bearer", ARG_STRG|ARG_CLEAR, ' ', C_OAUTH2_BEARER}, {"output", ARG_FILE, 'o', C_OUTPUT}, {"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}, - {"pass", ARG_STRG, ' ', C_PASS}, + {"pass", ARG_STRG|ARG_CLEAR, ' ', C_PASS}, {"path-as-is", ARG_BOOL, ' ', C_PATH_AS_IS}, {"pinnedpubkey", ARG_STRG|ARG_TLS, ' ', C_PINNEDPUBKEY}, {"post301", ARG_BOOL, ' ', C_POST301}, @@ -249,7 +249,7 @@ static const struct LongShort aliases[]= { {"proxy-ca-native", ARG_BOOL|ARG_TLS, ' ', C_PROXY_CA_NATIVE}, {"proxy-cacert", ARG_FILE|ARG_TLS, ' ', C_PROXY_CACERT}, {"proxy-capath", ARG_FILE|ARG_TLS, ' ', C_PROXY_CAPATH}, - {"proxy-cert", ARG_FILE|ARG_TLS, ' ', C_PROXY_CERT}, + {"proxy-cert", ARG_FILE|ARG_TLS|ARG_CLEAR, ' ', C_PROXY_CERT}, {"proxy-cert-type", ARG_STRG|ARG_TLS, ' ', C_PROXY_CERT_TYPE}, {"proxy-ciphers", ARG_STRG|ARG_TLS, ' ', C_PROXY_CIPHERS}, {"proxy-crlfile", ARG_FILE|ARG_TLS, ' ', C_PROXY_CRLFILE}, @@ -261,7 +261,7 @@ static const struct LongShort aliases[]= { {"proxy-key-type", ARG_STRG|ARG_TLS, ' ', C_PROXY_KEY_TYPE}, {"proxy-negotiate", ARG_BOOL, ' ', C_PROXY_NEGOTIATE}, {"proxy-ntlm", ARG_BOOL, ' ', C_PROXY_NTLM}, - {"proxy-pass", ARG_STRG, ' ', C_PROXY_PASS}, + {"proxy-pass", ARG_STRG|ARG_CLEAR, ' ', C_PROXY_PASS}, {"proxy-pinnedpubkey", ARG_STRG|ARG_TLS, ' ', C_PROXY_PINNEDPUBKEY}, {"proxy-service-name", ARG_STRG, ' ', C_PROXY_SERVICE_NAME}, {"proxy-ssl-allow-beast", ARG_BOOL|ARG_TLS, ' ', @@ -270,15 +270,15 @@ static const struct LongShort aliases[]= { C_PROXY_SSL_AUTO_CLIENT_CERT}, {"proxy-tls13-ciphers", ARG_STRG|ARG_TLS, ' ', C_PROXY_TLS13_CIPHERS}, {"proxy-tlsauthtype", ARG_STRG|ARG_TLS, ' ', C_PROXY_TLSAUTHTYPE}, - {"proxy-tlspassword", ARG_STRG|ARG_TLS, ' ', C_PROXY_TLSPASSWORD}, - {"proxy-tlsuser", ARG_STRG|ARG_TLS, ' ', C_PROXY_TLSUSER}, + {"proxy-tlspassword", ARG_STRG|ARG_TLS|ARG_CLEAR, ' ', C_PROXY_TLSPASSWORD}, + {"proxy-tlsuser", ARG_STRG|ARG_TLS|ARG_CLEAR, ' ', C_PROXY_TLSUSER}, {"proxy-tlsv1", ARG_NONE|ARG_TLS, ' ', C_PROXY_TLSV1}, - {"proxy-user", ARG_STRG, 'U', C_PROXY_USER}, + {"proxy-user", ARG_STRG|ARG_CLEAR, 'U', C_PROXY_USER}, {"proxy1.0", ARG_STRG, ' ', C_PROXY1_0}, {"proxytunnel", ARG_BOOL, 'p', C_PROXYTUNNEL}, {"pubkey", ARG_STRG, ' ', C_PUBKEY}, {"quote", ARG_STRG, 'Q', C_QUOTE}, - {"random-file", ARG_FILE, ' ', C_RANDOM_FILE}, + {"random-file", ARG_FILE|ARG_DEPR, ' ', C_RANDOM_FILE}, {"range", ARG_STRG, 'r', C_RANGE}, {"rate", ARG_STRG, ' ', C_RATE}, {"raw", ARG_BOOL, ' ', C_RAW}, @@ -325,8 +325,8 @@ static const struct LongShort aliases[]= { {"ssl-revoke-best-effort", ARG_BOOL|ARG_TLS, ' ', C_SSL_REVOKE_BEST_EFFORT}, {"ssl-sessions", ARG_FILE|ARG_TLS, ' ', C_SSL_SESSIONS}, - {"sslv2", ARG_NONE|ARG_TLS, '2', C_SSLV2}, - {"sslv3", ARG_NONE|ARG_TLS, '3', C_SSLV3}, + {"sslv2", ARG_NONE|ARG_DEPR, '2', C_SSLV2}, + {"sslv3", ARG_NONE|ARG_DEPR, '3', C_SSLV3}, {"stderr", ARG_FILE, ' ', C_STDERR}, {"styled-output", ARG_BOOL, ' ', C_STYLED_OUTPUT}, {"suppress-connect-headers", ARG_BOOL, ' ', C_SUPPRESS_CONNECT_HEADERS}, @@ -344,8 +344,8 @@ static const struct LongShort aliases[]= { {"tls-max", ARG_STRG|ARG_TLS, ' ', C_TLS_MAX}, {"tls13-ciphers", ARG_STRG|ARG_TLS, ' ', C_TLS13_CIPHERS}, {"tlsauthtype", ARG_STRG|ARG_TLS, ' ', C_TLSAUTHTYPE}, - {"tlspassword", ARG_STRG|ARG_TLS, ' ', C_TLSPASSWORD}, - {"tlsuser", ARG_STRG|ARG_TLS, ' ', C_TLSUSER}, + {"tlspassword", ARG_STRG|ARG_TLS|ARG_CLEAR, ' ', C_TLSPASSWORD}, + {"tlsuser", ARG_STRG|ARG_TLS|ARG_CLEAR, ' ', C_TLSUSER}, {"tlsv1", ARG_NONE|ARG_TLS, '1', C_TLSV1}, {"tlsv1.0", ARG_NONE|ARG_TLS, ' ', C_TLSV1_0}, {"tlsv1.1", ARG_NONE|ARG_TLS, ' ', C_TLSV1_1}, @@ -363,7 +363,7 @@ static const struct LongShort aliases[]= { {"url", ARG_STRG, ' ', C_URL}, {"url-query", ARG_STRG, ' ', C_URL_QUERY}, {"use-ascii", ARG_BOOL, 'B', C_USE_ASCII}, - {"user", ARG_STRG, 'u', C_USER}, + {"user", ARG_STRG|ARG_CLEAR, 'u', C_USER}, {"user-agent", ARG_STRG, 'A', C_USER_AGENT}, {"variable", ARG_STRG, ' ', C_VARIABLE}, {"verbose", ARG_BOOL, 'v', C_VERBOSE}, @@ -573,18 +573,18 @@ static ParameterError GetSizeParameter(struct GlobalConfig *global, } #ifdef HAVE_WRITABLE_ARGV -static void cleanarg(argv_item_t str) +static void cleanarg(char *str) { /* now that getstr has copied the contents of nextarg, wipe the next * argument out so that the username:password is not displayed in the * system process list */ if(str) { size_t len = strlen(str); - memset(str, ' ', len); + memset(str, '*', len); } } #else -#define cleanarg(x) +#define cleanarg(x) tool_nop_stmt #endif /* the maximum size we allow the dynbuf generated string */ @@ -1469,9 +1469,10 @@ static ParameterError parse_upload_file(struct OperationConfig *config, return err; } +static size_t verbose_nopts; + static ParameterError parse_verbose(struct GlobalConfig *global, - bool toggle, - size_t nopts) + bool toggle) { ParameterError err = PARAM_OK; @@ -1484,7 +1485,7 @@ static ParameterError parse_verbose(struct GlobalConfig *global, global->tracetype = TRACE_NONE; return err; } - else if(!nopts) { + else if(!verbose_nopts) { /* fist `-v` in an argument resets to base verbosity */ global->verbosity = 0; if(set_trace_config(global, "-all")) @@ -1673,23 +1674,501 @@ static ParameterError parse_upload_flags(struct OperationConfig *config, return err; } -ParameterError getparameter(const char *flag, /* f or -long-flag */ - const char *nextarg, /* NULL if unset */ - argv_item_t cleararg1, - argv_item_t cleararg2, - bool *usedarg, /* set to TRUE if the arg - has been used */ - struct GlobalConfig *global, - struct OperationConfig *config) +/* if 'toggle' is TRUE, set the 'bits' in 'modify'. + if 'toggle' is FALSE, clear the 'bits' in 'modify' +*/ +static void togglebit(bool toggle, unsigned long *modify, unsigned long bits) +{ + if(toggle) + *modify |= bits; + else + *modify &= ~bits; +} + +/* opt_depr is the function that handles ARG_DEPR options */ +static void opt_depr(struct GlobalConfig *global, + const struct LongShort *a) +{ + warnf(global, "--%s is deprecated and has no function anymore", a->lname); +} + +/* opt_none is the function that handles ARG_NONE options */ +static ParameterError opt_none(struct GlobalConfig *global, + struct OperationConfig *config, + const struct LongShort *a) +{ + switch(a->cmd) { + case C_DUMP_CA_EMBED: /* --dump-ca-embed */ + return PARAM_CA_EMBED_REQUESTED; + + case C_HTTP1_0: /* --http1.0 */ + /* HTTP version 1.0 */ + sethttpver(global, config, CURL_HTTP_VERSION_1_0); + break; + case C_HTTP1_1: /* --http1.1 */ + /* HTTP version 1.1 */ + sethttpver(global, config, CURL_HTTP_VERSION_1_1); + break; + case C_HTTP2: /* --http2 */ + /* HTTP version 2.0 */ + if(!feature_http2) + return PARAM_LIBCURL_DOESNT_SUPPORT; + sethttpver(global, config, CURL_HTTP_VERSION_2_0); + break; + case C_HTTP2_PRIOR_KNOWLEDGE: /* --http2-prior-knowledge */ + /* HTTP version 2.0 over clean TCP */ + if(!feature_http2) + return PARAM_LIBCURL_DOESNT_SUPPORT; + sethttpver(global, config, CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE); + break; + case C_HTTP3: /* --http3: */ + /* Try HTTP/3, allow fallback */ + if(!feature_http3) + return PARAM_LIBCURL_DOESNT_SUPPORT; + else + sethttpver(global, config, CURL_HTTP_VERSION_3); + break; + case C_HTTP3_ONLY: /* --http3-only */ + /* Try HTTP/3 without fallback */ + if(!feature_http3) + return PARAM_LIBCURL_DOESNT_SUPPORT; + else + sethttpver(global, config, CURL_HTTP_VERSION_3ONLY); + break; + case C_TLSV1: /* --tlsv1 */ + config->ssl_version = CURL_SSLVERSION_TLSv1; + break; + case C_TLSV1_0: /* --tlsv1.0 */ + config->ssl_version = CURL_SSLVERSION_TLSv1_0; + break; + case C_TLSV1_1: /* --tlsv1.1 */ + config->ssl_version = CURL_SSLVERSION_TLSv1_1; + break; + case C_TLSV1_2: /* --tlsv1.2 */ + config->ssl_version = CURL_SSLVERSION_TLSv1_2; + break; + case C_TLSV1_3: /* --tlsv1.3 */ + config->ssl_version = CURL_SSLVERSION_TLSv1_3; + break; + case C_IPV4: /* --ipv4 */ + config->ip_version = CURL_IPRESOLVE_V4; + break; + case C_IPV6: /* --ipv6 */ + config->ip_version = CURL_IPRESOLVE_V6; + break; + case C_NEXT: /* --next */ + return PARAM_NEXT_OPERATION; + case C_PROXY_TLSV1: /* --proxy-tlsv1 */ + /* TLS version 1 for proxy */ + config->proxy_ssl_version = CURL_SSLVERSION_TLSv1; + break; + } + return PARAM_OK; +} + +/* opt_bool is the function that handles boolean options */ +static ParameterError opt_bool(struct GlobalConfig *global, + struct OperationConfig *config, + const struct LongShort *a, + bool toggle) +{ + switch(a->cmd) { + case C_ALPN: /* --alpn */ + config->noalpn = !toggle; + break; + case C_DISABLE_EPSV: /* --disable-epsv */ + config->disable_epsv = toggle; + break; + case C_DISALLOW_USERNAME_IN_URL: /* --disallow-username-in-url */ + config->disallow_username_in_url = toggle; + break; + case C_EPSV: /* --epsv */ + config->disable_epsv = !toggle; + break; + case C_COMPRESSED: /* --compressed */ + if(toggle && !(feature_libz || feature_brotli || feature_zstd)) + return PARAM_LIBCURL_DOESNT_SUPPORT; + else + config->encoding = toggle; + break; + case C_TR_ENCODING: /* --tr-encoding */ + config->tr_encoding = toggle; + break; + case C_DIGEST: /* --digest */ + togglebit(toggle, &config->authtype, CURLAUTH_DIGEST); + break; + case C_FTP_CREATE_DIRS: /* --ftp-create-dirs */ + config->ftp_create_dirs = toggle; + break; + case C_CREATE_DIRS: /* --create-dirs */ + config->create_dirs = toggle; + break; + case C_PROXY_NTLM: /* --proxy-ntlm */ + if(!feature_ntlm) + return PARAM_LIBCURL_DOESNT_SUPPORT; + else + config->proxyntlm = toggle; + break; + case C_CRLF: /* --crlf */ + config->crlf = toggle; + break; + case C_HAPROXY_PROTOCOL: /* --haproxy-protocol */ + config->haproxy_protocol = toggle; + break; + case C_NEGOTIATE: /* --negotiate */ + if(!feature_spnego && toggle) + return PARAM_LIBCURL_DOESNT_SUPPORT; + togglebit(toggle, &config->authtype, CURLAUTH_NEGOTIATE); + break; + case C_NTLM: /* --ntlm */ + if(!feature_ntlm && toggle) + return PARAM_LIBCURL_DOESNT_SUPPORT; + togglebit(toggle, &config->authtype, CURLAUTH_NTLM); + break; + case C_BASIC: /* --basic */ + togglebit(toggle, &config->authtype, CURLAUTH_BASIC); + break; + case C_ANYAUTH: /* --anyauth */ + if(toggle) + config->authtype = CURLAUTH_ANY; + /* --no-anyauth simply does not touch it */ + break; +#ifdef USE_WATT32 + case C_WDEBUG: /* --wdebug */ + dbug_init(); + break; +#endif + case C_DISABLE_EPRT: /* --disable-eprt */ + config->disable_eprt = toggle; + break; + case C_EPRT: /* --eprt */ + config->disable_eprt = !toggle; + break; + case C_XATTR: /* --xattr */ + config->xattr = toggle; + break; + case C_FTP_SSL: /* --ftp-ssl */ + case C_SSL: /* --ssl */ + config->ftp_ssl = toggle; + if(config->ftp_ssl) + warnf(global, "--%s is an insecure option, consider --ssl-reqd instead", + a->lname); + break; + case C_FTP_SSL_CCC: /* --ftp-ssl-ccc */ + config->ftp_ssl_ccc = toggle; + if(!config->ftp_ssl_ccc_mode) + config->ftp_ssl_ccc_mode = CURLFTPSSL_CCC_PASSIVE; + break; + case C_TCP_NODELAY: /* --tcp-nodelay */ + config->tcp_nodelay = toggle; + break; + case C_PROXY_DIGEST: /* --proxy-digest */ + config->proxydigest = toggle; + break; + case C_PROXY_BASIC: /* --proxy-basic */ + config->proxybasic = toggle; + break; + case C_RETRY_CONNREFUSED: /* --retry-connrefused */ + config->retry_connrefused = toggle; + break; + case C_RETRY_ALL_ERRORS: /* --retry-all-errors */ + config->retry_all_errors = toggle; + break; + case C_PROXY_NEGOTIATE: /* --proxy-negotiate */ + if(!feature_spnego) + return PARAM_LIBCURL_DOESNT_SUPPORT; + else + config->proxynegotiate = toggle; + break; + case C_FORM_ESCAPE: /* --form-escape */ + togglebit(toggle, &config->mime_options, CURLMIMEOPT_FORMESCAPE); + break; + case C_PROXY_ANYAUTH: /* --proxy-anyauth */ + config->proxyanyauth = toggle; + break; + case C_TRACE_TIME: /* --trace-time */ + global->tracetime = toggle; + break; + case C_IGNORE_CONTENT_LENGTH: /* --ignore-content-length */ + config->ignorecl = toggle; + break; + case C_FTP_SKIP_PASV_IP: /* --ftp-skip-pasv-ip */ + config->ftp_skip_ip = toggle; + break; + case C_FTP_SSL_REQD: /* --ftp-ssl-reqd */ + case C_SSL_REQD: /* --ssl-reqd */ + config->ftp_ssl_reqd = toggle; + break; + case C_SESSIONID: /* --sessionid */ + config->disable_sessionid = !toggle; + break; + case C_FTP_SSL_CONTROL: /* --ftp-ssl-control */ + config->ftp_ssl_control = toggle; + break; + case C_RAW: /* --raw */ + config->raw = toggle; + break; + case C_KEEPALIVE: /* --keepalive */ + config->nokeepalive = !toggle; + break; + case C_POST301: /* --post301 */ + config->post301 = toggle; + break; + case C_POST302: /* --post302 */ + config->post302 = toggle; + break; + case C_POST303: /* --post303 */ + config->post303 = toggle; + break; + case C_SOCKS5_GSSAPI_NEC: /* --socks5-gssapi-nec */ + config->socks5_gssapi_nec = toggle; + break; + case C_FTP_PRET: /* --ftp-pret */ + config->ftp_pret = toggle; + break; + case C_SASL_IR: /* --sasl-ir */ + config->sasl_ir = toggle; + break; +#ifdef DEBUGBUILD + case C_TEST_DUPHANDLE: /* --test-duphandle */ + global->test_duphandle = toggle; + break; + case C_TEST_EVENT: /* --test-event */ + global->test_event_based = toggle; + break; +#endif + case C_PATH_AS_IS: /* --path-as-is */ + config->path_as_is = toggle; + break; + case C_TFTP_NO_OPTIONS: /* --tftp-no-options */ + config->tftp_no_options = toggle; + break; + case C_TLS_EARLYDATA: /* --tls-earlydata */ + config->ssl_allow_earlydata = toggle; + break; + case C_SUPPRESS_CONNECT_HEADERS: /* --suppress-connect-headers */ + config->suppress_connect_headers = toggle; + break; + case C_COMPRESSED_SSH: /* --compressed-ssh */ + config->ssh_compression = toggle; + break; + case C_TRACE_IDS: /* --trace-ids */ + global->traceids = toggle; + break; + case C_PROGRESS_METER: /* --progress-meter */ + global->noprogress = !toggle; + break; + case C_PROGRESS_BAR: /* --progress-bar */ + global->progressmode = toggle ? CURL_PROGRESS_BAR : CURL_PROGRESS_STATS; + break; + case C_HTTP0_9: /* --http0.9 */ + config->http09_allowed = toggle; + break; + case C_PROXY_HTTP2: /* --proxy-http2 */ + if(!feature_httpsproxy || !feature_http2) + return PARAM_LIBCURL_DOESNT_SUPPORT; + + config->proxyver = toggle ? CURLPROXY_HTTPS2 : CURLPROXY_HTTPS; + break; + case C_APPEND: /* --append */ + config->ftp_append = toggle; + break; + case C_USE_ASCII: /* --use-ascii */ + config->use_ascii = toggle; + break; + case C_CA_NATIVE: /* --ca-native */ + config->native_ca_store = toggle; + break; + case C_PROXY_CA_NATIVE: /* --proxy-ca-native */ + config->proxy_native_ca_store = toggle; + break; + case C_SSL_ALLOW_BEAST: /* --ssl-allow-beast */ + config->ssl_allow_beast = toggle; + break; + case C_SSL_AUTO_CLIENT_CERT: /* --ssl-auto-client-cert */ + config->ssl_auto_client_cert = toggle; + break; + case C_PROXY_SSL_AUTO_CLIENT_CERT: /* --proxy-ssl-auto-client-cert */ + config->proxy_ssl_auto_client_cert = toggle; + break; + case C_CERT_STATUS: /* --cert-status */ + config->verifystatus = toggle; + break; + case C_DOH_CERT_STATUS: /* --doh-cert-status */ + config->doh_verifystatus = toggle; + break; + case C_FALSE_START: /* --false-start */ + config->falsestart = toggle; + break; + case C_SSL_NO_REVOKE: /* --ssl-no-revoke */ + config->ssl_no_revoke = toggle; + break; + case C_SSL_REVOKE_BEST_EFFORT: /* --ssl-revoke-best-effort */ + config->ssl_revoke_best_effort = toggle; + break; + case C_TCP_FASTOPEN: /* --tcp-fastopen */ + config->tcp_fastopen = toggle; + break; + case C_PROXY_SSL_ALLOW_BEAST: /* --proxy-ssl-allow-beast */ + config->proxy_ssl_allow_beast = toggle; + break; + case C_PROXY_INSECURE: /* --proxy-insecure */ + config->proxy_insecure_ok = toggle; + break; + case C_SOCKS5_BASIC: /* --socks5-basic */ + togglebit(toggle, &config->socks5_auth, CURLAUTH_BASIC); + break; + case C_SOCKS5_GSSAPI: /* --socks5-gssapi */ + togglebit(toggle, &config->socks5_auth, CURLAUTH_GSSAPI); + break; + case C_FAIL_EARLY: /* --fail-early */ + global->fail_early = toggle; + break; + case C_STYLED_OUTPUT: /* --styled-output */ + global->styled_output = toggle; + break; + case C_MAIL_RCPT_ALLOWFAILS: /* --mail-rcpt-allowfails */ + config->mail_rcpt_allowfails = toggle; + break; + case C_FAIL_WITH_BODY: /* --fail-with-body */ + config->failwithbody = toggle; + if(config->failonerror && config->failwithbody) { + errorf(config->global, "You must select either --fail or " + "--fail-with-body, not both."); + return PARAM_BAD_USE; + } + break; + case C_REMOVE_ON_ERROR: /* --remove-on-error */ + if(config->use_resume && toggle) { + errorf(config->global, + "--continue-at is mutually exclusive with --remove-on-error"); + return PARAM_BAD_USE; + } + config->rm_partial = toggle; + break; + case C_FAIL: /* --fail */ + config->failonerror = toggle; + if(config->failonerror && config->failwithbody) { + errorf(config->global, "You must select either --fail or " + "--fail-with-body, not both."); + return PARAM_BAD_USE; + } + break; + case C_GLOBOFF: /* --globoff */ + config->globoff = toggle; + break; + case C_GET: /* --get */ + config->use_httpget = toggle; + break; + case C_INCLUDE: /* --include */ + case C_SHOW_HEADERS: /* --show-headers */ + config->show_headers = toggle; + break; + case C_JUNK_SESSION_COOKIES: /* --junk-session-cookies */ + config->cookiesession = toggle; + break; + case C_HEAD: /* --head */ + config->no_body = toggle; + config->show_headers = toggle; + if(SetHTTPrequest(config, (config->no_body) ? TOOL_HTTPREQ_HEAD : + TOOL_HTTPREQ_GET, &config->httpreq)) + return PARAM_BAD_USE; + break; + case C_REMOTE_HEADER_NAME: /* --remote-header-name */ + config->content_disposition = toggle; + break; + case C_INSECURE: /* --insecure */ + config->insecure_ok = toggle; + break; + case C_DOH_INSECURE: /* --doh-insecure */ + config->doh_insecure_ok = toggle; + break; + case C_LIST_ONLY: /* --list-only */ + config->dirlistonly = toggle; /* only list the names of the FTP dir */ + break; + case C_LOCATION_TRUSTED: /* --location-trusted */ + config->unrestricted_auth = toggle; + FALLTHROUGH(); + case C_LOCATION: /* --location */ + config->followlocation = toggle; /* Follow Location: HTTP headers */ + break; + case C_MANUAL: /* --manual */ + if(toggle) /* --no-manual shows no manual... */ + return PARAM_MANUAL_REQUESTED; + break; + case C_NETRC_OPTIONAL: /* --netrc-optional */ + config->netrc_opt = toggle; + break; + case C_NETRC: /* --netrc */ + config->netrc = toggle; + break; + case C_BUFFER: /* --buffer */ + config->nobuffer = !toggle; + break; + case C_REMOTE_NAME_ALL: /* --remote-name-all */ + config->remote_name_all = toggle; + break; + case C_CLOBBER: /* --clobber */ + if(config->use_resume && !toggle) { + errorf(config->global, + "--continue-at is mutually exclusive with --no-clobber"); + return PARAM_BAD_USE; + } + config->file_clobber_mode = toggle ? CLOBBER_ALWAYS : CLOBBER_NEVER; + break; + case C_REMOTE_NAME: /* --remote-name */ + return parse_remote_name(config, toggle); + break; + case C_PROXYTUNNEL: /* --proxytunnel */ + config->proxytunnel = toggle; + break; + case C_DISABLE: /* --disable */ + /* if used first, already taken care of, we do it like this so we do not + cause an error! */ + break; + case C_REMOTE_TIME: /* --remote-time */ + config->remote_time = toggle; + break; + 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; + case C_VERBOSE: /* --verbose */ + return parse_verbose(global, toggle); + break; + case C_VERSION: /* --version */ + if(toggle) /* --no-version yields no output! */ + return PARAM_VERSION_INFO_REQUESTED; + break; + case C_PARALLEL: /* --parallel */ + global->parallel = toggle; + break; + case C_PARALLEL_IMMEDIATE: /* --parallel-immediate */ + global->parallel_connect = toggle; + break; + case C_MPTCP: /* --mptcp */ + config->mptcp = toggle; + break; + default: + return PARAM_OPTION_UNKNOWN; + } + return PARAM_OK; +} + + +/* opt_filestring handles string and file options */ +static ParameterError opt_filestring(struct GlobalConfig *global, + struct OperationConfig *config, + const struct LongShort *a, + const char *nextarg) { - const char *parse = NULL; - bool longopt = FALSE; - bool singleopt = FALSE; /* when true means '-o foo' used '-ofoo' */ - size_t nopts = 0; /* options processed in `flag`*/ ParameterError err = PARAM_OK; - bool toggle = TRUE; /* how to switch boolean options, on or off. Controlled - by using --OPTION or --no-OPTION */ - bool nextalloc = FALSE; /* if nextarg is allocated */ + curl_off_t value; static const char *redir_protos[] = { "http", "https", @@ -1697,1270 +2176,772 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ "ftps", NULL }; - const struct LongShort *a = NULL; - curl_off_t value; -#ifdef HAVE_WRITABLE_ARGV - argv_item_t clearthis = NULL; -#else - (void)cleararg1; - (void)cleararg2; -#endif - - *usedarg = FALSE; /* default is that we do not use the arg */ - - if(('-' != flag[0]) || ('-' == flag[1])) { - /* this should be a long name */ - const char *word = ('-' == flag[0]) ? flag + 2 : flag; - bool noflagged = FALSE; - bool expand = FALSE; + if(!nextarg) + nextarg = ""; + + switch(a->cmd) { + case C_DNS_IPV4_ADDR: /* --dns-ipv4-addr */ + if(!curlinfo->ares_num) /* c-ares is needed for this */ + return PARAM_LIBCURL_DOESNT_SUPPORT; + /* addr in dot notation */ + return getstr(&config->dns_ipv4_addr, nextarg, DENY_BLANK); + + case C_DNS_IPV6_ADDR: /* --dns-ipv6-addr */ + if(!curlinfo->ares_num) /* c-ares is needed for this */ + return PARAM_LIBCURL_DOESNT_SUPPORT; + /* addr in dot notation */ + return getstr(&config->dns_ipv6_addr, nextarg, DENY_BLANK); + + case C_OAUTH2_BEARER: /* --oauth2-bearer */ + 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]) + /* if given a blank string, make it NULL again */ + tool_safefree(config->doh_url); + break; - if(!strncmp(word, "no-", 3)) { - /* disable this option but ignore the "no-" part when looking for it */ - word += 3; - toggle = FALSE; - noflagged = TRUE; - } - else if(!strncmp(word, "expand-", 7)) { - /* variable expansions is to be done on the argument */ - word += 7; - expand = TRUE; - } + case C_CIPHERS: /* -- ciphers */ + err = getstr(&config->cipher_list, nextarg, DENY_BLANK); + break; - a = findlongopt(word); - if(a) { - longopt = TRUE; - } - else { - err = PARAM_OPTION_UNKNOWN; - goto error; + case C_DNS_INTERFACE: /* --dns-interface */ + if(!curlinfo->ares_num) /* c-ares is needed for this */ + err = PARAM_LIBCURL_DOESNT_SUPPORT; + else + /* interface name */ + err = getstr(&config->dns_interface, nextarg, DENY_BLANK); + break; + case C_DNS_SERVERS: /* --dns-servers */ + if(!curlinfo->ares_num) /* c-ares is needed for this */ + err = PARAM_LIBCURL_DOESNT_SUPPORT; + else + /* IP addrs of DNS servers */ + err = getstr(&config->dns_servers, nextarg, DENY_BLANK); + break; + case C_TRACE: /* --trace */ + err = getstr(&global->trace_dump, nextarg, DENY_BLANK); + if(!err) { + if(global->tracetype && (global->tracetype != TRACE_BIN)) + warnf(global, "--trace overrides an earlier trace/verbose option"); + global->tracetype = TRACE_BIN; } - if(noflagged && (ARGTYPE(a->desc) != ARG_BOOL)) { - /* --no- prefixed an option that is not boolean! */ - err = PARAM_NO_NOT_BOOLEAN; - goto error; + break; + case C_TRACE_ASCII: /* --trace-ascii */ + err = getstr(&global->trace_dump, nextarg, DENY_BLANK); + if(!err) { + if(global->tracetype && (global->tracetype != TRACE_ASCII)) + warnf(global, + "--trace-ascii overrides an earlier trace/verbose option"); + global->tracetype = TRACE_ASCII; } - else if(expand && nextarg) { - 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 */ - err = PARAM_EXPAND_ERROR; - goto error; - } - err = varexpand(global, nextarg, &nbuf, &replaced); - if(err) { - curlx_dyn_free(&nbuf); - goto error; - } - if(replaced) { - nextarg = curlx_dyn_ptr(&nbuf); - nextalloc = TRUE; - } + break; + case C_LIMIT_RATE: /* --limit-rate */ + err = GetSizeParameter(global, nextarg, "rate", &value); + if(!err) { + config->recvpersecond = value; + config->sendpersecond = value; } - } - else { - flag++; /* prefixed with one dash, pass it */ - parse = flag; - } - - do { - /* we can loop here if we have multiple single-letters */ - cmdline_t cmd; - - if(!longopt && !a) { - a = findshortopt(*parse); - if(!a) { - err = PARAM_OPTION_UNKNOWN; - break; - } - } - cmd = (cmdline_t)a->cmd; - if(ARGTYPE(a->desc) >= ARG_STRG) { - /* this option requires an extra parameter */ - if(!longopt && parse[1]) { - nextarg = &parse[1]; /* this is the actual extra parameter */ - singleopt = TRUE; /* do not loop anymore after this */ -#ifdef HAVE_WRITABLE_ARGV - if(cleararg1) - clearthis = &cleararg1[parse + 2 - flag]; -#endif - } - else if(!nextarg) { - err = PARAM_REQUIRES_PARAMETER; - break; - } - else { -#ifdef HAVE_WRITABLE_ARGV - if(cleararg2) - clearthis = cleararg2; -#endif - *usedarg = TRUE; /* mark it as used */ - } - - if((ARGTYPE(a->desc) == ARG_FILE) && - (nextarg[0] == '-') && nextarg[1]) { - /* if the filename looks like a command line option */ - warnf(global, "The filename argument '%s' looks like a flag.", - nextarg); - } - else if(!strncmp("\xe2\x80\x9c", nextarg, 3)) { - warnf(global, "The argument '%s' starts with a Unicode quote where " - "maybe an ASCII \" was intended?", - nextarg); - } - } - else if((ARGTYPE(a->desc) == ARG_NONE) && !toggle) { - err = PARAM_NO_PREFIX; - break; - } - else if((a->desc & ARG_TLS) && !feature_ssl) { - err = PARAM_LIBCURL_DOESNT_SUPPORT; - break; - } - - if(!nextarg) - /* this is a precaution mostly to please scan-build, as all arguments - that use nextarg should be marked as such and they will check that - nextarg is set before continuing, but code analyzers are not always - that aware of that state */ - nextarg = ""; - - switch(cmd) { - case C_RANDOM_FILE: /* --random-file */ - case C_EGD_FILE: /* --egd-file */ - case C_NTLM_WB: /* --ntlm-wb */ - case C_NPN: /* --npn */ - case C_SSLV2: /* --sslv2 */ - case C_SSLV3: /* --sslv3 */ - warnf(global, "--%s is deprecated and has no function anymore", - a->lname); - break; - case C_DNS_IPV4_ADDR: /* --dns-ipv4-addr */ - if(!curlinfo->ares_num) /* c-ares is needed for this */ - err = PARAM_LIBCURL_DOESNT_SUPPORT; - else - /* addr in dot notation */ - err = getstr(&config->dns_ipv4_addr, nextarg, DENY_BLANK); - break; - case C_DNS_IPV6_ADDR: /* --dns-ipv6-addr */ - if(!curlinfo->ares_num) /* c-ares is needed for this */ - err = PARAM_LIBCURL_DOESNT_SUPPORT; - else - /* addr in dot notation */ - err = getstr(&config->dns_ipv6_addr, nextarg, DENY_BLANK); - break; - case C_OAUTH2_BEARER: /* --oauth2-bearer */ - err = getstr(&config->oauth_bearer, nextarg, DENY_BLANK); - if(!err) { - cleanarg(clearthis); - config->authtype |= CURLAUTH_BEARER; - } - break; - case C_CONNECT_TIMEOUT: /* --connect-timeout */ - err = secs2ms(&config->connecttimeout_ms, nextarg); - break; - case C_DOH_URL: /* --doh-url */ - err = getstr(&config->doh_url, nextarg, ALLOW_BLANK); - if(!err && config->doh_url && !config->doh_url[0]) - /* if given a blank string, make it NULL again */ - tool_safefree(config->doh_url); - break; - case C_CIPHERS: /* -- ciphers */ - err = getstr(&config->cipher_list, nextarg, DENY_BLANK); - break; - case C_DNS_INTERFACE: /* --dns-interface */ - if(!curlinfo->ares_num) /* c-ares is needed for this */ - err = PARAM_LIBCURL_DOESNT_SUPPORT; - else - /* interface name */ - err = getstr(&config->dns_interface, nextarg, DENY_BLANK); - break; - case C_DISABLE_EPSV: /* --disable-epsv */ - config->disable_epsv = toggle; - break; - case C_DISALLOW_USERNAME_IN_URL: /* --disallow-username-in-url */ - config->disallow_username_in_url = toggle; - break; - case C_EPSV: /* --epsv */ - config->disable_epsv = !toggle; - break; - case C_DNS_SERVERS: /* --dns-servers */ - if(!curlinfo->ares_num) /* c-ares is needed for this */ - err = PARAM_LIBCURL_DOESNT_SUPPORT; - else - /* IP addrs of DNS servers */ - err = getstr(&config->dns_servers, nextarg, DENY_BLANK); - break; - case C_TRACE: /* --trace */ - err = getstr(&global->trace_dump, nextarg, DENY_BLANK); - if(!err) { - if(global->tracetype && (global->tracetype != TRACE_BIN)) - warnf(global, "--trace overrides an earlier trace/verbose option"); - global->tracetype = TRACE_BIN; - } - break; - case C_TRACE_ASCII: /* --trace-ascii */ - err = getstr(&global->trace_dump, nextarg, DENY_BLANK); - if(!err) { - if(global->tracetype && (global->tracetype != TRACE_ASCII)) - warnf(global, - "--trace-ascii overrides an earlier trace/verbose option"); - global->tracetype = TRACE_ASCII; - } - break; - case C_ALPN: /* --alpn */ - config->noalpn = !toggle; - break; - case C_LIMIT_RATE: /* --limit-rate */ - err = GetSizeParameter(global, nextarg, "rate", &value); - if(!err) { - config->recvpersecond = value; - config->sendpersecond = value; - } - break; - case C_RATE: - err = set_rate(global, nextarg); - break; - case C_COMPRESSED: /* --compressed */ - if(toggle && !(feature_libz || feature_brotli || feature_zstd)) - err = PARAM_LIBCURL_DOESNT_SUPPORT; - else - config->encoding = toggle; - break; - case C_TR_ENCODING: /* --tr-encoding */ - config->tr_encoding = toggle; - break; - case C_DIGEST: /* --digest */ - if(toggle) - config->authtype |= CURLAUTH_DIGEST; - else - config->authtype &= ~CURLAUTH_DIGEST; - break; - case C_NEGOTIATE: /* --negotiate */ - if(!toggle) - config->authtype &= ~CURLAUTH_NEGOTIATE; - else if(feature_spnego) - config->authtype |= CURLAUTH_NEGOTIATE; - else - err = PARAM_LIBCURL_DOESNT_SUPPORT; - break; - case C_NTLM: /* --ntlm */ - if(!toggle) - config->authtype &= ~CURLAUTH_NTLM; - else if(feature_ntlm) - config->authtype |= CURLAUTH_NTLM; - else - err = PARAM_LIBCURL_DOESNT_SUPPORT; - break; - case C_BASIC: /* --basic */ - if(toggle) - config->authtype |= CURLAUTH_BASIC; - else - config->authtype &= ~CURLAUTH_BASIC; - break; - case C_ANYAUTH: /* --anyauth */ - if(toggle) - config->authtype = CURLAUTH_ANY; - /* --no-anyauth simply does not touch it */ - break; -#ifdef USE_WATT32 - case C_WDEBUG: /* --wdebug */ - dbug_init(); - break; -#endif - case C_FTP_CREATE_DIRS: /* --ftp-create-dirs */ - config->ftp_create_dirs = toggle; - break; - case C_CREATE_DIRS: /* --create-dirs */ - config->create_dirs = toggle; - break; - case C_CREATE_FILE_MODE: /* --create-file-mode */ - err = oct2nummax(&config->create_file_mode, nextarg, 0777); - break; - case C_MAX_REDIRS: /* --max-redirs */ - /* specified max no of redirects (http(s)), this accepts -1 as a - special condition */ - err = str2num(&config->maxredirs, nextarg); - if(!err && (config->maxredirs < -1)) - err = PARAM_BAD_NUMERIC; - break; + break; + case C_RATE: + err = set_rate(global, nextarg); + break; + case C_CREATE_FILE_MODE: /* --create-file-mode */ + err = oct2nummax(&config->create_file_mode, nextarg, 0777); + break; + case C_MAX_REDIRS: /* --max-redirs */ + /* specified max no of redirects (http(s)), this accepts -1 as a + special condition */ + err = str2num(&config->maxredirs, nextarg); + if(!err && (config->maxredirs < -1)) + err = PARAM_BAD_NUMERIC; + break; #ifndef CURL_DISABLE_IPFS - case C_IPFS_GATEWAY: /* --ipfs-gateway */ - err = getstr(&config->ipfs_gateway, nextarg, DENY_BLANK); - break; + case C_IPFS_GATEWAY: /* --ipfs-gateway */ + err = getstr(&config->ipfs_gateway, nextarg, DENY_BLANK); + break; #endif /* !CURL_DISABLE_IPFS */ - case C_PROXY_NTLM: /* --proxy-ntlm */ - if(!feature_ntlm) - err = PARAM_LIBCURL_DOESNT_SUPPORT; - else - config->proxyntlm = toggle; - break; - case C_CRLF: /* --crlf */ - /* LF -> CRLF conversion? */ - config->crlf = toggle; - break; - case C_AWS_SIGV4: /* --aws-sigv4 */ - config->authtype |= CURLAUTH_AWS_SIGV4; - err = getstr(&config->aws_sigv4, nextarg, ALLOW_BLANK); - break; - case C_STDERR: /* --stderr */ - tool_set_stderr_file(global, nextarg); - break; - case C_INTERFACE: /* --interface */ - /* interface */ - err = getstr(&config->iface, nextarg, DENY_BLANK); - break; - case C_KRB: /* --krb */ - /* kerberos level string */ - if(!feature_spnego) - err = PARAM_LIBCURL_DOESNT_SUPPORT; - else - err = getstr(&config->krblevel, nextarg, DENY_BLANK); - break; - case C_HAPROXY_PROTOCOL: /* --haproxy-protocol */ - config->haproxy_protocol = toggle; - break; - case C_HAPROXY_CLIENTIP: /* --haproxy-clientip */ - err = getstr(&config->haproxy_clientip, nextarg, DENY_BLANK); - break; - case C_MAX_FILESIZE: /* --max-filesize */ - err = GetSizeParameter(global, nextarg, "max-filesize", &value); - if(!err) - config->max_filesize = value; - break; - case C_DISABLE_EPRT: /* --disable-eprt */ - config->disable_eprt = toggle; - break; - case C_EPRT: /* --eprt */ - config->disable_eprt = !toggle; - break; - case C_XATTR: /* --xattr */ - config->xattr = toggle; - break; - case C_URL: /* --url */ - err = parse_url(global, config, nextarg); - break; - case C_FTP_SSL: /* --ftp-ssl */ - case C_SSL: /* --ssl */ - config->ftp_ssl = toggle; - if(config->ftp_ssl) - warnf(global, - "--%s is an insecure option, consider --ssl-reqd instead", - a->lname); - break; - case C_FTP_PASV: /* --ftp-pasv */ - tool_safefree(config->ftpport); - break; - case C_SOCKS5: /* --socks5 */ - /* socks5 proxy to use, and resolves the name locally and passes on the - resolved address */ - err = getstr(&config->proxy, nextarg, DENY_BLANK); - config->proxyver = CURLPROXY_SOCKS5; - break; - case C_SOCKS4: /* --socks4 */ - err = getstr(&config->proxy, nextarg, DENY_BLANK); - config->proxyver = CURLPROXY_SOCKS4; - break; - case C_SOCKS4A: /* --socks4a */ - err = getstr(&config->proxy, nextarg, DENY_BLANK); - config->proxyver = CURLPROXY_SOCKS4A; - break; - case C_SOCKS5_HOSTNAME: /* --socks5-hostname */ - err = getstr(&config->proxy, nextarg, DENY_BLANK); - config->proxyver = CURLPROXY_SOCKS5_HOSTNAME; - break; - case C_TCP_NODELAY: /* --tcp-nodelay */ - config->tcp_nodelay = toggle; - break; - case C_IP_TOS: { /* --ip-tos */ - struct TOSEntry find; - const struct TOSEntry *entry; - find.name = nextarg; - entry = bsearch(&find, tos_entries, - CURL_ARRAYSIZE(tos_entries), - sizeof(*tos_entries), find_tos); - if(entry) - config->ip_tos = entry->value; - else /* numeric tos value */ - err = str2unummax(&config->ip_tos, nextarg, 0xFF); - break; - } - case C_VLAN_PRIORITY: /* --vlan-priority */ - err = str2unummax(&config->vlan_priority, nextarg, 7); - break; - case C_PROXY_DIGEST: /* --proxy-digest */ - config->proxydigest = toggle; - break; - case C_PROXY_BASIC: /* --proxy-basic */ - config->proxybasic = toggle; - break; - case C_RETRY: /* --retry */ - err = str2unum(&config->req_retry, nextarg); - break; - case C_RETRY_CONNREFUSED: /* --retry-connrefused */ - config->retry_connrefused = toggle; - break; - case C_RETRY_DELAY: /* --retry-delay */ - err = str2unummax(&config->retry_delay, nextarg, LONG_MAX/1000); - break; - case C_RETRY_MAX_TIME: /* --retry-max-time */ - err = str2unummax(&config->retry_maxtime, nextarg, LONG_MAX/1000); - break; - case C_RETRY_ALL_ERRORS: /* --retry-all-errors */ - config->retry_all_errors = toggle; - break; - case C_PROXY_NEGOTIATE: /* --proxy-negotiate */ - if(!feature_spnego) - err = PARAM_LIBCURL_DOESNT_SUPPORT; - else - config->proxynegotiate = toggle; - break; - case C_FORM_ESCAPE: /* --form-escape */ - config->mime_options &= ~CURLMIMEOPT_FORMESCAPE; - if(toggle) - config->mime_options |= CURLMIMEOPT_FORMESCAPE; - break; - case C_FTP_ACCOUNT: /* --ftp-account */ - err = getstr(&config->ftp_account, nextarg, DENY_BLANK); - break; - case C_PROXY_ANYAUTH: /* --proxy-anyauth */ - config->proxyanyauth = toggle; - break; - case C_TRACE_TIME: /* --trace-time */ - global->tracetime = toggle; - break; - case C_IGNORE_CONTENT_LENGTH: /* --ignore-content-length */ - config->ignorecl = toggle; - break; - case C_FTP_SKIP_PASV_IP: /* --ftp-skip-pasv-ip */ - config->ftp_skip_ip = toggle; - break; - case C_FTP_METHOD: /* --ftp-method */ - config->ftp_filemethod = ftpfilemethod(config, nextarg); - break; - case C_LOCAL_PORT: /* --local-port */ - err = parse_localport(config, nextarg); - break; - case C_FTP_ALTERNATIVE_TO_USER: /* --ftp-alternative-to-user */ - err = getstr(&config->ftp_alternative_to_user, nextarg, DENY_BLANK); - break; - case C_FTP_SSL_REQD: /* --ftp-ssl-reqd */ - case C_SSL_REQD: /* --ssl-reqd */ - config->ftp_ssl_reqd = toggle; - break; - case C_SESSIONID: /* --sessionid */ - config->disable_sessionid = !toggle; - break; - case C_FTP_SSL_CONTROL: /* --ftp-ssl-control */ - config->ftp_ssl_control = toggle; - break; - case C_FTP_SSL_CCC: /* --ftp-ssl-ccc */ - config->ftp_ssl_ccc = toggle; - if(!config->ftp_ssl_ccc_mode) - config->ftp_ssl_ccc_mode = CURLFTPSSL_CCC_PASSIVE; - break; - case C_FTP_SSL_CCC_MODE: /* --ftp-ssl-ccc-mode */ - config->ftp_ssl_ccc = TRUE; - config->ftp_ssl_ccc_mode = ftpcccmethod(config, nextarg); - break; - case C_LIBCURL: /* --libcurl */ + case C_AWS_SIGV4: /* --aws-sigv4 */ + config->authtype |= CURLAUTH_AWS_SIGV4; + err = getstr(&config->aws_sigv4, nextarg, ALLOW_BLANK); + break; + case C_STDERR: /* --stderr */ + tool_set_stderr_file(global, nextarg); + break; + case C_INTERFACE: /* --interface */ + /* interface */ + err = getstr(&config->iface, nextarg, DENY_BLANK); + break; + case C_KRB: /* --krb */ + /* kerberos level string */ + if(!feature_spnego) + err = PARAM_LIBCURL_DOESNT_SUPPORT; + else + err = getstr(&config->krblevel, nextarg, DENY_BLANK); + break; + case C_HAPROXY_CLIENTIP: /* --haproxy-clientip */ + err = getstr(&config->haproxy_clientip, nextarg, DENY_BLANK); + break; + case C_MAX_FILESIZE: /* --max-filesize */ + err = GetSizeParameter(global, nextarg, "max-filesize", &value); + if(!err) + config->max_filesize = value; + break; + case C_URL: /* --url */ + err = parse_url(global, config, nextarg); + break; + case C_FTP_PASV: /* --ftp-pasv */ + tool_safefree(config->ftpport); + break; + case C_SOCKS5: /* --socks5 */ + /* socks5 proxy to use, and resolves the name locally and passes on the + resolved address */ + err = getstr(&config->proxy, nextarg, DENY_BLANK); + config->proxyver = CURLPROXY_SOCKS5; + break; + case C_SOCKS4: /* --socks4 */ + err = getstr(&config->proxy, nextarg, DENY_BLANK); + config->proxyver = CURLPROXY_SOCKS4; + break; + case C_SOCKS4A: /* --socks4a */ + err = getstr(&config->proxy, nextarg, DENY_BLANK); + config->proxyver = CURLPROXY_SOCKS4A; + break; + case C_SOCKS5_HOSTNAME: /* --socks5-hostname */ + err = getstr(&config->proxy, nextarg, DENY_BLANK); + config->proxyver = CURLPROXY_SOCKS5_HOSTNAME; + break; + case C_IP_TOS: { /* --ip-tos */ + struct TOSEntry find; + const struct TOSEntry *entry; + find.name = nextarg; + entry = bsearch(&find, tos_entries, + CURL_ARRAYSIZE(tos_entries), + sizeof(*tos_entries), find_tos); + if(entry) + config->ip_tos = entry->value; + else /* numeric tos value */ + 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 = str2unummax(&config->retry_delay, nextarg, LONG_MAX/1000); + break; + case C_RETRY_MAX_TIME: /* --retry-max-time */ + err = str2unummax(&config->retry_maxtime, nextarg, LONG_MAX/1000); + break; + case C_FTP_ACCOUNT: /* --ftp-account */ + err = getstr(&config->ftp_account, nextarg, DENY_BLANK); + break; + case C_FTP_METHOD: /* --ftp-method */ + config->ftp_filemethod = ftpfilemethod(config, nextarg); + break; + case C_LOCAL_PORT: /* --local-port */ + err = parse_localport(config, nextarg); + break; + case C_FTP_ALTERNATIVE_TO_USER: /* --ftp-alternative-to-user */ + err = getstr(&config->ftp_alternative_to_user, nextarg, DENY_BLANK); + break; + case C_LIBCURL: /* --libcurl */ #ifdef CURL_DISABLE_LIBCURL_OPTION - warnf(global, - "--libcurl option was disabled at build-time"); - err = PARAM_OPTION_UNKNOWN; + warnf(global, + "--libcurl option was disabled at build-time"); + err = PARAM_OPTION_UNKNOWN; #else - err = getstr(&global->libcurl, nextarg, DENY_BLANK); + err = getstr(&global->libcurl, nextarg, DENY_BLANK); #endif - break; - case C_RAW: /* --raw */ - config->raw = toggle; - break; - case C_KEEPALIVE: /* --keepalive */ - config->nokeepalive = !toggle; - 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_POST301: /* --post301 */ - config->post301 = toggle; - break; - case C_POST302: /* --post302 */ - config->post302 = toggle; - break; - case C_POST303: /* --post303 */ - config->post303 = toggle; - break; - case C_NOPROXY: /* --noproxy */ - /* This specifies the noproxy list */ - err = getstr(&config->noproxy, nextarg, ALLOW_BLANK); - break; - case C_SOCKS5_GSSAPI_NEC: /* --socks5-gssapi-nec */ - config->socks5_gssapi_nec = toggle; - break; - case C_PROXY1_0: /* --proxy1.0 */ - /* http 1.0 proxy */ - 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; - case C_MAIL_RCPT: /* --mail-rcpt */ - /* append receiver to a list */ - err = add2list(&config->mail_rcpt, nextarg); - break; - case C_FTP_PRET: /* --ftp-pret */ - config->ftp_pret = toggle; - break; - case C_PROTO: /* --proto */ - config->proto_present = TRUE; - err = proto2num(config, built_in_protos, &config->proto_str, nextarg); - break; - case C_PROTO_REDIR: /* --proto-redir */ - config->proto_redir_present = TRUE; - if(proto2num(config, redir_protos, &config->proto_redir_str, - nextarg)) - err = PARAM_BAD_USE; - break; - case C_RESOLVE: /* --resolve */ - err = add2list(&config->resolve, nextarg); - break; - case C_DELEGATION: /* --delegation */ - config->gssapi_delegation = delegation(config, nextarg); - break; - case C_MAIL_AUTH: /* --mail-auth */ - err = getstr(&config->mail_auth, nextarg, DENY_BLANK); - break; - case C_METALINK: /* --metalink */ - errorf(global, "--metalink is disabled"); + 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); + break; + case C_PROXY1_0: /* --proxy1.0 */ + /* http 1.0 proxy */ + 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; + case C_MAIL_RCPT: /* --mail-rcpt */ + /* append receiver to a list */ + err = add2list(&config->mail_rcpt, nextarg); + break; + case C_PROTO: /* --proto */ + config->proto_present = TRUE; + err = proto2num(config, built_in_protos, &config->proto_str, nextarg); + break; + case C_PROTO_REDIR: /* --proto-redir */ + config->proto_redir_present = TRUE; + if(proto2num(config, redir_protos, &config->proto_redir_str, + nextarg)) err = PARAM_BAD_USE; + break; + case C_RESOLVE: /* --resolve */ + err = add2list(&config->resolve, nextarg); + break; + case C_DELEGATION: /* --delegation */ + config->gssapi_delegation = delegation(config, nextarg); + break; + case C_MAIL_AUTH: /* --mail-auth */ + err = getstr(&config->mail_auth, nextarg, DENY_BLANK); + break; + case C_SASL_AUTHZID: /* --sasl-authzid */ + err = getstr(&config->sasl_authzid, nextarg, DENY_BLANK); + break; + case C_UNIX_SOCKET: /* --unix-socket */ + config->abstract_unix_socket = FALSE; + err = getstr(&config->unix_socket_path, nextarg, DENY_BLANK); + break; + case C_PROXY_SERVICE_NAME: /* --proxy-service-name */ + err = getstr(&config->proxy_service_name, nextarg, DENY_BLANK); + break; + case C_SERVICE_NAME: /* --service-name */ + err = getstr(&config->service_name, nextarg, DENY_BLANK); + break; + case C_PROTO_DEFAULT: /* --proto-default */ + err = getstr(&config->proto_default, nextarg, DENY_BLANK); + 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; + case C_ABSTRACT_UNIX_SOCKET: /* --abstract-unix-socket */ + config->abstract_unix_socket = TRUE; + err = getstr(&config->unix_socket_path, nextarg, DENY_BLANK); + break; + case C_TLS_MAX: /* --tls-max */ + err = str2tls_max(&config->ssl_version_max, nextarg); + 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 */ + if(set_trace_config(global, nextarg)) + err = PARAM_NO_MEM; + break; + case C_VARIABLE: /* --variable */ + err = setvariable(global, nextarg); + break; + case C_TLS13_CIPHERS: /* --tls13-ciphers */ + err = getstr(&config->cipher13_list, nextarg, DENY_BLANK); + break; + case C_PROXY_TLS13_CIPHERS: /* --proxy-tls13-ciphers */ + err = getstr(&config->proxy_cipher13_list, nextarg, DENY_BLANK); + break; + case C_USER_AGENT: /* --user-agent */ + err = getstr(&config->useragent, nextarg, ALLOW_BLANK); + break; + case C_ALT_SVC: /* --alt-svc */ + if(!feature_altsvc) + err = PARAM_LIBCURL_DOESNT_SUPPORT; + else + err = getstr(&config->altsvc, nextarg, ALLOW_BLANK); + break; + case C_HSTS: /* --hsts */ + if(!feature_hsts) + err = PARAM_LIBCURL_DOESNT_SUPPORT; + else + err = getstr(&config->hsts, nextarg, ALLOW_BLANK); + break; + case C_COOKIE: /* --cookie */ + if(strchr(nextarg, '=')) { + /* A cookie string must have a =-letter */ + err = add2list(&config->cookies, nextarg); break; - case C_SASL_AUTHZID: /* --sasl-authzid */ - err = getstr(&config->sasl_authzid, nextarg, DENY_BLANK); - break; - case C_SASL_IR: /* --sasl-ir */ - config->sasl_ir = toggle; - break; -#ifdef DEBUGBUILD - case C_TEST_DUPHANDLE: /* --test-duphandle */ - global->test_duphandle = toggle; - break; - case C_TEST_EVENT: /* --test-event */ - global->test_event_based = toggle; - break; -#endif - case C_UNIX_SOCKET: /* --unix-socket */ - config->abstract_unix_socket = FALSE; - err = getstr(&config->unix_socket_path, nextarg, DENY_BLANK); - break; - case C_PATH_AS_IS: /* --path-as-is */ - config->path_as_is = toggle; - break; - case C_PROXY_SERVICE_NAME: /* --proxy-service-name */ - err = getstr(&config->proxy_service_name, nextarg, DENY_BLANK); - break; - case C_SERVICE_NAME: /* --service-name */ - err = getstr(&config->service_name, nextarg, DENY_BLANK); - break; - case C_PROTO_DEFAULT: /* --proto-default */ - err = getstr(&config->proto_default, nextarg, DENY_BLANK); - if(!err) - err = check_protocol(config->proto_default); - break; - case C_EXPECT100_TIMEOUT: /* --expect100-timeout */ - err = secs2ms(&config->expect100timeout_ms, nextarg); - break; - case C_TFTP_NO_OPTIONS: /* --tftp-no-options */ - config->tftp_no_options = toggle; - break; - case C_CONNECT_TO: /* --connect-to */ - err = add2list(&config->connect_to, nextarg); - break; - case C_ABSTRACT_UNIX_SOCKET: /* --abstract-unix-socket */ - config->abstract_unix_socket = TRUE; - err = getstr(&config->unix_socket_path, nextarg, DENY_BLANK); - break; - case C_TLS_EARLYDATA: /* --tls-earlydata */ - config->ssl_allow_earlydata = toggle; - break; - case C_TLS_MAX: /* --tls-max */ - err = str2tls_max(&config->ssl_version_max, nextarg); - break; - case C_SUPPRESS_CONNECT_HEADERS: /* --suppress-connect-headers */ - config->suppress_connect_headers = toggle; - break; - case C_COMPRESSED_SSH: /* --compressed-ssh */ - config->ssh_compression = toggle; - 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_IDS: /* --trace-ids */ - global->traceids = toggle; - break; - case C_TRACE_CONFIG: /* --trace-config */ - if(set_trace_config(global, nextarg)) - err = PARAM_NO_MEM; - break; - case C_PROGRESS_METER: /* --progress-meter */ - global->noprogress = !toggle; - break; - case C_PROGRESS_BAR: /* --progress-bar */ - global->progressmode = toggle ? CURL_PROGRESS_BAR : CURL_PROGRESS_STATS; - break; - case C_VARIABLE: /* --variable */ - err = setvariable(global, nextarg); - break; - case C_NEXT: /* --next */ - err = PARAM_NEXT_OPERATION; - break; - case C_HTTP1_0: /* --http1.0 */ - /* HTTP version 1.0 */ - sethttpver(global, config, CURL_HTTP_VERSION_1_0); - break; - case C_HTTP1_1: /* --http1.1 */ - /* HTTP version 1.1 */ - sethttpver(global, config, CURL_HTTP_VERSION_1_1); - break; - case C_HTTP2: /* --http2 */ - /* HTTP version 2.0 */ - if(!feature_http2) - return PARAM_LIBCURL_DOESNT_SUPPORT; - sethttpver(global, config, CURL_HTTP_VERSION_2_0); - break; - case C_HTTP2_PRIOR_KNOWLEDGE: /* --http2-prior-knowledge */ - /* HTTP version 2.0 over clean TCP */ - if(!feature_http2) - return PARAM_LIBCURL_DOESNT_SUPPORT; - sethttpver(global, config, CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE); - break; - case C_HTTP3: /* --http3: */ - /* Try HTTP/3, allow fallback */ - if(!feature_http3) - err = PARAM_LIBCURL_DOESNT_SUPPORT; - else - sethttpver(global, config, CURL_HTTP_VERSION_3); - break; - case C_HTTP3_ONLY: /* --http3-only */ - /* Try HTTP/3 without fallback */ - if(!feature_http3) - err = PARAM_LIBCURL_DOESNT_SUPPORT; - else - sethttpver(global, config, CURL_HTTP_VERSION_3ONLY); - break; - case C_HTTP0_9: /* --http0.9 */ - /* Allow HTTP/0.9 responses! */ - config->http09_allowed = toggle; - break; - case C_PROXY_HTTP2: /* --proxy-http2 */ - if(!feature_httpsproxy || !feature_http2) - err = PARAM_LIBCURL_DOESNT_SUPPORT; - else - config->proxyver = CURLPROXY_HTTPS2; - break; - case C_TLSV1: /* --tlsv1 */ - config->ssl_version = CURL_SSLVERSION_TLSv1; - break; - case C_TLSV1_0: /* --tlsv1.0 */ - config->ssl_version = CURL_SSLVERSION_TLSv1_0; - break; - case C_TLSV1_1: /* --tlsv1.1 */ - config->ssl_version = CURL_SSLVERSION_TLSv1_1; - break; - case C_TLSV1_2: /* --tlsv1.2 */ - config->ssl_version = CURL_SSLVERSION_TLSv1_2; - break; - case C_TLSV1_3: /* --tlsv1.3 */ - config->ssl_version = CURL_SSLVERSION_TLSv1_3; - break; - case C_TLS13_CIPHERS: /* --tls13-ciphers */ - err = getstr(&config->cipher13_list, nextarg, DENY_BLANK); - break; - case C_PROXY_TLS13_CIPHERS: /* --proxy-tls13-ciphers */ - err = getstr(&config->proxy_cipher13_list, nextarg, DENY_BLANK); - break; - case C_IPV4: /* --ipv4 */ - config->ip_version = CURL_IPRESOLVE_V4; - break; - case C_IPV6: /* --ipv6 */ - config->ip_version = CURL_IPRESOLVE_V6; - break; - case C_APPEND: /* --append */ - /* This makes the FTP sessions use APPE instead of STOR */ - config->ftp_append = toggle; - break; - case C_USER_AGENT: /* --user-agent */ - err = getstr(&config->useragent, nextarg, ALLOW_BLANK); - break; - case C_ALT_SVC: /* --alt-svc */ - if(!feature_altsvc) - err = PARAM_LIBCURL_DOESNT_SUPPORT; - else - err = getstr(&config->altsvc, nextarg, ALLOW_BLANK); - break; - case C_HSTS: /* --hsts */ - if(!feature_hsts) - err = PARAM_LIBCURL_DOESNT_SUPPORT; - else - err = getstr(&config->hsts, nextarg, ALLOW_BLANK); - break; - case C_COOKIE: /* --cookie */ - if(strchr(nextarg, '=')) { - /* A cookie string must have a =-letter */ - err = add2list(&config->cookies, nextarg); - break; - } - else { - /* We have a cookie file to read from! */ - err = add2list(&config->cookiefiles, nextarg); - } - break; - case C_USE_ASCII: /* --use-ascii */ - config->use_ascii = toggle; - break; - case C_COOKIE_JAR: /* --cookie-jar */ - err = getstr(&config->cookiejar, nextarg, DENY_BLANK); - break; - case C_CONTINUE_AT: /* --continue-at */ - err = parse_continue_at(global, config, nextarg); - break; - case C_DATA: /* --data */ - case C_DATA_ASCII: /* --data-ascii */ - case C_DATA_BINARY: /* --data-binary */ - case C_DATA_URLENCODE: /* --data-urlencode */ - case C_JSON: /* --json */ - case C_DATA_RAW: /* --data-raw */ - err = set_data(cmd, nextarg, global, config); - break; - case C_URL_QUERY: /* --url-query */ - err = url_query(nextarg, global, config); - break; - case C_DUMP_CA_EMBED: /* --dump-ca-embed */ - err = PARAM_CA_EMBED_REQUESTED; - break; - case C_DUMP_HEADER: /* --dump-header */ - err = getstr(&config->headerfile, nextarg, DENY_BLANK); - break; - case C_REFERER: { /* --referer */ - const char *ptr = strstr(nextarg, ";auto"); - size_t len; - if(ptr) { - /* Automatic referer requested, this may be combined with a - set initial one */ - config->autoreferer = TRUE; - len = ptr - nextarg; - } - else { - config->autoreferer = FALSE; - len = strlen(nextarg); - } - ptr = len ? nextarg : NULL; - err = getstrn(&config->referer, ptr, len, ALLOW_BLANK); } + else { + /* We have a cookie file to read from! */ + err = add2list(&config->cookiefiles, nextarg); + } + break; + case C_COOKIE_JAR: /* --cookie-jar */ + err = getstr(&config->cookiejar, nextarg, DENY_BLANK); + break; + case C_CONTINUE_AT: /* --continue-at */ + err = parse_continue_at(global, config, nextarg); + break; + case C_DATA: /* --data */ + case C_DATA_ASCII: /* --data-ascii */ + case C_DATA_BINARY: /* --data-binary */ + case C_DATA_URLENCODE: /* --data-urlencode */ + case C_JSON: /* --json */ + case C_DATA_RAW: /* --data-raw */ + err = set_data((cmdline_t)a->cmd, nextarg, global, config); + break; + case C_URL_QUERY: /* --url-query */ + err = url_query(nextarg, global, config); + break; + case C_DUMP_HEADER: /* --dump-header */ + err = getstr(&config->headerfile, nextarg, DENY_BLANK); + break; + case C_REFERER: { /* --referer */ + size_t len = strlen(nextarg); + /* does it end with ;auto ? */ + if(len >= 5 && !strcmp(";auto", &nextarg[len - 5])) { + /* Automatic referer requested, this may be combined with a set initial + one */ + config->autoreferer = TRUE; + len -= 5; + } + else + config->autoreferer = FALSE; + + if(len) + err = getstrn(&config->referer, nextarg, len, ALLOW_BLANK); + else + tool_safefree(config->referer); + } + break; + case C_CERT: /* --cert */ + GetFileAndPassword(nextarg, &config->cert, &config->key_passwd); + break; + case C_CACERT: /* --cacert */ + err = getstr(&config->cacert, nextarg, DENY_BLANK); + break; + case C_CERT_TYPE: /* --cert-type */ + err = getstr(&config->cert_type, nextarg, DENY_BLANK); + break; + case C_KEY: /* --key */ + err = getstr(&config->key, nextarg, DENY_BLANK); + break; + case C_KEY_TYPE: /* --key-type */ + err = getstr(&config->key_type, nextarg, DENY_BLANK); + break; + case C_PASS: /* --pass */ + err = getstr(&config->key_passwd, nextarg, DENY_BLANK); + break; + case C_ENGINE: /* --engine */ + err = getstr(&config->engine, nextarg, DENY_BLANK); + if(!err && + config->engine && !strcmp(config->engine, "list")) { + err = PARAM_ENGINES_REQUESTED; + } + break; + case C_ECH: /* --ech */ + err = parse_ech(global, config, nextarg); + break; + case C_CAPATH: /* --capath */ + err = getstr(&config->capath, nextarg, DENY_BLANK); + break; + case C_PUBKEY: /* --pubkey */ + err = getstr(&config->pubkey, nextarg, DENY_BLANK); + break; + case C_HOSTPUBMD5: /* --hostpubmd5 */ + err = getstr(&config->hostpubmd5, nextarg, DENY_BLANK); + if(!err) { + if(!config->hostpubmd5 || strlen(config->hostpubmd5) != 32) + err = PARAM_BAD_USE; + } + break; + case C_HOSTPUBSHA256: /* --hostpubsha256 */ + if(!feature_libssh2) + err = PARAM_LIBCURL_DOESNT_SUPPORT; + else + err = getstr(&config->hostpubsha256, nextarg, DENY_BLANK); + break; + case C_CRLFILE: /* --crlfile */ + err = getstr(&config->crlfile, nextarg, DENY_BLANK); + break; + case C_TLSUSER: /* --tlsuser */ + if(!feature_tls_srp) + err = PARAM_LIBCURL_DOESNT_SUPPORT; + else + err = getstr(&config->tls_username, nextarg, DENY_BLANK); + break; + case C_TLSPASSWORD: /* --tlspassword */ + if(!feature_tls_srp) + err = PARAM_LIBCURL_DOESNT_SUPPORT; + else + err = getstr(&config->tls_password, nextarg, ALLOW_BLANK); + break; + case C_TLSAUTHTYPE: /* --tlsauthtype */ + if(!feature_tls_srp) + err = PARAM_LIBCURL_DOESNT_SUPPORT; + else { + err = getstr(&config->tls_authtype, nextarg, DENY_BLANK); + if(!err && config->tls_authtype && strcmp(config->tls_authtype, "SRP")) + err = PARAM_LIBCURL_DOESNT_SUPPORT; /* only support TLS-SRP */ + } + break; + case C_PINNEDPUBKEY: /* --pinnedpubkey */ + err = getstr(&config->pinnedpubkey, nextarg, DENY_BLANK); + break; + case C_PROXY_PINNEDPUBKEY: /* --proxy-pinnedpubkey */ + err = getstr(&config->proxy_pinnedpubkey, nextarg, DENY_BLANK); + break; + case C_SSL_SESSIONS: /* --ssl-sessions */ + if(feature_ssls_export) + err = getstr(&global->ssl_sessions, nextarg, DENY_BLANK); + else + err = PARAM_LIBCURL_DOESNT_SUPPORT; + break; + case C_PROXY_TLSUSER: /* --proxy-tlsuser */ + if(!feature_tls_srp) + err = PARAM_LIBCURL_DOESNT_SUPPORT; + else + err = getstr(&config->proxy_tls_username, nextarg, ALLOW_BLANK); + break; + case C_PROXY_TLSPASSWORD: /* --proxy-tlspassword */ + if(!feature_tls_srp) + err = PARAM_LIBCURL_DOESNT_SUPPORT; + else + err = getstr(&config->proxy_tls_password, nextarg, DENY_BLANK); + break; + case C_PROXY_TLSAUTHTYPE: /* --proxy-tlsauthtype */ + if(!feature_tls_srp) + err = PARAM_LIBCURL_DOESNT_SUPPORT; + else { + err = getstr(&config->proxy_tls_authtype, nextarg, DENY_BLANK); + if(!err && config->proxy_tls_authtype && + strcmp(config->proxy_tls_authtype, "SRP")) + err = PARAM_LIBCURL_DOESNT_SUPPORT; /* only support TLS-SRP */ + } + break; + case C_PROXY_CERT: /* --proxy-cert */ + GetFileAndPassword(nextarg, &config->proxy_cert, + &config->proxy_key_passwd); + break; + case C_PROXY_CERT_TYPE: /* --proxy-cert-type */ + err = getstr(&config->proxy_cert_type, nextarg, DENY_BLANK); + break; + case C_PROXY_KEY: /* --proxy-key */ + err = getstr(&config->proxy_key, nextarg, ALLOW_BLANK); + break; + case C_PROXY_KEY_TYPE: /* --proxy-key-type */ + err = getstr(&config->proxy_key_type, nextarg, DENY_BLANK); + break; + case C_PROXY_PASS: /* --proxy-pass */ + err = getstr(&config->proxy_key_passwd, nextarg, ALLOW_BLANK); + break; + case C_PROXY_CIPHERS: /* --proxy-ciphers */ + err = getstr(&config->proxy_cipher_list, nextarg, DENY_BLANK); + break; + case C_PROXY_CRLFILE: /* --proxy-crlfile */ + err = getstr(&config->proxy_crlfile, nextarg, DENY_BLANK); + break; + case C_LOGIN_OPTIONS: /* --login-options */ + err = getstr(&config->login_options, nextarg, ALLOW_BLANK); + break; + case C_PROXY_CACERT: /* --proxy-cacert */ + err = getstr(&config->proxy_cacert, nextarg, DENY_BLANK); + break; + case C_PROXY_CAPATH: /* --proxy-capath */ + err = getstr(&config->proxy_capath, nextarg, DENY_BLANK); + break; + case C_ETAG_SAVE: /* --etag-save */ + if(config->num_urls > 1) { + errorf(global, "The etag options only work on a single URL"); + err = PARAM_BAD_USE; + } + else + err = getstr(&config->etag_save_file, nextarg, DENY_BLANK); + break; + case C_ETAG_COMPARE: /* --etag-compare */ + if(config->num_urls > 1) { + errorf(global, "The etag options only work on a single URL"); + err = PARAM_BAD_USE; + } + else + err = getstr(&config->etag_compare_file, nextarg, DENY_BLANK); + break; + case C_CURVES: /* --curves */ + err = getstr(&config->ssl_ec_curves, nextarg, DENY_BLANK); + break; + case C_SIGNATURE_ALGORITHMS: /* --sigalgs */ + err = getstr(&config->ssl_signature_algorithms, nextarg, DENY_BLANK); + break; + case C_FORM: /* --form */ + case C_FORM_STRING: /* --form-string */ + /* "form data" simulation, this is a little advanced so lets do our best + to sort this out slowly and carefully */ + if(formparse(config, + nextarg, + &config->mimeroot, + &config->mimecurrent, + (a->cmd == C_FORM_STRING))) /* literal string */ + err = PARAM_BAD_USE; + else if(SetHTTPrequest(config, TOOL_HTTPREQ_MIMEPOST, &config->httpreq)) + err = PARAM_BAD_USE; + break; + case C_REQUEST_TARGET: /* --request-target */ + err = getstr(&config->request_target, nextarg, DENY_BLANK); + break; + case C_HEADER: /* --header */ + case C_PROXY_HEADER: /* --proxy-header */ + err = parse_header(global, config, (cmdline_t)a->cmd, nextarg); + break; + case C_CONFIG: /* --config */ + if(parseconfig(nextarg, global)) { + errorf(global, "cannot read config from '%s'", nextarg); + err = PARAM_READ_ERROR; + } + break; + case C_MAX_TIME: /* --max-time */ + /* specified max time */ + err = secs2ms(&config->timeout_ms, nextarg); + break; + case C_NETRC_FILE: /* --netrc-file */ + err = getstr(&config->netrc_file, nextarg, DENY_BLANK); + break; + case C_OUTPUT_DIR: /* --output-dir */ + err = getstr(&config->output_dir, nextarg, DENY_BLANK); + break; + case C_OUTPUT: /* --output */ + err = parse_output(config, nextarg); + break; + case C_FTP_PORT: /* --ftp-port */ + /* This makes the FTP sessions use PORT instead of PASV */ + /* use or <192.168.10.10> style addresses. Anything except + this will make us try to get the "default" address. + NOTE: this is a changed behavior since the released 4.1! + */ + err = getstr(&config->ftpport, nextarg, DENY_BLANK); + break; + case C_FTP_SSL_CCC_MODE: /* --ftp-ssl-ccc-mode */ + config->ftp_ssl_ccc = TRUE; + config->ftp_ssl_ccc_mode = ftpcccmethod(config, nextarg); + break; + case C_QUOTE: /* --quote */ + err = parse_quote(config, nextarg); + break; + case C_RANGE: /* --range */ + err = parse_range(global, config, nextarg); + break; + case C_TELNET_OPTION: /* --telnet-option */ + /* Telnet options */ + err = add2list(&config->telnet_options, nextarg); + break; + case C_UPLOAD_FILE: /* --upload-file */ + err = parse_upload_file(config, nextarg); + break; + case C_USER: /* --user */ + /* user:password */ + err = getstr(&config->userpwd, nextarg, ALLOW_BLANK); + break; + case C_PROXY_USER: /* --proxy-user */ + /* Proxy user:password */ + err = getstr(&config->proxyuserpwd, nextarg, ALLOW_BLANK); + break; + case C_WRITE_OUT: /* --write-out */ + err = parse_writeout(global, config, nextarg); + break; + case C_PREPROXY: /* --preproxy */ + err = getstr(&config->preproxy, nextarg, DENY_BLANK); + break; + case C_PROXY: /* --proxy */ + /* --proxy */ + err = getstr(&config->proxy, nextarg, ALLOW_BLANK); + if(config->proxyver != CURLPROXY_HTTPS2) + config->proxyver = CURLPROXY_HTTP; + break; + case C_REQUEST: /* --request */ + /* 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_MAX: { /* --parallel-max */ + long val; + err = str2unum(&val, nextarg); + if(err) break; - case C_CERT: /* --cert */ - GetFileAndPassword(nextarg, &config->cert, &config->key_passwd); - cleanarg(clearthis); - break; - case C_CACERT: /* --cacert */ - err = getstr(&config->cacert, nextarg, DENY_BLANK); - break; - case C_CA_NATIVE: /* --ca-native */ - config->native_ca_store = toggle; - break; - case C_PROXY_CA_NATIVE: /* --proxy-ca-native */ - config->proxy_native_ca_store = toggle; - break; - case C_CERT_TYPE: /* --cert-type */ - err = getstr(&config->cert_type, nextarg, DENY_BLANK); - break; - case C_KEY: /* --key */ - err = getstr(&config->key, nextarg, DENY_BLANK); - break; - case C_KEY_TYPE: /* --key-type */ - err = getstr(&config->key_type, nextarg, DENY_BLANK); - break; - case C_PASS: /* --pass */ - err = getstr(&config->key_passwd, nextarg, DENY_BLANK); - cleanarg(clearthis); - break; - case C_ENGINE: /* --engine */ - err = getstr(&config->engine, nextarg, DENY_BLANK); - if(!err && - config->engine && !strcmp(config->engine, "list")) { - err = PARAM_ENGINES_REQUESTED; - } - break; - case C_ECH: /* --ech */ - err = parse_ech(global, config, nextarg); - break; - case C_CAPATH: /* --capath */ - err = getstr(&config->capath, nextarg, DENY_BLANK); - break; - case C_PUBKEY: /* --pubkey */ - err = getstr(&config->pubkey, nextarg, DENY_BLANK); - break; - case C_HOSTPUBMD5: /* --hostpubmd5 */ - err = getstr(&config->hostpubmd5, nextarg, DENY_BLANK); - if(!err) { - if(!config->hostpubmd5 || strlen(config->hostpubmd5) != 32) - err = PARAM_BAD_USE; - } - break; - case C_HOSTPUBSHA256: /* --hostpubsha256 */ - if(!feature_libssh2) - err = PARAM_LIBCURL_DOESNT_SUPPORT; - else - err = getstr(&config->hostpubsha256, nextarg, DENY_BLANK); - break; - case C_CRLFILE: /* --crlfile */ - err = getstr(&config->crlfile, nextarg, DENY_BLANK); - break; - case C_TLSUSER: /* --tlsuser */ - if(!feature_tls_srp) - err = PARAM_LIBCURL_DOESNT_SUPPORT; - else - err = getstr(&config->tls_username, nextarg, DENY_BLANK); - cleanarg(clearthis); - break; - case C_TLSPASSWORD: /* --tlspassword */ - if(!feature_tls_srp) - err = PARAM_LIBCURL_DOESNT_SUPPORT; - else - err = getstr(&config->tls_password, nextarg, ALLOW_BLANK); - cleanarg(clearthis); - break; - case C_TLSAUTHTYPE: /* --tlsauthtype */ - if(!feature_tls_srp) - err = PARAM_LIBCURL_DOESNT_SUPPORT; - else { - err = getstr(&config->tls_authtype, nextarg, DENY_BLANK); - if(!err && strcmp(config->tls_authtype, "SRP")) - err = PARAM_LIBCURL_DOESNT_SUPPORT; /* only support TLS-SRP */ - } - break; - case C_SSL_ALLOW_BEAST: /* --ssl-allow-beast */ - config->ssl_allow_beast = toggle; - break; - case C_SSL_AUTO_CLIENT_CERT: /* --ssl-auto-client-cert */ - config->ssl_auto_client_cert = toggle; - break; - case C_PROXY_SSL_AUTO_CLIENT_CERT: /* --proxy-ssl-auto-client-cert */ - config->proxy_ssl_auto_client_cert = toggle; - break; - case C_PINNEDPUBKEY: /* --pinnedpubkey */ - err = getstr(&config->pinnedpubkey, nextarg, DENY_BLANK); - break; - case C_PROXY_PINNEDPUBKEY: /* --proxy-pinnedpubkey */ - err = getstr(&config->proxy_pinnedpubkey, nextarg, DENY_BLANK); - break; - case C_CERT_STATUS: /* --cert-status */ - config->verifystatus = TRUE; - break; - case C_DOH_CERT_STATUS: /* --doh-cert-status */ - config->doh_verifystatus = TRUE; - break; - case C_FALSE_START: /* --false-start */ - config->falsestart = TRUE; - break; - case C_SSL_NO_REVOKE: /* --ssl-no-revoke */ - config->ssl_no_revoke = TRUE; - break; - case C_SSL_REVOKE_BEST_EFFORT: /* --ssl-revoke-best-effort */ - config->ssl_revoke_best_effort = TRUE; - break; - case C_SSL_SESSIONS: /* --ssl-sessions */ - if(feature_ssls_export) - err = getstr(&global->ssl_sessions, nextarg, DENY_BLANK); - else - err = PARAM_LIBCURL_DOESNT_SUPPORT; - break; - case C_TCP_FASTOPEN: /* --tcp-fastopen */ - config->tcp_fastopen = TRUE; - break; - case C_PROXY_TLSUSER: /* --proxy-tlsuser */ - if(!feature_tls_srp) - err = PARAM_LIBCURL_DOESNT_SUPPORT; - else - err = getstr(&config->proxy_tls_username, nextarg, ALLOW_BLANK); - cleanarg(clearthis); - break; - case C_PROXY_TLSPASSWORD: /* --proxy-tlspassword */ - if(!feature_tls_srp) - err = PARAM_LIBCURL_DOESNT_SUPPORT; - else - err = getstr(&config->proxy_tls_password, nextarg, DENY_BLANK); - cleanarg(clearthis); - break; - case C_PROXY_TLSAUTHTYPE: /* --proxy-tlsauthtype */ - if(!feature_tls_srp) - err = PARAM_LIBCURL_DOESNT_SUPPORT; - else { - err = getstr(&config->proxy_tls_authtype, nextarg, DENY_BLANK); - if(!err && strcmp(config->proxy_tls_authtype, "SRP")) - err = PARAM_LIBCURL_DOESNT_SUPPORT; /* only support TLS-SRP */ - } - break; - case C_PROXY_CERT: /* --proxy-cert */ - GetFileAndPassword(nextarg, &config->proxy_cert, - &config->proxy_key_passwd); - cleanarg(clearthis); - break; - case C_PROXY_CERT_TYPE: /* --proxy-cert-type */ - err = getstr(&config->proxy_cert_type, nextarg, DENY_BLANK); - break; - case C_PROXY_KEY: /* --proxy-key */ - err = getstr(&config->proxy_key, nextarg, ALLOW_BLANK); - break; - case C_PROXY_KEY_TYPE: /* --proxy-key-type */ - err = getstr(&config->proxy_key_type, nextarg, DENY_BLANK); - break; - case C_PROXY_PASS: /* --proxy-pass */ - err = getstr(&config->proxy_key_passwd, nextarg, ALLOW_BLANK); - cleanarg(clearthis); - break; - case C_PROXY_CIPHERS: /* --proxy-ciphers */ - err = getstr(&config->proxy_cipher_list, nextarg, DENY_BLANK); - break; - case C_PROXY_CRLFILE: /* --proxy-crlfile */ - err = getstr(&config->proxy_crlfile, nextarg, DENY_BLANK); - break; - case C_PROXY_SSL_ALLOW_BEAST: /* --proxy-ssl-allow-beast */ - config->proxy_ssl_allow_beast = toggle; - break; - case C_LOGIN_OPTIONS: /* --login-options */ - err = getstr(&config->login_options, nextarg, ALLOW_BLANK); - break; - case C_PROXY_CACERT: /* --proxy-cacert */ - err = getstr(&config->proxy_cacert, nextarg, DENY_BLANK); - break; - case C_PROXY_CAPATH: /* --proxy-capath */ - err = getstr(&config->proxy_capath, nextarg, DENY_BLANK); - break; - case C_PROXY_INSECURE: /* --proxy-insecure */ - config->proxy_insecure_ok = toggle; - break; - case C_PROXY_TLSV1: /* --proxy-tlsv1 */ - /* TLS version 1 for proxy */ - config->proxy_ssl_version = CURL_SSLVERSION_TLSv1; - break; - case C_SOCKS5_BASIC: /* --socks5-basic */ - if(toggle) - config->socks5_auth |= CURLAUTH_BASIC; - else - config->socks5_auth &= ~CURLAUTH_BASIC; - break; - case C_SOCKS5_GSSAPI: /* --socks5-gssapi */ - if(toggle) - config->socks5_auth |= CURLAUTH_GSSAPI; - else - config->socks5_auth &= ~CURLAUTH_GSSAPI; - break; - case C_ETAG_SAVE: /* --etag-save */ - if(config->num_urls > 1) { - errorf(global, "The etag options only work on a single URL"); - err = PARAM_BAD_USE; + 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(global, config, nextarg); + break; + case C_UPLOAD_FLAGS: /* --upload-flags */ + err = parse_upload_flags(config, nextarg); + break; + } + return err; +} + +ParameterError getparameter(const char *flag, /* f or -long-flag */ + const char *nextarg, /* NULL if unset */ + bool *usedarg, /* set to TRUE if the arg + has been used */ + struct GlobalConfig *global, + struct OperationConfig *config) +{ + const char *parse = NULL; + bool longopt = FALSE; + bool singleopt = FALSE; /* when true means '-o foo' used '-ofoo' */ + ParameterError err = PARAM_OK; + bool toggle = TRUE; /* how to switch boolean options, on or off. Controlled + by using --OPTION or --no-OPTION */ + bool nextalloc = FALSE; /* if nextarg is allocated */ + const struct LongShort *a = NULL; + verbose_nopts = 0; /* options processed in `flag`*/ + + *usedarg = FALSE; /* default is that we do not use the arg */ + + if(('-' != flag[0]) || ('-' == flag[1])) { + /* this should be a long name */ + const char *word = ('-' == flag[0]) ? flag + 2 : flag; + bool noflagged = FALSE; + bool expand = FALSE; + + if(!strncmp(word, "no-", 3)) { + /* disable this option but ignore the "no-" part when looking for it */ + word += 3; + toggle = FALSE; + noflagged = TRUE; + } + else if(!strncmp(word, "expand-", 7)) { + /* variable expansions is to be done on the argument */ + word += 7; + expand = TRUE; + } + + a = findlongopt(word); + if(a) { + longopt = TRUE; + } + else { + err = PARAM_OPTION_UNKNOWN; + goto error; + } + if(noflagged && (ARGTYPE(a->desc) != ARG_BOOL)) { + /* --no- prefixed an option that is not boolean! */ + err = PARAM_NO_PREFIX; + goto error; + } + else if(expand && nextarg) { + 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 */ + err = PARAM_EXPAND_ERROR; + goto error; } - else - err = getstr(&config->etag_save_file, nextarg, DENY_BLANK); - break; - case C_ETAG_COMPARE: /* --etag-compare */ - if(config->num_urls > 1) { - errorf(global, "The etag options only work on a single URL"); - err = PARAM_BAD_USE; + err = varexpand(global, nextarg, &nbuf, &replaced); + if(err) { + curlx_dyn_free(&nbuf); + goto error; } - else - err = getstr(&config->etag_compare_file, nextarg, DENY_BLANK); - break; - case C_CURVES: /* --curves */ - err = getstr(&config->ssl_ec_curves, nextarg, DENY_BLANK); - break; - case C_SIGNATURE_ALGORITHMS: /* --sigalgs */ - err = getstr(&config->ssl_signature_algorithms, nextarg, DENY_BLANK); - break; - case C_FAIL_EARLY: /* --fail-early */ - global->fail_early = toggle; - break; - case C_STYLED_OUTPUT: /* --styled-output */ - global->styled_output = toggle; - break; - case C_MAIL_RCPT_ALLOWFAILS: /* --mail-rcpt-allowfails */ - config->mail_rcpt_allowfails = toggle; - break; - case C_FAIL_WITH_BODY: /* --fail-with-body */ - config->failwithbody = toggle; - if(config->failonerror && config->failwithbody) { - errorf(config->global, "You must select either --fail or " - "--fail-with-body, not both."); - err = PARAM_BAD_USE; + if(replaced) { + nextarg = curlx_dyn_ptr(&nbuf); + nextalloc = TRUE; } - break; - case C_REMOVE_ON_ERROR: /* --remove-on-error */ - if(config->use_resume && toggle) { - errorf(config->global, - "--continue-at is mutually exclusive with --remove-on-error"); - return PARAM_BAD_USE; + } + } + else { + flag++; /* prefixed with one dash, pass it */ + parse = flag; + } + + do { + /* we can loop here if we have multiple single-letters */ + if(!longopt) { + a = findshortopt(*parse); + if(!a) { + err = PARAM_OPTION_UNKNOWN; + break; } - config->rm_partial = toggle; + } + if((a->desc & ARG_TLS) && !feature_ssl) { + err = PARAM_LIBCURL_DOESNT_SUPPORT; break; - case C_FAIL: /* --fail */ - config->failonerror = toggle; - if(config->failonerror && config->failwithbody) { - errorf(config->global, "You must select either --fail or " - "--fail-with-body, not both."); - err = PARAM_BAD_USE; + } + else if(ARGTYPE(a->desc) >= ARG_STRG) { + /* this option requires an extra parameter */ + if(!longopt && parse[1]) { + nextarg = &parse[1]; /* this is the actual extra parameter */ + singleopt = TRUE; /* do not loop anymore after this */ } - break; - case C_FORM: /* --form */ - case C_FORM_STRING: /* --form-string */ - /* "form data" simulation, this is a little advanced so lets do our best - to sort this out slowly and carefully */ - if(formparse(config, - nextarg, - &config->mimeroot, - &config->mimecurrent, - (cmd == C_FORM_STRING))) /* literal string */ - err = PARAM_BAD_USE; - else if(SetHTTPrequest(config, TOOL_HTTPREQ_MIMEPOST, &config->httpreq)) - err = PARAM_BAD_USE; - break; - case C_GLOBOFF: /* --globoff */ - config->globoff = toggle; - break; - case C_GET: /* --get */ - config->use_httpget = toggle; - break; - case C_REQUEST_TARGET: /* --request-target */ - err = getstr(&config->request_target, nextarg, DENY_BLANK); - break; - case C_HELP: /* --help */ - if(toggle) { + else if(a->cmd == C_HELP) { + /* --help is special */ tool_help((nextarg && *nextarg) ? nextarg : NULL); err = PARAM_HELP_REQUESTED; + break; } - /* we now actually support --no-help too! */ - break; - case C_HEADER: /* --header */ - case C_PROXY_HEADER: /* --proxy-header */ - err = parse_header(global, config, cmd, nextarg); - break; - case C_INCLUDE: /* --include */ - case C_SHOW_HEADERS: /* --show-headers */ - config->show_headers = toggle; /* show the headers as well in the - general output stream */ - break; - case C_JUNK_SESSION_COOKIES: /* --junk-session-cookies */ - config->cookiesession = toggle; - break; - case C_HEAD: /* --head */ - config->no_body = toggle; - config->show_headers = toggle; - if(SetHTTPrequest(config, (config->no_body) ? TOOL_HTTPREQ_HEAD : - TOOL_HTTPREQ_GET, &config->httpreq)) - err = PARAM_BAD_USE; - break; - case C_REMOTE_HEADER_NAME: /* --remote-header-name */ - config->content_disposition = toggle; - break; - case C_INSECURE: /* --insecure */ - config->insecure_ok = toggle; - break; - case C_DOH_INSECURE: /* --doh-insecure */ - config->doh_insecure_ok = toggle; - break; - case C_CONFIG: /* --config */ - if(parseconfig(nextarg, global)) { - errorf(global, "cannot read config from '%s'", nextarg); - err = PARAM_READ_ERROR; + else if(!nextarg) { + err = PARAM_REQUIRES_PARAMETER; + break; } - break; - case C_LIST_ONLY: /* --list-only */ - config->dirlistonly = toggle; /* only list the names of the FTP dir */ - break; - case C_LOCATION_TRUSTED: /* --location-trusted */ - /* Continue to send authentication (user+password) when following - * locations, even when hostname changed */ - config->unrestricted_auth = toggle; - FALLTHROUGH(); - case C_LOCATION: /* --location */ - config->followlocation = toggle; /* Follow Location: HTTP headers */ - break; - case C_MAX_TIME: /* --max-time */ - /* specified max time */ - err = secs2ms(&config->timeout_ms, nextarg); - break; - case C_MANUAL: /* --manual */ - if(toggle) { /* --no-manual shows no manual... */ - err = PARAM_MANUAL_REQUESTED; + else { + *usedarg = TRUE; /* mark it as used */ } - break; - case C_NETRC_OPTIONAL: /* --netrc-optional */ - config->netrc_opt = toggle; - break; - case C_NETRC_FILE: /* --netrc-file */ - err = getstr(&config->netrc_file, nextarg, DENY_BLANK); - break; - case C_NETRC: /* --netrc */ - /* pick info from .netrc, if this is used for http, curl will - automatically enforce user+password with the request */ - config->netrc = toggle; - break; - case C_BUFFER: /* --buffer */ - /* disable the output I/O buffering. note that the option is called - --buffer but is mostly used in the negative form: --no-buffer */ - config->nobuffer = (bool)(longopt ? !toggle : TRUE); - break; - case C_REMOTE_NAME_ALL: /* --remote-name-all */ - config->remote_name_all = toggle; - break; - case C_OUTPUT_DIR: /* --output-dir */ - err = getstr(&config->output_dir, nextarg, DENY_BLANK); - break; - case C_CLOBBER: /* --clobber */ - if(config->use_resume && !toggle) { - errorf(config->global, - "--continue-at is mutually exclusive with --no-clobber"); - return PARAM_BAD_USE; + if(a->desc & ARG_DEPR) { + opt_depr(global, a); + break; } - config->file_clobber_mode = toggle ? CLOBBER_ALWAYS : CLOBBER_NEVER; - break; - case C_OUTPUT: /* --output */ - err = parse_output(config, nextarg); - break; - case C_REMOTE_NAME: /* --remote-name */ - err = parse_remote_name(config, toggle); - break; - case C_FTP_PORT: /* --ftp-port */ - /* This makes the FTP sessions use PORT instead of PASV */ - /* use or <192.168.10.10> style addresses. Anything except - this will make us try to get the "default" address. - NOTE: this is a changed behavior since the released 4.1! - */ - err = getstr(&config->ftpport, nextarg, DENY_BLANK); - break; - case C_PROXYTUNNEL: /* --proxytunnel */ - /* proxy tunnel for non-http protocols */ - config->proxytunnel = toggle; - break; - case C_DISABLE: /* --disable */ - /* if used first, already taken care of, we do it like this so we do not - cause an error! */ - break; - case C_QUOTE: /* --quote */ - err = parse_quote(config, nextarg); - break; - case C_RANGE: /* --range */ - err = parse_range(global, config, nextarg); - break; - case C_REMOTE_TIME: /* --remote-time */ - /* use remote file's time */ - config->remote_time = toggle; - break; - 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; - case C_TELNET_OPTION: /* --telnet-option */ - /* Telnet options */ - err = add2list(&config->telnet_options, nextarg); - break; - case C_UPLOAD_FILE: /* --upload-file */ - err = parse_upload_file(config, nextarg); - break; - case C_USER: /* --user */ - /* user:password */ - err = getstr(&config->userpwd, nextarg, ALLOW_BLANK); - cleanarg(clearthis); - break; - case C_PROXY_USER: /* --proxy-user */ - /* Proxy user:password */ - err = getstr(&config->proxyuserpwd, nextarg, ALLOW_BLANK); - cleanarg(clearthis); - break; - case C_VERBOSE: /* --verbose */ - err = parse_verbose(global, toggle, nopts); - break; - case C_VERSION: /* --version */ - if(toggle) /* --no-version yields no output! */ - err = PARAM_VERSION_INFO_REQUESTED; - break; - case C_WRITE_OUT: /* --write-out */ - err = parse_writeout(global, config, nextarg); - break; - case C_PREPROXY: /* --preproxy */ - err = getstr(&config->preproxy, nextarg, DENY_BLANK); - break; - case C_PROXY: /* --proxy */ - /* --proxy */ - err = getstr(&config->proxy, nextarg, ALLOW_BLANK); - if(config->proxyver != CURLPROXY_HTTPS2) - config->proxyver = CURLPROXY_HTTP; - break; - case C_REQUEST: /* --request */ - /* 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: /* --parallel */ - global->parallel = toggle; - break; - case C_PARALLEL_MAX: { /* --parallel-max */ - long val; - err = str2unum(&val, nextarg); - if(err) + if((ARGTYPE(a->desc) == ARG_FILE) && + (nextarg[0] == '-') && nextarg[1]) { + /* if the filename looks like a command line option */ + warnf(global, "The filename argument '%s' looks like a flag.", + nextarg); + } + else if(!strncmp("\xe2\x80\x9c", nextarg, 3)) { + warnf(global, "The argument '%s' starts with a Unicode quote where " + "maybe an ASCII \" was intended?", + nextarg); + } + /* ARG_FILE | ARG_STRG */ + err = opt_filestring(global, config, a, nextarg); + if(a->desc & ARG_CLEAR) + cleanarg(CURL_UNCONST(nextarg)); + } + else { + if(a->desc & ARG_DEPR) { + opt_depr(global, a); break; - if(val > MAX_PARALLEL) - global->parallel_max = MAX_PARALLEL; - else if(val < 1) - global->parallel_max = PARALLEL_DEFAULT; + } + /* ARG_NONE | ARG_BOOL */ + if(ARGTYPE(a->desc) == ARG_BOOL) + err = opt_bool(global, config, a, toggle); else - global->parallel_max = (unsigned short)val; - break; + err = opt_none(global, config, a); } - case C_PARALLEL_IMMEDIATE: /* --parallel-immediate */ - global->parallel_connect = toggle; - break; - case C_TIME_COND: /* --time-cond */ - err = parse_time_cond(global, config, nextarg); - break; - case C_MPTCP: /* --mptcp */ - config->mptcp = TRUE; - break; - case C_UPLOAD_FLAGS: /* --upload-flags */ - err = parse_upload_flags(config, nextarg); - break; - default: /* unknown flag */ - err = PARAM_OPTION_UNKNOWN; - break; - } - a = NULL; - ++nopts; /* processed one option from `flag` input, loop for more */ + + ++verbose_nopts; /* processed one option from `flag` input, loop for + more */ } while(!longopt && !singleopt && *++parse && !*usedarg && !err); error: @@ -3000,8 +2981,7 @@ ParameterError parse_args(struct GlobalConfig *global, int argc, } } - result = getparameter(orig_opt, nextarg, argv[i], argv[i + 1], - &passarg, global, config); + result = getparameter(orig_opt, nextarg, &passarg, global, config); unicodefree(nextarg); config = global->last; @@ -3043,8 +3023,7 @@ ParameterError parse_args(struct GlobalConfig *global, int argc, bool used; /* Just add the URL please */ - result = getparameter("--url", orig_opt, NULL, NULL, - &used, global, config); + result = getparameter("--url", orig_opt, &used, global, config); } if(!result) { diff --git a/src/tool_getparam.h b/src/tool_getparam.h index b0f924816c..d84e852225 100644 --- a/src/tool_getparam.h +++ b/src/tool_getparam.h @@ -317,6 +317,8 @@ typedef enum { #define ARG_TYPEMASK 0x03 #define ARGTYPE(x) ((x) & ARG_TYPEMASK) +#define ARG_DEPR 0x10 /* deprecated option */ +#define ARG_CLEAR 0x20 /* clear cmdline argument */ #define ARG_TLS 0x40 /* requires TLS support */ #define ARG_NO 0x80 /* set if the option is documented as --no-* */ @@ -347,7 +349,6 @@ typedef enum { PARAM_NEXT_OPERATION, PARAM_NO_PREFIX, PARAM_NUMBER_TOO_LARGE, - PARAM_NO_NOT_BOOLEAN, PARAM_CONTDISP_RESUME_FROM, /* --continue-at and --remote-header-name */ PARAM_READ_ERROR, PARAM_EXPAND_ERROR, /* --expand problem */ @@ -363,8 +364,6 @@ const struct LongShort *findlongopt(const char *opt); const struct LongShort *findshortopt(char letter); ParameterError getparameter(const char *flag, const char *nextarg, - argv_item_t cleararg1, - argv_item_t cleararg2, bool *usedarg, struct GlobalConfig *global, struct OperationConfig *operation); diff --git a/src/tool_helpers.c b/src/tool_helpers.c index b3bb09e68e..5026fcf1c6 100644 --- a/src/tool_helpers.c +++ b/src/tool_helpers.c @@ -62,8 +62,6 @@ const char *param2text(ParameterError error) return "the given option cannot be reversed with a --no- prefix"; case PARAM_NUMBER_TOO_LARGE: return "too large number"; - case PARAM_NO_NOT_BOOLEAN: - return "used '--no-' for option that is not a boolean"; case PARAM_CONTDISP_RESUME_FROM: return "--continue-at and --remote-header-name cannot be combined"; case PARAM_READ_ERROR: diff --git a/src/tool_parsecfg.c b/src/tool_parsecfg.c index 9ce734be6e..0c59bdef78 100644 --- a/src/tool_parsecfg.c +++ b/src/tool_parsecfg.c @@ -172,8 +172,7 @@ int parseconfig(const char *filename, struct GlobalConfig *global) #ifdef DEBUG_CONFIG fprintf(tool_stderr, "PARAM: \"%s\"\n",(param ? param : "(null)")); #endif - res = getparameter(option, param, NULL, NULL, - &usedarg, global, operation); + res = getparameter(option, param, &usedarg, global, operation); operation = global->last; if(!res && param && *param && !usedarg)