• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #define ALLOW_BLANK TRUE
49 #define DENY_BLANK FALSE
50 
getstr(char ** str,const char * val,bool allowblank)51 static ParameterError getstr(char **str, const char *val, bool allowblank)
52 {
53   if(*str) {
54     free(*str);
55     *str = NULL;
56   }
57   if(val) {
58     if(!allowblank && !val[0])
59       return PARAM_BLANK_STRING;
60 
61     *str = strdup(val);
62     if(!*str)
63       return PARAM_NO_MEM;
64   }
65   return PARAM_OK;
66 }
67 
68 /* this array MUST be alphasorted based on the 'lname' */
69 static const struct LongShort aliases[]= {
70   {"abstract-unix-socket",       ARG_FILE, ' ', C_ABSTRACT_UNIX_SOCKET},
71   {"alpn",                       ARG_BOOL|ARG_NO, ' ', C_ALPN},
72   {"alt-svc",                    ARG_STRG, ' ', C_ALT_SVC},
73   {"anyauth",                    ARG_BOOL, ' ', C_ANYAUTH},
74   {"append",                     ARG_BOOL, 'a', C_APPEND},
75   {"aws-sigv4",                  ARG_STRG, ' ', C_AWS_SIGV4},
76   {"basic",                      ARG_BOOL, ' ', C_BASIC},
77   {"buffer",                     ARG_BOOL|ARG_NO, 'N', C_BUFFER},
78   {"ca-native",                  ARG_BOOL, ' ', C_CA_NATIVE},
79   {"cacert",                     ARG_FILE, ' ', C_CACERT},
80   {"capath",                     ARG_FILE, ' ', C_CAPATH},
81   {"cert",                       ARG_FILE, 'E', C_CERT},
82   {"cert-status",                ARG_BOOL, ' ', C_CERT_STATUS},
83   {"cert-type",                  ARG_STRG, ' ', C_CERT_TYPE},
84   {"ciphers",                    ARG_STRG, ' ', C_CIPHERS},
85   {"clobber",                    ARG_BOOL|ARG_NO, ' ', C_CLOBBER},
86   {"compressed",                 ARG_BOOL, ' ', C_COMPRESSED},
87   {"compressed-ssh",             ARG_BOOL, ' ', C_COMPRESSED_SSH},
88   {"config",                     ARG_FILE, 'K', C_CONFIG},
89   {"connect-timeout",            ARG_STRG, ' ', C_CONNECT_TIMEOUT},
90   {"connect-to",                 ARG_STRG, ' ', C_CONNECT_TO},
91   {"continue-at",                ARG_STRG, 'C', C_CONTINUE_AT},
92   {"cookie",                     ARG_STRG, 'b', C_COOKIE},
93   {"cookie-jar",                 ARG_STRG, 'c', C_COOKIE_JAR},
94   {"create-dirs",                ARG_BOOL, ' ', C_CREATE_DIRS},
95   {"create-file-mode",           ARG_STRG, ' ', C_CREATE_FILE_MODE},
96   {"crlf",                       ARG_BOOL, ' ', C_CRLF},
97   {"crlfile",                    ARG_FILE, ' ', C_CRLFILE},
98   {"curves",                     ARG_STRG, ' ', C_CURVES},
99   {"data",                       ARG_STRG, 'd', C_DATA},
100   {"data-ascii",                 ARG_STRG, ' ', C_DATA_ASCII},
101   {"data-binary",                ARG_STRG, ' ', C_DATA_BINARY},
102   {"data-raw",                   ARG_STRG, ' ', C_DATA_RAW},
103   {"data-urlencode",             ARG_STRG, ' ', C_DATA_URLENCODE},
104   {"delegation",                 ARG_STRG, ' ', C_DELEGATION},
105   {"digest",                     ARG_BOOL, ' ', C_DIGEST},
106   {"disable",                    ARG_BOOL, 'q', C_DISABLE},
107   {"disable-eprt",               ARG_BOOL, ' ', C_DISABLE_EPRT},
108   {"disable-epsv",               ARG_BOOL, ' ', C_DISABLE_EPSV},
109   {"disallow-username-in-url",   ARG_BOOL, ' ', C_DISALLOW_USERNAME_IN_URL},
110   {"dns-interface",              ARG_STRG, ' ', C_DNS_INTERFACE},
111   {"dns-ipv4-addr",              ARG_STRG, ' ', C_DNS_IPV4_ADDR},
112   {"dns-ipv6-addr",              ARG_STRG, ' ', C_DNS_IPV6_ADDR},
113   {"dns-servers",                ARG_STRG, ' ', C_DNS_SERVERS},
114   {"doh-cert-status",            ARG_BOOL, ' ', C_DOH_CERT_STATUS},
115   {"doh-insecure",               ARG_BOOL, ' ', C_DOH_INSECURE},
116   {"doh-url"        ,            ARG_STRG, ' ', C_DOH_URL},
117   {"dump-ca-embed",              ARG_NONE, ' ', C_DUMP_CA_EMBED},
118   {"dump-header",                ARG_FILE, 'D', C_DUMP_HEADER},
119   {"ech",                        ARG_STRG, ' ', C_ECH},
120   {"egd-file",                   ARG_STRG, ' ', C_EGD_FILE},
121   {"engine",                     ARG_STRG, ' ', C_ENGINE},
122   {"eprt",                       ARG_BOOL, ' ', C_EPRT},
123   {"epsv",                       ARG_BOOL, ' ', C_EPSV},
124   {"etag-compare",               ARG_FILE, ' ', C_ETAG_COMPARE},
125   {"etag-save",                  ARG_FILE, ' ', C_ETAG_SAVE},
126   {"expect100-timeout",          ARG_STRG, ' ', C_EXPECT100_TIMEOUT},
127   {"fail",                       ARG_BOOL, 'f', C_FAIL},
128   {"fail-early",                 ARG_BOOL, ' ', C_FAIL_EARLY},
129   {"fail-with-body",             ARG_BOOL, ' ', C_FAIL_WITH_BODY},
130   {"false-start",                ARG_BOOL, ' ', C_FALSE_START},
131   {"form",                       ARG_STRG, 'F', C_FORM},
132   {"form-escape",                ARG_BOOL, ' ', C_FORM_ESCAPE},
133   {"form-string",                ARG_STRG, ' ', C_FORM_STRING},
134   {"ftp-account",                ARG_STRG, ' ', C_FTP_ACCOUNT},
135   {"ftp-alternative-to-user",    ARG_STRG, ' ', C_FTP_ALTERNATIVE_TO_USER},
136   {"ftp-create-dirs",            ARG_BOOL, ' ', C_FTP_CREATE_DIRS},
137   {"ftp-method",                 ARG_STRG, ' ', C_FTP_METHOD},
138   {"ftp-pasv",                   ARG_BOOL, ' ', C_FTP_PASV},
139   {"ftp-port",                   ARG_STRG, 'P', C_FTP_PORT},
140   {"ftp-pret",                   ARG_BOOL, ' ', C_FTP_PRET},
141   {"ftp-skip-pasv-ip",           ARG_BOOL, ' ', C_FTP_SKIP_PASV_IP},
142   {"ftp-ssl",                    ARG_BOOL, ' ', C_FTP_SSL},
143   {"ftp-ssl-ccc",                ARG_BOOL, ' ', C_FTP_SSL_CCC},
144   {"ftp-ssl-ccc-mode",           ARG_STRG, ' ', C_FTP_SSL_CCC_MODE},
145   {"ftp-ssl-control",            ARG_BOOL, ' ', C_FTP_SSL_CONTROL},
146   {"ftp-ssl-reqd",               ARG_BOOL, ' ', C_FTP_SSL_REQD},
147   {"get",                        ARG_BOOL, 'G', C_GET},
148   {"globoff",                    ARG_BOOL, 'g', C_GLOBOFF},
149   {"happy-eyeballs-timeout-ms",  ARG_STRG, ' ', C_HAPPY_EYEBALLS_TIMEOUT_MS},
150   {"haproxy-clientip",           ARG_STRG, ' ', C_HAPROXY_CLIENTIP},
151   {"haproxy-protocol",           ARG_BOOL, ' ', C_HAPROXY_PROTOCOL},
152   {"head",                       ARG_BOOL, 'I', C_HEAD},
153   {"header",                     ARG_STRG, 'H', C_HEADER},
154   {"help",                       ARG_BOOL, 'h', C_HELP},
155   {"hostpubmd5",                 ARG_STRG, ' ', C_HOSTPUBMD5},
156   {"hostpubsha256",              ARG_STRG, ' ', C_HOSTPUBSHA256},
157   {"hsts",                       ARG_STRG, ' ', C_HSTS},
158   {"http0.9",                    ARG_BOOL, ' ', C_HTTP0_9},
159   {"http1.0",                    ARG_NONE, '0', C_HTTP1_0},
160   {"http1.1",                    ARG_NONE, ' ', C_HTTP1_1},
161   {"http2",                      ARG_NONE, ' ', C_HTTP2},
162   {"http2-prior-knowledge",      ARG_NONE, ' ', C_HTTP2_PRIOR_KNOWLEDGE},
163   {"http3",                      ARG_NONE, ' ', C_HTTP3},
164   {"http3-only",                 ARG_NONE, ' ', C_HTTP3_ONLY},
165   {"ignore-content-length",      ARG_BOOL, ' ', C_IGNORE_CONTENT_LENGTH},
166   {"include",                    ARG_BOOL, ' ', C_INCLUDE},
167   {"insecure",                   ARG_BOOL, 'k', C_INSECURE},
168   {"interface",                  ARG_STRG, ' ', C_INTERFACE},
169   {"ip-tos",                     ARG_STRG, ' ', C_IP_TOS},
170 #ifndef CURL_DISABLE_IPFS
171   {"ipfs-gateway",               ARG_STRG, ' ', C_IPFS_GATEWAY},
172 #endif /* !CURL_DISABLE_IPFS */
173   {"ipv4",                       ARG_NONE, '4', C_IPV4},
174   {"ipv6",                       ARG_NONE, '6', C_IPV6},
175   {"json",                       ARG_STRG, ' ', C_JSON},
176   {"junk-session-cookies",       ARG_BOOL, 'j', C_JUNK_SESSION_COOKIES},
177   {"keepalive",                  ARG_BOOL|ARG_NO, ' ', C_KEEPALIVE},
178   {"keepalive-cnt",              ARG_STRG, ' ', C_KEEPALIVE_CNT},
179   {"keepalive-time",             ARG_STRG, ' ', C_KEEPALIVE_TIME},
180   {"key",                        ARG_FILE, ' ', C_KEY},
181   {"key-type",                   ARG_STRG, ' ', C_KEY_TYPE},
182   {"krb",                        ARG_STRG, ' ', C_KRB},
183   {"krb4",                       ARG_STRG, ' ', C_KRB4},
184   {"libcurl",                    ARG_STRG, ' ', C_LIBCURL},
185   {"limit-rate",                 ARG_STRG, ' ', C_LIMIT_RATE},
186   {"list-only",                  ARG_BOOL, 'l', C_LIST_ONLY},
187   {"local-port",                 ARG_STRG, ' ', C_LOCAL_PORT},
188   {"location",                   ARG_BOOL, 'L', C_LOCATION},
189   {"location-trusted",           ARG_BOOL, ' ', C_LOCATION_TRUSTED},
190   {"login-options",              ARG_STRG, ' ', C_LOGIN_OPTIONS},
191   {"mail-auth",                  ARG_STRG, ' ', C_MAIL_AUTH},
192   {"mail-from",                  ARG_STRG, ' ', C_MAIL_FROM},
193   {"mail-rcpt",                  ARG_STRG, ' ', C_MAIL_RCPT},
194   {"mail-rcpt-allowfails",       ARG_BOOL, ' ', C_MAIL_RCPT_ALLOWFAILS},
195   {"manual",                     ARG_BOOL, 'M', C_MANUAL},
196   {"max-filesize",               ARG_STRG, ' ', C_MAX_FILESIZE},
197   {"max-redirs",                 ARG_STRG, ' ', C_MAX_REDIRS},
198   {"max-time",                   ARG_STRG, 'm', C_MAX_TIME},
199   {"metalink",                   ARG_BOOL, ' ', C_METALINK},
200   {"mptcp",                      ARG_BOOL, ' ', C_MPTCP},
201   {"negotiate",                  ARG_BOOL, ' ', C_NEGOTIATE},
202   {"netrc",                      ARG_BOOL, 'n', C_NETRC},
203   {"netrc-file",                 ARG_FILE, ' ', C_NETRC_FILE},
204   {"netrc-optional",             ARG_BOOL, ' ', C_NETRC_OPTIONAL},
205   {"next",                       ARG_NONE, ':', C_NEXT},
206   {"noproxy",                    ARG_STRG, ' ', C_NOPROXY},
207   {"npn",                        ARG_BOOL|ARG_NO, ' ', C_NPN},
208   {"ntlm",                       ARG_BOOL, ' ', C_NTLM},
209   {"ntlm-wb",                    ARG_BOOL, ' ', C_NTLM_WB},
210   {"oauth2-bearer",              ARG_STRG, ' ', C_OAUTH2_BEARER},
211   {"output",                     ARG_FILE, 'o', C_OUTPUT},
212   {"output-dir",                 ARG_STRG, ' ', C_OUTPUT_DIR},
213   {"parallel",                   ARG_BOOL, 'Z', C_PARALLEL},
214   {"parallel-immediate",         ARG_BOOL, ' ', C_PARALLEL_IMMEDIATE},
215   {"parallel-max",               ARG_STRG, ' ', C_PARALLEL_MAX},
216   {"pass",                       ARG_STRG, ' ', C_PASS},
217   {"path-as-is",                 ARG_BOOL, ' ', C_PATH_AS_IS},
218   {"pinnedpubkey",               ARG_STRG, ' ', C_PINNEDPUBKEY},
219   {"post301",                    ARG_BOOL, ' ', C_POST301},
220   {"post302",                    ARG_BOOL, ' ', C_POST302},
221   {"post303",                    ARG_BOOL, ' ', C_POST303},
222   {"preproxy",                   ARG_STRG, ' ', C_PREPROXY},
223   {"progress-bar",               ARG_BOOL, '#', C_PROGRESS_BAR},
224   {"progress-meter",             ARG_BOOL|ARG_NO, ' ', C_PROGRESS_METER},
225   {"proto",                      ARG_STRG, ' ', C_PROTO},
226   {"proto-default",              ARG_STRG, ' ', C_PROTO_DEFAULT},
227   {"proto-redir",                ARG_STRG, ' ', C_PROTO_REDIR},
228   {"proxy",                      ARG_STRG, 'x', C_PROXY},
229   {"proxy-anyauth",              ARG_BOOL, ' ', C_PROXY_ANYAUTH},
230   {"proxy-basic",                ARG_BOOL, ' ', C_PROXY_BASIC},
231   {"proxy-ca-native",            ARG_BOOL, ' ', C_PROXY_CA_NATIVE},
232   {"proxy-cacert",               ARG_FILE, ' ', C_PROXY_CACERT},
233   {"proxy-capath",               ARG_FILE, ' ', C_PROXY_CAPATH},
234   {"proxy-cert",                 ARG_FILE, ' ', C_PROXY_CERT},
235   {"proxy-cert-type",            ARG_STRG, ' ', C_PROXY_CERT_TYPE},
236   {"proxy-ciphers",              ARG_STRG, ' ', C_PROXY_CIPHERS},
237   {"proxy-crlfile",              ARG_FILE, ' ', C_PROXY_CRLFILE},
238   {"proxy-digest",               ARG_BOOL, ' ', C_PROXY_DIGEST},
239   {"proxy-header",               ARG_STRG, ' ', C_PROXY_HEADER},
240   {"proxy-http2",                ARG_BOOL, ' ', C_PROXY_HTTP2},
241   {"proxy-insecure",             ARG_BOOL, ' ', C_PROXY_INSECURE},
242   {"proxy-key",                  ARG_FILE, ' ', C_PROXY_KEY},
243   {"proxy-key-type",             ARG_STRG, ' ', C_PROXY_KEY_TYPE},
244   {"proxy-negotiate",            ARG_BOOL, ' ', C_PROXY_NEGOTIATE},
245   {"proxy-ntlm",                 ARG_BOOL, ' ', C_PROXY_NTLM},
246   {"proxy-pass",                 ARG_STRG, ' ', C_PROXY_PASS},
247   {"proxy-pinnedpubkey",         ARG_STRG, ' ', C_PROXY_PINNEDPUBKEY},
248   {"proxy-service-name",         ARG_STRG, ' ', C_PROXY_SERVICE_NAME},
249   {"proxy-ssl-allow-beast",      ARG_BOOL, ' ', C_PROXY_SSL_ALLOW_BEAST},
250   {"proxy-ssl-auto-client-cert", ARG_BOOL, ' ', C_PROXY_SSL_AUTO_CLIENT_CERT},
251   {"proxy-tls13-ciphers",        ARG_STRG, ' ', C_PROXY_TLS13_CIPHERS},
252   {"proxy-tlsauthtype",          ARG_STRG, ' ', C_PROXY_TLSAUTHTYPE},
253   {"proxy-tlspassword",          ARG_STRG, ' ', C_PROXY_TLSPASSWORD},
254   {"proxy-tlsuser",              ARG_STRG, ' ', C_PROXY_TLSUSER},
255   {"proxy-tlsv1",                ARG_NONE, ' ', C_PROXY_TLSV1},
256   {"proxy-user",                 ARG_STRG, 'U', C_PROXY_USER},
257   {"proxy1.0",                   ARG_STRG, ' ', C_PROXY1_0},
258   {"proxytunnel",                ARG_BOOL, 'p', C_PROXYTUNNEL},
259   {"pubkey",                     ARG_STRG, ' ', C_PUBKEY},
260   {"quote",                      ARG_STRG, 'Q', C_QUOTE},
261   {"random-file",                ARG_FILE, ' ', C_RANDOM_FILE},
262   {"range",                      ARG_STRG, 'r', C_RANGE},
263   {"rate",                       ARG_STRG, ' ', C_RATE},
264   {"raw",                        ARG_BOOL, ' ', C_RAW},
265   {"referer",                    ARG_STRG, 'e', C_REFERER},
266   {"remote-header-name",         ARG_BOOL, 'J', C_REMOTE_HEADER_NAME},
267   {"remote-name",                ARG_BOOL, 'O', C_REMOTE_NAME},
268   {"remote-name-all",            ARG_BOOL, ' ', C_REMOTE_NAME_ALL},
269   {"remote-time",                ARG_BOOL, 'R', C_REMOTE_TIME},
270   {"remove-on-error",            ARG_BOOL, ' ', C_REMOVE_ON_ERROR},
271   {"request",                    ARG_STRG, 'X', C_REQUEST},
272   {"request-target",             ARG_STRG, ' ', C_REQUEST_TARGET},
273   {"resolve",                    ARG_STRG, ' ', C_RESOLVE},
274   {"retry",                      ARG_STRG, ' ', C_RETRY},
275   {"retry-all-errors",           ARG_BOOL, ' ', C_RETRY_ALL_ERRORS},
276   {"retry-connrefused",          ARG_BOOL, ' ', C_RETRY_CONNREFUSED},
277   {"retry-delay",                ARG_STRG, ' ', C_RETRY_DELAY},
278   {"retry-max-time",             ARG_STRG, ' ', C_RETRY_MAX_TIME},
279   {"sasl-authzid",               ARG_STRG, ' ', C_SASL_AUTHZID},
280   {"sasl-ir",                    ARG_BOOL, ' ', C_SASL_IR},
281   {"service-name",               ARG_STRG, ' ', C_SERVICE_NAME},
282   {"sessionid",                  ARG_BOOL|ARG_NO, ' ', C_SESSIONID},
283   {"show-error",                 ARG_BOOL, 'S', C_SHOW_ERROR},
284   {"show-headers",               ARG_BOOL, 'i', C_SHOW_HEADERS},
285   {"silent",                     ARG_BOOL, 's', C_SILENT},
286   {"skip-existing",              ARG_BOOL, ' ', C_SKIP_EXISTING},
287   {"socks4",                     ARG_STRG, ' ', C_SOCKS4},
288   {"socks4a",                    ARG_STRG, ' ', C_SOCKS4A},
289   {"socks5",                     ARG_STRG, ' ', C_SOCKS5},
290   {"socks5-basic",               ARG_BOOL, ' ', C_SOCKS5_BASIC},
291   {"socks5-gssapi",              ARG_BOOL, ' ', C_SOCKS5_GSSAPI},
292   {"socks5-gssapi-nec",          ARG_BOOL, ' ', C_SOCKS5_GSSAPI_NEC},
293   {"socks5-gssapi-service",      ARG_STRG, ' ', C_SOCKS5_GSSAPI_SERVICE},
294   {"socks5-hostname",            ARG_STRG, ' ', C_SOCKS5_HOSTNAME},
295   {"speed-limit",                ARG_STRG, 'Y', C_SPEED_LIMIT},
296   {"speed-time",                 ARG_STRG, 'y', C_SPEED_TIME},
297   {"ssl",                        ARG_BOOL, ' ', C_SSL},
298   {"ssl-allow-beast",            ARG_BOOL, ' ', C_SSL_ALLOW_BEAST},
299   {"ssl-auto-client-cert",       ARG_BOOL, ' ', C_SSL_AUTO_CLIENT_CERT},
300   {"ssl-no-revoke",              ARG_BOOL, ' ', C_SSL_NO_REVOKE},
301   {"ssl-reqd",                   ARG_BOOL, ' ', C_SSL_REQD},
302   {"ssl-revoke-best-effort",     ARG_BOOL, ' ', C_SSL_REVOKE_BEST_EFFORT},
303   {"ssl-sessions",               ARG_FILE, ' ', C_SSL_SESSIONS},
304   {"sslv2",                      ARG_NONE, '2', C_SSLV2},
305   {"sslv3",                      ARG_NONE, '3', C_SSLV3},
306   {"stderr",                     ARG_FILE, ' ', C_STDERR},
307   {"styled-output",              ARG_BOOL, ' ', C_STYLED_OUTPUT},
308   {"suppress-connect-headers",   ARG_BOOL, ' ', C_SUPPRESS_CONNECT_HEADERS},
309   {"tcp-fastopen",               ARG_BOOL, ' ', C_TCP_FASTOPEN},
310   {"tcp-nodelay",                ARG_BOOL, ' ', C_TCP_NODELAY},
311   {"telnet-option",              ARG_STRG, 't', C_TELNET_OPTION},
312 #ifdef DEBUGBUILD
313   {"test-duphandle",             ARG_BOOL, ' ', C_TEST_DUPHANDLE},
314   {"test-event",                 ARG_BOOL, ' ', C_TEST_EVENT},
315 #endif
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       CURL_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         CURL_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 
parse_url(struct GlobalConfig * global,struct OperationConfig * config,const char * nextarg)1019 static ParameterError parse_url(struct GlobalConfig *global,
1020                                 struct OperationConfig *config,
1021                                 const char *nextarg)
1022 {
1023   ParameterError err = PARAM_OK;
1024   struct getout *url;
1025 
1026   if(!config->url_get)
1027     config->url_get = config->url_list;
1028 
1029   if(config->url_get) {
1030     /* there is a node here, if it already is filled-in continue to find
1031        an "empty" node */
1032     while(config->url_get && (config->url_get->flags & GETOUT_URL))
1033       config->url_get = config->url_get->next;
1034   }
1035 
1036   /* now there might or might not be an available node to fill in! */
1037 
1038   if(config->url_get)
1039     /* existing node */
1040     url = config->url_get;
1041   else
1042     /* there was no free node, create one! */
1043     config->url_get = url = new_getout(config);
1044 
1045   if(!url)
1046     return PARAM_NO_MEM;
1047   else {
1048     /* fill in the URL */
1049     err = getstr(&url->url, nextarg, DENY_BLANK);
1050     url->flags |= GETOUT_URL;
1051     if(!err && (++config->num_urls > 1) && (config->etag_save_file ||
1052                                             config->etag_compare_file)) {
1053       errorf(global, "The etag options only work on a single URL");
1054       return PARAM_BAD_USE;
1055     }
1056   }
1057   return err;
1058 }
1059 
parse_localport(struct OperationConfig * config,char * nextarg)1060 static ParameterError parse_localport(struct OperationConfig *config,
1061                                       char *nextarg)
1062 {
1063   char *pp = NULL;
1064   char *p = nextarg;
1065   while(ISDIGIT(*p))
1066     p++;
1067   if(*p) {
1068     pp = p;
1069     /* check for ' - [end]' */
1070     if(ISSPACE(*pp))
1071       pp++;
1072     if(*pp != '-')
1073       return PARAM_BAD_USE;
1074     pp++;
1075     if(*pp && ISSPACE(*pp))
1076       pp++;
1077     *p = 0; /* null-terminate to make str2unum() work below */
1078   }
1079 
1080   if(str2unummax(&config->localport, nextarg, 65535))
1081     return PARAM_BAD_USE;
1082   if(!pp)
1083     config->localportrange = 1; /* default number of ports to try */
1084   else {
1085     if(str2unummax(&config->localportrange, pp, 65535))
1086       return PARAM_BAD_USE;
1087     config->localportrange -= (config->localport-1);
1088     if(config->localportrange < 1)
1089       return PARAM_BAD_USE;
1090   }
1091   return PARAM_OK;
1092 }
1093 
parse_continue_at(struct GlobalConfig * global,struct OperationConfig * config,const char * nextarg)1094 static ParameterError parse_continue_at(struct GlobalConfig *global,
1095                                         struct OperationConfig *config,
1096                                         const char *nextarg)
1097 {
1098   ParameterError err = PARAM_OK;
1099   if(config->range) {
1100     errorf(global, "--continue-at is mutually exclusive with --range");
1101     return PARAM_BAD_USE;
1102   }
1103   if(config->rm_partial) {
1104     errorf(config->global,
1105            "--continue-at is mutually exclusive with --remove-on-error");
1106     return PARAM_BAD_USE;
1107   }
1108   if(config->file_clobber_mode == CLOBBER_NEVER) {
1109     errorf(config->global,
1110            "--continue-at is mutually exclusive with --no-clobber");
1111     return PARAM_BAD_USE;
1112   }
1113   /* This makes us continue an ftp transfer at given position */
1114   if(strcmp(nextarg, "-")) {
1115     err = str2offset(&config->resume_from, nextarg);
1116     config->resume_from_current = FALSE;
1117   }
1118   else {
1119     config->resume_from_current = TRUE;
1120     config->resume_from = 0;
1121   }
1122   config->use_resume = TRUE;
1123   return err;
1124 }
1125 
parse_ech(struct GlobalConfig * global,struct OperationConfig * config,const char * nextarg)1126 static ParameterError parse_ech(struct GlobalConfig *global,
1127                                 struct OperationConfig *config,
1128                                 const char *nextarg)
1129 {
1130   ParameterError err = PARAM_OK;
1131   if(!feature_ech)
1132     err = PARAM_LIBCURL_DOESNT_SUPPORT;
1133   else if(strlen(nextarg) > 4 && strncasecompare("pn:", nextarg, 3)) {
1134     /* a public_name */
1135     err = getstr(&config->ech_public, nextarg, DENY_BLANK);
1136   }
1137   else if(strlen(nextarg) > 5 && strncasecompare("ecl:", nextarg, 4)) {
1138     /* an ECHConfigList */
1139     if('@' != *(nextarg + 4)) {
1140       err = getstr(&config->ech_config, nextarg, DENY_BLANK);
1141     }
1142     else {
1143       /* Indirect case: @filename or @- for stdin */
1144       char *tmpcfg = NULL;
1145       FILE *file;
1146 
1147       nextarg += 5;        /* skip over 'ecl:@' */
1148       if(!strcmp("-", nextarg)) {
1149         file = stdin;
1150       }
1151       else {
1152         file = fopen(nextarg, FOPEN_READTEXT);
1153       }
1154       if(!file) {
1155         warnf(global,
1156               "Couldn't read file \"%s\" "
1157               "specified for \"--ech ecl:\" option",
1158               nextarg);
1159         return PARAM_BAD_USE; /*  */
1160       }
1161       err = file2string(&tmpcfg, file);
1162       if(file != stdin)
1163         fclose(file);
1164       if(err)
1165         return err;
1166       config->ech_config = aprintf("ecl:%s",tmpcfg);
1167       free(tmpcfg);
1168       if(!config->ech_config)
1169         return PARAM_NO_MEM;
1170     } /* file done */
1171   }
1172   else {
1173     /* Simple case: just a string, with a keyword */
1174     err = getstr(&config->ech, nextarg, DENY_BLANK);
1175   }
1176   return err;
1177 }
1178 
parse_header(struct GlobalConfig * global,struct OperationConfig * config,cmdline_t cmd,const char * nextarg)1179 static ParameterError parse_header(struct GlobalConfig *global,
1180                                    struct OperationConfig *config,
1181                                    cmdline_t cmd,
1182                                    const char *nextarg)
1183 {
1184   ParameterError err = PARAM_OK;
1185 
1186   /* A custom header to append to a list */
1187   if(nextarg[0] == '@') {
1188     /* read many headers from a file or stdin */
1189     char *string;
1190     size_t len;
1191     bool use_stdin = !strcmp(&nextarg[1], "-");
1192     FILE *file = use_stdin ? stdin : fopen(&nextarg[1], FOPEN_READTEXT);
1193     if(!file) {
1194       errorf(global, "Failed to open %s", &nextarg[1]);
1195       err = PARAM_READ_ERROR;
1196     }
1197     else {
1198       err = file2memory(&string, &len, file);
1199       if(!err && string) {
1200         /* Allow strtok() here since this is not used threaded */
1201         /* !checksrc! disable BANNEDFUNC 2 */
1202         char *h = strtok(string, "\r\n");
1203         while(h) {
1204           if(cmd == C_PROXY_HEADER) /* --proxy-header */
1205             err = add2list(&config->proxyheaders, h);
1206           else
1207             err = add2list(&config->headers, h);
1208           if(err)
1209             break;
1210           h = strtok(NULL, "\r\n");
1211         }
1212         free(string);
1213       }
1214       if(!use_stdin)
1215         fclose(file);
1216     }
1217   }
1218   else {
1219     if(cmd == C_PROXY_HEADER) /* --proxy-header */
1220       err = add2list(&config->proxyheaders, nextarg);
1221     else
1222       err = add2list(&config->headers, nextarg);
1223   }
1224   return err;
1225 }
1226 
parse_output(struct OperationConfig * config,const char * nextarg)1227 static ParameterError parse_output(struct OperationConfig *config,
1228                                    const char *nextarg)
1229 {
1230   ParameterError err = PARAM_OK;
1231   struct getout *url;
1232 
1233   /* output file */
1234   if(!config->url_out)
1235     config->url_out = config->url_list;
1236   if(config->url_out) {
1237     /* there is a node here, if it already is filled-in continue to find
1238        an "empty" node */
1239     while(config->url_out && (config->url_out->flags & GETOUT_OUTFILE))
1240       config->url_out = config->url_out->next;
1241   }
1242 
1243   /* now there might or might not be an available node to fill in! */
1244 
1245   if(config->url_out)
1246     /* existing node */
1247     url = config->url_out;
1248   else {
1249     /* there was no free node, create one! */
1250     config->url_out = url = new_getout(config);
1251   }
1252 
1253   if(!url)
1254     return PARAM_NO_MEM;
1255 
1256   /* fill in the outfile */
1257   err = getstr(&url->outfile, nextarg, DENY_BLANK);
1258   url->flags &= ~GETOUT_USEREMOTE; /* switch off */
1259   url->flags |= GETOUT_OUTFILE;
1260   return err;
1261 }
1262 
parse_remote_name(struct OperationConfig * config,bool toggle)1263 static ParameterError parse_remote_name(struct OperationConfig *config,
1264                                         bool toggle)
1265 {
1266   ParameterError err = PARAM_OK;
1267   struct getout *url;
1268 
1269   if(!toggle && !config->default_node_flags)
1270     return err; /* nothing to do */
1271 
1272   /* output file */
1273   if(!config->url_out)
1274     config->url_out = config->url_list;
1275   if(config->url_out) {
1276     /* there is a node here, if it already is filled-in continue to find
1277        an "empty" node */
1278     while(config->url_out && (config->url_out->flags & GETOUT_OUTFILE))
1279       config->url_out = config->url_out->next;
1280   }
1281 
1282   /* now there might or might not be an available node to fill in! */
1283 
1284   if(config->url_out)
1285     /* existing node */
1286     url = config->url_out;
1287   else {
1288     /* there was no free node, create one! */
1289     config->url_out = url = new_getout(config);
1290   }
1291 
1292   if(!url)
1293     return PARAM_NO_MEM;
1294 
1295   url->outfile = NULL; /* leave it */
1296   if(toggle)
1297     url->flags |= GETOUT_USEREMOTE;  /* switch on */
1298   else
1299     url->flags &= ~GETOUT_USEREMOTE; /* switch off */
1300   url->flags |= GETOUT_OUTFILE;
1301   return PARAM_OK;
1302 }
1303 
parse_quote(struct OperationConfig * config,const char * nextarg)1304 static ParameterError parse_quote(struct OperationConfig *config,
1305                                   const char *nextarg)
1306 {
1307   ParameterError err = PARAM_OK;
1308 
1309   /* QUOTE command to send to FTP server */
1310   switch(nextarg[0]) {
1311   case '-':
1312     /* prefixed with a dash makes it a POST TRANSFER one */
1313     nextarg++;
1314     err = add2list(&config->postquote, nextarg);
1315     break;
1316   case '+':
1317     /* prefixed with a plus makes it a just-before-transfer one */
1318     nextarg++;
1319     err = add2list(&config->prequote, nextarg);
1320         break;
1321   default:
1322     err = add2list(&config->quote, nextarg);
1323     break;
1324   }
1325   return err;
1326 }
1327 
parse_range(struct GlobalConfig * global,struct OperationConfig * config,const char * nextarg)1328 static ParameterError parse_range(struct GlobalConfig *global,
1329                                   struct OperationConfig *config,
1330                                   const char *nextarg)
1331 {
1332   ParameterError err = PARAM_OK;
1333 
1334   if(config->use_resume) {
1335     errorf(global, "--continue-at is mutually exclusive with --range");
1336     return PARAM_BAD_USE;
1337   }
1338   /* Specifying a range WITHOUT A DASH will create an illegal HTTP range
1339      (and will not actually be range by definition). The manpage
1340      previously claimed that to be a good way, why this code is added to
1341      work-around it. */
1342   if(ISDIGIT(*nextarg) && !strchr(nextarg, '-')) {
1343     char buffer[32];
1344     curl_off_t value;
1345     if(curlx_strtoofft(nextarg, NULL, 10, &value)) {
1346       warnf(global, "unsupported range point");
1347       err = PARAM_BAD_USE;
1348     }
1349     else {
1350       warnf(global,
1351             "A specified range MUST include at least one dash (-). "
1352             "Appending one for you");
1353       msnprintf(buffer, sizeof(buffer), "%" CURL_FORMAT_CURL_OFF_T "-",
1354                 value);
1355       Curl_safefree(config->range);
1356       config->range = strdup(buffer);
1357       if(!config->range)
1358         err = PARAM_NO_MEM;
1359     }
1360   }
1361   else {
1362     /* byte range requested */
1363     const char *tmp_range = nextarg;
1364     while(*tmp_range) {
1365       if(!ISDIGIT(*tmp_range) && *tmp_range != '-' && *tmp_range != ',') {
1366         warnf(global, "Invalid character is found in given range. "
1367               "A specified range MUST have only digits in "
1368               "\'start\'-\'stop\'. The server's response to this "
1369               "request is uncertain.");
1370         break;
1371       }
1372       tmp_range++;
1373     }
1374     err = getstr(&config->range, nextarg, DENY_BLANK);
1375   }
1376   return err;
1377 }
1378 
parse_upload_file(struct OperationConfig * config,const char * nextarg)1379 static ParameterError parse_upload_file(struct OperationConfig *config,
1380                                         const char *nextarg)
1381 {
1382   ParameterError err = PARAM_OK;
1383   struct getout *url;
1384 
1385   /* we are uploading */
1386   if(!config->url_ul)
1387     config->url_ul = config->url_list;
1388   if(config->url_ul) {
1389     /* there is a node here, if it already is filled-in continue to find
1390        an "empty" node */
1391     while(config->url_ul && (config->url_ul->flags & GETOUT_UPLOAD))
1392       config->url_ul = config->url_ul->next;
1393   }
1394 
1395   /* now there might or might not be an available node to fill in! */
1396 
1397   if(config->url_ul)
1398     /* existing node */
1399     url = config->url_ul;
1400   else
1401     /* there was no free node, create one! */
1402     config->url_ul = url = new_getout(config);
1403 
1404   if(!url)
1405     return PARAM_NO_MEM;
1406 
1407   url->flags |= GETOUT_UPLOAD; /* mark -T used */
1408   if(!*nextarg)
1409     url->flags |= GETOUT_NOUPLOAD;
1410   else {
1411     /* "-" equals stdin, but keep the string around for now */
1412     err = getstr(&url->infile, nextarg, DENY_BLANK);
1413   }
1414   return err;
1415 }
1416 
parse_verbose(struct GlobalConfig * global,bool toggle,size_t nopts)1417 static ParameterError parse_verbose(struct GlobalConfig *global,
1418                                     bool toggle,
1419                                     size_t nopts)
1420 {
1421   ParameterError err = PARAM_OK;
1422 
1423   /* This option is a super-boolean with side effect when applied
1424    * more than once in the same argument flag, like `-vvv`. */
1425   if(!toggle) {
1426     global->verbosity = 0;
1427     if(set_trace_config(global, "-all"))
1428       err = PARAM_NO_MEM;
1429     global->tracetype = TRACE_NONE;
1430     return err;
1431   }
1432   else if(!nopts) {
1433     /* fist `-v` in an argument resets to base verbosity */
1434     global->verbosity = 0;
1435     if(set_trace_config(global, "-all"))
1436       return PARAM_NO_MEM;
1437   }
1438   /* the '%' thing here will cause the trace get sent to stderr */
1439   switch(global->verbosity) {
1440   case 0:
1441     global->verbosity = 1;
1442     Curl_safefree(global->trace_dump);
1443     global->trace_dump = strdup("%");
1444     if(!global->trace_dump)
1445       err = PARAM_NO_MEM;
1446     else {
1447       if(global->tracetype && (global->tracetype != TRACE_PLAIN))
1448         warnf(global,
1449               "-v, --verbose overrides an earlier trace option");
1450       global->tracetype = TRACE_PLAIN;
1451     }
1452     break;
1453   case 1:
1454     global->verbosity = 2;
1455     if(set_trace_config(global, "ids,time,protocol"))
1456       err = PARAM_NO_MEM;
1457     break;
1458   case 2:
1459     global->verbosity = 3;
1460     global->tracetype = TRACE_ASCII;
1461     if(set_trace_config(global, "ssl,read,write"))
1462       err = PARAM_NO_MEM;
1463     break;
1464   case 3:
1465     global->verbosity = 4;
1466     if(set_trace_config(global, "network"))
1467       err = PARAM_NO_MEM;
1468     break;
1469   default:
1470     /* no effect for now */
1471     break;
1472   }
1473   return err;
1474 }
1475 
parse_writeout(struct GlobalConfig * global,struct OperationConfig * config,const char * nextarg)1476 static ParameterError parse_writeout(struct GlobalConfig *global,
1477                                      struct OperationConfig *config,
1478                                      const char *nextarg)
1479 {
1480   ParameterError err = PARAM_OK;
1481 
1482   /* get the output string */
1483   if('@' == *nextarg) {
1484     /* the data begins with a '@' letter, it means that a filename
1485        or - (stdin) follows */
1486     FILE *file;
1487     const char *fname;
1488     nextarg++; /* pass the @ */
1489     if(!strcmp("-", nextarg)) {
1490       fname = "<stdin>";
1491       file = stdin;
1492     }
1493     else {
1494       fname = nextarg;
1495       file = fopen(fname, FOPEN_READTEXT);
1496       if(!file) {
1497         errorf(global, "Failed to open %s", fname);
1498         return PARAM_READ_ERROR;
1499       }
1500     }
1501     Curl_safefree(config->writeout);
1502     err = file2string(&config->writeout, file);
1503     if(file && (file != stdin))
1504       fclose(file);
1505     if(err)
1506       return err;
1507     if(!config->writeout)
1508       warnf(global, "Failed to read %s", fname);
1509   }
1510   else
1511     err = getstr(&config->writeout, nextarg, ALLOW_BLANK);
1512 
1513   return err;
1514 }
1515 
parse_time_cond(struct GlobalConfig * global,struct OperationConfig * config,const char * nextarg)1516 static ParameterError parse_time_cond(struct GlobalConfig *global,
1517                                       struct OperationConfig *config,
1518                                       const char *nextarg)
1519 {
1520   ParameterError err = PARAM_OK;
1521 
1522   switch(*nextarg) {
1523   case '+':
1524     nextarg++;
1525     FALLTHROUGH();
1526   default:
1527     /* If-Modified-Since: (section 14.28 in RFC2068) */
1528     config->timecond = CURL_TIMECOND_IFMODSINCE;
1529     break;
1530   case '-':
1531     /* If-Unmodified-Since:  (section 14.24 in RFC2068) */
1532     config->timecond = CURL_TIMECOND_IFUNMODSINCE;
1533     nextarg++;
1534     break;
1535   case '=':
1536     /* Last-Modified:  (section 14.29 in RFC2068) */
1537     config->timecond = CURL_TIMECOND_LASTMOD;
1538     nextarg++;
1539     break;
1540   }
1541   config->condtime = (curl_off_t)curl_getdate(nextarg, NULL);
1542   if(-1 == config->condtime) {
1543     curl_off_t value;
1544     /* now let's see if it is a filename to get the time from instead! */
1545     int rc = getfiletime(nextarg, global, &value);
1546     if(!rc)
1547       /* pull the time out from the file */
1548       config->condtime = value;
1549     else {
1550       /* failed, remove time condition */
1551       config->timecond = CURL_TIMECOND_NONE;
1552       warnf(global,
1553             "Illegal date format for -z, --time-cond (and not "
1554             "a filename). Disabling time condition. "
1555             "See curl_getdate(3) for valid date syntax.");
1556     }
1557   }
1558   return err;
1559 }
1560 
getparameter(const char * flag,char * nextarg,argv_item_t cleararg,bool * usedarg,struct GlobalConfig * global,struct OperationConfig * config)1561 ParameterError getparameter(const char *flag, /* f or -long-flag */
1562                             char *nextarg,    /* NULL if unset */
1563                             argv_item_t cleararg,
1564                             bool *usedarg,    /* set to TRUE if the arg
1565                                                  has been used */
1566                             struct GlobalConfig *global,
1567                             struct OperationConfig *config)
1568 {
1569   const char *parse = NULL;
1570   bool longopt = FALSE;
1571   bool singleopt = FALSE; /* when true means '-o foo' used '-ofoo' */
1572   size_t nopts = 0; /* options processed in `flag`*/
1573   ParameterError err = PARAM_OK;
1574   bool toggle = TRUE; /* how to switch boolean options, on or off. Controlled
1575                          by using --OPTION or --no-OPTION */
1576   bool nextalloc = FALSE; /* if nextarg is allocated */
1577   static const char *redir_protos[] = {
1578     "http",
1579     "https",
1580     "ftp",
1581     "ftps",
1582     NULL
1583   };
1584   const struct LongShort *a = NULL;
1585   curl_off_t value;
1586 #ifdef HAVE_WRITABLE_ARGV
1587   argv_item_t clearthis = NULL;
1588 #else
1589   (void)cleararg;
1590 #endif
1591 
1592   *usedarg = FALSE; /* default is that we do not use the arg */
1593 
1594   if(('-' != flag[0]) || ('-' == flag[1])) {
1595     /* this should be a long name */
1596     const char *word = ('-' == flag[0]) ? flag + 2 : flag;
1597     bool noflagged = FALSE;
1598     bool expand = FALSE;
1599 
1600     if(!strncmp(word, "no-", 3)) {
1601       /* disable this option but ignore the "no-" part when looking for it */
1602       word += 3;
1603       toggle = FALSE;
1604       noflagged = TRUE;
1605     }
1606     else if(!strncmp(word, "expand-", 7)) {
1607       /* variable expansions is to be done on the argument */
1608       word += 7;
1609       expand = TRUE;
1610     }
1611 
1612     a = findlongopt(word);
1613     if(a) {
1614       longopt = TRUE;
1615     }
1616     else {
1617       err = PARAM_OPTION_UNKNOWN;
1618       goto error;
1619     }
1620     if(noflagged && (ARGTYPE(a->desc) != ARG_BOOL)) {
1621       /* --no- prefixed an option that is not boolean! */
1622       err = PARAM_NO_NOT_BOOLEAN;
1623       goto error;
1624     }
1625     else if(expand && nextarg) {
1626       struct curlx_dynbuf nbuf;
1627       bool replaced;
1628 
1629       if((ARGTYPE(a->desc) != ARG_STRG) &&
1630          (ARGTYPE(a->desc) != ARG_FILE)) {
1631         /* --expand on an option that is not a string or a filename */
1632         err = PARAM_EXPAND_ERROR;
1633         goto error;
1634       }
1635       err = varexpand(global, nextarg, &nbuf, &replaced);
1636       if(err) {
1637         curlx_dyn_free(&nbuf);
1638         goto error;
1639       }
1640       if(replaced) {
1641         nextarg = curlx_dyn_ptr(&nbuf);
1642         nextalloc = TRUE;
1643       }
1644     }
1645   }
1646   else {
1647     flag++; /* prefixed with one dash, pass it */
1648     parse = flag;
1649   }
1650 
1651   do {
1652     /* we can loop here if we have multiple single-letters */
1653     cmdline_t cmd;
1654 
1655     if(!longopt && !a) {
1656       a = findshortopt(*parse);
1657       if(!a) {
1658         err = PARAM_OPTION_UNKNOWN;
1659         break;
1660       }
1661     }
1662     cmd = (cmdline_t)a->cmd;
1663     if(ARGTYPE(a->desc) >= ARG_STRG) {
1664       /* this option requires an extra parameter */
1665       if(!longopt && parse[1]) {
1666         nextarg = (char *)&parse[1]; /* this is the actual extra parameter */
1667         singleopt = TRUE;   /* do not loop anymore after this */
1668       }
1669       else if(!nextarg) {
1670         err = PARAM_REQUIRES_PARAMETER;
1671         break;
1672       }
1673       else {
1674 #ifdef HAVE_WRITABLE_ARGV
1675         clearthis = cleararg;
1676 #endif
1677         *usedarg = TRUE; /* mark it as used */
1678       }
1679 
1680       if((ARGTYPE(a->desc) == ARG_FILE) &&
1681          (nextarg[0] == '-') && nextarg[1]) {
1682         /* if the filename looks like a command line option */
1683         warnf(global, "The filename argument '%s' looks like a flag.",
1684               nextarg);
1685       }
1686       else if(!strncmp("\xe2\x80\x9c", nextarg, 3)) {
1687         warnf(global, "The argument '%s' starts with a Unicode quote where "
1688               "maybe an ASCII \" was intended?",
1689               nextarg);
1690       }
1691     }
1692     else if((ARGTYPE(a->desc) == ARG_NONE) && !toggle) {
1693       err = PARAM_NO_PREFIX;
1694       break;
1695     }
1696 
1697     if(!nextarg)
1698       /* this is a precaution mostly to please scan-build, as all arguments
1699          that use nextarg should be marked as such and they will check that
1700          nextarg is set before continuing, but code analyzers are not always
1701          that aware of that state */
1702       nextarg = (char *)"";
1703 
1704     switch(cmd) {
1705     case C_RANDOM_FILE: /* --random-file */
1706     case C_EGD_FILE: /* --egd-file */
1707     case C_NTLM_WB: /* --ntlm-wb */
1708       warnf(global, "--%s is deprecated and has no function anymore",
1709             a->lname);
1710       break;
1711     case C_DNS_IPV4_ADDR: /* --dns-ipv4-addr */
1712       if(!curlinfo->ares_num) /* c-ares is needed for this */
1713         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1714       else
1715         /* addr in dot notation */
1716         err = getstr(&config->dns_ipv4_addr, nextarg, DENY_BLANK);
1717       break;
1718     case C_DNS_IPV6_ADDR: /* --dns-ipv6-addr */
1719       if(!curlinfo->ares_num) /* c-ares is needed for this */
1720         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1721       else
1722         /* addr in dot notation */
1723         err = getstr(&config->dns_ipv6_addr, nextarg, DENY_BLANK);
1724       break;
1725     case C_OAUTH2_BEARER: /* --oauth2-bearer */
1726       err = getstr(&config->oauth_bearer, nextarg, DENY_BLANK);
1727       if(!err) {
1728         cleanarg(clearthis);
1729         config->authtype |= CURLAUTH_BEARER;
1730       }
1731       break;
1732     case C_CONNECT_TIMEOUT: /* --connect-timeout */
1733       err = secs2ms(&config->connecttimeout_ms, nextarg);
1734       break;
1735     case C_DOH_URL: /* --doh-url */
1736       err = getstr(&config->doh_url, nextarg, ALLOW_BLANK);
1737       if(!err && config->doh_url && !config->doh_url[0])
1738         /* if given a blank string, make it NULL again */
1739         Curl_safefree(config->doh_url);
1740       break;
1741     case C_CIPHERS: /* -- ciphers */
1742       err = getstr(&config->cipher_list, nextarg, DENY_BLANK);
1743       break;
1744     case C_DNS_INTERFACE: /* --dns-interface */
1745       if(!curlinfo->ares_num) /* c-ares is needed for this */
1746         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1747       else
1748         /* interface name */
1749         err = getstr(&config->dns_interface, nextarg, DENY_BLANK);
1750       break;
1751     case C_DISABLE_EPSV: /* --disable-epsv */
1752       config->disable_epsv = toggle;
1753       break;
1754     case C_DISALLOW_USERNAME_IN_URL: /* --disallow-username-in-url */
1755       config->disallow_username_in_url = toggle;
1756       break;
1757     case C_EPSV: /* --epsv */
1758       config->disable_epsv = !toggle;
1759       break;
1760     case C_DNS_SERVERS: /* --dns-servers */
1761       if(!curlinfo->ares_num) /* c-ares is needed for this */
1762         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1763       else
1764         /* IP addrs of DNS servers */
1765         err = getstr(&config->dns_servers, nextarg, DENY_BLANK);
1766       break;
1767     case C_TRACE: /* --trace */
1768       err = getstr(&global->trace_dump, nextarg, DENY_BLANK);
1769       if(!err) {
1770         if(global->tracetype && (global->tracetype != TRACE_BIN))
1771           warnf(global, "--trace overrides an earlier trace/verbose option");
1772         global->tracetype = TRACE_BIN;
1773       }
1774       break;
1775     case C_NPN: /* --npn */
1776       warnf(global, "--npn is no longer supported");
1777       break;
1778     case C_TRACE_ASCII: /* --trace-ascii */
1779       err = getstr(&global->trace_dump, nextarg, DENY_BLANK);
1780       if(!err) {
1781         if(global->tracetype && (global->tracetype != TRACE_ASCII))
1782           warnf(global,
1783                 "--trace-ascii overrides an earlier trace/verbose option");
1784         global->tracetype = TRACE_ASCII;
1785       }
1786       break;
1787     case C_ALPN: /* --alpn */
1788       config->noalpn = !toggle;
1789       break;
1790     case C_LIMIT_RATE: /* --limit-rate */
1791       err = GetSizeParameter(global, nextarg, "rate", &value);
1792       if(!err) {
1793         config->recvpersecond = value;
1794         config->sendpersecond = value;
1795       }
1796       break;
1797     case C_RATE:
1798       err = set_rate(global, nextarg);
1799       break;
1800     case C_COMPRESSED: /* --compressed */
1801       if(toggle && !(feature_libz || feature_brotli || feature_zstd))
1802         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1803       else
1804         config->encoding = toggle;
1805       break;
1806     case C_TR_ENCODING: /* --tr-encoding */
1807       config->tr_encoding = toggle;
1808       break;
1809     case C_DIGEST: /* --digest */
1810       if(toggle)
1811         config->authtype |= CURLAUTH_DIGEST;
1812       else
1813         config->authtype &= ~CURLAUTH_DIGEST;
1814       break;
1815     case C_NEGOTIATE: /* --negotiate */
1816       if(!toggle)
1817         config->authtype &= ~CURLAUTH_NEGOTIATE;
1818       else if(feature_spnego)
1819         config->authtype |= CURLAUTH_NEGOTIATE;
1820       else
1821         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1822       break;
1823     case C_NTLM: /* --ntlm */
1824       if(!toggle)
1825         config->authtype &= ~CURLAUTH_NTLM;
1826       else if(feature_ntlm)
1827         config->authtype |= CURLAUTH_NTLM;
1828       else
1829         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1830       break;
1831     case C_BASIC: /* --basic */
1832       if(toggle)
1833         config->authtype |= CURLAUTH_BASIC;
1834       else
1835         config->authtype &= ~CURLAUTH_BASIC;
1836       break;
1837     case C_ANYAUTH: /* --anyauth */
1838       if(toggle)
1839         config->authtype = CURLAUTH_ANY;
1840       /* --no-anyauth simply does not touch it */
1841       break;
1842 #ifdef USE_WATT32
1843     case C_WDEBUG: /* --wdebug */
1844       dbug_init();
1845       break;
1846 #endif
1847     case C_FTP_CREATE_DIRS: /* --ftp-create-dirs */
1848       config->ftp_create_dirs = toggle;
1849       break;
1850     case C_CREATE_DIRS: /* --create-dirs */
1851       config->create_dirs = toggle;
1852       break;
1853     case C_CREATE_FILE_MODE: /* --create-file-mode */
1854       err = oct2nummax(&config->create_file_mode, nextarg, 0777);
1855       break;
1856     case C_MAX_REDIRS: /* --max-redirs */
1857       /* specified max no of redirects (http(s)), this accepts -1 as a
1858          special condition */
1859       err = str2num(&config->maxredirs, nextarg);
1860       if(!err && (config->maxredirs < -1))
1861         err = PARAM_BAD_NUMERIC;
1862       break;
1863 #ifndef CURL_DISABLE_IPFS
1864     case C_IPFS_GATEWAY: /* --ipfs-gateway */
1865       err = getstr(&config->ipfs_gateway, nextarg, DENY_BLANK);
1866       break;
1867 #endif /* !CURL_DISABLE_IPFS */
1868     case C_PROXY_NTLM: /* --proxy-ntlm */
1869       if(!feature_ntlm)
1870         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1871       else
1872         config->proxyntlm = toggle;
1873       break;
1874     case C_CRLF: /* --crlf */
1875       /* LF -> CRLF conversion? */
1876       config->crlf = toggle;
1877       break;
1878     case C_AWS_SIGV4: /* --aws-sigv4 */
1879       config->authtype |= CURLAUTH_AWS_SIGV4;
1880       err = getstr(&config->aws_sigv4, nextarg, DENY_BLANK);
1881       break;
1882     case C_STDERR: /* --stderr */
1883       tool_set_stderr_file(global, nextarg);
1884       break;
1885     case C_INTERFACE: /* --interface */
1886       /* interface */
1887       err = getstr(&config->iface, nextarg, DENY_BLANK);
1888       break;
1889     case C_KRB: /* --krb */
1890       /* kerberos level string */
1891       if(!feature_spnego)
1892         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1893       else
1894         err = getstr(&config->krblevel, nextarg, DENY_BLANK);
1895       break;
1896     case C_HAPROXY_PROTOCOL: /* --haproxy-protocol */
1897       config->haproxy_protocol = toggle;
1898       break;
1899     case C_HAPROXY_CLIENTIP: /* --haproxy-clientip */
1900       err = getstr(&config->haproxy_clientip, nextarg, DENY_BLANK);
1901       break;
1902     case C_MAX_FILESIZE: /* --max-filesize */
1903       err = GetSizeParameter(global, nextarg, "max-filesize", &value);
1904       if(!err)
1905         config->max_filesize = value;
1906       break;
1907     case C_DISABLE_EPRT: /* --disable-eprt */
1908       config->disable_eprt = toggle;
1909       break;
1910     case C_EPRT: /* --eprt */
1911       config->disable_eprt = !toggle;
1912       break;
1913     case C_XATTR: /* --xattr */
1914       config->xattr = toggle;
1915       break;
1916     case C_URL: /* --url */
1917       err = parse_url(global, config, nextarg);
1918       break;
1919     case C_FTP_SSL: /* --ftp-ssl */
1920     case C_SSL: /* --ssl */
1921       if(toggle && !feature_ssl)
1922         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1923       else {
1924         config->ftp_ssl = toggle;
1925         if(config->ftp_ssl)
1926           warnf(global,
1927                 "--%s is an insecure option, consider --ssl-reqd instead",
1928                 a->lname);
1929       }
1930       break;
1931     case C_FTP_PASV: /* --ftp-pasv */
1932       Curl_safefree(config->ftpport);
1933       break;
1934     case C_SOCKS5: /* --socks5 */
1935       /*  socks5 proxy to use, and resolves the name locally and passes on the
1936           resolved address */
1937       err = getstr(&config->proxy, nextarg, DENY_BLANK);
1938       config->proxyver = CURLPROXY_SOCKS5;
1939       break;
1940     case C_SOCKS4: /* --socks4 */
1941       err = getstr(&config->proxy, nextarg, DENY_BLANK);
1942       config->proxyver = CURLPROXY_SOCKS4;
1943       break;
1944     case C_SOCKS4A: /* --socks4a */
1945       err = getstr(&config->proxy, nextarg, DENY_BLANK);
1946       config->proxyver = CURLPROXY_SOCKS4A;
1947       break;
1948     case C_SOCKS5_HOSTNAME: /* --socks5-hostname */
1949       err = getstr(&config->proxy, nextarg, DENY_BLANK);
1950       config->proxyver = CURLPROXY_SOCKS5_HOSTNAME;
1951       break;
1952     case C_TCP_NODELAY: /* --tcp-nodelay */
1953       config->tcp_nodelay = toggle;
1954       break;
1955     case C_IP_TOS: { /* --ip-tos */
1956       struct TOSEntry find;
1957       const struct TOSEntry *entry;
1958       find.name = nextarg;
1959       entry = bsearch(&find, tos_entries,
1960                       sizeof(tos_entries)/sizeof(*tos_entries),
1961                       sizeof(*tos_entries), find_tos);
1962       if(entry)
1963         config->ip_tos = entry->value;
1964       else /* numeric tos value */
1965         err = str2unummax(&config->ip_tos, nextarg, 0xFF);
1966       break;
1967     }
1968     case C_VLAN_PRIORITY: /* --vlan-priority */
1969       err = str2unummax(&config->vlan_priority, nextarg, 7);
1970       break;
1971     case C_PROXY_DIGEST: /* --proxy-digest */
1972       config->proxydigest = toggle;
1973       break;
1974     case C_PROXY_BASIC: /* --proxy-basic */
1975       config->proxybasic = toggle;
1976       break;
1977     case C_RETRY: /* --retry */
1978       err = str2unum(&config->req_retry, nextarg);
1979       break;
1980     case C_RETRY_CONNREFUSED: /* --retry-connrefused */
1981       config->retry_connrefused = toggle;
1982       break;
1983     case C_RETRY_DELAY: /* --retry-delay */
1984       err = str2unummax(&config->retry_delay, nextarg, LONG_MAX/1000);
1985       break;
1986     case C_RETRY_MAX_TIME: /* --retry-max-time */
1987       err = str2unummax(&config->retry_maxtime, nextarg, LONG_MAX/1000);
1988       break;
1989     case C_RETRY_ALL_ERRORS: /* --retry-all-errors */
1990       config->retry_all_errors = toggle;
1991       break;
1992     case C_PROXY_NEGOTIATE: /* --proxy-negotiate */
1993       if(!feature_spnego)
1994         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1995       else
1996         config->proxynegotiate = toggle;
1997       break;
1998     case C_FORM_ESCAPE: /* --form-escape */
1999       config->mime_options &= ~CURLMIMEOPT_FORMESCAPE;
2000       if(toggle)
2001         config->mime_options |= CURLMIMEOPT_FORMESCAPE;
2002       break;
2003     case C_FTP_ACCOUNT: /* --ftp-account */
2004       err = getstr(&config->ftp_account, nextarg, DENY_BLANK);
2005       break;
2006     case C_PROXY_ANYAUTH: /* --proxy-anyauth */
2007       config->proxyanyauth = toggle;
2008       break;
2009     case C_TRACE_TIME: /* --trace-time */
2010       global->tracetime = toggle;
2011       break;
2012     case C_IGNORE_CONTENT_LENGTH: /* --ignore-content-length */
2013       config->ignorecl = toggle;
2014       break;
2015     case C_FTP_SKIP_PASV_IP: /* --ftp-skip-pasv-ip */
2016       config->ftp_skip_ip = toggle;
2017       break;
2018     case C_FTP_METHOD: /* --ftp-method */
2019       config->ftp_filemethod = ftpfilemethod(config, nextarg);
2020       break;
2021     case C_LOCAL_PORT: /* --local-port */
2022       err = parse_localport(config, nextarg);
2023       break;
2024     case C_FTP_ALTERNATIVE_TO_USER: /* --ftp-alternative-to-user */
2025       err = getstr(&config->ftp_alternative_to_user, nextarg, DENY_BLANK);
2026       break;
2027     case C_FTP_SSL_REQD: /* --ftp-ssl-reqd */
2028     case C_SSL_REQD: /* --ssl-reqd */
2029       if(toggle && !feature_ssl) {
2030         err = PARAM_LIBCURL_DOESNT_SUPPORT;
2031         break;
2032       }
2033       config->ftp_ssl_reqd = toggle;
2034       break;
2035     case C_SESSIONID: /* --sessionid */
2036       config->disable_sessionid = !toggle;
2037       break;
2038     case C_FTP_SSL_CONTROL: /* --ftp-ssl-control */
2039       if(toggle && !feature_ssl)
2040         err = PARAM_LIBCURL_DOESNT_SUPPORT;
2041       else
2042         config->ftp_ssl_control = toggle;
2043       break;
2044     case C_FTP_SSL_CCC: /* --ftp-ssl-ccc */
2045       config->ftp_ssl_ccc = toggle;
2046       if(!config->ftp_ssl_ccc_mode)
2047         config->ftp_ssl_ccc_mode = CURLFTPSSL_CCC_PASSIVE;
2048       break;
2049     case C_FTP_SSL_CCC_MODE: /* --ftp-ssl-ccc-mode */
2050       config->ftp_ssl_ccc = TRUE;
2051       config->ftp_ssl_ccc_mode = ftpcccmethod(config, nextarg);
2052       break;
2053     case C_LIBCURL: /* --libcurl */
2054 #ifdef CURL_DISABLE_LIBCURL_OPTION
2055       warnf(global,
2056             "--libcurl option was disabled at build-time");
2057       err = PARAM_OPTION_UNKNOWN;
2058 #else
2059       err = getstr(&global->libcurl, nextarg, DENY_BLANK);
2060 #endif
2061       break;
2062     case C_RAW: /* --raw */
2063       config->raw = toggle;
2064       break;
2065     case C_KEEPALIVE: /* --keepalive */
2066       config->nokeepalive = !toggle;
2067       break;
2068     case C_KEEPALIVE_TIME: /* --keepalive-time */
2069       err = str2unum(&config->alivetime, nextarg);
2070       break;
2071     case C_KEEPALIVE_CNT: /* --keepalive-cnt */
2072       err = str2unum(&config->alivecnt, nextarg);
2073       break;
2074     case C_POST301: /* --post301 */
2075       config->post301 = toggle;
2076       break;
2077     case C_POST302: /* --post302 */
2078       config->post302 = toggle;
2079       break;
2080     case C_POST303: /* --post303 */
2081       config->post303 = toggle;
2082       break;
2083     case C_NOPROXY: /* --noproxy */
2084       /* This specifies the noproxy list */
2085       err = getstr(&config->noproxy, nextarg, ALLOW_BLANK);
2086       break;
2087     case C_SOCKS5_GSSAPI_NEC: /* --socks5-gssapi-nec */
2088       config->socks5_gssapi_nec = toggle;
2089       break;
2090     case C_PROXY1_0: /* --proxy1.0 */
2091       /* http 1.0 proxy */
2092       err = getstr(&config->proxy, nextarg, DENY_BLANK);
2093       config->proxyver = CURLPROXY_HTTP_1_0;
2094       break;
2095     case C_TFTP_BLKSIZE: /* --tftp-blksize */
2096       err = str2unum(&config->tftp_blksize, nextarg);
2097       break;
2098     case C_MAIL_FROM: /* --mail-from */
2099       err = getstr(&config->mail_from, nextarg, DENY_BLANK);
2100       break;
2101     case C_MAIL_RCPT: /* --mail-rcpt */
2102       /* append receiver to a list */
2103       err = add2list(&config->mail_rcpt, nextarg);
2104       break;
2105     case C_FTP_PRET: /* --ftp-pret */
2106       config->ftp_pret = toggle;
2107       break;
2108     case C_PROTO: /* --proto */
2109       config->proto_present = TRUE;
2110       err = proto2num(config, built_in_protos, &config->proto_str, nextarg);
2111       break;
2112     case C_PROTO_REDIR: /* --proto-redir */
2113       config->proto_redir_present = TRUE;
2114       if(proto2num(config, redir_protos, &config->proto_redir_str,
2115                    nextarg))
2116         err = PARAM_BAD_USE;
2117       break;
2118     case C_RESOLVE: /* --resolve */
2119       err = add2list(&config->resolve, nextarg);
2120       break;
2121     case C_DELEGATION: /* --delegation */
2122       config->gssapi_delegation = delegation(config, nextarg);
2123       break;
2124     case C_MAIL_AUTH: /* --mail-auth */
2125       err = getstr(&config->mail_auth, nextarg, DENY_BLANK);
2126       break;
2127     case C_METALINK: /* --metalink */
2128       errorf(global, "--metalink is disabled");
2129       err = PARAM_BAD_USE;
2130       break;
2131     case C_SASL_AUTHZID: /* --sasl-authzid */
2132       err = getstr(&config->sasl_authzid, nextarg, DENY_BLANK);
2133       break;
2134     case C_SASL_IR: /* --sasl-ir */
2135       config->sasl_ir = toggle;
2136       break;
2137 #ifdef DEBUGBUILD
2138     case C_TEST_DUPHANDLE: /* --test-duphandle */
2139       global->test_duphandle = toggle;
2140       break;
2141     case C_TEST_EVENT: /* --test-event */
2142       global->test_event_based = toggle;
2143       break;
2144 #endif
2145     case C_UNIX_SOCKET: /* --unix-socket */
2146       config->abstract_unix_socket = FALSE;
2147       err = getstr(&config->unix_socket_path, nextarg, DENY_BLANK);
2148       break;
2149     case C_PATH_AS_IS: /* --path-as-is */
2150       config->path_as_is = toggle;
2151       break;
2152     case C_PROXY_SERVICE_NAME: /* --proxy-service-name */
2153       err = getstr(&config->proxy_service_name, nextarg, DENY_BLANK);
2154       break;
2155     case C_SERVICE_NAME: /* --service-name */
2156       err = getstr(&config->service_name, nextarg, DENY_BLANK);
2157       break;
2158     case C_PROTO_DEFAULT: /* --proto-default */
2159       err = getstr(&config->proto_default, nextarg, DENY_BLANK);
2160       if(!err)
2161         err = check_protocol(config->proto_default);
2162       break;
2163     case C_EXPECT100_TIMEOUT: /* --expect100-timeout */
2164       err = secs2ms(&config->expect100timeout_ms, nextarg);
2165       break;
2166     case C_TFTP_NO_OPTIONS: /* --tftp-no-options */
2167       config->tftp_no_options = toggle;
2168       break;
2169     case C_CONNECT_TO: /* --connect-to */
2170       err = add2list(&config->connect_to, nextarg);
2171       break;
2172     case C_ABSTRACT_UNIX_SOCKET: /* --abstract-unix-socket */
2173       config->abstract_unix_socket = TRUE;
2174       err = getstr(&config->unix_socket_path, nextarg, DENY_BLANK);
2175       break;
2176     case C_TLS_EARLYDATA: /* --tls-earlydata */
2177       if(feature_ssl)
2178         config->ssl_allow_earlydata = toggle;
2179       break;
2180     case C_TLS_MAX: /* --tls-max */
2181       err = str2tls_max(&config->ssl_version_max, nextarg);
2182       break;
2183     case C_SUPPRESS_CONNECT_HEADERS: /* --suppress-connect-headers */
2184       config->suppress_connect_headers = toggle;
2185       break;
2186     case C_COMPRESSED_SSH: /* --compressed-ssh */
2187       config->ssh_compression = toggle;
2188       break;
2189     case C_HAPPY_EYEBALLS_TIMEOUT_MS: /* --happy-eyeballs-timeout-ms */
2190       err = str2unum(&config->happy_eyeballs_timeout_ms, nextarg);
2191       /* 0 is a valid value for this timeout */
2192       break;
2193     case C_TRACE_IDS: /* --trace-ids */
2194       global->traceids = toggle;
2195       break;
2196     case C_TRACE_CONFIG: /* --trace-config */
2197       if(set_trace_config(global, nextarg))
2198         err = PARAM_NO_MEM;
2199       break;
2200     case C_PROGRESS_METER: /* --progress-meter */
2201       global->noprogress = !toggle;
2202       break;
2203     case C_PROGRESS_BAR: /* --progress-bar */
2204       global->progressmode = toggle ? CURL_PROGRESS_BAR : CURL_PROGRESS_STATS;
2205       break;
2206     case C_VARIABLE: /* --variable */
2207       err = setvariable(global, nextarg);
2208       break;
2209     case C_NEXT: /* --next */
2210       err = PARAM_NEXT_OPERATION;
2211       break;
2212     case C_HTTP1_0: /* --http1.0 */
2213       /* HTTP version 1.0 */
2214       sethttpver(global, config, CURL_HTTP_VERSION_1_0);
2215       break;
2216     case C_HTTP1_1: /* --http1.1 */
2217       /* HTTP version 1.1 */
2218       sethttpver(global, config, CURL_HTTP_VERSION_1_1);
2219       break;
2220     case C_HTTP2: /* --http2 */
2221       /* HTTP version 2.0 */
2222       if(!feature_http2)
2223         return PARAM_LIBCURL_DOESNT_SUPPORT;
2224       sethttpver(global, config, CURL_HTTP_VERSION_2_0);
2225       break;
2226     case C_HTTP2_PRIOR_KNOWLEDGE: /* --http2-prior-knowledge */
2227       /* HTTP version 2.0 over clean TCP */
2228       if(!feature_http2)
2229         return PARAM_LIBCURL_DOESNT_SUPPORT;
2230       sethttpver(global, config, CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE);
2231       break;
2232     case C_HTTP3: /* --http3: */
2233       /* Try HTTP/3, allow fallback */
2234       if(!feature_http3)
2235         err = PARAM_LIBCURL_DOESNT_SUPPORT;
2236       else
2237         sethttpver(global, config, CURL_HTTP_VERSION_3);
2238       break;
2239     case C_HTTP3_ONLY: /* --http3-only */
2240       /* Try HTTP/3 without fallback */
2241       if(!feature_http3)
2242         err = PARAM_LIBCURL_DOESNT_SUPPORT;
2243       else
2244         sethttpver(global, config, CURL_HTTP_VERSION_3ONLY);
2245       break;
2246     case C_HTTP0_9: /* --http0.9 */
2247       /* Allow HTTP/0.9 responses! */
2248       config->http09_allowed = toggle;
2249       break;
2250     case C_PROXY_HTTP2: /* --proxy-http2 */
2251       if(!feature_httpsproxy || !feature_http2)
2252         err = PARAM_LIBCURL_DOESNT_SUPPORT;
2253       else
2254         config->proxyver = CURLPROXY_HTTPS2;
2255       break;
2256     case C_TLSV1: /* --tlsv1 */
2257       config->ssl_version = CURL_SSLVERSION_TLSv1;
2258       break;
2259     case C_TLSV1_0: /* --tlsv1.0 */
2260       config->ssl_version = CURL_SSLVERSION_TLSv1_0;
2261       break;
2262     case C_TLSV1_1: /* --tlsv1.1 */
2263       config->ssl_version = CURL_SSLVERSION_TLSv1_1;
2264       break;
2265     case C_TLSV1_2: /* --tlsv1.2 */
2266       config->ssl_version = CURL_SSLVERSION_TLSv1_2;
2267       break;
2268     case C_TLSV1_3: /* --tlsv1.3 */
2269       config->ssl_version = CURL_SSLVERSION_TLSv1_3;
2270       break;
2271     case C_TLS13_CIPHERS: /* --tls13-ciphers */
2272       err = getstr(&config->cipher13_list, nextarg, DENY_BLANK);
2273       break;
2274     case C_PROXY_TLS13_CIPHERS: /* --proxy-tls13-ciphers */
2275       err = getstr(&config->proxy_cipher13_list, nextarg, DENY_BLANK);
2276       break;
2277     case C_SSLV2: /* --sslv2 */
2278       warnf(global, "Ignores instruction to use SSLv2");
2279       break;
2280     case C_SSLV3: /* --sslv3 */
2281       warnf(global, "Ignores instruction to use SSLv3");
2282       break;
2283     case C_IPV4: /* --ipv4 */
2284       config->ip_version = CURL_IPRESOLVE_V4;
2285       break;
2286     case C_IPV6: /* --ipv6 */
2287       config->ip_version = CURL_IPRESOLVE_V6;
2288       break;
2289     case C_APPEND: /* --append */
2290       /* This makes the FTP sessions use APPE instead of STOR */
2291       config->ftp_append = toggle;
2292       break;
2293     case C_USER_AGENT: /* --user-agent */
2294       err = getstr(&config->useragent, nextarg, ALLOW_BLANK);
2295       break;
2296     case C_ALT_SVC: /* --alt-svc */
2297       if(!feature_altsvc)
2298         err = PARAM_LIBCURL_DOESNT_SUPPORT;
2299       else
2300         err = getstr(&config->altsvc, nextarg, ALLOW_BLANK);
2301       break;
2302     case C_HSTS: /* --hsts */
2303       if(!feature_hsts)
2304         err = PARAM_LIBCURL_DOESNT_SUPPORT;
2305       else
2306         err = getstr(&config->hsts, nextarg, ALLOW_BLANK);
2307       break;
2308     case C_COOKIE: /* --cookie */
2309       if(strchr(nextarg, '=')) {
2310         /* A cookie string must have a =-letter */
2311         err = add2list(&config->cookies, nextarg);
2312         break;
2313       }
2314       else {
2315         /* We have a cookie file to read from! */
2316         err = add2list(&config->cookiefiles, nextarg);
2317       }
2318       break;
2319     case C_USE_ASCII: /* --use-ascii */
2320       config->use_ascii = toggle;
2321       break;
2322     case C_COOKIE_JAR: /* --cookie-jar */
2323       err = getstr(&config->cookiejar, nextarg, DENY_BLANK);
2324       break;
2325     case C_CONTINUE_AT: /* --continue-at */
2326       err = parse_continue_at(global, config, nextarg);
2327       break;
2328     case C_DATA: /* --data */
2329     case C_DATA_ASCII:  /* --data-ascii */
2330     case C_DATA_BINARY:  /* --data-binary */
2331     case C_DATA_URLENCODE:  /* --data-urlencode */
2332     case C_JSON:  /* --json */
2333     case C_DATA_RAW:  /* --data-raw */
2334       err = set_data(cmd, nextarg, global, config);
2335       break;
2336     case C_URL_QUERY:  /* --url-query */
2337       err = url_query(nextarg, global, config);
2338       break;
2339     case C_DUMP_CA_EMBED: /* --dump-ca-embed */
2340       err = PARAM_CA_EMBED_REQUESTED;
2341       break;
2342     case C_DUMP_HEADER: /* --dump-header */
2343       err = getstr(&config->headerfile, nextarg, DENY_BLANK);
2344       break;
2345     case C_REFERER: { /* --referer */
2346       char *ptr = strstr(nextarg, ";auto");
2347       if(ptr) {
2348         /* Automatic referer requested, this may be combined with a
2349            set initial one */
2350         config->autoreferer = TRUE;
2351         *ptr = 0; /* null-terminate here */
2352       }
2353       else
2354         config->autoreferer = FALSE;
2355       ptr = *nextarg ? nextarg : NULL;
2356       err = getstr(&config->referer, ptr, ALLOW_BLANK);
2357     }
2358       break;
2359     case C_CERT: /* --cert */
2360       cleanarg(clearthis);
2361       GetFileAndPassword(nextarg, &config->cert, &config->key_passwd);
2362       break;
2363     case C_CACERT: /* --cacert */
2364       err = getstr(&config->cacert, nextarg, DENY_BLANK);
2365       break;
2366     case C_CA_NATIVE: /* --ca-native */
2367       config->native_ca_store = toggle;
2368       break;
2369     case C_PROXY_CA_NATIVE: /* --proxy-ca-native */
2370       config->proxy_native_ca_store = toggle;
2371       break;
2372     case C_CERT_TYPE: /* --cert-type */
2373       err = getstr(&config->cert_type, nextarg, DENY_BLANK);
2374       break;
2375     case C_KEY: /* --key */
2376       err = getstr(&config->key, nextarg, DENY_BLANK);
2377       break;
2378     case C_KEY_TYPE: /* --key-type */
2379       err = getstr(&config->key_type, nextarg, DENY_BLANK);
2380       break;
2381     case C_PASS: /* --pass */
2382       err = getstr(&config->key_passwd, nextarg, DENY_BLANK);
2383       cleanarg(clearthis);
2384       break;
2385     case C_ENGINE: /* --engine */
2386       err = getstr(&config->engine, nextarg, DENY_BLANK);
2387       if(!err &&
2388          config->engine && !strcmp(config->engine, "list")) {
2389         err = PARAM_ENGINES_REQUESTED;
2390       }
2391       break;
2392     case C_ECH: /* --ech */
2393       err = parse_ech(global, config, nextarg);
2394       break;
2395     case C_CAPATH: /* --capath */
2396       err = getstr(&config->capath, nextarg, DENY_BLANK);
2397       break;
2398     case C_PUBKEY: /* --pubkey */
2399       err = getstr(&config->pubkey, nextarg, DENY_BLANK);
2400       break;
2401     case C_HOSTPUBMD5: /* --hostpubmd5 */
2402       err = getstr(&config->hostpubmd5, nextarg, DENY_BLANK);
2403       if(!err) {
2404         if(!config->hostpubmd5 || strlen(config->hostpubmd5) != 32)
2405           err = PARAM_BAD_USE;
2406       }
2407       break;
2408     case C_HOSTPUBSHA256: /* --hostpubsha256 */
2409       if(!feature_libssh2)
2410         err = PARAM_LIBCURL_DOESNT_SUPPORT;
2411       else
2412         err = getstr(&config->hostpubsha256, nextarg, DENY_BLANK);
2413       break;
2414     case C_CRLFILE: /* --crlfile */
2415       err = getstr(&config->crlfile, nextarg, DENY_BLANK);
2416       break;
2417     case C_TLSUSER: /* --tlsuser */
2418       if(!feature_tls_srp)
2419         err = PARAM_LIBCURL_DOESNT_SUPPORT;
2420       else
2421         err = getstr(&config->tls_username, nextarg, DENY_BLANK);
2422       cleanarg(clearthis);
2423       break;
2424     case C_TLSPASSWORD: /* --tlspassword */
2425       if(!feature_tls_srp)
2426         err = PARAM_LIBCURL_DOESNT_SUPPORT;
2427       else
2428         err = getstr(&config->tls_password, nextarg, ALLOW_BLANK);
2429       cleanarg(clearthis);
2430       break;
2431     case C_TLSAUTHTYPE: /* --tlsauthtype */
2432       if(!feature_tls_srp)
2433         err = PARAM_LIBCURL_DOESNT_SUPPORT;
2434       else {
2435         err = getstr(&config->tls_authtype, nextarg, DENY_BLANK);
2436         if(!err && strcmp(config->tls_authtype, "SRP"))
2437           err = PARAM_LIBCURL_DOESNT_SUPPORT; /* only support TLS-SRP */
2438       }
2439       break;
2440     case C_SSL_ALLOW_BEAST: /* --ssl-allow-beast */
2441       if(feature_ssl)
2442         config->ssl_allow_beast = toggle;
2443       break;
2444     case C_SSL_AUTO_CLIENT_CERT: /* --ssl-auto-client-cert */
2445       if(feature_ssl)
2446         config->ssl_auto_client_cert = toggle;
2447       break;
2448     case C_PROXY_SSL_AUTO_CLIENT_CERT: /* --proxy-ssl-auto-client-cert */
2449       if(feature_ssl)
2450         config->proxy_ssl_auto_client_cert = toggle;
2451       break;
2452     case C_PINNEDPUBKEY: /* --pinnedpubkey */
2453       err = getstr(&config->pinnedpubkey, nextarg, DENY_BLANK);
2454       break;
2455     case C_PROXY_PINNEDPUBKEY: /* --proxy-pinnedpubkey */
2456       err = getstr(&config->proxy_pinnedpubkey, nextarg, DENY_BLANK);
2457       break;
2458     case C_CERT_STATUS: /* --cert-status */
2459       config->verifystatus = TRUE;
2460       break;
2461     case C_DOH_CERT_STATUS: /* --doh-cert-status */
2462       config->doh_verifystatus = TRUE;
2463       break;
2464     case C_FALSE_START: /* --false-start */
2465       config->falsestart = TRUE;
2466       break;
2467     case C_SSL_NO_REVOKE: /* --ssl-no-revoke */
2468       if(feature_ssl)
2469         config->ssl_no_revoke = TRUE;
2470       break;
2471     case C_SSL_REVOKE_BEST_EFFORT: /* --ssl-revoke-best-effort */
2472       if(feature_ssl)
2473         config->ssl_revoke_best_effort = TRUE;
2474       break;
2475     case C_SSL_SESSIONS: /* --ssl-sessions */
2476       if(feature_ssls_export)
2477         err = getstr(&global->ssl_sessions, nextarg, DENY_BLANK);
2478       else
2479         err = PARAM_LIBCURL_DOESNT_SUPPORT;
2480       break;
2481     case C_TCP_FASTOPEN: /* --tcp-fastopen */
2482       config->tcp_fastopen = TRUE;
2483       break;
2484     case C_PROXY_TLSUSER: /* --proxy-tlsuser */
2485       cleanarg(clearthis);
2486       if(!feature_tls_srp)
2487         err = PARAM_LIBCURL_DOESNT_SUPPORT;
2488       else
2489         err = getstr(&config->proxy_tls_username, nextarg, ALLOW_BLANK);
2490       break;
2491     case C_PROXY_TLSPASSWORD: /* --proxy-tlspassword */
2492       cleanarg(clearthis);
2493       if(!feature_tls_srp)
2494         err = PARAM_LIBCURL_DOESNT_SUPPORT;
2495       else
2496         err = getstr(&config->proxy_tls_password, nextarg, DENY_BLANK);
2497       break;
2498     case C_PROXY_TLSAUTHTYPE: /* --proxy-tlsauthtype */
2499       if(!feature_tls_srp)
2500         err = PARAM_LIBCURL_DOESNT_SUPPORT;
2501       else {
2502         err = getstr(&config->proxy_tls_authtype, nextarg, DENY_BLANK);
2503         if(!err && strcmp(config->proxy_tls_authtype, "SRP"))
2504           err = PARAM_LIBCURL_DOESNT_SUPPORT; /* only support TLS-SRP */
2505       }
2506       break;
2507     case C_PROXY_CERT: /* --proxy-cert */
2508       cleanarg(clearthis);
2509       GetFileAndPassword(nextarg, &config->proxy_cert,
2510                          &config->proxy_key_passwd);
2511       break;
2512     case C_PROXY_CERT_TYPE: /* --proxy-cert-type */
2513       err = getstr(&config->proxy_cert_type, nextarg, DENY_BLANK);
2514       break;
2515     case C_PROXY_KEY: /* --proxy-key */
2516       err = getstr(&config->proxy_key, nextarg, ALLOW_BLANK);
2517       break;
2518     case C_PROXY_KEY_TYPE: /* --proxy-key-type */
2519       err = getstr(&config->proxy_key_type, nextarg, DENY_BLANK);
2520       break;
2521     case C_PROXY_PASS: /* --proxy-pass */
2522       err = getstr(&config->proxy_key_passwd, nextarg, ALLOW_BLANK);
2523       cleanarg(clearthis);
2524       break;
2525     case C_PROXY_CIPHERS: /* --proxy-ciphers */
2526       err = getstr(&config->proxy_cipher_list, nextarg, DENY_BLANK);
2527       break;
2528     case C_PROXY_CRLFILE: /* --proxy-crlfile */
2529       err = getstr(&config->proxy_crlfile, nextarg, DENY_BLANK);
2530       break;
2531     case C_PROXY_SSL_ALLOW_BEAST: /* --proxy-ssl-allow-beast */
2532       if(feature_ssl)
2533         config->proxy_ssl_allow_beast = toggle;
2534       break;
2535     case C_LOGIN_OPTIONS: /* --login-options */
2536       err = getstr(&config->login_options, nextarg, ALLOW_BLANK);
2537       break;
2538     case C_PROXY_CACERT: /* --proxy-cacert */
2539       err = getstr(&config->proxy_cacert, nextarg, DENY_BLANK);
2540       break;
2541     case C_PROXY_CAPATH: /* --proxy-capath */
2542       err = getstr(&config->proxy_capath, nextarg, DENY_BLANK);
2543       break;
2544     case C_PROXY_INSECURE: /* --proxy-insecure */
2545       config->proxy_insecure_ok = toggle;
2546       break;
2547     case C_PROXY_TLSV1: /* --proxy-tlsv1 */
2548       /* TLS version 1 for proxy */
2549       config->proxy_ssl_version = CURL_SSLVERSION_TLSv1;
2550       break;
2551     case C_SOCKS5_BASIC: /* --socks5-basic */
2552       if(toggle)
2553         config->socks5_auth |= CURLAUTH_BASIC;
2554       else
2555         config->socks5_auth &= ~CURLAUTH_BASIC;
2556       break;
2557     case C_SOCKS5_GSSAPI: /* --socks5-gssapi */
2558       if(toggle)
2559         config->socks5_auth |= CURLAUTH_GSSAPI;
2560       else
2561         config->socks5_auth &= ~CURLAUTH_GSSAPI;
2562       break;
2563     case C_ETAG_SAVE: /* --etag-save */
2564       if(config->num_urls > 1) {
2565         errorf(global, "The etag options only work on a single URL");
2566         err = PARAM_BAD_USE;
2567       }
2568       else
2569         err = getstr(&config->etag_save_file, nextarg, DENY_BLANK);
2570       break;
2571     case C_ETAG_COMPARE: /* --etag-compare */
2572       if(config->num_urls > 1) {
2573         errorf(global, "The etag options only work on a single URL");
2574         err = PARAM_BAD_USE;
2575       }
2576       else
2577         err = getstr(&config->etag_compare_file, nextarg, DENY_BLANK);
2578       break;
2579     case C_CURVES: /* --curves */
2580       err = getstr(&config->ssl_ec_curves, nextarg, DENY_BLANK);
2581       break;
2582     case C_FAIL_EARLY: /* --fail-early */
2583       global->fail_early = toggle;
2584       break;
2585     case C_STYLED_OUTPUT: /* --styled-output */
2586       global->styled_output = toggle;
2587       break;
2588     case C_MAIL_RCPT_ALLOWFAILS: /* --mail-rcpt-allowfails */
2589       config->mail_rcpt_allowfails = toggle;
2590       break;
2591     case C_FAIL_WITH_BODY: /* --fail-with-body */
2592       config->failwithbody = toggle;
2593       if(config->failonerror && config->failwithbody) {
2594         errorf(config->global, "You must select either --fail or "
2595                "--fail-with-body, not both.");
2596         err = PARAM_BAD_USE;
2597       }
2598       break;
2599     case C_REMOVE_ON_ERROR: /* --remove-on-error */
2600       if(config->use_resume && toggle) {
2601         errorf(config->global,
2602                "--continue-at is mutually exclusive with --remove-on-error");
2603         return PARAM_BAD_USE;
2604       }
2605       config->rm_partial = toggle;
2606       break;
2607     case C_FAIL: /* --fail */
2608       config->failonerror = toggle;
2609       if(config->failonerror && config->failwithbody) {
2610         errorf(config->global, "You must select either --fail or "
2611                "--fail-with-body, not both.");
2612         err = PARAM_BAD_USE;
2613       }
2614       break;
2615     case C_FORM: /* --form */
2616     case C_FORM_STRING: /* --form-string */
2617       /* "form data" simulation, this is a little advanced so lets do our best
2618          to sort this out slowly and carefully */
2619       if(formparse(config,
2620                    nextarg,
2621                    &config->mimeroot,
2622                    &config->mimecurrent,
2623                    (cmd == C_FORM_STRING))) /* literal string */
2624         err = PARAM_BAD_USE;
2625       else if(SetHTTPrequest(config, TOOL_HTTPREQ_MIMEPOST, &config->httpreq))
2626         err = PARAM_BAD_USE;
2627       break;
2628     case C_GLOBOFF: /* --globoff */
2629       config->globoff = toggle;
2630       break;
2631     case C_GET: /* --get */
2632       config->use_httpget = toggle;
2633       break;
2634     case C_REQUEST_TARGET: /* --request-target */
2635       err = getstr(&config->request_target, nextarg, DENY_BLANK);
2636       break;
2637     case C_HELP: /* --help */
2638       if(toggle) {
2639         if(*nextarg) {
2640           global->help_category = strdup(nextarg);
2641           if(!global->help_category) {
2642             err = PARAM_NO_MEM;
2643             break;
2644           }
2645         }
2646         err = PARAM_HELP_REQUESTED;
2647       }
2648       /* we now actually support --no-help too! */
2649       break;
2650     case C_HEADER: /* --header */
2651     case C_PROXY_HEADER: /* --proxy-header */
2652       err = parse_header(global, config, cmd, nextarg);
2653       break;
2654     case C_INCLUDE: /* --include */
2655     case C_SHOW_HEADERS: /* --show-headers */
2656       config->show_headers = toggle; /* show the headers as well in the
2657                                         general output stream */
2658       break;
2659     case C_JUNK_SESSION_COOKIES: /* --junk-session-cookies */
2660       config->cookiesession = toggle;
2661       break;
2662     case C_HEAD: /* --head */
2663       config->no_body = toggle;
2664       config->show_headers = toggle;
2665       if(SetHTTPrequest(config, (config->no_body) ? TOOL_HTTPREQ_HEAD :
2666                         TOOL_HTTPREQ_GET, &config->httpreq))
2667         err = PARAM_BAD_USE;
2668       break;
2669     case C_REMOTE_HEADER_NAME: /* --remote-header-name */
2670       config->content_disposition = toggle;
2671       break;
2672     case C_INSECURE: /* --insecure */
2673       config->insecure_ok = toggle;
2674       break;
2675     case C_DOH_INSECURE: /* --doh-insecure */
2676       config->doh_insecure_ok = toggle;
2677       break;
2678     case C_CONFIG: /* --config */
2679       if(parseconfig(nextarg, global)) {
2680         errorf(global, "cannot read config from '%s'", nextarg);
2681         err = PARAM_READ_ERROR;
2682       }
2683       break;
2684     case C_LIST_ONLY: /* --list-only */
2685       config->dirlistonly = toggle; /* only list the names of the FTP dir */
2686       break;
2687     case C_LOCATION_TRUSTED: /* --location-trusted */
2688       /* Continue to send authentication (user+password) when following
2689        * locations, even when hostname changed */
2690       config->unrestricted_auth = toggle;
2691       FALLTHROUGH();
2692     case C_LOCATION: /* --location */
2693       config->followlocation = toggle; /* Follow Location: HTTP headers */
2694       break;
2695     case C_MAX_TIME: /* --max-time */
2696       /* specified max time */
2697       err = secs2ms(&config->timeout_ms, nextarg);
2698       break;
2699     case C_MANUAL: /* --manual */
2700       if(toggle) { /* --no-manual shows no manual... */
2701 #ifndef USE_MANUAL
2702         warnf(global,
2703               "built-in manual was disabled at build-time");
2704 #endif
2705         err = PARAM_MANUAL_REQUESTED;
2706       }
2707       break;
2708     case C_NETRC_OPTIONAL: /* --netrc-optional */
2709       config->netrc_opt = toggle;
2710       break;
2711     case C_NETRC_FILE: /* --netrc-file */
2712       err = getstr(&config->netrc_file, nextarg, DENY_BLANK);
2713       break;
2714     case C_NETRC: /* --netrc */
2715       /* pick info from .netrc, if this is used for http, curl will
2716          automatically enforce user+password with the request */
2717       config->netrc = toggle;
2718       break;
2719     case C_BUFFER: /* --buffer */
2720       /* disable the output I/O buffering. note that the option is called
2721          --buffer but is mostly used in the negative form: --no-buffer */
2722       config->nobuffer = longopt ? !toggle : TRUE;
2723       break;
2724     case C_REMOTE_NAME_ALL: /* --remote-name-all */
2725       config->default_node_flags = toggle ? GETOUT_USEREMOTE : 0;
2726       break;
2727     case C_OUTPUT_DIR: /* --output-dir */
2728       err = getstr(&config->output_dir, nextarg, DENY_BLANK);
2729       break;
2730     case C_CLOBBER: /* --clobber */
2731       if(config->use_resume && !toggle) {
2732         errorf(config->global,
2733                "--continue-at is mutually exclusive with --no-clobber");
2734         return PARAM_BAD_USE;
2735       }
2736       config->file_clobber_mode = toggle ? CLOBBER_ALWAYS : CLOBBER_NEVER;
2737       break;
2738     case C_OUTPUT: /* --output */
2739       err = parse_output(config, nextarg);
2740       break;
2741     case C_REMOTE_NAME: /* --remote-name */
2742       err = parse_remote_name(config, toggle);
2743       break;
2744     case C_FTP_PORT: /* --ftp-port */
2745       /* This makes the FTP sessions use PORT instead of PASV */
2746       /* use <eth0> or <192.168.10.10> style addresses. Anything except
2747          this will make us try to get the "default" address.
2748          NOTE: this is a changed behavior since the released 4.1!
2749       */
2750       err = getstr(&config->ftpport, nextarg, DENY_BLANK);
2751       break;
2752     case C_PROXYTUNNEL: /* --proxytunnel */
2753       /* proxy tunnel for non-http protocols */
2754       config->proxytunnel = toggle;
2755       break;
2756 
2757     case C_DISABLE: /* --disable */
2758       /* if used first, already taken care of, we do it like this so we do not
2759          cause an error! */
2760       break;
2761     case C_QUOTE: /* --quote */
2762       err = parse_quote(config, nextarg);
2763       break;
2764     case C_RANGE: /* --range */
2765       err = parse_range(global, config, nextarg);
2766       break;
2767     case C_REMOTE_TIME: /* --remote-time */
2768       /* use remote file's time */
2769       config->remote_time = toggle;
2770       break;
2771     case C_SILENT: /* --silent */
2772       global->silent = toggle;
2773       break;
2774     case C_SKIP_EXISTING: /* --skip-existing */
2775       config->skip_existing = toggle;
2776       break;
2777     case C_SHOW_ERROR: /* --show-error */
2778       global->showerror = toggle;
2779       break;
2780     case C_TELNET_OPTION: /* --telnet-option */
2781       /* Telnet options */
2782       err = add2list(&config->telnet_options, nextarg);
2783       break;
2784     case C_UPLOAD_FILE: /* --upload-file */
2785       err = parse_upload_file(config, nextarg);
2786       break;
2787     case C_USER: /* --user */
2788       /* user:password  */
2789       err = getstr(&config->userpwd, nextarg, ALLOW_BLANK);
2790       cleanarg(clearthis);
2791       break;
2792     case C_PROXY_USER: /* --proxy-user */
2793       /* Proxy user:password  */
2794       err = getstr(&config->proxyuserpwd, nextarg, ALLOW_BLANK);
2795       cleanarg(clearthis);
2796       break;
2797     case C_VERBOSE: /* --verbose */
2798       err = parse_verbose(global, toggle, nopts);
2799       break;
2800     case C_VERSION: /* --version */
2801       if(toggle)    /* --no-version yields no output! */
2802         err = PARAM_VERSION_INFO_REQUESTED;
2803       break;
2804     case C_WRITE_OUT: /* --write-out */
2805       err = parse_writeout(global, config, nextarg);
2806       break;
2807     case C_PREPROXY: /* --preproxy */
2808       err = getstr(&config->preproxy, nextarg, DENY_BLANK);
2809       break;
2810     case C_PROXY: /* --proxy */
2811       /* --proxy */
2812       err = getstr(&config->proxy, nextarg, ALLOW_BLANK);
2813       if(config->proxyver != CURLPROXY_HTTPS2)
2814         config->proxyver = CURLPROXY_HTTP;
2815       break;
2816     case C_REQUEST: /* --request */
2817       /* set custom request */
2818       err = getstr(&config->customrequest, nextarg, DENY_BLANK);
2819       break;
2820     case C_SPEED_TIME: /* --speed-time */
2821       /* low speed time */
2822       err = str2unum(&config->low_speed_time, nextarg);
2823       if(!err && !config->low_speed_limit)
2824         config->low_speed_limit = 1;
2825       break;
2826     case C_SPEED_LIMIT: /* --speed-limit */
2827       /* low speed limit */
2828       err = str2unum(&config->low_speed_limit, nextarg);
2829       if(!err && !config->low_speed_time)
2830         config->low_speed_time = 30;
2831       break;
2832     case C_PARALLEL: /* --parallel */
2833       global->parallel = toggle;
2834       break;
2835     case C_PARALLEL_MAX: {  /* --parallel-max */
2836       long val;
2837       err = str2unum(&val, nextarg);
2838       if(err)
2839         break;
2840       if(val > MAX_PARALLEL)
2841         global->parallel_max = MAX_PARALLEL;
2842       else if(val < 1)
2843         global->parallel_max = PARALLEL_DEFAULT;
2844       else
2845         global->parallel_max = (unsigned short)val;
2846       break;
2847     }
2848     case C_PARALLEL_IMMEDIATE:   /* --parallel-immediate */
2849       global->parallel_connect = toggle;
2850       break;
2851     case C_TIME_COND: /* --time-cond */
2852       err = parse_time_cond(global, config, nextarg);
2853       break;
2854     case C_MPTCP: /* --mptcp */
2855       config->mptcp = TRUE;
2856       break;
2857     default: /* unknown flag */
2858       err = PARAM_OPTION_UNKNOWN;
2859       break;
2860     }
2861     a = NULL;
2862     ++nopts; /* processed one option from `flag` input, loop for more */
2863   } while(!longopt && !singleopt && *++parse && !*usedarg && !err);
2864 
2865 error:
2866   if(nextalloc)
2867     free(nextarg);
2868   return err;
2869 }
2870 
parse_args(struct GlobalConfig * global,int argc,argv_item_t argv[])2871 ParameterError parse_args(struct GlobalConfig *global, int argc,
2872                           argv_item_t argv[])
2873 {
2874   int i;
2875   bool stillflags;
2876   char *orig_opt = NULL;
2877   ParameterError result = PARAM_OK;
2878   struct OperationConfig *config = global->first;
2879 
2880   for(i = 1, stillflags = TRUE; i < argc && !result; i++) {
2881     orig_opt = curlx_convert_tchar_to_UTF8(argv[i]);
2882     if(!orig_opt)
2883       return PARAM_NO_MEM;
2884 
2885     if(stillflags && ('-' == orig_opt[0])) {
2886       bool passarg;
2887 
2888       if(!strcmp("--", orig_opt))
2889         /* This indicates the end of the flags and thus enables the
2890            following (URL) argument to start with -. */
2891         stillflags = FALSE;
2892       else {
2893         char *nextarg = NULL;
2894         if(i < (argc - 1)) {
2895           nextarg = curlx_convert_tchar_to_UTF8(argv[i + 1]);
2896           if(!nextarg) {
2897             curlx_unicodefree(orig_opt);
2898             return PARAM_NO_MEM;
2899           }
2900         }
2901 
2902         result = getparameter(orig_opt, nextarg, argv[i + 1], &passarg,
2903                               global, config);
2904 
2905         curlx_unicodefree(nextarg);
2906         config = global->last;
2907         if(result == PARAM_NEXT_OPERATION) {
2908           /* Reset result as PARAM_NEXT_OPERATION is only used here and not
2909              returned from this function */
2910           result = PARAM_OK;
2911 
2912           if(config->url_list && config->url_list->url) {
2913             /* Allocate the next config */
2914             config->next = malloc(sizeof(struct OperationConfig));
2915             if(config->next) {
2916               /* Initialise the newly created config */
2917               config_init(config->next);
2918 
2919               /* Set the global config pointer */
2920               config->next->global = global;
2921 
2922               /* Update the last config pointer */
2923               global->last = config->next;
2924 
2925               /* Move onto the new config */
2926               config->next->prev = config;
2927               config = config->next;
2928             }
2929             else
2930               result = PARAM_NO_MEM;
2931           }
2932           else {
2933             errorf(global, "missing URL before --next");
2934             result = PARAM_BAD_USE;
2935           }
2936         }
2937         else if(!result && passarg)
2938           i++; /* we are supposed to skip this */
2939       }
2940     }
2941     else {
2942       bool used;
2943 
2944       /* Just add the URL please */
2945       result = getparameter("--url", orig_opt, argv[i], &used, global, config);
2946     }
2947 
2948     if(!result)
2949       curlx_unicodefree(orig_opt);
2950   }
2951 
2952   if(!result && config->content_disposition) {
2953     if(config->resume_from_current)
2954       result = PARAM_CONTDISP_RESUME_FROM;
2955   }
2956 
2957   if(result && result != PARAM_HELP_REQUESTED &&
2958      result != PARAM_MANUAL_REQUESTED &&
2959      result != PARAM_VERSION_INFO_REQUESTED &&
2960      result != PARAM_ENGINES_REQUESTED &&
2961      result != PARAM_CA_EMBED_REQUESTED) {
2962     const char *reason = param2text(result);
2963 
2964     if(orig_opt && strcmp(":", orig_opt))
2965       helpf(tool_stderr, "option %s: %s", orig_opt, reason);
2966     else
2967       helpf(tool_stderr, "%s", reason);
2968   }
2969 
2970   curlx_unicodefree(orig_opt);
2971   return result;
2972 }
2973