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