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