xref: /curl/src/tool_getparam.c (revision ce7d0d41)
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at https://curl.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  * SPDX-License-Identifier: curl
22  *
23  ***************************************************************************/
24 #include "tool_setup.h"
25 
26 #include "strcase.h"
27 
28 #include "curlx.h"
29 
30 #include "tool_binmode.h"
31 #include "tool_cfgable.h"
32 #include "tool_cb_prg.h"
33 #include "tool_filetime.h"
34 #include "tool_formparse.h"
35 #include "tool_getparam.h"
36 #include "tool_helpers.h"
37 #include "tool_libinfo.h"
38 #include "tool_msgs.h"
39 #include "tool_paramhlp.h"
40 #include "tool_parsecfg.h"
41 #include "tool_main.h"
42 #include "dynbuf.h"
43 #include "tool_stderr.h"
44 #include "var.h"
45 
46 #include "memdebug.h" /* keep this as LAST include */
47 
48 #ifdef MSDOS
49 #  define USE_WATT32
50 #endif
51 
52 #define ALLOW_BLANK TRUE
53 #define DENY_BLANK FALSE
54 
getstr(char ** str,const char * val,bool allowblank)55 static ParameterError getstr(char **str, const char *val, bool allowblank)
56 {
57   if(*str) {
58     free(*str);
59     *str = NULL;
60   }
61   if(val) {
62     if(!allowblank && !val[0])
63       return PARAM_BLANK_STRING;
64 
65     *str = strdup(val);
66     if(!*str)
67       return PARAM_NO_MEM;
68   }
69   return PARAM_OK;
70 }
71 
72 /* this array MUST be alphasorted based on the 'lname' */
73 static const struct LongShort aliases[]= {
74   {"abstract-unix-socket",       ARG_FILE, ' ', C_ABSTRACT_UNIX_SOCKET},
75   {"alpn",                       ARG_BOOL|ARG_NO, ' ', C_ALPN},
76   {"alt-svc",                    ARG_STRG, ' ', C_ALT_SVC},
77   {"anyauth",                    ARG_BOOL, ' ', C_ANYAUTH},
78   {"append",                     ARG_BOOL, 'a', C_APPEND},
79   {"aws-sigv4",                  ARG_STRG, ' ', C_AWS_SIGV4},
80   {"basic",                      ARG_BOOL, ' ', C_BASIC},
81   {"buffer",                     ARG_BOOL|ARG_NO, 'N', C_BUFFER},
82   {"ca-native",                  ARG_BOOL, ' ', C_CA_NATIVE},
83   {"cacert",                     ARG_FILE, ' ', C_CACERT},
84   {"capath",                     ARG_FILE, ' ', C_CAPATH},
85   {"cert",                       ARG_FILE, 'E', C_CERT},
86   {"cert-status",                ARG_BOOL, ' ', C_CERT_STATUS},
87   {"cert-type",                  ARG_STRG, ' ', C_CERT_TYPE},
88   {"ciphers",                    ARG_STRG, ' ', C_CIPHERS},
89   {"clobber",                    ARG_BOOL|ARG_NO, ' ', C_CLOBBER},
90   {"compressed",                 ARG_BOOL, ' ', C_COMPRESSED},
91   {"compressed-ssh",             ARG_BOOL, ' ', C_COMPRESSED_SSH},
92   {"config",                     ARG_FILE, 'K', C_CONFIG},
93   {"connect-timeout",            ARG_STRG, ' ', C_CONNECT_TIMEOUT},
94   {"connect-to",                 ARG_STRG, ' ', C_CONNECT_TO},
95   {"continue-at",                ARG_STRG, 'C', C_CONTINUE_AT},
96   {"cookie",                     ARG_STRG, 'b', C_COOKIE},
97   {"cookie-jar",                 ARG_STRG, 'c', C_COOKIE_JAR},
98   {"create-dirs",                ARG_BOOL, ' ', C_CREATE_DIRS},
99   {"create-file-mode",           ARG_STRG, ' ', C_CREATE_FILE_MODE},
100   {"crlf",                       ARG_BOOL, ' ', C_CRLF},
101   {"crlfile",                    ARG_FILE, ' ', C_CRLFILE},
102   {"curves",                     ARG_STRG, ' ', C_CURVES},
103   {"data",                       ARG_STRG, 'd', C_DATA},
104   {"data-ascii",                 ARG_STRG, ' ', C_DATA_ASCII},
105   {"data-binary",                ARG_STRG, ' ', C_DATA_BINARY},
106   {"data-raw",                   ARG_STRG, ' ', C_DATA_RAW},
107   {"data-urlencode",             ARG_STRG, ' ', C_DATA_URLENCODE},
108   {"delegation",                 ARG_STRG, ' ', C_DELEGATION},
109   {"digest",                     ARG_BOOL, ' ', C_DIGEST},
110   {"disable",                    ARG_BOOL, 'q', C_DISABLE},
111   {"disable-eprt",               ARG_BOOL, ' ', C_DISABLE_EPRT},
112   {"disable-epsv",               ARG_BOOL, ' ', C_DISABLE_EPSV},
113   {"disallow-username-in-url",   ARG_BOOL, ' ', C_DISALLOW_USERNAME_IN_URL},
114   {"dns-interface",              ARG_STRG, ' ', C_DNS_INTERFACE},
115   {"dns-ipv4-addr",              ARG_STRG, ' ', C_DNS_IPV4_ADDR},
116   {"dns-ipv6-addr",              ARG_STRG, ' ', C_DNS_IPV6_ADDR},
117   {"dns-servers",                ARG_STRG, ' ', C_DNS_SERVERS},
118   {"doh-cert-status",            ARG_BOOL, ' ', C_DOH_CERT_STATUS},
119   {"doh-insecure",               ARG_BOOL, ' ', C_DOH_INSECURE},
120   {"doh-url"        ,            ARG_STRG, ' ', C_DOH_URL},
121   {"dump-ca-embed",              ARG_NONE, ' ', C_DUMP_CA_EMBED},
122   {"dump-header",                ARG_FILE, 'D', C_DUMP_HEADER},
123   {"ech",                        ARG_STRG, ' ', C_ECH},
124   {"egd-file",                   ARG_STRG, ' ', C_EGD_FILE},
125   {"engine",                     ARG_STRG, ' ', C_ENGINE},
126   {"eprt",                       ARG_BOOL, ' ', C_EPRT},
127   {"epsv",                       ARG_BOOL, ' ', C_EPSV},
128   {"etag-compare",               ARG_FILE, ' ', C_ETAG_COMPARE},
129   {"etag-save",                  ARG_FILE, ' ', C_ETAG_SAVE},
130   {"expect100-timeout",          ARG_STRG, ' ', C_EXPECT100_TIMEOUT},
131   {"fail",                       ARG_BOOL, 'f', C_FAIL},
132   {"fail-early",                 ARG_BOOL, ' ', C_FAIL_EARLY},
133   {"fail-with-body",             ARG_BOOL, ' ', C_FAIL_WITH_BODY},
134   {"false-start",                ARG_BOOL, ' ', C_FALSE_START},
135   {"form",                       ARG_STRG, 'F', C_FORM},
136   {"form-escape",                ARG_BOOL, ' ', C_FORM_ESCAPE},
137   {"form-string",                ARG_STRG, ' ', C_FORM_STRING},
138   {"ftp-account",                ARG_STRG, ' ', C_FTP_ACCOUNT},
139   {"ftp-alternative-to-user",    ARG_STRG, ' ', C_FTP_ALTERNATIVE_TO_USER},
140   {"ftp-create-dirs",            ARG_BOOL, ' ', C_FTP_CREATE_DIRS},
141   {"ftp-method",                 ARG_STRG, ' ', C_FTP_METHOD},
142   {"ftp-pasv",                   ARG_BOOL, ' ', C_FTP_PASV},
143   {"ftp-port",                   ARG_STRG, 'P', C_FTP_PORT},
144   {"ftp-pret",                   ARG_BOOL, ' ', C_FTP_PRET},
145   {"ftp-skip-pasv-ip",           ARG_BOOL, ' ', C_FTP_SKIP_PASV_IP},
146   {"ftp-ssl",                    ARG_BOOL, ' ', C_FTP_SSL},
147   {"ftp-ssl-ccc",                ARG_BOOL, ' ', C_FTP_SSL_CCC},
148   {"ftp-ssl-ccc-mode",           ARG_STRG, ' ', C_FTP_SSL_CCC_MODE},
149   {"ftp-ssl-control",            ARG_BOOL, ' ', C_FTP_SSL_CONTROL},
150   {"ftp-ssl-reqd",               ARG_BOOL, ' ', C_FTP_SSL_REQD},
151   {"get",                        ARG_BOOL, 'G', C_GET},
152   {"globoff",                    ARG_BOOL, 'g', C_GLOBOFF},
153   {"happy-eyeballs-timeout-ms",  ARG_STRG, ' ', C_HAPPY_EYEBALLS_TIMEOUT_MS},
154   {"haproxy-clientip",           ARG_STRG, ' ', C_HAPROXY_CLIENTIP},
155   {"haproxy-protocol",           ARG_BOOL, ' ', C_HAPROXY_PROTOCOL},
156   {"head",                       ARG_BOOL, 'I', C_HEAD},
157   {"header",                     ARG_STRG, 'H', C_HEADER},
158   {"help",                       ARG_BOOL, 'h', C_HELP},
159   {"hostpubmd5",                 ARG_STRG, ' ', C_HOSTPUBMD5},
160   {"hostpubsha256",              ARG_STRG, ' ', C_HOSTPUBSHA256},
161   {"hsts",                       ARG_STRG, ' ', C_HSTS},
162   {"http0.9",                    ARG_BOOL, ' ', C_HTTP0_9},
163   {"http1.0",                    ARG_NONE, '0', C_HTTP1_0},
164   {"http1.1",                    ARG_NONE, ' ', C_HTTP1_1},
165   {"http2",                      ARG_NONE, ' ', C_HTTP2},
166   {"http2-prior-knowledge",      ARG_NONE, ' ', C_HTTP2_PRIOR_KNOWLEDGE},
167   {"http3",                      ARG_NONE, ' ', C_HTTP3},
168   {"http3-only",                 ARG_NONE, ' ', C_HTTP3_ONLY},
169   {"ignore-content-length",      ARG_BOOL, ' ', C_IGNORE_CONTENT_LENGTH},
170   {"include",                    ARG_BOOL, ' ', C_INCLUDE},
171   {"insecure",                   ARG_BOOL, 'k', C_INSECURE},
172   {"interface",                  ARG_STRG, ' ', C_INTERFACE},
173   {"ip-tos",                     ARG_STRG, ' ', C_IP_TOS},
174 #ifndef CURL_DISABLE_IPFS
175   {"ipfs-gateway",               ARG_STRG, ' ', C_IPFS_GATEWAY},
176 #endif /* !CURL_DISABLE_IPFS */
177   {"ipv4",                       ARG_NONE, '4', C_IPV4},
178   {"ipv6",                       ARG_NONE, '6', C_IPV6},
179   {"json",                       ARG_STRG, ' ', C_JSON},
180   {"junk-session-cookies",       ARG_BOOL, 'j', C_JUNK_SESSION_COOKIES},
181   {"keepalive",                  ARG_BOOL|ARG_NO, ' ', C_KEEPALIVE},
182   {"keepalive-cnt",              ARG_STRG, ' ', C_KEEPALIVE_CNT},
183   {"keepalive-time",             ARG_STRG, ' ', C_KEEPALIVE_TIME},
184   {"key",                        ARG_FILE, ' ', C_KEY},
185   {"key-type",                   ARG_STRG, ' ', C_KEY_TYPE},
186   {"krb",                        ARG_STRG, ' ', C_KRB},
187   {"krb4",                       ARG_STRG, ' ', C_KRB4},
188   {"libcurl",                    ARG_STRG, ' ', C_LIBCURL},
189   {"limit-rate",                 ARG_STRG, ' ', C_LIMIT_RATE},
190   {"list-only",                  ARG_BOOL, 'l', C_LIST_ONLY},
191   {"local-port",                 ARG_STRG, ' ', C_LOCAL_PORT},
192   {"location",                   ARG_BOOL, 'L', C_LOCATION},
193   {"location-trusted",           ARG_BOOL, ' ', C_LOCATION_TRUSTED},
194   {"login-options",              ARG_STRG, ' ', C_LOGIN_OPTIONS},
195   {"mail-auth",                  ARG_STRG, ' ', C_MAIL_AUTH},
196   {"mail-from",                  ARG_STRG, ' ', C_MAIL_FROM},
197   {"mail-rcpt",                  ARG_STRG, ' ', C_MAIL_RCPT},
198   {"mail-rcpt-allowfails",       ARG_BOOL, ' ', C_MAIL_RCPT_ALLOWFAILS},
199   {"manual",                     ARG_BOOL, 'M', C_MANUAL},
200   {"max-filesize",               ARG_STRG, ' ', C_MAX_FILESIZE},
201   {"max-redirs",                 ARG_STRG, ' ', C_MAX_REDIRS},
202   {"max-time",                   ARG_STRG, 'm', C_MAX_TIME},
203   {"metalink",                   ARG_BOOL, ' ', C_METALINK},
204   {"mptcp",                      ARG_BOOL, ' ', C_MPTCP},
205   {"negotiate",                  ARG_BOOL, ' ', C_NEGOTIATE},
206   {"netrc",                      ARG_BOOL, 'n', C_NETRC},
207   {"netrc-file",                 ARG_FILE, ' ', C_NETRC_FILE},
208   {"netrc-optional",             ARG_BOOL, ' ', C_NETRC_OPTIONAL},
209   {"next",                       ARG_NONE, ':', C_NEXT},
210   {"noproxy",                    ARG_STRG, ' ', C_NOPROXY},
211   {"npn",                        ARG_BOOL|ARG_NO, ' ', C_NPN},
212   {"ntlm",                       ARG_BOOL, ' ', C_NTLM},
213   {"ntlm-wb",                    ARG_BOOL, ' ', C_NTLM_WB},
214   {"oauth2-bearer",              ARG_STRG, ' ', C_OAUTH2_BEARER},
215   {"output",                     ARG_FILE, 'o', C_OUTPUT},
216   {"output-dir",                 ARG_STRG, ' ', C_OUTPUT_DIR},
217   {"parallel",                   ARG_BOOL, 'Z', C_PARALLEL},
218   {"parallel-immediate",         ARG_BOOL, ' ', C_PARALLEL_IMMEDIATE},
219   {"parallel-max",               ARG_STRG, ' ', C_PARALLEL_MAX},
220   {"pass",                       ARG_STRG, ' ', C_PASS},
221   {"path-as-is",                 ARG_BOOL, ' ', C_PATH_AS_IS},
222   {"pinnedpubkey",               ARG_STRG, ' ', C_PINNEDPUBKEY},
223   {"post301",                    ARG_BOOL, ' ', C_POST301},
224   {"post302",                    ARG_BOOL, ' ', C_POST302},
225   {"post303",                    ARG_BOOL, ' ', C_POST303},
226   {"preproxy",                   ARG_STRG, ' ', C_PREPROXY},
227   {"progress-bar",               ARG_BOOL, '#', C_PROGRESS_BAR},
228   {"progress-meter",             ARG_BOOL|ARG_NO, ' ', C_PROGRESS_METER},
229   {"proto",                      ARG_STRG, ' ', C_PROTO},
230   {"proto-default",              ARG_STRG, ' ', C_PROTO_DEFAULT},
231   {"proto-redir",                ARG_STRG, ' ', C_PROTO_REDIR},
232   {"proxy",                      ARG_STRG, 'x', C_PROXY},
233   {"proxy-anyauth",              ARG_BOOL, ' ', C_PROXY_ANYAUTH},
234   {"proxy-basic",                ARG_BOOL, ' ', C_PROXY_BASIC},
235   {"proxy-ca-native",            ARG_BOOL, ' ', C_PROXY_CA_NATIVE},
236   {"proxy-cacert",               ARG_FILE, ' ', C_PROXY_CACERT},
237   {"proxy-capath",               ARG_FILE, ' ', C_PROXY_CAPATH},
238   {"proxy-cert",                 ARG_FILE, ' ', C_PROXY_CERT},
239   {"proxy-cert-type",            ARG_STRG, ' ', C_PROXY_CERT_TYPE},
240   {"proxy-ciphers",              ARG_STRG, ' ', C_PROXY_CIPHERS},
241   {"proxy-crlfile",              ARG_FILE, ' ', C_PROXY_CRLFILE},
242   {"proxy-digest",               ARG_BOOL, ' ', C_PROXY_DIGEST},
243   {"proxy-header",               ARG_STRG, ' ', C_PROXY_HEADER},
244   {"proxy-http2",                ARG_BOOL, ' ', C_PROXY_HTTP2},
245   {"proxy-insecure",             ARG_BOOL, ' ', C_PROXY_INSECURE},
246   {"proxy-key",                  ARG_FILE, ' ', C_PROXY_KEY},
247   {"proxy-key-type",             ARG_STRG, ' ', C_PROXY_KEY_TYPE},
248   {"proxy-negotiate",            ARG_BOOL, ' ', C_PROXY_NEGOTIATE},
249   {"proxy-ntlm",                 ARG_BOOL, ' ', C_PROXY_NTLM},
250   {"proxy-pass",                 ARG_STRG, ' ', C_PROXY_PASS},
251   {"proxy-pinnedpubkey",         ARG_STRG, ' ', C_PROXY_PINNEDPUBKEY},
252   {"proxy-service-name",         ARG_STRG, ' ', C_PROXY_SERVICE_NAME},
253   {"proxy-ssl-allow-beast",      ARG_BOOL, ' ', C_PROXY_SSL_ALLOW_BEAST},
254   {"proxy-ssl-auto-client-cert", ARG_BOOL, ' ', C_PROXY_SSL_AUTO_CLIENT_CERT},
255   {"proxy-tls13-ciphers",        ARG_STRG, ' ', C_PROXY_TLS13_CIPHERS},
256   {"proxy-tlsauthtype",          ARG_STRG, ' ', C_PROXY_TLSAUTHTYPE},
257   {"proxy-tlspassword",          ARG_STRG, ' ', C_PROXY_TLSPASSWORD},
258   {"proxy-tlsuser",              ARG_STRG, ' ', C_PROXY_TLSUSER},
259   {"proxy-tlsv1",                ARG_NONE, ' ', C_PROXY_TLSV1},
260   {"proxy-user",                 ARG_STRG, 'U', C_PROXY_USER},
261   {"proxy1.0",                   ARG_STRG, ' ', C_PROXY1_0},
262   {"proxytunnel",                ARG_BOOL, 'p', C_PROXYTUNNEL},
263   {"pubkey",                     ARG_STRG, ' ', C_PUBKEY},
264   {"quote",                      ARG_STRG, 'Q', C_QUOTE},
265   {"random-file",                ARG_FILE, ' ', C_RANDOM_FILE},
266   {"range",                      ARG_STRG, 'r', C_RANGE},
267   {"rate",                       ARG_STRG, ' ', C_RATE},
268   {"raw",                        ARG_BOOL, ' ', C_RAW},
269   {"referer",                    ARG_STRG, 'e', C_REFERER},
270   {"remote-header-name",         ARG_BOOL, 'J', C_REMOTE_HEADER_NAME},
271   {"remote-name",                ARG_BOOL, 'O', C_REMOTE_NAME},
272   {"remote-name-all",            ARG_BOOL, ' ', C_REMOTE_NAME_ALL},
273   {"remote-time",                ARG_BOOL, 'R', C_REMOTE_TIME},
274   {"remove-on-error",            ARG_BOOL, ' ', C_REMOVE_ON_ERROR},
275   {"request",                    ARG_STRG, 'X', C_REQUEST},
276   {"request-target",             ARG_STRG, ' ', C_REQUEST_TARGET},
277   {"resolve",                    ARG_STRG, ' ', C_RESOLVE},
278   {"retry",                      ARG_STRG, ' ', C_RETRY},
279   {"retry-all-errors",           ARG_BOOL, ' ', C_RETRY_ALL_ERRORS},
280   {"retry-connrefused",          ARG_BOOL, ' ', C_RETRY_CONNREFUSED},
281   {"retry-delay",                ARG_STRG, ' ', C_RETRY_DELAY},
282   {"retry-max-time",             ARG_STRG, ' ', C_RETRY_MAX_TIME},
283   {"sasl-authzid",               ARG_STRG, ' ', C_SASL_AUTHZID},
284   {"sasl-ir",                    ARG_BOOL, ' ', C_SASL_IR},
285   {"service-name",               ARG_STRG, ' ', C_SERVICE_NAME},
286   {"sessionid",                  ARG_BOOL|ARG_NO, ' ', C_SESSIONID},
287   {"show-error",                 ARG_BOOL, 'S', C_SHOW_ERROR},
288   {"show-headers",               ARG_BOOL, 'i', C_SHOW_HEADERS},
289   {"silent",                     ARG_BOOL, 's', C_SILENT},
290   {"skip-existing",              ARG_BOOL, ' ', C_SKIP_EXISTING},
291   {"socks4",                     ARG_STRG, ' ', C_SOCKS4},
292   {"socks4a",                    ARG_STRG, ' ', C_SOCKS4A},
293   {"socks5",                     ARG_STRG, ' ', C_SOCKS5},
294   {"socks5-basic",               ARG_BOOL, ' ', C_SOCKS5_BASIC},
295   {"socks5-gssapi",              ARG_BOOL, ' ', C_SOCKS5_GSSAPI},
296   {"socks5-gssapi-nec",          ARG_BOOL, ' ', C_SOCKS5_GSSAPI_NEC},
297   {"socks5-gssapi-service",      ARG_STRG, ' ', C_SOCKS5_GSSAPI_SERVICE},
298   {"socks5-hostname",            ARG_STRG, ' ', C_SOCKS5_HOSTNAME},
299   {"speed-limit",                ARG_STRG, 'Y', C_SPEED_LIMIT},
300   {"speed-time",                 ARG_STRG, 'y', C_SPEED_TIME},
301   {"ssl",                        ARG_BOOL, ' ', C_SSL},
302   {"ssl-allow-beast",            ARG_BOOL, ' ', C_SSL_ALLOW_BEAST},
303   {"ssl-auto-client-cert",       ARG_BOOL, ' ', C_SSL_AUTO_CLIENT_CERT},
304   {"ssl-no-revoke",              ARG_BOOL, ' ', C_SSL_NO_REVOKE},
305   {"ssl-reqd",                   ARG_BOOL, ' ', C_SSL_REQD},
306   {"ssl-revoke-best-effort",     ARG_BOOL, ' ', C_SSL_REVOKE_BEST_EFFORT},
307   {"sslv2",                      ARG_NONE, '2', C_SSLV2},
308   {"sslv3",                      ARG_NONE, '3', C_SSLV3},
309   {"stderr",                     ARG_FILE, ' ', C_STDERR},
310   {"styled-output",              ARG_BOOL, ' ', C_STYLED_OUTPUT},
311   {"suppress-connect-headers",   ARG_BOOL, ' ', C_SUPPRESS_CONNECT_HEADERS},
312   {"tcp-fastopen",               ARG_BOOL, ' ', C_TCP_FASTOPEN},
313   {"tcp-nodelay",                ARG_BOOL, ' ', C_TCP_NODELAY},
314   {"telnet-option",              ARG_STRG, 't', C_TELNET_OPTION},
315   {"test-event",                 ARG_BOOL, ' ', C_TEST_EVENT},
316   {"tftp-blksize",               ARG_STRG, ' ', C_TFTP_BLKSIZE},
317   {"tftp-no-options",            ARG_BOOL, ' ', C_TFTP_NO_OPTIONS},
318   {"time-cond",                  ARG_STRG, 'z', C_TIME_COND},
319   {"tls-max",                    ARG_STRG, ' ', C_TLS_MAX},
320   {"tls13-ciphers",              ARG_STRG, ' ', C_TLS13_CIPHERS},
321   {"tlsauthtype",                ARG_STRG, ' ', C_TLSAUTHTYPE},
322   {"tlspassword",                ARG_STRG, ' ', C_TLSPASSWORD},
323   {"tlsuser",                    ARG_STRG, ' ', C_TLSUSER},
324   {"tlsv1",                      ARG_NONE, '1', C_TLSV1},
325   {"tlsv1.0",                    ARG_NONE, ' ', C_TLSV1_0},
326   {"tlsv1.1",                    ARG_NONE, ' ', C_TLSV1_1},
327   {"tlsv1.2",                    ARG_NONE, ' ', C_TLSV1_2},
328   {"tlsv1.3",                    ARG_NONE, ' ', C_TLSV1_3},
329   {"tr-encoding",                ARG_BOOL, ' ', C_TR_ENCODING},
330   {"trace",                      ARG_FILE, ' ', C_TRACE},
331   {"trace-ascii",                ARG_FILE, ' ', C_TRACE_ASCII},
332   {"trace-config",               ARG_STRG, ' ', C_TRACE_CONFIG},
333   {"trace-ids",                  ARG_BOOL, ' ', C_TRACE_IDS},
334   {"trace-time",                 ARG_BOOL, ' ', C_TRACE_TIME},
335   {"unix-socket",                ARG_FILE, ' ', C_UNIX_SOCKET},
336   {"upload-file",                ARG_FILE, 'T', C_UPLOAD_FILE},
337   {"url",                        ARG_STRG, ' ', C_URL},
338   {"url-query",                  ARG_STRG, ' ', C_URL_QUERY},
339   {"use-ascii",                  ARG_BOOL, 'B', C_USE_ASCII},
340   {"user",                       ARG_STRG, 'u', C_USER},
341   {"user-agent",                 ARG_STRG, 'A', C_USER_AGENT},
342   {"variable",                   ARG_STRG, ' ', C_VARIABLE},
343   {"verbose",                    ARG_BOOL, 'v', C_VERBOSE},
344   {"version",                    ARG_BOOL, 'V', C_VERSION},
345   {"vlan-priority",              ARG_STRG, ' ', C_VLAN_PRIORITY},
346 #ifdef USE_WATT32
347   {"wdebug",                     ARG_BOOL, ' ', C_WDEBUG},
348 #endif
349   {"write-out",                  ARG_STRG, 'w', C_WRITE_OUT},
350   {"xattr",                      ARG_BOOL, ' ', C_XATTR},
351 };
352 
353 /* Split the argument of -E to 'certname' and 'passphrase' separated by colon.
354  * We allow ':' and '\' to be escaped by '\' so that we can use certificate
355  * nicknames containing ':'. See <https://sourceforge.net/p/curl/bugs/1196/>
356  * for details. */
357 #ifndef UNITTESTS
358 static
359 #endif
parse_cert_parameter(const char * cert_parameter,char ** certname,char ** passphrase)360 void parse_cert_parameter(const char *cert_parameter,
361                           char **certname,
362                           char **passphrase)
363 {
364   size_t param_length = strlen(cert_parameter);
365   size_t span;
366   const char *param_place = NULL;
367   char *certname_place = NULL;
368   *certname = NULL;
369   *passphrase = NULL;
370 
371   /* most trivial assumption: cert_parameter is empty */
372   if(param_length == 0)
373     return;
374 
375   /* next less trivial: cert_parameter starts 'pkcs11:' and thus
376    * looks like a RFC7512 PKCS#11 URI which can be used as-is.
377    * Also if cert_parameter contains no colon nor backslash, this
378    * means no passphrase was given and no characters escaped */
379   if(curl_strnequal(cert_parameter, "pkcs11:", 7) ||
380      !strpbrk(cert_parameter, ":\\")) {
381     *certname = strdup(cert_parameter);
382     return;
383   }
384   /* deal with escaped chars; find unescaped colon if it exists */
385   certname_place = malloc(param_length + 1);
386   if(!certname_place)
387     return;
388 
389   *certname = certname_place;
390   param_place = cert_parameter;
391   while(*param_place) {
392     span = strcspn(param_place, ":\\");
393     strncpy(certname_place, param_place, span);
394     param_place += span;
395     certname_place += span;
396     /* we just ate all the non-special chars. now we are on either a special
397      * char or the end of the string. */
398     switch(*param_place) {
399     case '\0':
400       break;
401     case '\\':
402       param_place++;
403       switch(*param_place) {
404         case '\0':
405           *certname_place++ = '\\';
406           break;
407         case '\\':
408           *certname_place++ = '\\';
409           param_place++;
410           break;
411         case ':':
412           *certname_place++ = ':';
413           param_place++;
414           break;
415         default:
416           *certname_place++ = '\\';
417           *certname_place++ = *param_place;
418           param_place++;
419           break;
420       }
421       break;
422     case ':':
423       /* Since we live in a world of weirdness and confusion, the Windows
424          dudes can use : when using drive letters and thus c:\file:password
425          needs to work. In order not to break compatibility, we still use : as
426          separator, but we try to detect when it is used for a filename! On
427          Windows. */
428 #ifdef _WIN32
429       if((param_place == &cert_parameter[1]) &&
430          (cert_parameter[2] == '\\' || cert_parameter[2] == '/') &&
431          (ISALPHA(cert_parameter[0])) ) {
432         /* colon in the second column, followed by a backslash, and the
433            first character is an alphabetic letter:
434 
435            this is a drive letter colon */
436         *certname_place++ = ':';
437         param_place++;
438         break;
439       }
440 #endif
441       /* escaped colons and Windows drive letter colons were handled
442        * above; if we are still here, this is a separating colon */
443       param_place++;
444       if(*param_place) {
445         *passphrase = strdup(param_place);
446       }
447       goto done;
448     }
449   }
450 done:
451   *certname_place = '\0';
452 }
453 
454 /* Replace (in-place) '%20' by '+' according to RFC1866 */
replace_url_encoded_space_by_plus(char * url)455 static size_t replace_url_encoded_space_by_plus(char *url)
456 {
457   size_t orig_len = strlen(url);
458   size_t orig_index = 0;
459   size_t new_index = 0;
460 
461   while(orig_index < orig_len) {
462     if((url[orig_index] == '%') &&
463        (url[orig_index + 1] == '2') &&
464        (url[orig_index + 2] == '0')) {
465       url[new_index] = '+';
466       orig_index += 3;
467     }
468     else{
469       if(new_index != orig_index) {
470         url[new_index] = url[orig_index];
471       }
472       orig_index++;
473     }
474     new_index++;
475   }
476 
477   url[new_index] = 0; /* terminate string */
478 
479   return new_index; /* new size */
480 }
481 
482 static void
GetFileAndPassword(char * nextarg,char ** file,char ** password)483 GetFileAndPassword(char *nextarg, char **file, char **password)
484 {
485   char *certname, *passphrase;
486   if(nextarg) {
487     parse_cert_parameter(nextarg, &certname, &passphrase);
488     Curl_safefree(*file);
489     *file = certname;
490     if(passphrase) {
491       Curl_safefree(*password);
492       *password = passphrase;
493     }
494   }
495 }
496 
497 /* Get a size parameter for '--limit-rate' or '--max-filesize'.
498  * We support a 'G', 'M' or 'K' suffix too.
499   */
GetSizeParameter(struct GlobalConfig * global,const char * arg,const char * which,curl_off_t * value_out)500 static ParameterError GetSizeParameter(struct GlobalConfig *global,
501                                        const char *arg,
502                                        const char *which,
503                                        curl_off_t *value_out)
504 {
505   char *unit;
506   curl_off_t value;
507 
508   if(curlx_strtoofft(arg, &unit, 10, &value)) {
509     warnf(global, "invalid number specified for %s", which);
510     return PARAM_BAD_USE;
511   }
512 
513   if(!*unit)
514     unit = (char *)"b";
515   else if(strlen(unit) > 1)
516     unit = (char *)"w"; /* unsupported */
517 
518   switch(*unit) {
519   case 'G':
520   case 'g':
521     if(value > (CURL_OFF_T_MAX / (1024*1024*1024)))
522       return PARAM_NUMBER_TOO_LARGE;
523     value *= 1024*1024*1024;
524     break;
525   case 'M':
526   case 'm':
527     if(value > (CURL_OFF_T_MAX / (1024*1024)))
528       return PARAM_NUMBER_TOO_LARGE;
529     value *= 1024*1024;
530     break;
531   case 'K':
532   case 'k':
533     if(value > (CURL_OFF_T_MAX / 1024))
534       return PARAM_NUMBER_TOO_LARGE;
535     value *= 1024;
536     break;
537   case 'b':
538   case 'B':
539     /* for plain bytes, leave as-is */
540     break;
541   default:
542     warnf(global, "unsupported %s unit. Use G, M, K or B", which);
543     return PARAM_BAD_USE;
544   }
545   *value_out = value;
546   return PARAM_OK;
547 }
548 
549 #ifdef HAVE_WRITABLE_ARGV
cleanarg(argv_item_t str)550 static void cleanarg(argv_item_t str)
551 {
552   /* now that getstr has copied the contents of nextarg, wipe the next
553    * argument out so that the username:password is not displayed in the
554    * system process list */
555   if(str) {
556     size_t len = strlen(str);
557     memset(str, ' ', len);
558   }
559 }
560 #else
561 #define cleanarg(x)
562 #endif
563 
564 /* the maximum size we allow the dynbuf generated string */
565 #define MAX_DATAURLENCODE (500*1024*1024)
566 
567 /* --data-urlencode */
data_urlencode(struct GlobalConfig * global,char * nextarg,char ** postp,size_t * lenp)568 static ParameterError data_urlencode(struct GlobalConfig *global,
569                                      char *nextarg,
570                                      char **postp,
571                                      size_t *lenp)
572 {
573   /* [name]=[content], we encode the content part only
574    * [name]@[filename]
575    *
576    * Case 2: we first load the file using that name and then encode
577    * the content.
578    */
579   ParameterError err;
580   const char *p = strchr(nextarg, '=');
581   size_t nlen;
582   char is_file;
583   char *postdata = NULL;
584   size_t size = 0;
585   if(!p)
586     /* there was no '=' letter, check for a '@' instead */
587     p = strchr(nextarg, '@');
588   if(p) {
589     nlen = p - nextarg; /* length of the name part */
590     is_file = *p++; /* pass the separator */
591   }
592   else {
593     /* neither @ nor =, so no name and it is not a file */
594     nlen = 0;
595     is_file = 0;
596     p = nextarg;
597   }
598   if('@' == is_file) {
599     FILE *file;
600     /* a '@' letter, it means that a filename or - (stdin) follows */
601     if(!strcmp("-", p)) {
602       file = stdin;
603       set_binmode(stdin);
604     }
605     else {
606       file = fopen(p, "rb");
607       if(!file) {
608         errorf(global, "Failed to open %s", p);
609         return PARAM_READ_ERROR;
610       }
611     }
612 
613     err = file2memory(&postdata, &size, file);
614 
615     if(file && (file != stdin))
616       fclose(file);
617     if(err)
618       return err;
619   }
620   else {
621     err = getstr(&postdata, p, ALLOW_BLANK);
622     if(err)
623       goto error;
624     size = strlen(postdata);
625   }
626 
627   if(!postdata) {
628     /* no data from the file, point to a zero byte string to make this
629        get sent as a POST anyway */
630     postdata = strdup("");
631     if(!postdata)
632       return PARAM_NO_MEM;
633     size = 0;
634   }
635   else {
636     char *enc = curl_easy_escape(NULL, postdata, (int)size);
637     Curl_safefree(postdata); /* no matter if it worked or not */
638     if(enc) {
639       char *n;
640       replace_url_encoded_space_by_plus(enc);
641       if(nlen > 0) { /* only append '=' if we have a name */
642         struct curlx_dynbuf dyn;
643         curlx_dyn_init(&dyn, MAX_DATAURLENCODE);
644         if(curlx_dyn_addn(&dyn, nextarg, nlen) ||
645            curlx_dyn_addn(&dyn, "=", 1) ||
646            curlx_dyn_add(&dyn, enc)) {
647           curl_free(enc);
648           return PARAM_NO_MEM;
649         }
650         curl_free(enc);
651         n = curlx_dyn_ptr(&dyn);
652         size = curlx_dyn_len(&dyn);
653       }
654       else {
655         n = enc;
656         size = strlen(n);
657       }
658       postdata = n;
659     }
660     else
661       return PARAM_NO_MEM;
662   }
663   *postp = postdata;
664   *lenp = size;
665   return PARAM_OK;
666 error:
667   return err;
668 }
669 
sethttpver(struct GlobalConfig * global,struct OperationConfig * config,long httpversion)670 static void sethttpver(struct GlobalConfig *global,
671                        struct OperationConfig *config,
672                        long httpversion)
673 {
674   if(config->httpversion &&
675      (config->httpversion != httpversion))
676     warnf(global, "Overrides previous HTTP version option");
677 
678   config->httpversion = httpversion;
679 }
680 
set_trace_config(struct GlobalConfig * global,const char * config)681 static CURLcode set_trace_config(struct GlobalConfig *global,
682                                  const char *config)
683 {
684   CURLcode result = CURLE_OK;
685   char *token, *tmp, *name;
686   bool toggle;
687 
688   tmp = strdup(config);
689   if(!tmp)
690     return CURLE_OUT_OF_MEMORY;
691 
692   /* Allow strtok() here since this is not used threaded */
693   /* !checksrc! disable BANNEDFUNC 2 */
694   token = strtok(tmp, ", ");
695   while(token) {
696     switch(*token) {
697       case '-':
698         toggle = FALSE;
699         name = token + 1;
700         break;
701       case '+':
702         toggle = TRUE;
703         name = token + 1;
704         break;
705       default:
706         toggle = TRUE;
707         name = token;
708         break;
709     }
710 
711     if(strcasecompare(name, "all")) {
712       global->traceids = toggle;
713       global->tracetime = toggle;
714       result = curl_global_trace(token);
715       if(result)
716         goto out;
717     }
718     else if(strcasecompare(name, "ids")) {
719       global->traceids = toggle;
720     }
721     else if(strcasecompare(name, "time")) {
722       global->tracetime = toggle;
723     }
724     else {
725       result = curl_global_trace(token);
726       if(result)
727         goto out;
728     }
729     token = strtok(NULL, ", ");
730   }
731 out:
732   free(tmp);
733   return result;
734 }
735 
findarg(const void * a,const void * b)736 static int findarg(const void *a, const void *b)
737 {
738   const struct LongShort *aa = a;
739   const struct LongShort *bb = b;
740   return strcmp(aa->lname, bb->lname);
741 }
742 
findshortopt(char letter)743 const struct LongShort *findshortopt(char letter)
744 {
745   static const struct LongShort *singles[128 - ' ']; /* ASCII => pointer */
746   static bool singles_done = FALSE;
747   if((letter >= 127) || (letter <= ' '))
748     return NULL;
749 
750   if(!singles_done) {
751     unsigned int j;
752     for(j = 0; j < sizeof(aliases)/sizeof(aliases[0]); j++) {
753       if(aliases[j].letter != ' ') {
754         unsigned char l = (unsigned char)aliases[j].letter;
755         singles[l - ' '] = &aliases[j];
756       }
757     }
758     singles_done = TRUE;
759   }
760   return singles[letter - ' '];
761 }
762 
763 struct TOSEntry {
764   const char *name;
765   unsigned char value;
766 };
767 
768 static const struct TOSEntry tos_entries[] = {
769   {"AF11", 0x28},
770   {"AF12", 0x30},
771   {"AF13", 0x38},
772   {"AF21", 0x48},
773   {"AF22", 0x50},
774   {"AF23", 0x58},
775   {"AF31", 0x68},
776   {"AF32", 0x70},
777   {"AF33", 0x78},
778   {"AF41", 0x88},
779   {"AF42", 0x90},
780   {"AF43", 0x98},
781   {"CE",   0x03},
782   {"CS0",  0x00},
783   {"CS1",  0x20},
784   {"CS2",  0x40},
785   {"CS3",  0x60},
786   {"CS4",  0x80},
787   {"CS5",  0xa0},
788   {"CS6",  0xc0},
789   {"CS7",  0xe0},
790   {"ECT0", 0x02},
791   {"ECT1", 0x01},
792   {"EF",   0xb8},
793   {"LE",   0x04},
794   {"LOWCOST",     0x02},
795   {"LOWDELAY",    0x10},
796   {"MINCOST",     0x02},
797   {"RELIABILITY", 0x04},
798   {"THROUGHPUT",  0x08},
799   {"VOICE-ADMIT", 0xb0}
800 };
801 
find_tos(const void * a,const void * b)802 static int find_tos(const void *a, const void *b)
803 {
804   const struct TOSEntry *aa = a;
805   const struct TOSEntry *bb = b;
806   return strcmp(aa->name, bb->name);
807 }
808 
809 #define MAX_QUERY_LEN 100000 /* larger is not likely to ever work */
url_query(char * nextarg,struct GlobalConfig * global,struct OperationConfig * config)810 static ParameterError url_query(char *nextarg,
811                                 struct GlobalConfig *global,
812                                 struct OperationConfig *config)
813 {
814   size_t size = 0;
815   ParameterError err = PARAM_OK;
816   char *query;
817   struct curlx_dynbuf dyn;
818   curlx_dyn_init(&dyn, MAX_QUERY_LEN);
819 
820   if(*nextarg == '+') {
821     /* use without encoding */
822     query = strdup(&nextarg[1]);
823     if(!query)
824       err = PARAM_NO_MEM;
825   }
826   else
827     err = data_urlencode(global, nextarg, &query, &size);
828 
829   if(!err) {
830     if(config->query) {
831       CURLcode result = curlx_dyn_addf(&dyn, "%s&%s", config->query, query);
832       free(query);
833       if(result)
834         err = PARAM_NO_MEM;
835       else {
836         free(config->query);
837         config->query = curlx_dyn_ptr(&dyn);
838       }
839     }
840     else
841       config->query = query;
842   }
843   return err;
844 }
845 
set_data(cmdline_t cmd,char * nextarg,struct GlobalConfig * global,struct OperationConfig * config)846 static ParameterError set_data(cmdline_t cmd,
847                                char *nextarg,
848                                struct GlobalConfig *global,
849                                struct OperationConfig *config)
850 {
851   char *postdata = NULL;
852   FILE *file;
853   size_t size = 0;
854   ParameterError err = PARAM_OK;
855 
856   if(cmd == C_DATA_URLENCODE) { /* --data-urlencode */
857     err = data_urlencode(global, nextarg, &postdata, &size);
858     if(err)
859       return err;
860   }
861   else if('@' == *nextarg && (cmd != C_DATA_RAW)) {
862     /* the data begins with a '@' letter, it means that a filename
863        or - (stdin) follows */
864     nextarg++; /* pass the @ */
865 
866     if(!strcmp("-", nextarg)) {
867       file = stdin;
868       if(cmd == C_DATA_BINARY) /* forced data-binary */
869         set_binmode(stdin);
870     }
871     else {
872       file = fopen(nextarg, "rb");
873       if(!file) {
874         errorf(global, "Failed to open %s", nextarg);
875         return PARAM_READ_ERROR;
876       }
877     }
878 
879     if((cmd == C_DATA_BINARY) || /* --data-binary */
880        (cmd == C_JSON) /* --json */)
881       /* forced binary */
882       err = file2memory(&postdata, &size, file);
883     else {
884       err = file2string(&postdata, file);
885       if(postdata)
886         size = strlen(postdata);
887     }
888 
889     if(file && (file != stdin))
890       fclose(file);
891     if(err)
892       return err;
893 
894     if(!postdata) {
895       /* no data from the file, point to a zero byte string to make this
896          get sent as a POST anyway */
897       postdata = strdup("");
898       if(!postdata)
899         return PARAM_NO_MEM;
900     }
901   }
902   else {
903     err = getstr(&postdata, nextarg, ALLOW_BLANK);
904     if(err)
905       return err;
906     size = strlen(postdata);
907   }
908   if(cmd == C_JSON)
909     config->jsoned = TRUE;
910 
911   if(curlx_dyn_len(&config->postdata)) {
912     /* skip separator append for --json */
913     if(!err && (cmd != C_JSON)  &&
914        curlx_dyn_addn(&config->postdata, "&", 1))
915       err = PARAM_NO_MEM;
916   }
917 
918   if(!err && curlx_dyn_addn(&config->postdata, postdata, size))
919     err = PARAM_NO_MEM;
920 
921   Curl_safefree(postdata);
922 
923   config->postfields = curlx_dyn_ptr(&config->postdata);
924   return err;
925 }
926 
set_rate(struct GlobalConfig * global,char * nextarg)927 static ParameterError set_rate(struct GlobalConfig *global,
928                                char *nextarg)
929 {
930   /* --rate */
931   /* support a few different suffixes, extract the suffix first, then
932      get the number and convert to per hour.
933      /s == per second
934      /m == per minute
935      /h == per hour (default)
936      /d == per day (24 hours)
937   */
938   ParameterError err = PARAM_OK;
939   char *div = strchr(nextarg, '/');
940   char number[26];
941   long denominator;
942   long numerator = 60*60*1000; /* default per hour */
943   size_t numlen = div ? (size_t)(div - nextarg) : strlen(nextarg);
944   if(numlen > sizeof(number) -1)
945     return PARAM_NUMBER_TOO_LARGE;
946 
947   strncpy(number, nextarg, numlen);
948   number[numlen] = 0;
949   err = str2unum(&denominator, number);
950   if(err)
951     return err;
952 
953   if(denominator < 1)
954     return PARAM_BAD_USE;
955 
956   if(div) {
957     char unit = div[1];
958     curl_off_t numunits;
959     char *endp;
960 
961     if(curlx_strtoofft(&div[1], &endp, 10, &numunits)) {
962       /* if it fails, there is no legit number specified */
963       if(endp == &div[1])
964         /* if endp did not move, accept it as a 1 */
965         numunits = 1;
966       else
967         return PARAM_BAD_USE;
968     }
969     else
970       unit = *endp;
971 
972     switch(unit) {
973     case 's': /* per second */
974       numerator = 1000;
975       break;
976     case 'm': /* per minute */
977       numerator = 60*1000;
978       break;
979     case 'h': /* per hour */
980       break;
981     case 'd': /* per day */
982       numerator = 24*60*60*1000;
983       break;
984     default:
985       errorf(global, "unsupported --rate unit");
986       err = PARAM_BAD_USE;
987       break;
988     }
989 
990     if((LONG_MAX / numerator) < numunits) {
991       /* overflow, too large number */
992       errorf(global, "too large --rate unit");
993       err = PARAM_NUMBER_TOO_LARGE;
994     }
995     /* this typecast is okay based on the check above */
996     numerator *= (long)numunits;
997   }
998 
999   if(err)
1000     ;
1001   else if(denominator > numerator)
1002     err = PARAM_NUMBER_TOO_LARGE;
1003   else
1004     global->ms_per_transfer = numerator/denominator;
1005 
1006   return err;
1007 }
1008 
findlongopt(const char * opt)1009 const struct LongShort *findlongopt(const char *opt)
1010 {
1011   struct LongShort key;
1012   key.lname = opt;
1013 
1014   return bsearch(&key, aliases, sizeof(aliases)/sizeof(aliases[0]),
1015                  sizeof(aliases[0]), findarg);
1016 }
1017 
1018 
getparameter(const char * flag,char * nextarg,argv_item_t cleararg,bool * usedarg,struct GlobalConfig * global,struct OperationConfig * config)1019 ParameterError getparameter(const char *flag, /* f or -long-flag */
1020                             char *nextarg,    /* NULL if unset */
1021                             argv_item_t cleararg,
1022                             bool *usedarg,    /* set to TRUE if the arg
1023                                                  has been used */
1024                             struct GlobalConfig *global,
1025                             struct OperationConfig *config)
1026 {
1027   int rc;
1028   const char *parse = NULL;
1029   time_t now;
1030   bool longopt = FALSE;
1031   bool singleopt = FALSE; /* when true means '-o foo' used '-ofoo' */
1032   size_t nopts = 0; /* options processed in `flag`*/
1033   ParameterError err = PARAM_OK;
1034   bool toggle = TRUE; /* how to switch boolean options, on or off. Controlled
1035                          by using --OPTION or --no-OPTION */
1036   bool nextalloc = FALSE; /* if nextarg is allocated */
1037   struct getout *url;
1038   static const char *redir_protos[] = {
1039     "http",
1040     "https",
1041     "ftp",
1042     "ftps",
1043     NULL
1044   };
1045   const struct LongShort *a = NULL;
1046   curl_off_t value;
1047 #ifdef HAVE_WRITABLE_ARGV
1048   argv_item_t clearthis = NULL;
1049 #else
1050   (void)cleararg;
1051 #endif
1052 
1053   *usedarg = FALSE; /* default is that we do not use the arg */
1054 
1055   if(('-' != flag[0]) || ('-' == flag[1])) {
1056     /* this should be a long name */
1057     const char *word = ('-' == flag[0]) ? flag + 2 : flag;
1058     bool noflagged = FALSE;
1059     bool expand = FALSE;
1060 
1061     if(!strncmp(word, "no-", 3)) {
1062       /* disable this option but ignore the "no-" part when looking for it */
1063       word += 3;
1064       toggle = FALSE;
1065       noflagged = TRUE;
1066     }
1067     else if(!strncmp(word, "expand-", 7)) {
1068       /* variable expansions is to be done on the argument */
1069       word += 7;
1070       expand = TRUE;
1071     }
1072 
1073     a = findlongopt(word);
1074     if(a) {
1075       longopt = TRUE;
1076     }
1077     else {
1078       err = PARAM_OPTION_UNKNOWN;
1079       goto error;
1080     }
1081     if(noflagged && (ARGTYPE(a->desc) != ARG_BOOL)) {
1082       /* --no- prefixed an option that is not boolean! */
1083       err = PARAM_NO_NOT_BOOLEAN;
1084       goto error;
1085     }
1086     else if(expand && nextarg) {
1087       struct curlx_dynbuf nbuf;
1088       bool replaced;
1089 
1090       if((ARGTYPE(a->desc) != ARG_STRG) &&
1091          (ARGTYPE(a->desc) != ARG_FILE)) {
1092         /* --expand on an option that is not a string or a filename */
1093         err = PARAM_EXPAND_ERROR;
1094         goto error;
1095       }
1096       err = varexpand(global, nextarg, &nbuf, &replaced);
1097       if(err) {
1098         curlx_dyn_free(&nbuf);
1099         goto error;
1100       }
1101       if(replaced) {
1102         nextarg = curlx_dyn_ptr(&nbuf);
1103         nextalloc = TRUE;
1104       }
1105     }
1106   }
1107   else {
1108     flag++; /* prefixed with one dash, pass it */
1109     parse = flag;
1110   }
1111 
1112   do {
1113     /* we can loop here if we have multiple single-letters */
1114     char letter;
1115     cmdline_t cmd;
1116 
1117     if(!longopt && !a) {
1118       a = findshortopt(*parse);
1119       if(!a) {
1120         err = PARAM_OPTION_UNKNOWN;
1121         break;
1122       }
1123     }
1124     letter = a->letter;
1125     cmd = (cmdline_t)a->cmd;
1126     if(ARGTYPE(a->desc) >= ARG_STRG) {
1127       /* this option requires an extra parameter */
1128       if(!longopt && parse[1]) {
1129         nextarg = (char *)&parse[1]; /* this is the actual extra parameter */
1130         singleopt = TRUE;   /* do not loop anymore after this */
1131       }
1132       else if(!nextarg) {
1133         err = PARAM_REQUIRES_PARAMETER;
1134         break;
1135       }
1136       else {
1137 #ifdef HAVE_WRITABLE_ARGV
1138         clearthis = cleararg;
1139 #endif
1140         *usedarg = TRUE; /* mark it as used */
1141       }
1142 
1143       if((ARGTYPE(a->desc) == ARG_FILE) &&
1144          (nextarg[0] == '-') && nextarg[1]) {
1145         /* if the filename looks like a command line option */
1146         warnf(global, "The filename argument '%s' looks like a flag.",
1147               nextarg);
1148       }
1149       else if(!strncmp("\xe2\x80\x9c", nextarg, 3)) {
1150         warnf(global, "The argument '%s' starts with a Unicode quote where "
1151               "maybe an ASCII \" was intended?",
1152               nextarg);
1153       }
1154     }
1155     else if((ARGTYPE(a->desc) == ARG_NONE) && !toggle) {
1156       err = PARAM_NO_PREFIX;
1157       break;
1158     }
1159 
1160     if(!nextarg)
1161       /* this is a precaution mostly to please scan-build, as all arguments
1162          that use nextarg should be marked as such and they will check that
1163          nextarg is set before continuing, but code analyzers are not always
1164          that aware of that state */
1165       nextarg = (char *)"";
1166 
1167     switch(cmd) {
1168     case C_RANDOM_FILE: /* --random-file */
1169     case C_EGD_FILE: /* --egd-file */
1170     case C_NTLM_WB: /* --ntlm-wb */
1171       warnf(global, "--%s is deprecated and has no function anymore",
1172             a->lname);
1173       break;
1174     case C_DNS_IPV4_ADDR: /* --dns-ipv4-addr */
1175       if(!curlinfo->ares_num) /* c-ares is needed for this */
1176         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1177       else
1178         /* addr in dot notation */
1179         err = getstr(&config->dns_ipv4_addr, nextarg, DENY_BLANK);
1180       break;
1181     case C_DNS_IPV6_ADDR: /* --dns-ipv6-addr */
1182       if(!curlinfo->ares_num) /* c-ares is needed for this */
1183         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1184       else
1185         /* addr in dot notation */
1186         err = getstr(&config->dns_ipv6_addr, nextarg, DENY_BLANK);
1187       break;
1188     case C_OAUTH2_BEARER: /* --oauth2-bearer */
1189       err = getstr(&config->oauth_bearer, nextarg, DENY_BLANK);
1190       if(!err) {
1191         cleanarg(clearthis);
1192         config->authtype |= CURLAUTH_BEARER;
1193       }
1194       break;
1195     case C_CONNECT_TIMEOUT: /* --connect-timeout */
1196       err = secs2ms(&config->connecttimeout_ms, nextarg);
1197       break;
1198     case C_DOH_URL: /* --doh-url */
1199       err = getstr(&config->doh_url, nextarg, ALLOW_BLANK);
1200       if(!err && config->doh_url && !config->doh_url[0])
1201         /* if given a blank string, make it NULL again */
1202         Curl_safefree(config->doh_url);
1203       break;
1204     case C_CIPHERS: /* -- ciphers */
1205       err = getstr(&config->cipher_list, nextarg, DENY_BLANK);
1206       break;
1207     case C_DNS_INTERFACE: /* --dns-interface */
1208       if(!curlinfo->ares_num) /* c-ares is needed for this */
1209         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1210       else
1211         /* interface name */
1212         err = getstr(&config->dns_interface, nextarg, DENY_BLANK);
1213       break;
1214     case C_DISABLE_EPSV: /* --disable-epsv */
1215       config->disable_epsv = toggle;
1216       break;
1217     case C_DISALLOW_USERNAME_IN_URL: /* --disallow-username-in-url */
1218       config->disallow_username_in_url = toggle;
1219       break;
1220     case C_EPSV: /* --epsv */
1221       config->disable_epsv = (!toggle) ? TRUE : FALSE;
1222       break;
1223     case C_DNS_SERVERS: /* --dns-servers */
1224       if(!curlinfo->ares_num) /* c-ares is needed for this */
1225         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1226       else
1227         /* IP addrs of DNS servers */
1228         err = getstr(&config->dns_servers, nextarg, DENY_BLANK);
1229       break;
1230     case C_TRACE: /* --trace */
1231       err = getstr(&global->trace_dump, nextarg, DENY_BLANK);
1232       if(!err) {
1233         if(global->tracetype && (global->tracetype != TRACE_BIN))
1234           warnf(global, "--trace overrides an earlier trace/verbose option");
1235         global->tracetype = TRACE_BIN;
1236       }
1237       break;
1238     case C_NPN: /* --npn */
1239       warnf(global, "--npn is no longer supported");
1240       break;
1241     case C_TRACE_ASCII: /* --trace-ascii */
1242       err = getstr(&global->trace_dump, nextarg, DENY_BLANK);
1243       if(!err) {
1244         if(global->tracetype && (global->tracetype != TRACE_ASCII))
1245           warnf(global,
1246                 "--trace-ascii overrides an earlier trace/verbose option");
1247         global->tracetype = TRACE_ASCII;
1248       }
1249       break;
1250     case C_ALPN: /* --alpn */
1251       config->noalpn = (!toggle) ? TRUE : FALSE;
1252       break;
1253     case C_LIMIT_RATE: /* --limit-rate */
1254       err = GetSizeParameter(global, nextarg, "rate", &value);
1255       if(!err) {
1256         config->recvpersecond = value;
1257         config->sendpersecond = value;
1258       }
1259       break;
1260     case C_RATE:
1261       err = set_rate(global, nextarg);
1262       break;
1263     case C_COMPRESSED: /* --compressed */
1264       if(toggle && !(feature_libz || feature_brotli || feature_zstd))
1265         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1266       else
1267         config->encoding = toggle;
1268       break;
1269     case C_TR_ENCODING: /* --tr-encoding */
1270       config->tr_encoding = toggle;
1271       break;
1272     case C_DIGEST: /* --digest */
1273       if(toggle)
1274         config->authtype |= CURLAUTH_DIGEST;
1275       else
1276         config->authtype &= ~CURLAUTH_DIGEST;
1277       break;
1278     case C_NEGOTIATE: /* --negotiate */
1279       if(!toggle)
1280         config->authtype &= ~CURLAUTH_NEGOTIATE;
1281       else if(feature_spnego)
1282         config->authtype |= CURLAUTH_NEGOTIATE;
1283       else
1284         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1285       break;
1286     case C_NTLM: /* --ntlm */
1287       if(!toggle)
1288         config->authtype &= ~CURLAUTH_NTLM;
1289       else if(feature_ntlm)
1290         config->authtype |= CURLAUTH_NTLM;
1291       else
1292         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1293       break;
1294     case C_BASIC: /* --basic */
1295       if(toggle)
1296         config->authtype |= CURLAUTH_BASIC;
1297       else
1298         config->authtype &= ~CURLAUTH_BASIC;
1299       break;
1300     case C_ANYAUTH: /* --anyauth */
1301       if(toggle)
1302         config->authtype = CURLAUTH_ANY;
1303       /* --no-anyauth simply does not touch it */
1304       break;
1305 #ifdef USE_WATT32
1306     case C_WDEBUG: /* --wdebug */
1307       dbug_init();
1308       break;
1309 #endif
1310     case C_FTP_CREATE_DIRS: /* --ftp-create-dirs */
1311       config->ftp_create_dirs = toggle;
1312       break;
1313     case C_CREATE_DIRS: /* --create-dirs */
1314       config->create_dirs = toggle;
1315       break;
1316     case C_CREATE_FILE_MODE: /* --create-file-mode */
1317       err = oct2nummax(&config->create_file_mode, nextarg, 0777);
1318       break;
1319     case C_MAX_REDIRS: /* --max-redirs */
1320       /* specified max no of redirects (http(s)), this accepts -1 as a
1321          special condition */
1322       err = str2num(&config->maxredirs, nextarg);
1323       if(!err && (config->maxredirs < -1))
1324         err = PARAM_BAD_NUMERIC;
1325       break;
1326 #ifndef CURL_DISABLE_IPFS
1327     case C_IPFS_GATEWAY: /* --ipfs-gateway */
1328       err = getstr(&config->ipfs_gateway, nextarg, DENY_BLANK);
1329       break;
1330 #endif /* !CURL_DISABLE_IPFS */
1331     case C_PROXY_NTLM: /* --proxy-ntlm */
1332       if(!feature_ntlm)
1333         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1334       else
1335         config->proxyntlm = toggle;
1336       break;
1337     case C_CRLF: /* --crlf */
1338       /* LF -> CRLF conversion? */
1339       config->crlf = toggle;
1340       break;
1341     case C_AWS_SIGV4: /* --aws-sigv4 */
1342       config->authtype |= CURLAUTH_AWS_SIGV4;
1343       err = getstr(&config->aws_sigv4, nextarg, DENY_BLANK);
1344       break;
1345     case C_STDERR: /* --stderr */
1346       tool_set_stderr_file(global, nextarg);
1347       break;
1348     case C_INTERFACE: /* --interface */
1349       /* interface */
1350       err = getstr(&config->iface, nextarg, DENY_BLANK);
1351       break;
1352     case C_KRB: /* --krb */
1353       /* kerberos level string */
1354       if(!feature_spnego)
1355         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1356       else
1357         err = getstr(&config->krblevel, nextarg, DENY_BLANK);
1358       break;
1359     case C_HAPROXY_PROTOCOL: /* --haproxy-protocol */
1360       config->haproxy_protocol = toggle;
1361       break;
1362     case C_HAPROXY_CLIENTIP: /* --haproxy-clientip */
1363       err = getstr(&config->haproxy_clientip, nextarg, DENY_BLANK);
1364       break;
1365     case C_MAX_FILESIZE: /* --max-filesize */
1366       err = GetSizeParameter(global, nextarg, "max-filesize", &value);
1367       if(!err)
1368         config->max_filesize = value;
1369       break;
1370     case C_DISABLE_EPRT: /* --disable-eprt */
1371       config->disable_eprt = toggle;
1372       break;
1373     case C_EPRT: /* --eprt */
1374       config->disable_eprt = (!toggle) ? TRUE : FALSE;
1375       break;
1376     case C_XATTR: /* --xattr */
1377       config->xattr = toggle;
1378       break;
1379     case C_URL: /* --url */
1380       if(!config->url_get)
1381         config->url_get = config->url_list;
1382 
1383       if(config->url_get) {
1384         /* there is a node here, if it already is filled-in continue to find
1385            an "empty" node */
1386         while(config->url_get && (config->url_get->flags & GETOUT_URL))
1387           config->url_get = config->url_get->next;
1388       }
1389 
1390       /* now there might or might not be an available node to fill in! */
1391 
1392       if(config->url_get)
1393         /* existing node */
1394         url = config->url_get;
1395       else
1396         /* there was no free node, create one! */
1397         config->url_get = url = new_getout(config);
1398 
1399       if(!url)
1400         err = PARAM_NO_MEM;
1401       else {
1402         /* fill in the URL */
1403         err = getstr(&url->url, nextarg, DENY_BLANK);
1404         url->flags |= GETOUT_URL;
1405       }
1406       break;
1407     case C_FTP_SSL: /* --ftp-ssl */
1408     case C_SSL: /* --ssl */
1409       if(toggle && !feature_ssl)
1410         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1411       else {
1412         config->ftp_ssl = toggle;
1413         if(config->ftp_ssl)
1414           warnf(global,
1415                 "--%s is an insecure option, consider --ssl-reqd instead",
1416                 a->lname);
1417       }
1418       break;
1419     case C_FTP_PASV: /* --ftp-pasv */
1420       Curl_safefree(config->ftpport);
1421       break;
1422     case C_SOCKS5: /* --socks5 */
1423       /*  socks5 proxy to use, and resolves the name locally and passes on the
1424           resolved address */
1425       err = getstr(&config->proxy, nextarg, DENY_BLANK);
1426       config->proxyver = CURLPROXY_SOCKS5;
1427       break;
1428     case C_SOCKS4: /* --socks4 */
1429       err = getstr(&config->proxy, nextarg, DENY_BLANK);
1430       config->proxyver = CURLPROXY_SOCKS4;
1431       break;
1432     case C_SOCKS4A: /* --socks4a */
1433       err = getstr(&config->proxy, nextarg, DENY_BLANK);
1434       config->proxyver = CURLPROXY_SOCKS4A;
1435       break;
1436     case C_SOCKS5_HOSTNAME: /* --socks5-hostname */
1437       err = getstr(&config->proxy, nextarg, DENY_BLANK);
1438       config->proxyver = CURLPROXY_SOCKS5_HOSTNAME;
1439       break;
1440     case C_TCP_NODELAY: /* --tcp-nodelay */
1441       config->tcp_nodelay = toggle;
1442       break;
1443     case C_IP_TOS: { /* --ip-tos */
1444       struct TOSEntry find;
1445       const struct TOSEntry *entry;
1446       find.name = nextarg;
1447       entry = bsearch(&find, tos_entries,
1448                       sizeof(tos_entries)/sizeof(*tos_entries),
1449                       sizeof(*tos_entries), find_tos);
1450       if(entry)
1451         config->ip_tos = entry->value;
1452       else /* numeric tos value */
1453         err = str2unummax(&config->ip_tos, nextarg, 0xFF);
1454       break;
1455     }
1456     case C_VLAN_PRIORITY: /* --vlan-priority */
1457       err = str2unummax(&config->vlan_priority, nextarg, 7);
1458       break;
1459     case C_PROXY_DIGEST: /* --proxy-digest */
1460       config->proxydigest = toggle;
1461       break;
1462     case C_PROXY_BASIC: /* --proxy-basic */
1463       config->proxybasic = toggle;
1464       break;
1465     case C_RETRY: /* --retry */
1466       err = str2unum(&config->req_retry, nextarg);
1467       break;
1468     case C_RETRY_CONNREFUSED: /* --retry-connrefused */
1469       config->retry_connrefused = toggle;
1470       break;
1471     case C_RETRY_DELAY: /* --retry-delay */
1472       err = str2unummax(&config->retry_delay, nextarg, LONG_MAX/1000);
1473       break;
1474     case C_RETRY_MAX_TIME: /* --retry-max-time */
1475       err = str2unummax(&config->retry_maxtime, nextarg, LONG_MAX/1000);
1476       break;
1477     case C_RETRY_ALL_ERRORS: /* --retry-all-errors */
1478       config->retry_all_errors = toggle;
1479       break;
1480     case C_PROXY_NEGOTIATE: /* --proxy-negotiate */
1481       if(!feature_spnego)
1482         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1483       else
1484         config->proxynegotiate = toggle;
1485       break;
1486     case C_FORM_ESCAPE: /* --form-escape */
1487       config->mime_options &= ~CURLMIMEOPT_FORMESCAPE;
1488       if(toggle)
1489         config->mime_options |= CURLMIMEOPT_FORMESCAPE;
1490       break;
1491     case C_FTP_ACCOUNT: /* --ftp-account */
1492       err = getstr(&config->ftp_account, nextarg, DENY_BLANK);
1493       break;
1494     case C_PROXY_ANYAUTH: /* --proxy-anyauth */
1495       config->proxyanyauth = toggle;
1496       break;
1497     case C_TRACE_TIME: /* --trace-time */
1498       global->tracetime = toggle;
1499       break;
1500     case C_IGNORE_CONTENT_LENGTH: /* --ignore-content-length */
1501       config->ignorecl = toggle;
1502       break;
1503     case C_FTP_SKIP_PASV_IP: /* --ftp-skip-pasv-ip */
1504       config->ftp_skip_ip = toggle;
1505       break;
1506     case C_FTP_METHOD: /* --ftp-method */
1507       config->ftp_filemethod = ftpfilemethod(config, nextarg);
1508       break;
1509     case C_LOCAL_PORT: { /* --local-port */
1510       /* 16bit base 10 is 5 digits, but we allow 6 so that this catches
1511          overflows, not just truncates */
1512       char lrange[7]="";
1513       char *p = nextarg;
1514       while(ISDIGIT(*p))
1515         p++;
1516       if(*p) {
1517         /* if there is anything more than a plain decimal number */
1518         rc = sscanf(p, " - %6s", lrange);
1519         *p = 0; /* null-terminate to make str2unum() work below */
1520       }
1521       else
1522         rc = 0;
1523 
1524       err = str2unum(&config->localport, nextarg);
1525       if(err || (config->localport > 65535)) {
1526         err = PARAM_BAD_USE;
1527         break;
1528       }
1529       if(!rc)
1530         config->localportrange = 1; /* default number of ports to try */
1531       else {
1532         err = str2unum(&config->localportrange, lrange);
1533         if(err || (config->localportrange > 65535))
1534           err = PARAM_BAD_USE;
1535         else {
1536           config->localportrange -= (config->localport-1);
1537           if(config->localportrange < 1)
1538             err = PARAM_BAD_USE;
1539         }
1540       }
1541       break;
1542     }
1543     case C_FTP_ALTERNATIVE_TO_USER: /* --ftp-alternative-to-user */
1544       err = getstr(&config->ftp_alternative_to_user, nextarg, DENY_BLANK);
1545       break;
1546     case C_FTP_SSL_REQD: /* --ftp-ssl-reqd */
1547     case C_SSL_REQD: /* --ssl-reqd */
1548       if(toggle && !feature_ssl) {
1549         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1550         break;
1551       }
1552       config->ftp_ssl_reqd = toggle;
1553       break;
1554     case C_SESSIONID: /* --sessionid */
1555       config->disable_sessionid = (!toggle) ? TRUE : FALSE;
1556       break;
1557     case C_FTP_SSL_CONTROL: /* --ftp-ssl-control */
1558       if(toggle && !feature_ssl)
1559         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1560       else
1561         config->ftp_ssl_control = toggle;
1562       break;
1563     case C_FTP_SSL_CCC: /* --ftp-ssl-ccc */
1564       config->ftp_ssl_ccc = toggle;
1565       if(!config->ftp_ssl_ccc_mode)
1566         config->ftp_ssl_ccc_mode = CURLFTPSSL_CCC_PASSIVE;
1567       break;
1568     case C_FTP_SSL_CCC_MODE: /* --ftp-ssl-ccc-mode */
1569       config->ftp_ssl_ccc = TRUE;
1570       config->ftp_ssl_ccc_mode = ftpcccmethod(config, nextarg);
1571       break;
1572     case C_LIBCURL: /* --libcurl */
1573 #ifdef CURL_DISABLE_LIBCURL_OPTION
1574       warnf(global,
1575             "--libcurl option was disabled at build-time");
1576       err = PARAM_OPTION_UNKNOWN;
1577 #else
1578       err = getstr(&global->libcurl, nextarg, DENY_BLANK);
1579 #endif
1580       break;
1581     case C_RAW: /* --raw */
1582       config->raw = toggle;
1583       break;
1584     case C_KEEPALIVE: /* --keepalive */
1585       config->nokeepalive = (!toggle) ? TRUE : FALSE;
1586       break;
1587     case C_KEEPALIVE_TIME: /* --keepalive-time */
1588       err = str2unum(&config->alivetime, nextarg);
1589       break;
1590     case C_KEEPALIVE_CNT: /* --keepalive-cnt */
1591       err = str2unum(&config->alivecnt, nextarg);
1592       break;
1593     case C_POST301: /* --post301 */
1594       config->post301 = toggle;
1595       break;
1596     case C_POST302: /* --post302 */
1597       config->post302 = toggle;
1598       break;
1599     case C_POST303: /* --post303 */
1600       config->post303 = toggle;
1601       break;
1602     case C_NOPROXY: /* --noproxy */
1603       /* This specifies the noproxy list */
1604       err = getstr(&config->noproxy, nextarg, ALLOW_BLANK);
1605       break;
1606     case C_SOCKS5_GSSAPI_NEC: /* --socks5-gssapi-nec */
1607       config->socks5_gssapi_nec = toggle;
1608       break;
1609     case C_PROXY1_0: /* --proxy1.0 */
1610       /* http 1.0 proxy */
1611       err = getstr(&config->proxy, nextarg, DENY_BLANK);
1612       config->proxyver = CURLPROXY_HTTP_1_0;
1613       break;
1614     case C_TFTP_BLKSIZE: /* --tftp-blksize */
1615       err = str2unum(&config->tftp_blksize, nextarg);
1616       break;
1617     case C_MAIL_FROM: /* --mail-from */
1618       err = getstr(&config->mail_from, nextarg, DENY_BLANK);
1619       break;
1620     case C_MAIL_RCPT: /* --mail-rcpt */
1621       /* append receiver to a list */
1622       err = add2list(&config->mail_rcpt, nextarg);
1623       break;
1624     case C_FTP_PRET: /* --ftp-pret */
1625       config->ftp_pret = toggle;
1626       break;
1627     case C_PROTO: /* --proto */
1628       config->proto_present = TRUE;
1629       err = proto2num(config, built_in_protos, &config->proto_str, nextarg);
1630       break;
1631     case C_PROTO_REDIR: /* --proto-redir */
1632       config->proto_redir_present = TRUE;
1633       if(proto2num(config, redir_protos, &config->proto_redir_str,
1634                    nextarg))
1635         err = PARAM_BAD_USE;
1636       break;
1637     case C_RESOLVE: /* --resolve */
1638       err = add2list(&config->resolve, nextarg);
1639       break;
1640     case C_DELEGATION: /* --delegation */
1641       config->gssapi_delegation = delegation(config, nextarg);
1642       break;
1643     case C_MAIL_AUTH: /* --mail-auth */
1644       err = getstr(&config->mail_auth, nextarg, DENY_BLANK);
1645       break;
1646     case C_METALINK: /* --metalink */
1647       errorf(global, "--metalink is disabled");
1648       err = PARAM_BAD_USE;
1649       break;
1650     case C_SASL_AUTHZID: /* --sasl-authzid */
1651       err = getstr(&config->sasl_authzid, nextarg, DENY_BLANK);
1652       break;
1653     case C_SASL_IR: /* --sasl-ir */
1654       config->sasl_ir = toggle;
1655       break;
1656     case C_TEST_EVENT: /* --test-event */
1657 #ifdef DEBUGBUILD
1658       global->test_event_based = toggle;
1659 #else
1660       warnf(global, "--test-event is ignored unless a debug build");
1661 #endif
1662       break;
1663     case C_UNIX_SOCKET: /* --unix-socket */
1664       config->abstract_unix_socket = FALSE;
1665       err = getstr(&config->unix_socket_path, nextarg, DENY_BLANK);
1666       break;
1667     case C_PATH_AS_IS: /* --path-as-is */
1668       config->path_as_is = toggle;
1669       break;
1670     case C_PROXY_SERVICE_NAME: /* --proxy-service-name */
1671       err = getstr(&config->proxy_service_name, nextarg, DENY_BLANK);
1672       break;
1673     case C_SERVICE_NAME: /* --service-name */
1674       err = getstr(&config->service_name, nextarg, DENY_BLANK);
1675       break;
1676     case C_PROTO_DEFAULT: /* --proto-default */
1677       err = getstr(&config->proto_default, nextarg, DENY_BLANK);
1678       if(!err)
1679         err = check_protocol(config->proto_default);
1680       break;
1681     case C_EXPECT100_TIMEOUT: /* --expect100-timeout */
1682       err = secs2ms(&config->expect100timeout_ms, nextarg);
1683       break;
1684     case C_TFTP_NO_OPTIONS: /* --tftp-no-options */
1685       config->tftp_no_options = toggle;
1686       break;
1687     case C_CONNECT_TO: /* --connect-to */
1688       err = add2list(&config->connect_to, nextarg);
1689       break;
1690     case C_ABSTRACT_UNIX_SOCKET: /* --abstract-unix-socket */
1691       config->abstract_unix_socket = TRUE;
1692       err = getstr(&config->unix_socket_path, nextarg, DENY_BLANK);
1693       break;
1694     case C_TLS_MAX: /* --tls-max */
1695       err = str2tls_max(&config->ssl_version_max, nextarg);
1696       break;
1697     case C_SUPPRESS_CONNECT_HEADERS: /* --suppress-connect-headers */
1698       config->suppress_connect_headers = toggle;
1699       break;
1700     case C_COMPRESSED_SSH: /* --compressed-ssh */
1701       config->ssh_compression = toggle;
1702       break;
1703     case C_HAPPY_EYEBALLS_TIMEOUT_MS: /* --happy-eyeballs-timeout-ms */
1704       err = str2unum(&config->happy_eyeballs_timeout_ms, nextarg);
1705       /* 0 is a valid value for this timeout */
1706       break;
1707     case C_TRACE_IDS: /* --trace-ids */
1708       global->traceids = toggle;
1709       break;
1710     case C_TRACE_CONFIG: /* --trace-config */
1711       if(set_trace_config(global, nextarg))
1712         err = PARAM_NO_MEM;
1713       break;
1714     case C_PROGRESS_METER: /* --progress-meter */
1715       global->noprogress = !toggle;
1716       break;
1717     case C_PROGRESS_BAR: /* --progress-bar */
1718       global->progressmode = toggle ? CURL_PROGRESS_BAR : CURL_PROGRESS_STATS;
1719       break;
1720     case C_VARIABLE: /* --variable */
1721       err = setvariable(global, nextarg);
1722       break;
1723     case C_NEXT: /* --next */
1724       err = PARAM_NEXT_OPERATION;
1725       break;
1726     case C_HTTP1_0: /* --http1.0 */
1727       /* HTTP version 1.0 */
1728       sethttpver(global, config, CURL_HTTP_VERSION_1_0);
1729       break;
1730     case C_HTTP1_1: /* --http1.1 */
1731       /* HTTP version 1.1 */
1732       sethttpver(global, config, CURL_HTTP_VERSION_1_1);
1733       break;
1734     case C_HTTP2: /* --http2 */
1735       /* HTTP version 2.0 */
1736       if(!feature_http2)
1737         return PARAM_LIBCURL_DOESNT_SUPPORT;
1738       sethttpver(global, config, CURL_HTTP_VERSION_2_0);
1739       break;
1740     case C_HTTP2_PRIOR_KNOWLEDGE: /* --http2-prior-knowledge */
1741       /* HTTP version 2.0 over clean TCP */
1742       if(!feature_http2)
1743         return PARAM_LIBCURL_DOESNT_SUPPORT;
1744       sethttpver(global, config, CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE);
1745       break;
1746     case C_HTTP3: /* --http3: */
1747       /* Try HTTP/3, allow fallback */
1748       if(!feature_http3)
1749         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1750       else
1751         sethttpver(global, config, CURL_HTTP_VERSION_3);
1752       break;
1753     case C_HTTP3_ONLY: /* --http3-only */
1754       /* Try HTTP/3 without fallback */
1755       if(!feature_http3)
1756         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1757       else
1758         sethttpver(global, config, CURL_HTTP_VERSION_3ONLY);
1759       break;
1760     case C_HTTP0_9: /* --http0.9 */
1761       /* Allow HTTP/0.9 responses! */
1762       config->http09_allowed = toggle;
1763       break;
1764     case C_PROXY_HTTP2: /* --proxy-http2 */
1765       if(!feature_httpsproxy || !feature_http2)
1766         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1767       else
1768         config->proxyver = CURLPROXY_HTTPS2;
1769       break;
1770     case C_TLSV1: /* --tlsv1 */
1771       config->ssl_version = CURL_SSLVERSION_TLSv1;
1772       break;
1773     case C_TLSV1_0: /* --tlsv1.0 */
1774       config->ssl_version = CURL_SSLVERSION_TLSv1_0;
1775       break;
1776     case C_TLSV1_1: /* --tlsv1.1 */
1777       config->ssl_version = CURL_SSLVERSION_TLSv1_1;
1778       break;
1779     case C_TLSV1_2: /* --tlsv1.2 */
1780       config->ssl_version = CURL_SSLVERSION_TLSv1_2;
1781       break;
1782     case C_TLSV1_3: /* --tlsv1.3 */
1783       config->ssl_version = CURL_SSLVERSION_TLSv1_3;
1784       break;
1785     case C_TLS13_CIPHERS: /* --tls13-ciphers */
1786       err = getstr(&config->cipher13_list, nextarg, DENY_BLANK);
1787       break;
1788     case C_PROXY_TLS13_CIPHERS: /* --proxy-tls13-ciphers */
1789       err = getstr(&config->proxy_cipher13_list, nextarg, DENY_BLANK);
1790       break;
1791     case C_SSLV2: /* --sslv2 */
1792       warnf(global, "Ignores instruction to use SSLv2");
1793       break;
1794     case C_SSLV3: /* --sslv3 */
1795       warnf(global, "Ignores instruction to use SSLv3");
1796       break;
1797     case C_IPV4: /* --ipv4 */
1798       config->ip_version = CURL_IPRESOLVE_V4;
1799       break;
1800     case C_IPV6: /* --ipv6 */
1801       config->ip_version = CURL_IPRESOLVE_V6;
1802       break;
1803     case C_APPEND: /* --append */
1804       /* This makes the FTP sessions use APPE instead of STOR */
1805       config->ftp_append = toggle;
1806       break;
1807     case C_USER_AGENT: /* --user-agent */
1808       err = getstr(&config->useragent, nextarg, ALLOW_BLANK);
1809       break;
1810     case C_ALT_SVC: /* --alt-svc */
1811       if(!feature_altsvc)
1812         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1813       else
1814         err = getstr(&config->altsvc, nextarg, ALLOW_BLANK);
1815       break;
1816     case C_HSTS: /* --hsts */
1817       if(!feature_hsts)
1818         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1819       else
1820         err = getstr(&config->hsts, nextarg, ALLOW_BLANK);
1821       break;
1822     case C_COOKIE: /* --cookie */
1823       if(strchr(nextarg, '=')) {
1824         /* A cookie string must have a =-letter */
1825         err = add2list(&config->cookies, nextarg);
1826         break;
1827       }
1828       else {
1829         /* We have a cookie file to read from! */
1830         err = add2list(&config->cookiefiles, nextarg);
1831       }
1832       break;
1833     case C_USE_ASCII: /* --use-ascii */
1834       config->use_ascii = toggle;
1835       break;
1836     case C_COOKIE_JAR: /* --cookie-jar */
1837       err = getstr(&config->cookiejar, nextarg, DENY_BLANK);
1838       break;
1839     case C_CONTINUE_AT: /* --continue-at */
1840       /* This makes us continue an ftp transfer at given position */
1841       if(strcmp(nextarg, "-")) {
1842         err = str2offset(&config->resume_from, nextarg);
1843         config->resume_from_current = FALSE;
1844       }
1845       else {
1846         config->resume_from_current = TRUE;
1847         config->resume_from = 0;
1848       }
1849       config->use_resume = TRUE;
1850       break;
1851     case C_DATA: /* --data */
1852     case C_DATA_ASCII:  /* --data-ascii */
1853     case C_DATA_BINARY:  /* --data-binary */
1854     case C_DATA_URLENCODE:  /* --data-urlencode */
1855     case C_JSON:  /* --json */
1856     case C_DATA_RAW:  /* --data-raw */
1857       err = set_data(cmd, nextarg, global, config);
1858       break;
1859     case C_URL_QUERY:  /* --url-query */
1860       err = url_query(nextarg, global, config);
1861       break;
1862     case C_DUMP_CA_EMBED: /* --dump-ca-embed */
1863       err = PARAM_CA_EMBED_REQUESTED;
1864       break;
1865     case C_DUMP_HEADER: /* --dump-header */
1866       err = getstr(&config->headerfile, nextarg, DENY_BLANK);
1867       break;
1868     case C_REFERER: { /* --referer */
1869       char *ptr = strstr(nextarg, ";auto");
1870       if(ptr) {
1871         /* Automatic referer requested, this may be combined with a
1872            set initial one */
1873         config->autoreferer = TRUE;
1874         *ptr = 0; /* null-terminate here */
1875       }
1876       else
1877         config->autoreferer = FALSE;
1878       ptr = *nextarg ? nextarg : NULL;
1879       err = getstr(&config->referer, ptr, ALLOW_BLANK);
1880     }
1881       break;
1882     case C_CERT: /* --cert */
1883       cleanarg(clearthis);
1884       GetFileAndPassword(nextarg, &config->cert, &config->key_passwd);
1885       break;
1886     case C_CACERT: /* --cacert */
1887       err = getstr(&config->cacert, nextarg, DENY_BLANK);
1888       break;
1889     case C_CA_NATIVE: /* --ca-native */
1890       config->native_ca_store = toggle;
1891       break;
1892     case C_PROXY_CA_NATIVE: /* --proxy-ca-native */
1893       config->proxy_native_ca_store = toggle;
1894       break;
1895     case C_CERT_TYPE: /* --cert-type */
1896       err = getstr(&config->cert_type, nextarg, DENY_BLANK);
1897       break;
1898     case C_KEY: /* --key */
1899       err = getstr(&config->key, nextarg, DENY_BLANK);
1900       break;
1901     case C_KEY_TYPE: /* --key-type */
1902       err = getstr(&config->key_type, nextarg, DENY_BLANK);
1903       break;
1904     case C_PASS: /* --pass */
1905       err = getstr(&config->key_passwd, nextarg, DENY_BLANK);
1906       cleanarg(clearthis);
1907       break;
1908     case C_ENGINE: /* --engine */
1909       err = getstr(&config->engine, nextarg, DENY_BLANK);
1910       if(!err &&
1911          config->engine && !strcmp(config->engine, "list")) {
1912         err = PARAM_ENGINES_REQUESTED;
1913       }
1914       break;
1915 #ifndef USE_ECH
1916     case C_ECH: /* --ech, not implemented by default */
1917       err = PARAM_LIBCURL_DOESNT_SUPPORT;
1918       break;
1919 #else
1920     case C_ECH: /* --ech */
1921       if(strlen(nextarg) > 4 && strncasecompare("pn:", nextarg, 3)) {
1922         /* a public_name */
1923         err = getstr(&config->ech_public, nextarg, DENY_BLANK);
1924       }
1925       else if(strlen(nextarg) > 5 && strncasecompare("ecl:", nextarg, 4)) {
1926         /* an ECHConfigList */
1927         if('@' != *(nextarg + 4)) {
1928           err = getstr(&config->ech_config, nextarg, DENY_BLANK);
1929         }
1930         else {
1931           /* Indirect case: @filename or @- for stdin */
1932           char *tmpcfg = NULL;
1933           FILE *file;
1934 
1935           nextarg++;        /* skip over '@' */
1936           if(!strcmp("-", nextarg)) {
1937             file = stdin;
1938           }
1939           else {
1940             file = fopen(nextarg, FOPEN_READTEXT);
1941           }
1942           if(!file) {
1943             warnf(global,
1944                   "Couldn't read file \"%s\" "
1945                   "specified for \"--ech ecl:\" option",
1946                   nextarg);
1947             return PARAM_BAD_USE; /*  */
1948           }
1949           err = file2string(&tmpcfg, file);
1950           if(file != stdin)
1951             fclose(file);
1952           if(err)
1953             return err;
1954           config->ech_config = aprintf("ecl:%s",tmpcfg);
1955           if(!config->ech_config)
1956             return PARAM_NO_MEM;
1957           free(tmpcfg);
1958       } /* file done */
1959     }
1960     else {
1961       /* Simple case: just a string, with a keyword */
1962       err = getstr(&config->ech, nextarg, DENY_BLANK);
1963     }
1964     break;
1965 #endif
1966     case C_CAPATH: /* --capath */
1967       err = getstr(&config->capath, nextarg, DENY_BLANK);
1968       break;
1969     case C_PUBKEY: /* --pubkey */
1970       err = getstr(&config->pubkey, nextarg, DENY_BLANK);
1971       break;
1972     case C_HOSTPUBMD5: /* --hostpubmd5 */
1973       err = getstr(&config->hostpubmd5, nextarg, DENY_BLANK);
1974       if(!err) {
1975         if(!config->hostpubmd5 || strlen(config->hostpubmd5) != 32)
1976           err = PARAM_BAD_USE;
1977       }
1978       break;
1979     case C_HOSTPUBSHA256: /* --hostpubsha256 */
1980       err = getstr(&config->hostpubsha256, nextarg, DENY_BLANK);
1981       break;
1982     case C_CRLFILE: /* --crlfile */
1983       err = getstr(&config->crlfile, nextarg, DENY_BLANK);
1984       break;
1985     case C_TLSUSER: /* --tlsuser */
1986       if(!feature_tls_srp)
1987         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1988       else
1989         err = getstr(&config->tls_username, nextarg, DENY_BLANK);
1990       cleanarg(clearthis);
1991       break;
1992     case C_TLSPASSWORD: /* --tlspassword */
1993       if(!feature_tls_srp)
1994         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1995       else
1996         err = getstr(&config->tls_password, nextarg, ALLOW_BLANK);
1997       cleanarg(clearthis);
1998       break;
1999     case C_TLSAUTHTYPE: /* --tlsauthtype */
2000       if(!feature_tls_srp)
2001         err = PARAM_LIBCURL_DOESNT_SUPPORT;
2002       else {
2003         err = getstr(&config->tls_authtype, nextarg, DENY_BLANK);
2004         if(!err && strcmp(config->tls_authtype, "SRP"))
2005           err = PARAM_LIBCURL_DOESNT_SUPPORT; /* only support TLS-SRP */
2006       }
2007       break;
2008     case C_SSL_ALLOW_BEAST: /* --ssl-allow-beast */
2009       if(feature_ssl)
2010         config->ssl_allow_beast = toggle;
2011       break;
2012     case C_SSL_AUTO_CLIENT_CERT: /* --ssl-auto-client-cert */
2013       if(feature_ssl)
2014         config->ssl_auto_client_cert = toggle;
2015       break;
2016     case C_PROXY_SSL_AUTO_CLIENT_CERT: /* --proxy-ssl-auto-client-cert */
2017       if(feature_ssl)
2018         config->proxy_ssl_auto_client_cert = toggle;
2019       break;
2020     case C_PINNEDPUBKEY: /* --pinnedpubkey */
2021       err = getstr(&config->pinnedpubkey, nextarg, DENY_BLANK);
2022       break;
2023     case C_PROXY_PINNEDPUBKEY: /* --proxy-pinnedpubkey */
2024       err = getstr(&config->proxy_pinnedpubkey, nextarg, DENY_BLANK);
2025       break;
2026     case C_CERT_STATUS: /* --cert-status */
2027       config->verifystatus = TRUE;
2028       break;
2029     case C_DOH_CERT_STATUS: /* --doh-cert-status */
2030       config->doh_verifystatus = TRUE;
2031       break;
2032     case C_FALSE_START: /* --false-start */
2033       config->falsestart = TRUE;
2034       break;
2035     case C_SSL_NO_REVOKE: /* --ssl-no-revoke */
2036       if(feature_ssl)
2037         config->ssl_no_revoke = TRUE;
2038       break;
2039     case C_SSL_REVOKE_BEST_EFFORT: /* --ssl-revoke-best-effort */
2040       if(feature_ssl)
2041         config->ssl_revoke_best_effort = TRUE;
2042       break;
2043     case C_TCP_FASTOPEN: /* --tcp-fastopen */
2044       config->tcp_fastopen = TRUE;
2045       break;
2046     case C_PROXY_TLSUSER: /* --proxy-tlsuser */
2047       cleanarg(clearthis);
2048       if(!feature_tls_srp)
2049         err = PARAM_LIBCURL_DOESNT_SUPPORT;
2050       else
2051         err = getstr(&config->proxy_tls_username, nextarg, ALLOW_BLANK);
2052       break;
2053     case C_PROXY_TLSPASSWORD: /* --proxy-tlspassword */
2054       cleanarg(clearthis);
2055       if(!feature_tls_srp)
2056         err = PARAM_LIBCURL_DOESNT_SUPPORT;
2057       else
2058         err = getstr(&config->proxy_tls_password, nextarg, DENY_BLANK);
2059       break;
2060     case C_PROXY_TLSAUTHTYPE: /* --proxy-tlsauthtype */
2061       if(!feature_tls_srp)
2062         err = PARAM_LIBCURL_DOESNT_SUPPORT;
2063       else {
2064         err = getstr(&config->proxy_tls_authtype, nextarg, DENY_BLANK);
2065         if(!err && strcmp(config->proxy_tls_authtype, "SRP"))
2066           err = PARAM_LIBCURL_DOESNT_SUPPORT; /* only support TLS-SRP */
2067       }
2068       break;
2069     case C_PROXY_CERT: /* --proxy-cert */
2070       cleanarg(clearthis);
2071       GetFileAndPassword(nextarg, &config->proxy_cert,
2072                          &config->proxy_key_passwd);
2073       break;
2074     case C_PROXY_CERT_TYPE: /* --proxy-cert-type */
2075       err = getstr(&config->proxy_cert_type, nextarg, DENY_BLANK);
2076       break;
2077     case C_PROXY_KEY: /* --proxy-key */
2078       err = getstr(&config->proxy_key, nextarg, ALLOW_BLANK);
2079       break;
2080     case C_PROXY_KEY_TYPE: /* --proxy-key-type */
2081       err = getstr(&config->proxy_key_type, nextarg, DENY_BLANK);
2082       break;
2083     case C_PROXY_PASS: /* --proxy-pass */
2084       err = getstr(&config->proxy_key_passwd, nextarg, ALLOW_BLANK);
2085       cleanarg(clearthis);
2086       break;
2087     case C_PROXY_CIPHERS: /* --proxy-ciphers */
2088       err = getstr(&config->proxy_cipher_list, nextarg, DENY_BLANK);
2089       break;
2090     case C_PROXY_CRLFILE: /* --proxy-crlfile */
2091       err = getstr(&config->proxy_crlfile, nextarg, DENY_BLANK);
2092       break;
2093     case C_PROXY_SSL_ALLOW_BEAST: /* --proxy-ssl-allow-beast */
2094       if(feature_ssl)
2095         config->proxy_ssl_allow_beast = toggle;
2096       break;
2097     case C_LOGIN_OPTIONS: /* --login-options */
2098       err = getstr(&config->login_options, nextarg, ALLOW_BLANK);
2099       break;
2100     case C_PROXY_CACERT: /* --proxy-cacert */
2101       err = getstr(&config->proxy_cacert, nextarg, DENY_BLANK);
2102       break;
2103     case C_PROXY_CAPATH: /* --proxy-capath */
2104       err = getstr(&config->proxy_capath, nextarg, DENY_BLANK);
2105       break;
2106     case C_PROXY_INSECURE: /* --proxy-insecure */
2107       config->proxy_insecure_ok = toggle;
2108       break;
2109     case C_PROXY_TLSV1: /* --proxy-tlsv1 */
2110       /* TLS version 1 for proxy */
2111       config->proxy_ssl_version = CURL_SSLVERSION_TLSv1;
2112       break;
2113     case C_SOCKS5_BASIC: /* --socks5-basic */
2114       if(toggle)
2115         config->socks5_auth |= CURLAUTH_BASIC;
2116       else
2117         config->socks5_auth &= ~CURLAUTH_BASIC;
2118       break;
2119     case C_SOCKS5_GSSAPI: /* --socks5-gssapi */
2120       if(toggle)
2121         config->socks5_auth |= CURLAUTH_GSSAPI;
2122       else
2123         config->socks5_auth &= ~CURLAUTH_GSSAPI;
2124       break;
2125     case C_ETAG_SAVE: /* --etag-save */
2126       err = getstr(&config->etag_save_file, nextarg, DENY_BLANK);
2127       break;
2128     case C_ETAG_COMPARE: /* --etag-compare */
2129       err = getstr(&config->etag_compare_file, nextarg, DENY_BLANK);
2130       break;
2131     case C_CURVES: /* --curves */
2132       err = getstr(&config->ssl_ec_curves, nextarg, DENY_BLANK);
2133       break;
2134     case C_FAIL_EARLY: /* --fail-early */
2135       global->fail_early = toggle;
2136       break;
2137     case C_STYLED_OUTPUT: /* --styled-output */
2138       global->styled_output = toggle;
2139       break;
2140     case C_MAIL_RCPT_ALLOWFAILS: /* --mail-rcpt-allowfails */
2141       config->mail_rcpt_allowfails = toggle;
2142       break;
2143     case C_FAIL_WITH_BODY: /* --fail-with-body */
2144       config->failwithbody = toggle;
2145       if(config->failonerror && config->failwithbody) {
2146         errorf(config->global, "You must select either --fail or "
2147                "--fail-with-body, not both.");
2148         err = PARAM_BAD_USE;
2149       }
2150       break;
2151     case C_REMOVE_ON_ERROR: /* --remove-on-error */
2152       config->rm_partial = toggle;
2153       break;
2154     case C_FAIL: /* --fail */
2155       config->failonerror = toggle;
2156       if(config->failonerror && config->failwithbody) {
2157         errorf(config->global, "You must select either --fail or "
2158                "--fail-with-body, not both.");
2159         err = PARAM_BAD_USE;
2160       }
2161       break;
2162     case C_FORM: /* --form */
2163     case C_FORM_STRING: /* --form-string */
2164       /* "form data" simulation, this is a little advanced so lets do our best
2165          to sort this out slowly and carefully */
2166       if(formparse(config,
2167                    nextarg,
2168                    &config->mimeroot,
2169                    &config->mimecurrent,
2170                    (cmd == C_FORM_STRING) ? TRUE : FALSE)) /* literal string */
2171         err = PARAM_BAD_USE;
2172       else if(SetHTTPrequest(config, TOOL_HTTPREQ_MIMEPOST, &config->httpreq))
2173         err = PARAM_BAD_USE;
2174       break;
2175     case C_GLOBOFF: /* --globoff */
2176       config->globoff = toggle;
2177       break;
2178     case C_GET: /* --get */
2179       config->use_httpget = toggle;
2180       break;
2181     case C_REQUEST_TARGET: /* --request-target */
2182       err = getstr(&config->request_target, nextarg, DENY_BLANK);
2183       break;
2184     case C_HELP: /* --help */
2185       if(toggle) {
2186         if(*nextarg) {
2187           global->help_category = strdup(nextarg);
2188           if(!global->help_category) {
2189             err = PARAM_NO_MEM;
2190             break;
2191           }
2192         }
2193         err = PARAM_HELP_REQUESTED;
2194       }
2195       /* we now actually support --no-help too! */
2196       break;
2197     case C_HEADER: /* --header */
2198     case C_PROXY_HEADER: /* --proxy-header */
2199       /* A custom header to append to a list */
2200       if(nextarg[0] == '@') {
2201         /* read many headers from a file or stdin */
2202         char *string;
2203         size_t len;
2204         bool use_stdin = !strcmp(&nextarg[1], "-");
2205         FILE *file = use_stdin ? stdin : fopen(&nextarg[1], FOPEN_READTEXT);
2206         if(!file) {
2207           errorf(global, "Failed to open %s", &nextarg[1]);
2208           err = PARAM_READ_ERROR;
2209         }
2210         else {
2211           err = file2memory(&string, &len, file);
2212           if(!err && string) {
2213             /* Allow strtok() here since this is not used threaded */
2214             /* !checksrc! disable BANNEDFUNC 2 */
2215             char *h = strtok(string, "\r\n");
2216             while(h) {
2217               if(cmd == C_PROXY_HEADER) /* --proxy-header */
2218                 err = add2list(&config->proxyheaders, h);
2219               else
2220                 err = add2list(&config->headers, h);
2221               if(err)
2222                 break;
2223               h = strtok(NULL, "\r\n");
2224             }
2225             free(string);
2226           }
2227           if(!use_stdin)
2228             fclose(file);
2229         }
2230       }
2231       else {
2232         if(cmd == C_PROXY_HEADER) /* --proxy-header */
2233           err = add2list(&config->proxyheaders, nextarg);
2234         else
2235           err = add2list(&config->headers, nextarg);
2236       }
2237       break;
2238     case C_INCLUDE: /* --include */
2239     case C_SHOW_HEADERS: /* --show-headers */
2240       config->show_headers = toggle; /* show the headers as well in the
2241                                         general output stream */
2242       break;
2243     case C_JUNK_SESSION_COOKIES: /* --junk-session-cookies */
2244       config->cookiesession = toggle;
2245       break;
2246     case C_HEAD: /* --head */
2247       config->no_body = toggle;
2248       config->show_headers = toggle;
2249       if(SetHTTPrequest(config, (config->no_body) ? TOOL_HTTPREQ_HEAD :
2250                         TOOL_HTTPREQ_GET, &config->httpreq))
2251         err = PARAM_BAD_USE;
2252       break;
2253     case C_REMOTE_HEADER_NAME: /* --remote-header-name */
2254       config->content_disposition = toggle;
2255       break;
2256     case C_INSECURE: /* --insecure */
2257       config->insecure_ok = toggle;
2258       break;
2259     case C_DOH_INSECURE: /* --doh-insecure */
2260       config->doh_insecure_ok = toggle;
2261       break;
2262     case C_CONFIG: /* --config */
2263       if(parseconfig(nextarg, global)) {
2264         errorf(global, "cannot read config from '%s'", nextarg);
2265         err = PARAM_READ_ERROR;
2266       }
2267       break;
2268     case C_LIST_ONLY: /* --list-only */
2269       config->dirlistonly = toggle; /* only list the names of the FTP dir */
2270       break;
2271     case C_LOCATION_TRUSTED: /* --location-trusted */
2272       /* Continue to send authentication (user+password) when following
2273        * locations, even when hostname changed */
2274       config->unrestricted_auth = toggle;
2275       FALLTHROUGH();
2276     case C_LOCATION: /* --location */
2277       config->followlocation = toggle; /* Follow Location: HTTP headers */
2278       break;
2279     case C_MAX_TIME: /* --max-time */
2280       /* specified max time */
2281       err = secs2ms(&config->timeout_ms, nextarg);
2282       break;
2283     case C_MANUAL: /* --manual */
2284       if(toggle) { /* --no-manual shows no manual... */
2285 #ifndef USE_MANUAL
2286         warnf(global,
2287               "built-in manual was disabled at build-time");
2288 #endif
2289         err = PARAM_MANUAL_REQUESTED;
2290       }
2291       break;
2292     case C_NETRC_OPTIONAL: /* --netrc-optional */
2293       config->netrc_opt = toggle;
2294       break;
2295     case C_NETRC_FILE: /* --netrc-file */
2296       err = getstr(&config->netrc_file, nextarg, DENY_BLANK);
2297       break;
2298     case C_NETRC: /* --netrc */
2299       /* pick info from .netrc, if this is used for http, curl will
2300          automatically enforce user+password with the request */
2301       config->netrc = toggle;
2302       break;
2303     case C_BUFFER: /* --buffer */
2304       /* disable the output I/O buffering. note that the option is called
2305          --buffer but is mostly used in the negative form: --no-buffer */
2306       config->nobuffer = longopt ? !toggle : TRUE;
2307       break;
2308     case C_REMOTE_NAME_ALL: /* --remote-name-all */
2309       config->default_node_flags = toggle ? GETOUT_USEREMOTE : 0;
2310       break;
2311     case C_OUTPUT_DIR: /* --output-dir */
2312       err = getstr(&config->output_dir, nextarg, DENY_BLANK);
2313       break;
2314     case C_CLOBBER: /* --clobber */
2315       config->file_clobber_mode = toggle ? CLOBBER_ALWAYS : CLOBBER_NEVER;
2316       break;
2317     case C_OUTPUT: /* --output */
2318     case C_REMOTE_NAME: /* --remote-name */
2319       /* output file */
2320       if(!config->url_out)
2321         config->url_out = config->url_list;
2322       if(config->url_out) {
2323         /* there is a node here, if it already is filled-in continue to find
2324            an "empty" node */
2325         while(config->url_out && (config->url_out->flags & GETOUT_OUTFILE))
2326           config->url_out = config->url_out->next;
2327       }
2328 
2329       /* now there might or might not be an available node to fill in! */
2330 
2331       if(config->url_out)
2332         /* existing node */
2333         url = config->url_out;
2334       else {
2335         if(!toggle && !config->default_node_flags)
2336           break;
2337         /* there was no free node, create one! */
2338         config->url_out = url = new_getout(config);
2339       }
2340 
2341       if(!url) {
2342         err = PARAM_NO_MEM;
2343         break;
2344       }
2345 
2346       /* fill in the outfile */
2347       if('o' == letter) {
2348         err = getstr(&url->outfile, nextarg, DENY_BLANK);
2349         url->flags &= ~GETOUT_USEREMOTE; /* switch off */
2350       }
2351       else {
2352         url->outfile = NULL; /* leave it */
2353         if(toggle)
2354           url->flags |= GETOUT_USEREMOTE;  /* switch on */
2355         else
2356           url->flags &= ~GETOUT_USEREMOTE; /* switch off */
2357       }
2358       url->flags |= GETOUT_OUTFILE;
2359       break;
2360     case C_FTP_PORT: /* --ftp-port */
2361       /* This makes the FTP sessions use PORT instead of PASV */
2362       /* use <eth0> or <192.168.10.10> style addresses. Anything except
2363          this will make us try to get the "default" address.
2364          NOTE: this is a changed behavior since the released 4.1!
2365       */
2366       err = getstr(&config->ftpport, nextarg, DENY_BLANK);
2367       break;
2368     case C_PROXYTUNNEL: /* --proxytunnel */
2369       /* proxy tunnel for non-http protocols */
2370       config->proxytunnel = toggle;
2371       break;
2372 
2373     case C_DISABLE: /* --disable */
2374       /* if used first, already taken care of, we do it like this so we do not
2375          cause an error! */
2376       break;
2377     case C_QUOTE: /* --quote */
2378       /* QUOTE command to send to FTP server */
2379       switch(nextarg[0]) {
2380       case '-':
2381         /* prefixed with a dash makes it a POST TRANSFER one */
2382         nextarg++;
2383         err = add2list(&config->postquote, nextarg);
2384         break;
2385       case '+':
2386         /* prefixed with a plus makes it a just-before-transfer one */
2387         nextarg++;
2388         err = add2list(&config->prequote, nextarg);
2389         break;
2390       default:
2391         err = add2list(&config->quote, nextarg);
2392         break;
2393       }
2394       break;
2395     case C_RANGE: /* --range */
2396       /* Specifying a range WITHOUT A DASH will create an illegal HTTP range
2397          (and will not actually be range by definition). The manpage
2398          previously claimed that to be a good way, why this code is added to
2399          work-around it. */
2400       if(ISDIGIT(*nextarg) && !strchr(nextarg, '-')) {
2401         char buffer[32];
2402         if(curlx_strtoofft(nextarg, NULL, 10, &value)) {
2403           warnf(global, "unsupported range point");
2404           err = PARAM_BAD_USE;
2405         }
2406         else {
2407           warnf(global,
2408                 "A specified range MUST include at least one dash (-). "
2409                 "Appending one for you");
2410           msnprintf(buffer, sizeof(buffer), "%" CURL_FORMAT_CURL_OFF_T "-",
2411                     value);
2412           Curl_safefree(config->range);
2413           config->range = strdup(buffer);
2414           if(!config->range)
2415             err = PARAM_NO_MEM;
2416         }
2417       }
2418       else {
2419         /* byte range requested */
2420         const char *tmp_range = nextarg;
2421         while(*tmp_range) {
2422           if(!ISDIGIT(*tmp_range) && *tmp_range != '-' && *tmp_range != ',') {
2423             warnf(global, "Invalid character is found in given range. "
2424                   "A specified range MUST have only digits in "
2425                   "\'start\'-\'stop\'. The server's response to this "
2426                   "request is uncertain.");
2427             break;
2428           }
2429           tmp_range++;
2430         }
2431         err = getstr(&config->range, nextarg, DENY_BLANK);
2432       }
2433       break;
2434     case C_REMOTE_TIME: /* --remote-time */
2435       /* use remote file's time */
2436       config->remote_time = toggle;
2437       break;
2438     case C_SILENT: /* --silent */
2439       global->silent = toggle;
2440       break;
2441     case C_SKIP_EXISTING: /* --skip-existing */
2442       config->skip_existing = toggle;
2443       break;
2444     case C_SHOW_ERROR: /* --show-error */
2445       global->showerror = toggle;
2446       break;
2447     case C_TELNET_OPTION: /* --telnet-option */
2448       /* Telnet options */
2449       err = add2list(&config->telnet_options, nextarg);
2450       break;
2451     case C_UPLOAD_FILE: /* --upload-file */
2452       /* we are uploading */
2453       if(!config->url_ul)
2454         config->url_ul = config->url_list;
2455       if(config->url_ul) {
2456         /* there is a node here, if it already is filled-in continue to find
2457            an "empty" node */
2458         while(config->url_ul && (config->url_ul->flags & GETOUT_UPLOAD))
2459           config->url_ul = config->url_ul->next;
2460       }
2461 
2462       /* now there might or might not be an available node to fill in! */
2463 
2464       if(config->url_ul)
2465         /* existing node */
2466         url = config->url_ul;
2467       else
2468         /* there was no free node, create one! */
2469         config->url_ul = url = new_getout(config);
2470 
2471       if(!url) {
2472         err = PARAM_NO_MEM;
2473         break;
2474       }
2475 
2476       url->flags |= GETOUT_UPLOAD; /* mark -T used */
2477       if(!*nextarg)
2478         url->flags |= GETOUT_NOUPLOAD;
2479       else {
2480         /* "-" equals stdin, but keep the string around for now */
2481         err = getstr(&url->infile, nextarg, DENY_BLANK);
2482       }
2483       break;
2484     case C_USER: /* --user */
2485       /* user:password  */
2486       err = getstr(&config->userpwd, nextarg, ALLOW_BLANK);
2487       cleanarg(clearthis);
2488       break;
2489     case C_PROXY_USER: /* --proxy-user */
2490       /* Proxy user:password  */
2491       err = getstr(&config->proxyuserpwd, nextarg, ALLOW_BLANK);
2492       cleanarg(clearthis);
2493       break;
2494     case C_VERBOSE: /* --verbose */
2495       /* This option is a super-boolean with side effect when applied
2496        * more than once in the same argument flag, like `-vvv`. */
2497       if(!toggle) {
2498         global->verbosity = 0;
2499         if(set_trace_config(global, "-all"))
2500           err = PARAM_NO_MEM;
2501         global->tracetype = TRACE_NONE;
2502         break;
2503       }
2504       else if(!nopts) {
2505         /* fist `-v` in an argument resets to base verbosity */
2506         global->verbosity = 0;
2507         if(set_trace_config(global, "-all")) {
2508           err = PARAM_NO_MEM;
2509           break;
2510         }
2511       }
2512       /* the '%' thing here will cause the trace get sent to stderr */
2513       switch(global->verbosity) {
2514       case 0:
2515         global->verbosity = 1;
2516         Curl_safefree(global->trace_dump);
2517         global->trace_dump = strdup("%");
2518         if(!global->trace_dump)
2519           err = PARAM_NO_MEM;
2520         else {
2521           if(global->tracetype && (global->tracetype != TRACE_PLAIN))
2522             warnf(global,
2523                   "-v, --verbose overrides an earlier trace option");
2524           global->tracetype = TRACE_PLAIN;
2525         }
2526         break;
2527       case 1:
2528         global->verbosity = 2;
2529         if(set_trace_config(global, "ids,time,protocol"))
2530           err = PARAM_NO_MEM;
2531         break;
2532       case 2:
2533         global->verbosity = 3;
2534         global->tracetype = TRACE_ASCII;
2535         if(set_trace_config(global, "ssl,read,write"))
2536           err = PARAM_NO_MEM;
2537         break;
2538       case 3:
2539         global->verbosity = 4;
2540         if(set_trace_config(global, "network"))
2541           err = PARAM_NO_MEM;
2542         break;
2543       default:
2544         /* no effect for now */
2545         break;
2546       }
2547       break;
2548     case C_VERSION: /* --version */
2549       if(toggle)    /* --no-version yields no output! */
2550         err = PARAM_VERSION_INFO_REQUESTED;
2551       break;
2552     case C_WRITE_OUT: /* --write-out */
2553       /* get the output string */
2554       if('@' == *nextarg) {
2555         /* the data begins with a '@' letter, it means that a filename
2556            or - (stdin) follows */
2557         FILE *file;
2558         const char *fname;
2559         nextarg++; /* pass the @ */
2560         if(!strcmp("-", nextarg)) {
2561           fname = "<stdin>";
2562           file = stdin;
2563         }
2564         else {
2565           fname = nextarg;
2566           file = fopen(fname, FOPEN_READTEXT);
2567           if(!file) {
2568             errorf(global, "Failed to open %s", fname);
2569             err = PARAM_READ_ERROR;
2570             break;
2571           }
2572         }
2573         Curl_safefree(config->writeout);
2574         err = file2string(&config->writeout, file);
2575         if(file && (file != stdin))
2576           fclose(file);
2577         if(err)
2578           break;
2579         if(!config->writeout)
2580           warnf(global, "Failed to read %s", fname);
2581       }
2582       else
2583         err = getstr(&config->writeout, nextarg, ALLOW_BLANK);
2584       break;
2585     case C_PREPROXY: /* --preproxy */
2586       err = getstr(&config->preproxy, nextarg, DENY_BLANK);
2587       break;
2588     case C_PROXY: /* --proxy */
2589       /* --proxy */
2590       err = getstr(&config->proxy, nextarg, ALLOW_BLANK);
2591       if(config->proxyver != CURLPROXY_HTTPS2)
2592         config->proxyver = CURLPROXY_HTTP;
2593       break;
2594     case C_REQUEST: /* --request */
2595       /* set custom request */
2596       err = getstr(&config->customrequest, nextarg, DENY_BLANK);
2597       break;
2598     case C_SPEED_TIME: /* --speed-time */
2599       /* low speed time */
2600       err = str2unum(&config->low_speed_time, nextarg);
2601       if(!err && !config->low_speed_limit)
2602         config->low_speed_limit = 1;
2603       break;
2604     case C_SPEED_LIMIT: /* --speed-limit */
2605       /* low speed limit */
2606       err = str2unum(&config->low_speed_limit, nextarg);
2607       if(!err && !config->low_speed_time)
2608         config->low_speed_time = 30;
2609       break;
2610     case C_PARALLEL: /* --parallel */
2611       global->parallel = toggle;
2612       break;
2613     case C_PARALLEL_MAX: {  /* --parallel-max */
2614       long val;
2615       err = str2unum(&val, nextarg);
2616       if(err)
2617         break;
2618       if(val > MAX_PARALLEL)
2619         global->parallel_max = MAX_PARALLEL;
2620       else if(val < 1)
2621         global->parallel_max = PARALLEL_DEFAULT;
2622       else
2623         global->parallel_max = (unsigned short)val;
2624       break;
2625     }
2626     case C_PARALLEL_IMMEDIATE:   /* --parallel-immediate */
2627       global->parallel_connect = toggle;
2628       break;
2629     case C_TIME_COND: /* --time-cond */
2630       switch(*nextarg) {
2631       case '+':
2632         nextarg++;
2633         FALLTHROUGH();
2634       default:
2635         /* If-Modified-Since: (section 14.28 in RFC2068) */
2636         config->timecond = CURL_TIMECOND_IFMODSINCE;
2637         break;
2638       case '-':
2639         /* If-Unmodified-Since:  (section 14.24 in RFC2068) */
2640         config->timecond = CURL_TIMECOND_IFUNMODSINCE;
2641         nextarg++;
2642         break;
2643       case '=':
2644         /* Last-Modified:  (section 14.29 in RFC2068) */
2645         config->timecond = CURL_TIMECOND_LASTMOD;
2646         nextarg++;
2647         break;
2648       }
2649       now = time(NULL);
2650       config->condtime = (curl_off_t)curl_getdate(nextarg, &now);
2651       if(-1 == config->condtime) {
2652         /* now let's see if it is a filename to get the time from instead! */
2653         rc = getfiletime(nextarg, global, &value);
2654         if(!rc)
2655           /* pull the time out from the file */
2656           config->condtime = value;
2657         else {
2658           /* failed, remove time condition */
2659           config->timecond = CURL_TIMECOND_NONE;
2660           warnf(global,
2661                 "Illegal date format for -z, --time-cond (and not "
2662                 "a filename). Disabling time condition. "
2663                 "See curl_getdate(3) for valid date syntax.");
2664         }
2665       }
2666       break;
2667     case C_MPTCP: /* --mptcp */
2668       config->mptcp = TRUE;
2669       break;
2670     default: /* unknown flag */
2671       err = PARAM_OPTION_UNKNOWN;
2672       break;
2673     }
2674     a = NULL;
2675     ++nopts; /* processed one option from `flag` input, loop for more */
2676   } while(!longopt && !singleopt && *++parse && !*usedarg && !err);
2677 
2678 error:
2679   if(nextalloc)
2680     free(nextarg);
2681   return err;
2682 }
2683 
parse_args(struct GlobalConfig * global,int argc,argv_item_t argv[])2684 ParameterError parse_args(struct GlobalConfig *global, int argc,
2685                           argv_item_t argv[])
2686 {
2687   int i;
2688   bool stillflags;
2689   char *orig_opt = NULL;
2690   ParameterError result = PARAM_OK;
2691   struct OperationConfig *config = global->first;
2692 
2693   for(i = 1, stillflags = TRUE; i < argc && !result; i++) {
2694     orig_opt = curlx_convert_tchar_to_UTF8(argv[i]);
2695     if(!orig_opt)
2696       return PARAM_NO_MEM;
2697 
2698     if(stillflags && ('-' == orig_opt[0])) {
2699       bool passarg;
2700 
2701       if(!strcmp("--", orig_opt))
2702         /* This indicates the end of the flags and thus enables the
2703            following (URL) argument to start with -. */
2704         stillflags = FALSE;
2705       else {
2706         char *nextarg = NULL;
2707         if(i < (argc - 1)) {
2708           nextarg = curlx_convert_tchar_to_UTF8(argv[i + 1]);
2709           if(!nextarg) {
2710             curlx_unicodefree(orig_opt);
2711             return PARAM_NO_MEM;
2712           }
2713         }
2714 
2715         result = getparameter(orig_opt, nextarg, argv[i + 1], &passarg,
2716                               global, config);
2717 
2718         curlx_unicodefree(nextarg);
2719         config = global->last;
2720         if(result == PARAM_NEXT_OPERATION) {
2721           /* Reset result as PARAM_NEXT_OPERATION is only used here and not
2722              returned from this function */
2723           result = PARAM_OK;
2724 
2725           if(config->url_list && config->url_list->url) {
2726             /* Allocate the next config */
2727             config->next = malloc(sizeof(struct OperationConfig));
2728             if(config->next) {
2729               /* Initialise the newly created config */
2730               config_init(config->next);
2731 
2732               /* Set the global config pointer */
2733               config->next->global = global;
2734 
2735               /* Update the last config pointer */
2736               global->last = config->next;
2737 
2738               /* Move onto the new config */
2739               config->next->prev = config;
2740               config = config->next;
2741             }
2742             else
2743               result = PARAM_NO_MEM;
2744           }
2745           else {
2746             errorf(global, "missing URL before --next");
2747             result = PARAM_BAD_USE;
2748           }
2749         }
2750         else if(!result && passarg)
2751           i++; /* we are supposed to skip this */
2752       }
2753     }
2754     else {
2755       bool used;
2756 
2757       /* Just add the URL please */
2758       result = getparameter("--url", orig_opt, argv[i], &used, global, config);
2759     }
2760 
2761     if(!result)
2762       curlx_unicodefree(orig_opt);
2763   }
2764 
2765   if(!result && config->content_disposition) {
2766     if(config->show_headers)
2767       result = PARAM_CONTDISP_SHOW_HEADER;
2768     else if(config->resume_from_current)
2769       result = PARAM_CONTDISP_RESUME_FROM;
2770   }
2771 
2772   if(result && result != PARAM_HELP_REQUESTED &&
2773      result != PARAM_MANUAL_REQUESTED &&
2774      result != PARAM_VERSION_INFO_REQUESTED &&
2775      result != PARAM_ENGINES_REQUESTED &&
2776      result != PARAM_CA_EMBED_REQUESTED) {
2777     const char *reason = param2text(result);
2778 
2779     if(orig_opt && strcmp(":", orig_opt))
2780       helpf(tool_stderr, "option %s: %s", orig_opt, reason);
2781     else
2782       helpf(tool_stderr, "%s", reason);
2783   }
2784 
2785   curlx_unicodefree(orig_opt);
2786   return result;
2787 }
2788