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