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