Home | History | Annotate | Download | only in src
      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel (at) 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.haxx.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  ***************************************************************************/
     22 #include "tool_setup.h"
     23 
     24 #include "strcase.h"
     25 
     26 #define ENABLE_CURLX_PRINTF
     27 /* use our own printf() functions */
     28 #include "curlx.h"
     29 
     30 #include "tool_binmode.h"
     31 #include "tool_cfgable.h"
     32 #include "tool_cb_prg.h"
     33 #include "tool_convert.h"
     34 #include "tool_filetime.h"
     35 #include "tool_formparse.h"
     36 #include "tool_getparam.h"
     37 #include "tool_helpers.h"
     38 #include "tool_libinfo.h"
     39 #include "tool_metalink.h"
     40 #include "tool_msgs.h"
     41 #include "tool_paramhlp.h"
     42 #include "tool_parsecfg.h"
     43 
     44 #include "memdebug.h" /* keep this as LAST include */
     45 
     46 #ifdef MSDOS
     47 #  define USE_WATT32
     48 #endif
     49 
     50 #define GetStr(str,val) do { \
     51   if(*(str)) { \
     52     free(*(str)); \
     53     *(str) = NULL; \
     54   } \
     55   if((val)) {              \
     56     *(str) = strdup((val)); \
     57     if(!(*(str)))          \
     58       return PARAM_NO_MEM; \
     59   } \
     60 } WHILE_FALSE
     61 
     62 struct LongShort {
     63   const char *letter; /* short name option */
     64   const char *lname;  /* long name option */
     65   enum {
     66     ARG_NONE,   /* stand-alone but not a boolean */
     67     ARG_BOOL,   /* accepts a --no-[name] prefix */
     68     ARG_STRING, /* requires an argument */
     69     ARG_FILENAME /* requires an argument, usually a file name */
     70   } desc;
     71 };
     72 
     73 static const struct LongShort aliases[]= {
     74   /* 'letter' strings with more than one character have *no* short option to
     75      mention. */
     76   {"*@", "url",                      ARG_STRING},
     77   {"*4", "dns-ipv4-addr",            ARG_STRING},
     78   {"*6", "dns-ipv6-addr",            ARG_STRING},
     79   {"*a", "random-file",              ARG_FILENAME},
     80   {"*b", "egd-file",                 ARG_STRING},
     81   {"*B", "oauth2-bearer",            ARG_STRING},
     82   {"*c", "connect-timeout",          ARG_STRING},
     83   {"*C", "doh-url"        ,          ARG_STRING},
     84   {"*d", "ciphers",                  ARG_STRING},
     85   {"*D", "dns-interface",            ARG_STRING},
     86   {"*e", "disable-epsv",             ARG_BOOL},
     87   {"*f", "disallow-username-in-url", ARG_BOOL},
     88   {"*E", "epsv",                     ARG_BOOL},
     89          /* 'epsv' made like this to make --no-epsv and --epsv to work
     90              although --disable-epsv is the documented option */
     91   {"*F", "dns-servers",              ARG_STRING},
     92   {"*g", "trace",                    ARG_FILENAME},
     93   {"*G", "npn",                      ARG_BOOL},
     94   {"*h", "trace-ascii",              ARG_FILENAME},
     95   {"*H", "alpn",                     ARG_BOOL},
     96   {"*i", "limit-rate",               ARG_STRING},
     97   {"*j", "compressed",               ARG_BOOL},
     98   {"*J", "tr-encoding",              ARG_BOOL},
     99   {"*k", "digest",                   ARG_BOOL},
    100   {"*l", "negotiate",                ARG_BOOL},
    101   {"*m", "ntlm",                     ARG_BOOL},
    102   {"*M", "ntlm-wb",                  ARG_BOOL},
    103   {"*n", "basic",                    ARG_BOOL},
    104   {"*o", "anyauth",                  ARG_BOOL},
    105 #ifdef USE_WATT32
    106   {"*p", "wdebug",                   ARG_BOOL},
    107 #endif
    108   {"*q", "ftp-create-dirs",          ARG_BOOL},
    109   {"*r", "create-dirs",              ARG_BOOL},
    110   {"*s", "max-redirs",               ARG_STRING},
    111   {"*t", "proxy-ntlm",               ARG_BOOL},
    112   {"*u", "crlf",                     ARG_BOOL},
    113   {"*v", "stderr",                   ARG_FILENAME},
    114   {"*w", "interface",                ARG_STRING},
    115   {"*x", "krb",                      ARG_STRING},
    116   {"*x", "krb4",                     ARG_STRING},
    117          /* 'krb4' is the previous name */
    118   {"*X", "haproxy-protocol",         ARG_BOOL},
    119   {"*y", "max-filesize",             ARG_STRING},
    120   {"*z", "disable-eprt",             ARG_BOOL},
    121   {"*Z", "eprt",                     ARG_BOOL},
    122          /* 'eprt' made like this to make --no-eprt and --eprt to work
    123              although --disable-eprt is the documented option */
    124   {"*~", "xattr",                    ARG_BOOL},
    125   {"$a", "ftp-ssl",                  ARG_BOOL},
    126          /* 'ftp-ssl' deprecated name since 7.20.0 */
    127   {"$a", "ssl",                      ARG_BOOL},
    128          /* 'ssl' new option name in 7.20.0, previously this was ftp-ssl */
    129   {"$b", "ftp-pasv",                 ARG_BOOL},
    130   {"$c", "socks5",                   ARG_STRING},
    131   {"$d", "tcp-nodelay",              ARG_BOOL},
    132   {"$e", "proxy-digest",             ARG_BOOL},
    133   {"$f", "proxy-basic",              ARG_BOOL},
    134   {"$g", "retry",                    ARG_STRING},
    135   {"$V", "retry-connrefused",        ARG_BOOL},
    136   {"$h", "retry-delay",              ARG_STRING},
    137   {"$i", "retry-max-time",           ARG_STRING},
    138   {"$k", "proxy-negotiate",          ARG_BOOL},
    139   {"$m", "ftp-account",              ARG_STRING},
    140   {"$n", "proxy-anyauth",            ARG_BOOL},
    141   {"$o", "trace-time",               ARG_BOOL},
    142   {"$p", "ignore-content-length",    ARG_BOOL},
    143   {"$q", "ftp-skip-pasv-ip",         ARG_BOOL},
    144   {"$r", "ftp-method",               ARG_STRING},
    145   {"$s", "local-port",               ARG_STRING},
    146   {"$t", "socks4",                   ARG_STRING},
    147   {"$T", "socks4a",                  ARG_STRING},
    148   {"$u", "ftp-alternative-to-user",  ARG_STRING},
    149   {"$v", "ftp-ssl-reqd",             ARG_BOOL},
    150          /* 'ftp-ssl-reqd' deprecated name since 7.20.0 */
    151   {"$v", "ssl-reqd",                 ARG_BOOL},
    152          /* 'ssl-reqd' new in 7.20.0, previously this was ftp-ssl-reqd */
    153   {"$w", "sessionid",                ARG_BOOL},
    154          /* 'sessionid' listed as --no-sessionid in the help */
    155   {"$x", "ftp-ssl-control",          ARG_BOOL},
    156   {"$y", "ftp-ssl-ccc",              ARG_BOOL},
    157   {"$j", "ftp-ssl-ccc-mode",         ARG_STRING},
    158   {"$z", "libcurl",                  ARG_STRING},
    159   {"$#", "raw",                      ARG_BOOL},
    160   {"$0", "post301",                  ARG_BOOL},
    161   {"$1", "keepalive",                ARG_BOOL},
    162          /* 'keepalive' listed as --no-keepalive in the help */
    163   {"$2", "socks5-hostname",          ARG_STRING},
    164   {"$3", "keepalive-time",           ARG_STRING},
    165   {"$4", "post302",                  ARG_BOOL},
    166   {"$5", "noproxy",                  ARG_STRING},
    167   {"$7", "socks5-gssapi-nec",        ARG_BOOL},
    168   {"$8", "proxy1.0",                 ARG_STRING},
    169   {"$9", "tftp-blksize",             ARG_STRING},
    170   {"$A", "mail-from",                ARG_STRING},
    171   {"$B", "mail-rcpt",                ARG_STRING},
    172   {"$C", "ftp-pret",                 ARG_BOOL},
    173   {"$D", "proto",                    ARG_STRING},
    174   {"$E", "proto-redir",              ARG_STRING},
    175   {"$F", "resolve",                  ARG_STRING},
    176   {"$G", "delegation",               ARG_STRING},
    177   {"$H", "mail-auth",                ARG_STRING},
    178   {"$I", "post303",                  ARG_BOOL},
    179   {"$J", "metalink",                 ARG_BOOL},
    180   {"$K", "sasl-ir",                  ARG_BOOL},
    181   {"$L", "test-event",               ARG_BOOL},
    182   {"$M", "unix-socket",              ARG_FILENAME},
    183   {"$N", "path-as-is",               ARG_BOOL},
    184   {"$O", "socks5-gssapi-service",    ARG_STRING},
    185          /* 'socks5-gssapi-service' merged with'proxy-service-name' and
    186             deprecated since 7.49.0 */
    187   {"$O", "proxy-service-name",       ARG_STRING},
    188   {"$P", "service-name",             ARG_STRING},
    189   {"$Q", "proto-default",            ARG_STRING},
    190   {"$R", "expect100-timeout",        ARG_STRING},
    191   {"$S", "tftp-no-options",          ARG_BOOL},
    192   {"$U", "connect-to",               ARG_STRING},
    193   {"$W", "abstract-unix-socket",     ARG_FILENAME},
    194   {"$X", "tls-max",                  ARG_STRING},
    195   {"$Y", "suppress-connect-headers", ARG_BOOL},
    196   {"$Z", "compressed-ssh",           ARG_BOOL},
    197   {"$~", "happy-eyeballs-timeout-ms", ARG_STRING},
    198   {"0",   "http1.0",                 ARG_NONE},
    199   {"01",  "http1.1",                 ARG_NONE},
    200   {"02",  "http2",                   ARG_NONE},
    201   {"03",  "http2-prior-knowledge",   ARG_NONE},
    202   {"09",  "http0.9",                 ARG_BOOL},
    203   {"1",  "tlsv1",                    ARG_NONE},
    204   {"10",  "tlsv1.0",                 ARG_NONE},
    205   {"11",  "tlsv1.1",                 ARG_NONE},
    206   {"12",  "tlsv1.2",                 ARG_NONE},
    207   {"13",  "tlsv1.3",                 ARG_NONE},
    208   {"1A", "tls13-ciphers",            ARG_STRING},
    209   {"1B", "proxy-tls13-ciphers",      ARG_STRING},
    210   {"2",  "sslv2",                    ARG_NONE},
    211   {"3",  "sslv3",                    ARG_NONE},
    212   {"4",  "ipv4",                     ARG_NONE},
    213   {"6",  "ipv6",                     ARG_NONE},
    214   {"a",  "append",                   ARG_BOOL},
    215   {"A",  "user-agent",               ARG_STRING},
    216   {"b",  "cookie",                   ARG_STRING},
    217   {"ba", "alt-svc",                  ARG_STRING},
    218   {"B",  "use-ascii",                ARG_BOOL},
    219   {"c",  "cookie-jar",               ARG_STRING},
    220   {"C",  "continue-at",              ARG_STRING},
    221   {"d",  "data",                     ARG_STRING},
    222   {"dr", "data-raw",                 ARG_STRING},
    223   {"da", "data-ascii",               ARG_STRING},
    224   {"db", "data-binary",              ARG_STRING},
    225   {"de", "data-urlencode",           ARG_STRING},
    226   {"D",  "dump-header",              ARG_FILENAME},
    227   {"e",  "referer",                  ARG_STRING},
    228   {"E",  "cert",                     ARG_FILENAME},
    229   {"Ea", "cacert",                   ARG_FILENAME},
    230   {"Eb", "cert-type",                ARG_STRING},
    231   {"Ec", "key",                      ARG_FILENAME},
    232   {"Ed", "key-type",                 ARG_STRING},
    233   {"Ee", "pass",                     ARG_STRING},
    234   {"Ef", "engine",                   ARG_STRING},
    235   {"Eg", "capath",                   ARG_FILENAME},
    236   {"Eh", "pubkey",                   ARG_STRING},
    237   {"Ei", "hostpubmd5",               ARG_STRING},
    238   {"Ej", "crlfile",                  ARG_FILENAME},
    239   {"Ek", "tlsuser",                  ARG_STRING},
    240   {"El", "tlspassword",              ARG_STRING},
    241   {"Em", "tlsauthtype",              ARG_STRING},
    242   {"En", "ssl-allow-beast",          ARG_BOOL},
    243   {"Eo", "login-options",            ARG_STRING},
    244   {"Ep", "pinnedpubkey",             ARG_STRING},
    245   {"EP", "proxy-pinnedpubkey",       ARG_STRING},
    246   {"Eq", "cert-status",              ARG_BOOL},
    247   {"Er", "false-start",              ARG_BOOL},
    248   {"Es", "ssl-no-revoke",            ARG_BOOL},
    249   {"Et", "tcp-fastopen",             ARG_BOOL},
    250   {"Eu", "proxy-tlsuser",            ARG_STRING},
    251   {"Ev", "proxy-tlspassword",        ARG_STRING},
    252   {"Ew", "proxy-tlsauthtype",        ARG_STRING},
    253   {"Ex", "proxy-cert",               ARG_FILENAME},
    254   {"Ey", "proxy-cert-type",          ARG_STRING},
    255   {"Ez", "proxy-key",                ARG_FILENAME},
    256   {"E0", "proxy-key-type",           ARG_STRING},
    257   {"E1", "proxy-pass",               ARG_STRING},
    258   {"E2", "proxy-ciphers",            ARG_STRING},
    259   {"E3", "proxy-crlfile",            ARG_FILENAME},
    260   {"E4", "proxy-ssl-allow-beast",    ARG_BOOL},
    261   {"E5", "login-options",            ARG_STRING},
    262   {"E6", "proxy-cacert",             ARG_FILENAME},
    263   {"E7", "proxy-capath",             ARG_FILENAME},
    264   {"E8", "proxy-insecure",           ARG_BOOL},
    265   {"E9", "proxy-tlsv1",              ARG_NONE},
    266   {"EA", "socks5-basic",             ARG_BOOL},
    267   {"EB", "socks5-gssapi",            ARG_BOOL},
    268   {"f",  "fail",                     ARG_BOOL},
    269   {"fa", "fail-early",               ARG_BOOL},
    270   {"fb", "styled-output",            ARG_BOOL},
    271   {"F",  "form",                     ARG_STRING},
    272   {"Fs", "form-string",              ARG_STRING},
    273   {"g",  "globoff",                  ARG_BOOL},
    274   {"G",  "get",                      ARG_NONE},
    275   {"Ga", "request-target",           ARG_STRING},
    276   {"h",  "help",                     ARG_BOOL},
    277   {"H",  "header",                   ARG_STRING},
    278   {"Hp", "proxy-header",             ARG_STRING},
    279   {"i",  "include",                  ARG_BOOL},
    280   {"I",  "head",                     ARG_BOOL},
    281   {"j",  "junk-session-cookies",     ARG_BOOL},
    282   {"J",  "remote-header-name",       ARG_BOOL},
    283   {"k",  "insecure",                 ARG_BOOL},
    284   {"K",  "config",                   ARG_FILENAME},
    285   {"l",  "list-only",                ARG_BOOL},
    286   {"L",  "location",                 ARG_BOOL},
    287   {"Lt", "location-trusted",         ARG_BOOL},
    288   {"m",  "max-time",                 ARG_STRING},
    289   {"M",  "manual",                   ARG_BOOL},
    290   {"n",  "netrc",                    ARG_BOOL},
    291   {"no", "netrc-optional",           ARG_BOOL},
    292   {"ne", "netrc-file",               ARG_FILENAME},
    293   {"N",  "buffer",                   ARG_BOOL},
    294          /* 'buffer' listed as --no-buffer in the help */
    295   {"o",  "output",                   ARG_FILENAME},
    296   {"O",  "remote-name",              ARG_NONE},
    297   {"Oa", "remote-name-all",          ARG_BOOL},
    298   {"p",  "proxytunnel",              ARG_BOOL},
    299   {"P",  "ftp-port",                 ARG_STRING},
    300   {"q",  "disable",                  ARG_BOOL},
    301   {"Q",  "quote",                    ARG_STRING},
    302   {"r",  "range",                    ARG_STRING},
    303   {"R",  "remote-time",              ARG_BOOL},
    304   {"s",  "silent",                   ARG_BOOL},
    305   {"S",  "show-error",               ARG_BOOL},
    306   {"t",  "telnet-option",            ARG_STRING},
    307   {"T",  "upload-file",              ARG_FILENAME},
    308   {"u",  "user",                     ARG_STRING},
    309   {"U",  "proxy-user",               ARG_STRING},
    310   {"v",  "verbose",                  ARG_BOOL},
    311   {"V",  "version",                  ARG_BOOL},
    312   {"w",  "write-out",                ARG_STRING},
    313   {"x",  "proxy",                    ARG_STRING},
    314   {"xa", "preproxy",                 ARG_STRING},
    315   {"X",  "request",                  ARG_STRING},
    316   {"Y",  "speed-limit",              ARG_STRING},
    317   {"y",  "speed-time",               ARG_STRING},
    318   {"z",  "time-cond",                ARG_STRING},
    319   {"#",  "progress-bar",             ARG_BOOL},
    320   {":",  "next",                     ARG_NONE},
    321 };
    322 
    323 /* Split the argument of -E to 'certname' and 'passphrase' separated by colon.
    324  * We allow ':' and '\' to be escaped by '\' so that we can use certificate
    325  * nicknames containing ':'.  See <https://sourceforge.net/p/curl/bugs/1196/>
    326  * for details. */
    327 #ifndef UNITTESTS
    328 static
    329 #endif
    330 void parse_cert_parameter(const char *cert_parameter,
    331                           char **certname,
    332                           char **passphrase)
    333 {
    334   size_t param_length = strlen(cert_parameter);
    335   size_t span;
    336   const char *param_place = NULL;
    337   char *certname_place = NULL;
    338   *certname = NULL;
    339   *passphrase = NULL;
    340 
    341   /* most trivial assumption: cert_parameter is empty */
    342   if(param_length == 0)
    343     return;
    344 
    345   /* next less trivial: cert_parameter starts 'pkcs11:' and thus
    346    * looks like a RFC7512 PKCS#11 URI which can be used as-is.
    347    * Also if cert_parameter contains no colon nor backslash, this
    348    * means no passphrase was given and no characters escaped */
    349   if(curl_strnequal(cert_parameter, "pkcs11:", 7) ||
    350      !strpbrk(cert_parameter, ":\\")) {
    351     *certname = strdup(cert_parameter);
    352     return;
    353   }
    354   /* deal with escaped chars; find unescaped colon if it exists */
    355   certname_place = malloc(param_length + 1);
    356   if(!certname_place)
    357     return;
    358 
    359   *certname = certname_place;
    360   param_place = cert_parameter;
    361   while(*param_place) {
    362     span = strcspn(param_place, ":\\");
    363     strncpy(certname_place, param_place, span);
    364     param_place += span;
    365     certname_place += span;
    366     /* we just ate all the non-special chars. now we're on either a special
    367      * char or the end of the string. */
    368     switch(*param_place) {
    369     case '\0':
    370       break;
    371     case '\\':
    372       param_place++;
    373       switch(*param_place) {
    374         case '\0':
    375           *certname_place++ = '\\';
    376           break;
    377         case '\\':
    378           *certname_place++ = '\\';
    379           param_place++;
    380           break;
    381         case ':':
    382           *certname_place++ = ':';
    383           param_place++;
    384           break;
    385         default:
    386           *certname_place++ = '\\';
    387           *certname_place++ = *param_place;
    388           param_place++;
    389           break;
    390       }
    391       break;
    392     case ':':
    393       /* Since we live in a world of weirdness and confusion, the win32
    394          dudes can use : when using drive letters and thus c:\file:password
    395          needs to work. In order not to break compatibility, we still use : as
    396          separator, but we try to detect when it is used for a file name! On
    397          windows. */
    398 #ifdef WIN32
    399       if(param_place &&
    400           (param_place == &cert_parameter[1]) &&
    401           (cert_parameter[2] == '\\' || cert_parameter[2] == '/') &&
    402           (ISALPHA(cert_parameter[0])) ) {
    403         /* colon in the second column, followed by a backslash, and the
    404            first character is an alphabetic letter:
    405 
    406            this is a drive letter colon */
    407         *certname_place++ = ':';
    408         param_place++;
    409         break;
    410       }
    411 #endif
    412       /* escaped colons and Windows drive letter colons were handled
    413        * above; if we're still here, this is a separating colon */
    414       param_place++;
    415       if(strlen(param_place) > 0) {
    416         *passphrase = strdup(param_place);
    417       }
    418       goto done;
    419     }
    420   }
    421 done:
    422   *certname_place = '\0';
    423 }
    424 
    425 static void
    426 GetFileAndPassword(char *nextarg, char **file, char **password)
    427 {
    428   char *certname, *passphrase;
    429   parse_cert_parameter(nextarg, &certname, &passphrase);
    430   Curl_safefree(*file);
    431   *file = certname;
    432   if(passphrase) {
    433     Curl_safefree(*password);
    434     *password = passphrase;
    435   }
    436   cleanarg(nextarg);
    437 }
    438 
    439 /* Get a size parameter for '--limit-rate' or '--max-filesize'.
    440  * We support a 'G', 'M' or 'K' suffix too.
    441   */
    442 static ParameterError GetSizeParameter(struct GlobalConfig *global,
    443                                        const char *arg,
    444                                        const char *which,
    445                                        curl_off_t *value_out)
    446 {
    447   char *unit;
    448   curl_off_t value;
    449 
    450   if(curlx_strtoofft(arg, &unit, 0, &value)) {
    451     warnf(global, "invalid number specified for %s\n", which);
    452     return PARAM_BAD_USE;
    453   }
    454 
    455   if(!*unit)
    456     unit = (char *)"b";
    457   else if(strlen(unit) > 1)
    458     unit = (char *)"w"; /* unsupported */
    459 
    460   switch(*unit) {
    461   case 'G':
    462   case 'g':
    463     if(value > (CURL_OFF_T_MAX / (1024*1024*1024)))
    464       return PARAM_NUMBER_TOO_LARGE;
    465     value *= 1024*1024*1024;
    466     break;
    467   case 'M':
    468   case 'm':
    469     if(value > (CURL_OFF_T_MAX / (1024*1024)))
    470       return PARAM_NUMBER_TOO_LARGE;
    471     value *= 1024*1024;
    472     break;
    473   case 'K':
    474   case 'k':
    475     if(value > (CURL_OFF_T_MAX / 1024))
    476       return PARAM_NUMBER_TOO_LARGE;
    477     value *= 1024;
    478     break;
    479   case 'b':
    480   case 'B':
    481     /* for plain bytes, leave as-is */
    482     break;
    483   default:
    484     warnf(global, "unsupported %s unit. Use G, M, K or B!\n", which);
    485     return PARAM_BAD_USE;
    486   }
    487   *value_out = value;
    488   return PARAM_OK;
    489 }
    490 
    491 ParameterError getparameter(const char *flag, /* f or -long-flag */
    492                             char *nextarg,    /* NULL if unset */
    493                             bool *usedarg,    /* set to TRUE if the arg
    494                                                  has been used */
    495                             struct GlobalConfig *global,
    496                             struct OperationConfig *config)
    497 {
    498   char letter;
    499   char subletter = '\0'; /* subletters can only occur on long options */
    500   int rc;
    501   const char *parse = NULL;
    502   unsigned int j;
    503   time_t now;
    504   int hit = -1;
    505   bool longopt = FALSE;
    506   bool singleopt = FALSE; /* when true means '-o foo' used '-ofoo' */
    507   ParameterError err;
    508   bool toggle = TRUE; /* how to switch boolean options, on or off. Controlled
    509                          by using --OPTION or --no-OPTION */
    510 
    511   *usedarg = FALSE; /* default is that we don't use the arg */
    512 
    513   if(('-' != flag[0]) || ('-' == flag[1])) {
    514     /* this should be a long name */
    515     const char *word = ('-' == flag[0]) ? flag + 2 : flag;
    516     size_t fnam = strlen(word);
    517     int numhits = 0;
    518 
    519     if(!strncmp(word, "no-", 3)) {
    520       /* disable this option but ignore the "no-" part when looking for it */
    521       word += 3;
    522       toggle = FALSE;
    523     }
    524 
    525     for(j = 0; j < sizeof(aliases)/sizeof(aliases[0]); j++) {
    526       if(curl_strnequal(aliases[j].lname, word, fnam)) {
    527         longopt = TRUE;
    528         numhits++;
    529         if(curl_strequal(aliases[j].lname, word)) {
    530           parse = aliases[j].letter;
    531           hit = j;
    532           numhits = 1; /* a single unique hit */
    533           break;
    534         }
    535         parse = aliases[j].letter;
    536         hit = j;
    537       }
    538     }
    539     if(numhits > 1) {
    540       /* this is at least the second match! */
    541       return PARAM_OPTION_AMBIGUOUS;
    542     }
    543     if(hit < 0) {
    544       return PARAM_OPTION_UNKNOWN;
    545     }
    546   }
    547   else {
    548     flag++; /* prefixed with one dash, pass it */
    549     hit = -1;
    550     parse = flag;
    551   }
    552 
    553   do {
    554     /* we can loop here if we have multiple single-letters */
    555 
    556     if(!longopt) {
    557       letter = (char)*parse;
    558       subletter = '\0';
    559     }
    560     else {
    561       letter = parse[0];
    562       subletter = parse[1];
    563     }
    564 
    565     if(hit < 0) {
    566       for(j = 0; j < sizeof(aliases)/sizeof(aliases[0]); j++) {
    567         if(letter == aliases[j].letter[0]) {
    568           hit = j;
    569           break;
    570         }
    571       }
    572       if(hit < 0) {
    573         return PARAM_OPTION_UNKNOWN;
    574       }
    575     }
    576 
    577     if(aliases[hit].desc >= ARG_STRING) {
    578       /* this option requires an extra parameter */
    579       if(!longopt && parse[1]) {
    580         nextarg = (char *)&parse[1]; /* this is the actual extra parameter */
    581         singleopt = TRUE;   /* don't loop anymore after this */
    582       }
    583       else if(!nextarg)
    584         return PARAM_REQUIRES_PARAMETER;
    585       else
    586         *usedarg = TRUE; /* mark it as used */
    587 
    588       if((aliases[hit].desc == ARG_FILENAME) &&
    589          (nextarg[0] == '-') && nextarg[1]) {
    590         /* if the file name looks like a command line option */
    591         warnf(global, "The file name argument '%s' looks like a flag.\n",
    592               nextarg);
    593       }
    594     }
    595     else if((aliases[hit].desc == ARG_NONE) && !toggle)
    596       return PARAM_NO_PREFIX;
    597 
    598     switch(letter) {
    599     case '*': /* options without a short option */
    600       switch(subletter) {
    601       case '4': /* --dns-ipv4-addr */
    602         /* addr in dot notation */
    603         GetStr(&config->dns_ipv4_addr, nextarg);
    604         break;
    605       case '6': /* --dns-ipv6-addr */
    606         /* addr in dot notation */
    607         GetStr(&config->dns_ipv6_addr, nextarg);
    608         break;
    609       case 'a': /* random-file */
    610         GetStr(&config->random_file, nextarg);
    611         break;
    612       case 'b': /* egd-file */
    613         GetStr(&config->egd_file, nextarg);
    614         break;
    615       case 'B': /* OAuth 2.0 bearer token */
    616         GetStr(&config->oauth_bearer, nextarg);
    617         config->authtype |= CURLAUTH_BEARER;
    618         break;
    619       case 'c': /* connect-timeout */
    620         err = str2udouble(&config->connecttimeout, nextarg,
    621                           LONG_MAX/1000);
    622         if(err)
    623           return err;
    624         break;
    625       case 'C': /* doh-url */
    626         GetStr(&config->doh_url, nextarg);
    627         break;
    628       case 'd': /* ciphers */
    629         GetStr(&config->cipher_list, nextarg);
    630         break;
    631       case 'D': /* --dns-interface */
    632         /* interface name */
    633         GetStr(&config->dns_interface, nextarg);
    634         break;
    635       case 'e': /* --disable-epsv */
    636         config->disable_epsv = toggle;
    637         break;
    638       case 'f': /* --disallow-username-in-url */
    639         config->disallow_username_in_url = toggle;
    640         break;
    641       case 'E': /* --epsv */
    642         config->disable_epsv = (!toggle)?TRUE:FALSE;
    643         break;
    644       case 'F': /* --dns-servers */
    645         /* IP addrs of DNS servers */
    646         GetStr(&config->dns_servers, nextarg);
    647         break;
    648       case 'g': /* --trace */
    649         GetStr(&global->trace_dump, nextarg);
    650         if(global->tracetype && (global->tracetype != TRACE_BIN))
    651           warnf(global, "--trace overrides an earlier trace/verbose option\n");
    652         global->tracetype = TRACE_BIN;
    653         break;
    654       case 'G': /* --npn */
    655         config->nonpn = (!toggle)?TRUE:FALSE;
    656         break;
    657       case 'h': /* --trace-ascii */
    658         GetStr(&global->trace_dump, nextarg);
    659         if(global->tracetype && (global->tracetype != TRACE_ASCII))
    660           warnf(global,
    661                 "--trace-ascii overrides an earlier trace/verbose option\n");
    662         global->tracetype = TRACE_ASCII;
    663         break;
    664       case 'H': /* --alpn */
    665         config->noalpn = (!toggle)?TRUE:FALSE;
    666         break;
    667       case 'i': /* --limit-rate */
    668       {
    669         curl_off_t value;
    670         ParameterError pe = GetSizeParameter(global, nextarg, "rate", &value);
    671 
    672         if(pe != PARAM_OK)
    673            return pe;
    674         config->recvpersecond = value;
    675         config->sendpersecond = value;
    676       }
    677       break;
    678 
    679       case 'j': /* --compressed */
    680         if(toggle &&
    681            !(curlinfo->features & (CURL_VERSION_LIBZ | CURL_VERSION_BROTLI)))
    682           return PARAM_LIBCURL_DOESNT_SUPPORT;
    683         config->encoding = toggle;
    684         break;
    685 
    686       case 'J': /* --tr-encoding */
    687         config->tr_encoding = toggle;
    688         break;
    689 
    690       case 'k': /* --digest */
    691         if(toggle)
    692           config->authtype |= CURLAUTH_DIGEST;
    693         else
    694           config->authtype &= ~CURLAUTH_DIGEST;
    695         break;
    696 
    697       case 'l': /* --negotiate */
    698         if(toggle) {
    699           if(curlinfo->features & CURL_VERSION_SPNEGO)
    700             config->authtype |= CURLAUTH_NEGOTIATE;
    701           else
    702             return PARAM_LIBCURL_DOESNT_SUPPORT;
    703         }
    704         else
    705           config->authtype &= ~CURLAUTH_NEGOTIATE;
    706         break;
    707 
    708       case 'm': /* --ntlm */
    709         if(toggle) {
    710           if(curlinfo->features & CURL_VERSION_NTLM)
    711             config->authtype |= CURLAUTH_NTLM;
    712           else
    713             return PARAM_LIBCURL_DOESNT_SUPPORT;
    714         }
    715         else
    716           config->authtype &= ~CURLAUTH_NTLM;
    717         break;
    718 
    719       case 'M': /* --ntlm-wb */
    720         if(toggle) {
    721           if(curlinfo->features & CURL_VERSION_NTLM_WB)
    722             config->authtype |= CURLAUTH_NTLM_WB;
    723           else
    724             return PARAM_LIBCURL_DOESNT_SUPPORT;
    725         }
    726         else
    727           config->authtype &= ~CURLAUTH_NTLM_WB;
    728         break;
    729 
    730       case 'n': /* --basic for completeness */
    731         if(toggle)
    732           config->authtype |= CURLAUTH_BASIC;
    733         else
    734           config->authtype &= ~CURLAUTH_BASIC;
    735         break;
    736 
    737       case 'o': /* --anyauth, let libcurl pick it */
    738         if(toggle)
    739           config->authtype = CURLAUTH_ANY;
    740         /* --no-anyauth simply doesn't touch it */
    741         break;
    742 
    743 #ifdef USE_WATT32
    744       case 'p': /* --wdebug */
    745         dbug_init();
    746         break;
    747 #endif
    748       case 'q': /* --ftp-create-dirs */
    749         config->ftp_create_dirs = toggle;
    750         break;
    751 
    752       case 'r': /* --create-dirs */
    753         config->create_dirs = toggle;
    754         break;
    755 
    756       case 's': /* --max-redirs */
    757         /* specified max no of redirects (http(s)), this accepts -1 as a
    758            special condition */
    759         err = str2num(&config->maxredirs, nextarg);
    760         if(err)
    761           return err;
    762         if(config->maxredirs < -1)
    763           return PARAM_BAD_NUMERIC;
    764         break;
    765 
    766       case 't': /* --proxy-ntlm */
    767         if(curlinfo->features & CURL_VERSION_NTLM)
    768           config->proxyntlm = toggle;
    769         else
    770           return PARAM_LIBCURL_DOESNT_SUPPORT;
    771         break;
    772 
    773       case 'u': /* --crlf */
    774         /* LF -> CRLF conversion? */
    775         config->crlf = toggle;
    776         break;
    777 
    778       case 'v': /* --stderr */
    779         if(strcmp(nextarg, "-")) {
    780           FILE *newfile = fopen(nextarg, FOPEN_WRITETEXT);
    781           if(!newfile)
    782             warnf(global, "Failed to open %s!\n", nextarg);
    783           else {
    784             if(global->errors_fopened)
    785               fclose(global->errors);
    786             global->errors = newfile;
    787             global->errors_fopened = TRUE;
    788           }
    789         }
    790         else
    791           global->errors = stdout;
    792         break;
    793       case 'w': /* --interface */
    794         /* interface */
    795         GetStr(&config->iface, nextarg);
    796         break;
    797       case 'x': /* --krb */
    798         /* kerberos level string */
    799         if(curlinfo->features & CURL_VERSION_KERBEROS4)
    800           GetStr(&config->krblevel, nextarg);
    801         else
    802           return PARAM_LIBCURL_DOESNT_SUPPORT;
    803         break;
    804       case 'X': /* --haproxy-protocol */
    805         config->haproxy_protocol = toggle;
    806         break;
    807       case 'y': /* --max-filesize */
    808         {
    809           curl_off_t value;
    810           ParameterError pe =
    811             GetSizeParameter(global, nextarg, "max-filesize", &value);
    812 
    813           if(pe != PARAM_OK)
    814              return pe;
    815           config->max_filesize = value;
    816         }
    817         break;
    818       case 'z': /* --disable-eprt */
    819         config->disable_eprt = toggle;
    820         break;
    821       case 'Z': /* --eprt */
    822         config->disable_eprt = (!toggle)?TRUE:FALSE;
    823         break;
    824       case '~': /* --xattr */
    825         config->xattr = toggle;
    826         break;
    827       case '@': /* the URL! */
    828       {
    829         struct getout *url;
    830 
    831         if(!config->url_get)
    832           config->url_get = config->url_list;
    833 
    834         if(config->url_get) {
    835           /* there's a node here, if it already is filled-in continue to find
    836              an "empty" node */
    837           while(config->url_get && (config->url_get->flags & GETOUT_URL))
    838             config->url_get = config->url_get->next;
    839         }
    840 
    841         /* now there might or might not be an available node to fill in! */
    842 
    843         if(config->url_get)
    844           /* existing node */
    845           url = config->url_get;
    846         else
    847           /* there was no free node, create one! */
    848           config->url_get = url = new_getout(config);
    849 
    850         if(!url)
    851           return PARAM_NO_MEM;
    852 
    853         /* fill in the URL */
    854         GetStr(&url->url, nextarg);
    855         url->flags |= GETOUT_URL;
    856       }
    857       }
    858       break;
    859     case '$': /* more options without a short option */
    860       switch(subletter) {
    861       case 'a': /* --ssl */
    862         if(toggle && !(curlinfo->features & CURL_VERSION_SSL))
    863           return PARAM_LIBCURL_DOESNT_SUPPORT;
    864         config->ftp_ssl = toggle;
    865         break;
    866       case 'b': /* --ftp-pasv */
    867         Curl_safefree(config->ftpport);
    868         break;
    869       case 'c': /* --socks5 specifies a socks5 proxy to use, and resolves
    870                    the name locally and passes on the resolved address */
    871         GetStr(&config->proxy, nextarg);
    872         config->proxyver = CURLPROXY_SOCKS5;
    873         break;
    874       case 't': /* --socks4 specifies a socks4 proxy to use */
    875         GetStr(&config->proxy, nextarg);
    876         config->proxyver = CURLPROXY_SOCKS4;
    877         break;
    878       case 'T': /* --socks4a specifies a socks4a proxy to use */
    879         GetStr(&config->proxy, nextarg);
    880         config->proxyver = CURLPROXY_SOCKS4A;
    881         break;
    882       case '2': /* --socks5-hostname specifies a socks5 proxy and enables name
    883                    resolving with the proxy */
    884         GetStr(&config->proxy, nextarg);
    885         config->proxyver = CURLPROXY_SOCKS5_HOSTNAME;
    886         break;
    887       case 'd': /* --tcp-nodelay option */
    888         config->tcp_nodelay = toggle;
    889         break;
    890       case 'e': /* --proxy-digest */
    891         config->proxydigest = toggle;
    892         break;
    893       case 'f': /* --proxy-basic */
    894         config->proxybasic = toggle;
    895         break;
    896       case 'g': /* --retry */
    897         err = str2unum(&config->req_retry, nextarg);
    898         if(err)
    899           return err;
    900         break;
    901       case 'V': /* --retry-connrefused */
    902         config->retry_connrefused = toggle;
    903         break;
    904       case 'h': /* --retry-delay */
    905         err = str2unum(&config->retry_delay, nextarg);
    906         if(err)
    907           return err;
    908         break;
    909       case 'i': /* --retry-max-time */
    910         err = str2unum(&config->retry_maxtime, nextarg);
    911         if(err)
    912           return err;
    913         break;
    914 
    915       case 'k': /* --proxy-negotiate */
    916         if(curlinfo->features & CURL_VERSION_SPNEGO)
    917           config->proxynegotiate = toggle;
    918         else
    919           return PARAM_LIBCURL_DOESNT_SUPPORT;
    920         break;
    921 
    922       case 'm': /* --ftp-account */
    923         GetStr(&config->ftp_account, nextarg);
    924         break;
    925       case 'n': /* --proxy-anyauth */
    926         config->proxyanyauth = toggle;
    927         break;
    928       case 'o': /* --trace-time */
    929         global->tracetime = toggle;
    930         break;
    931       case 'p': /* --ignore-content-length */
    932         config->ignorecl = toggle;
    933         break;
    934       case 'q': /* --ftp-skip-pasv-ip */
    935         config->ftp_skip_ip = toggle;
    936         break;
    937       case 'r': /* --ftp-method (undocumented at this point) */
    938         config->ftp_filemethod = ftpfilemethod(config, nextarg);
    939         break;
    940       case 's': { /* --local-port */
    941         char lrange[7];  /* 16bit base 10 is 5 digits, but we allow 6 so that
    942                             this catches overflows, not just truncates */
    943         char *p = nextarg;
    944         while(ISDIGIT(*p))
    945           p++;
    946         if(*p) {
    947           /* if there's anything more than a plain decimal number */
    948           rc = sscanf(p, " - %6s", lrange);
    949           *p = 0; /* zero terminate to make str2unum() work below */
    950         }
    951         else
    952           rc = 0;
    953 
    954         err = str2unum(&config->localport, nextarg);
    955         if(err || (config->localport > 65535))
    956           return PARAM_BAD_USE;
    957         if(!rc)
    958           config->localportrange = 1; /* default number of ports to try */
    959         else {
    960           err = str2unum(&config->localportrange, lrange);
    961           if(err || (config->localportrange > 65535))
    962             return PARAM_BAD_USE;
    963           config->localportrange -= (config->localport-1);
    964           if(config->localportrange < 1)
    965             return PARAM_BAD_USE;
    966         }
    967         break;
    968       }
    969       case 'u': /* --ftp-alternative-to-user */
    970         GetStr(&config->ftp_alternative_to_user, nextarg);
    971         break;
    972       case 'v': /* --ssl-reqd */
    973         if(toggle && !(curlinfo->features & CURL_VERSION_SSL))
    974           return PARAM_LIBCURL_DOESNT_SUPPORT;
    975         config->ftp_ssl_reqd = toggle;
    976         break;
    977       case 'w': /* --no-sessionid */
    978         config->disable_sessionid = (!toggle)?TRUE:FALSE;
    979         break;
    980       case 'x': /* --ftp-ssl-control */
    981         if(toggle && !(curlinfo->features & CURL_VERSION_SSL))
    982           return PARAM_LIBCURL_DOESNT_SUPPORT;
    983         config->ftp_ssl_control = toggle;
    984         break;
    985       case 'y': /* --ftp-ssl-ccc */
    986         config->ftp_ssl_ccc = toggle;
    987         if(!config->ftp_ssl_ccc_mode)
    988           config->ftp_ssl_ccc_mode = CURLFTPSSL_CCC_PASSIVE;
    989         break;
    990       case 'j': /* --ftp-ssl-ccc-mode */
    991         config->ftp_ssl_ccc = TRUE;
    992         config->ftp_ssl_ccc_mode = ftpcccmethod(config, nextarg);
    993         break;
    994       case 'z': /* --libcurl */
    995 #ifdef CURL_DISABLE_LIBCURL_OPTION
    996         warnf(global,
    997               "--libcurl option was disabled at build-time!\n");
    998         return PARAM_OPTION_UNKNOWN;
    999 #else
   1000         GetStr(&global->libcurl, nextarg);
   1001         break;
   1002 #endif
   1003       case '#': /* --raw */
   1004         config->raw = toggle;
   1005         break;
   1006       case '0': /* --post301 */
   1007         config->post301 = toggle;
   1008         break;
   1009       case '1': /* --no-keepalive */
   1010         config->nokeepalive = (!toggle)?TRUE:FALSE;
   1011         break;
   1012       case '3': /* --keepalive-time */
   1013         err = str2unum(&config->alivetime, nextarg);
   1014         if(err)
   1015           return err;
   1016         break;
   1017       case '4': /* --post302 */
   1018         config->post302 = toggle;
   1019         break;
   1020       case 'I': /* --post303 */
   1021         config->post303 = toggle;
   1022         break;
   1023       case '5': /* --noproxy */
   1024         /* This specifies the noproxy list */
   1025         GetStr(&config->noproxy, nextarg);
   1026         break;
   1027        case '7': /* --socks5-gssapi-nec*/
   1028         config->socks5_gssapi_nec = toggle;
   1029         break;
   1030       case '8': /* --proxy1.0 */
   1031         /* http 1.0 proxy */
   1032         GetStr(&config->proxy, nextarg);
   1033         config->proxyver = CURLPROXY_HTTP_1_0;
   1034         break;
   1035       case '9': /* --tftp-blksize */
   1036         err = str2unum(&config->tftp_blksize, nextarg);
   1037         if(err)
   1038           return err;
   1039         break;
   1040       case 'A': /* --mail-from */
   1041         GetStr(&config->mail_from, nextarg);
   1042         break;
   1043       case 'B': /* --mail-rcpt */
   1044         /* append receiver to a list */
   1045         err = add2list(&config->mail_rcpt, nextarg);
   1046         if(err)
   1047           return err;
   1048         break;
   1049       case 'C': /* --ftp-pret */
   1050         config->ftp_pret = toggle;
   1051         break;
   1052       case 'D': /* --proto */
   1053         config->proto_present = TRUE;
   1054         if(proto2num(config, &config->proto, nextarg))
   1055           return PARAM_BAD_USE;
   1056         break;
   1057       case 'E': /* --proto-redir */
   1058         config->proto_redir_present = TRUE;
   1059         if(proto2num(config, &config->proto_redir, nextarg))
   1060           return PARAM_BAD_USE;
   1061         break;
   1062       case 'F': /* --resolve */
   1063         err = add2list(&config->resolve, nextarg);
   1064         if(err)
   1065           return err;
   1066         break;
   1067       case 'G': /* --delegation LEVEL */
   1068         config->gssapi_delegation = delegation(config, nextarg);
   1069         break;
   1070       case 'H': /* --mail-auth */
   1071         GetStr(&config->mail_auth, nextarg);
   1072         break;
   1073       case 'J': /* --metalink */
   1074         {
   1075 #ifdef USE_METALINK
   1076           int mlmaj, mlmin, mlpatch;
   1077           metalink_get_version(&mlmaj, &mlmin, &mlpatch);
   1078           if((mlmaj*10000)+(mlmin*100) + mlpatch < CURL_REQ_LIBMETALINK_VERS) {
   1079             warnf(global,
   1080                   "--metalink option cannot be used because the version of "
   1081                   "the linked libmetalink library is too old. "
   1082                   "Required: %d.%d.%d, found %d.%d.%d\n",
   1083                   CURL_REQ_LIBMETALINK_MAJOR,
   1084                   CURL_REQ_LIBMETALINK_MINOR,
   1085                   CURL_REQ_LIBMETALINK_PATCH,
   1086                   mlmaj, mlmin, mlpatch);
   1087             return PARAM_BAD_USE;
   1088           }
   1089           else
   1090             config->use_metalink = toggle;
   1091 #else
   1092           warnf(global, "--metalink option is ignored because the binary is "
   1093                 "built without the Metalink support.\n");
   1094 #endif
   1095           break;
   1096         }
   1097       case 'K': /* --sasl-ir */
   1098         config->sasl_ir = toggle;
   1099         break;
   1100       case 'L': /* --test-event */
   1101 #ifdef CURLDEBUG
   1102         config->test_event_based = toggle;
   1103 #else
   1104         warnf(global, "--test-event is ignored unless a debug build!\n");
   1105 #endif
   1106         break;
   1107       case 'M': /* --unix-socket */
   1108         config->abstract_unix_socket = FALSE;
   1109         GetStr(&config->unix_socket_path, nextarg);
   1110         break;
   1111       case 'N': /* --path-as-is */
   1112         config->path_as_is = toggle;
   1113         break;
   1114       case 'O': /* --proxy-service-name */
   1115         GetStr(&config->proxy_service_name, nextarg);
   1116         break;
   1117       case 'P': /* --service-name */
   1118         GetStr(&config->service_name, nextarg);
   1119         break;
   1120       case 'Q': /* --proto-default */
   1121         GetStr(&config->proto_default, nextarg);
   1122         err = check_protocol(config->proto_default);
   1123         if(err)
   1124           return err;
   1125         break;
   1126       case 'R': /* --expect100-timeout */
   1127         err = str2udouble(&config->expect100timeout, nextarg, LONG_MAX/1000);
   1128         if(err)
   1129           return err;
   1130         break;
   1131       case 'S': /* --tftp-no-options */
   1132         config->tftp_no_options = toggle;
   1133         break;
   1134       case 'U': /* --connect-to */
   1135         err = add2list(&config->connect_to, nextarg);
   1136         if(err)
   1137           return err;
   1138         break;
   1139       case 'W': /* --abstract-unix-socket */
   1140         config->abstract_unix_socket = TRUE;
   1141         GetStr(&config->unix_socket_path, nextarg);
   1142         break;
   1143       case 'X': /* --tls-max */
   1144         err = str2tls_max(&config->ssl_version_max, nextarg);
   1145         if(err)
   1146           return err;
   1147         break;
   1148       case 'Y': /* --suppress-connect-headers */
   1149         config->suppress_connect_headers = toggle;
   1150         break;
   1151       case 'Z': /* --compressed-ssh */
   1152         config->ssh_compression = toggle;
   1153         break;
   1154       case '~': /* --happy-eyeballs-timeout-ms */
   1155         err = str2unum(&config->happy_eyeballs_timeout_ms, nextarg);
   1156         if(err)
   1157           return err;
   1158         /* 0 is a valid value for this timeout */
   1159         break;
   1160       }
   1161       break;
   1162     case '#': /* --progress-bar */
   1163       if(toggle)
   1164         global->progressmode = CURL_PROGRESS_BAR;
   1165       else
   1166         global->progressmode = CURL_PROGRESS_STATS;
   1167       break;
   1168     case ':': /* --next */
   1169       return PARAM_NEXT_OPERATION;
   1170     case '0': /* --http* options */
   1171       switch(subletter) {
   1172       case '\0':
   1173         /* HTTP version 1.0 */
   1174         config->httpversion = CURL_HTTP_VERSION_1_0;
   1175         break;
   1176       case '1':
   1177         /* HTTP version 1.1 */
   1178         config->httpversion = CURL_HTTP_VERSION_1_1;
   1179         break;
   1180       case '2':
   1181         /* HTTP version 2.0 */
   1182         config->httpversion = CURL_HTTP_VERSION_2_0;
   1183         break;
   1184       case '3':
   1185         /* HTTP version 2.0 over clean TCP*/
   1186         config->httpversion = CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE;
   1187         break;
   1188       case '9':
   1189         /* Allow HTTP/0.9 responses! */
   1190         config->http09_allowed = toggle;
   1191         break;
   1192       }
   1193       break;
   1194     case '1': /* --tlsv1* options */
   1195       switch(subletter) {
   1196       case '\0':
   1197         /* TLS version 1.x */
   1198         config->ssl_version = CURL_SSLVERSION_TLSv1;
   1199         break;
   1200       case '0':
   1201         /* TLS version 1.0 */
   1202         config->ssl_version = CURL_SSLVERSION_TLSv1_0;
   1203         break;
   1204       case '1':
   1205         /* TLS version 1.1 */
   1206         config->ssl_version = CURL_SSLVERSION_TLSv1_1;
   1207         break;
   1208       case '2':
   1209         /* TLS version 1.2 */
   1210         config->ssl_version = CURL_SSLVERSION_TLSv1_2;
   1211         break;
   1212       case '3':
   1213         /* TLS version 1.3 */
   1214         config->ssl_version = CURL_SSLVERSION_TLSv1_3;
   1215         break;
   1216       case 'A': /* --tls13-ciphers */
   1217         GetStr(&config->cipher13_list, nextarg);
   1218         break;
   1219       case 'B': /* --proxy-tls13-ciphers */
   1220         GetStr(&config->proxy_cipher13_list, nextarg);
   1221         break;
   1222       }
   1223       break;
   1224     case '2':
   1225       /* SSL version 2 */
   1226       config->ssl_version = CURL_SSLVERSION_SSLv2;
   1227       break;
   1228     case '3':
   1229       /* SSL version 3 */
   1230       config->ssl_version = CURL_SSLVERSION_SSLv3;
   1231       break;
   1232     case '4':
   1233       /* IPv4 */
   1234       config->ip_version = 4;
   1235       break;
   1236     case '6':
   1237       /* IPv6 */
   1238       config->ip_version = 6;
   1239       break;
   1240     case 'a':
   1241       /* This makes the FTP sessions use APPE instead of STOR */
   1242       config->ftp_append = toggle;
   1243       break;
   1244     case 'A':
   1245       /* This specifies the User-Agent name */
   1246       GetStr(&config->useragent, nextarg);
   1247       break;
   1248     case 'b':
   1249       switch(subletter) {
   1250       case 'a': /* --alt-svc */
   1251         GetStr(&config->altsvc, nextarg);
   1252         break;
   1253       default:  /* --cookie string coming up: */
   1254         if(nextarg[0] == '@') {
   1255           nextarg++;
   1256         }
   1257         else if(strchr(nextarg, '=')) {
   1258           /* A cookie string must have a =-letter */
   1259           GetStr(&config->cookie, nextarg);
   1260           break;
   1261         }
   1262         /* We have a cookie file to read from! */
   1263         GetStr(&config->cookiefile, nextarg);
   1264       }
   1265       break;
   1266     case 'B':
   1267       /* use ASCII/text when transferring */
   1268       config->use_ascii = toggle;
   1269       break;
   1270     case 'c':
   1271       /* get the file name to dump all cookies in */
   1272       GetStr(&config->cookiejar, nextarg);
   1273       break;
   1274     case 'C':
   1275       /* This makes us continue an ftp transfer at given position */
   1276       if(strcmp(nextarg, "-")) {
   1277         err = str2offset(&config->resume_from, nextarg);
   1278         if(err)
   1279           return err;
   1280         config->resume_from_current = FALSE;
   1281       }
   1282       else {
   1283         config->resume_from_current = TRUE;
   1284         config->resume_from = 0;
   1285       }
   1286       config->use_resume = TRUE;
   1287       break;
   1288     case 'd':
   1289       /* postfield data */
   1290     {
   1291       char *postdata = NULL;
   1292       FILE *file;
   1293       size_t size = 0;
   1294       bool raw_mode = (subletter == 'r');
   1295 
   1296       if(subletter == 'e') { /* --data-urlencode*/
   1297         /* [name]=[content], we encode the content part only
   1298          * [name]@[file name]
   1299          *
   1300          * Case 2: we first load the file using that name and then encode
   1301          * the content.
   1302          */
   1303         const char *p = strchr(nextarg, '=');
   1304         size_t nlen;
   1305         char is_file;
   1306         if(!p)
   1307           /* there was no '=' letter, check for a '@' instead */
   1308           p = strchr(nextarg, '@');
   1309         if(p) {
   1310           nlen = p - nextarg; /* length of the name part */
   1311           is_file = *p++; /* pass the separator */
   1312         }
   1313         else {
   1314           /* neither @ nor =, so no name and it isn't a file */
   1315           nlen = is_file = 0;
   1316           p = nextarg;
   1317         }
   1318         if('@' == is_file) {
   1319           /* a '@' letter, it means that a file name or - (stdin) follows */
   1320           if(!strcmp("-", p)) {
   1321             file = stdin;
   1322             set_binmode(stdin);
   1323           }
   1324           else {
   1325             file = fopen(p, "rb");
   1326             if(!file)
   1327               warnf(global,
   1328                     "Couldn't read data from file \"%s\", this makes "
   1329                     "an empty POST.\n", nextarg);
   1330           }
   1331 
   1332           err = file2memory(&postdata, &size, file);
   1333 
   1334           if(file && (file != stdin))
   1335             fclose(file);
   1336           if(err)
   1337             return err;
   1338         }
   1339         else {
   1340           GetStr(&postdata, p);
   1341           if(postdata)
   1342             size = strlen(postdata);
   1343         }
   1344 
   1345         if(!postdata) {
   1346           /* no data from the file, point to a zero byte string to make this
   1347              get sent as a POST anyway */
   1348           postdata = strdup("");
   1349           if(!postdata)
   1350             return PARAM_NO_MEM;
   1351           size = 0;
   1352         }
   1353         else {
   1354           char *enc = curl_easy_escape(config->easy, postdata, (int)size);
   1355           Curl_safefree(postdata); /* no matter if it worked or not */
   1356           if(enc) {
   1357             /* now make a string with the name from above and append the
   1358                encoded string */
   1359             size_t outlen = nlen + strlen(enc) + 2;
   1360             char *n = malloc(outlen);
   1361             if(!n) {
   1362               curl_free(enc);
   1363               return PARAM_NO_MEM;
   1364             }
   1365             if(nlen > 0) { /* only append '=' if we have a name */
   1366               msnprintf(n, outlen, "%.*s=%s", nlen, nextarg, enc);
   1367               size = outlen-1;
   1368             }
   1369             else {
   1370               strcpy(n, enc);
   1371               size = outlen-2; /* since no '=' was inserted */
   1372             }
   1373             curl_free(enc);
   1374             postdata = n;
   1375           }
   1376           else
   1377             return PARAM_NO_MEM;
   1378         }
   1379       }
   1380       else if('@' == *nextarg && !raw_mode) {
   1381         /* the data begins with a '@' letter, it means that a file name
   1382            or - (stdin) follows */
   1383         nextarg++; /* pass the @ */
   1384 
   1385         if(!strcmp("-", nextarg)) {
   1386           file = stdin;
   1387           if(subletter == 'b') /* forced data-binary */
   1388             set_binmode(stdin);
   1389         }
   1390         else {
   1391           file = fopen(nextarg, "rb");
   1392           if(!file)
   1393             warnf(global, "Couldn't read data from file \"%s\", this makes "
   1394                   "an empty POST.\n", nextarg);
   1395         }
   1396 
   1397         if(subletter == 'b')
   1398           /* forced binary */
   1399           err = file2memory(&postdata, &size, file);
   1400         else {
   1401           err = file2string(&postdata, file);
   1402           if(postdata)
   1403             size = strlen(postdata);
   1404         }
   1405 
   1406         if(file && (file != stdin))
   1407           fclose(file);
   1408         if(err)
   1409           return err;
   1410 
   1411         if(!postdata) {
   1412           /* no data from the file, point to a zero byte string to make this
   1413              get sent as a POST anyway */
   1414           postdata = strdup("");
   1415           if(!postdata)
   1416             return PARAM_NO_MEM;
   1417         }
   1418       }
   1419       else {
   1420         GetStr(&postdata, nextarg);
   1421         if(postdata)
   1422           size = strlen(postdata);
   1423       }
   1424 
   1425 #ifdef CURL_DOES_CONVERSIONS
   1426       if(subletter != 'b') {
   1427         /* NOT forced binary, convert to ASCII */
   1428         if(convert_to_network(postdata, strlen(postdata))) {
   1429           Curl_safefree(postdata);
   1430           return PARAM_NO_MEM;
   1431         }
   1432       }
   1433 #endif
   1434 
   1435       if(config->postfields) {
   1436         /* we already have a string, we append this one with a separating
   1437            &-letter */
   1438         char *oldpost = config->postfields;
   1439         curl_off_t oldlen = config->postfieldsize;
   1440         curl_off_t newlen = oldlen + curlx_uztoso(size) + 2;
   1441         config->postfields = malloc((size_t)newlen);
   1442         if(!config->postfields) {
   1443           Curl_safefree(oldpost);
   1444           Curl_safefree(postdata);
   1445           return PARAM_NO_MEM;
   1446         }
   1447         memcpy(config->postfields, oldpost, (size_t)oldlen);
   1448         /* use byte value 0x26 for '&' to accommodate non-ASCII platforms */
   1449         config->postfields[oldlen] = '\x26';
   1450         memcpy(&config->postfields[oldlen + 1], postdata, size);
   1451         config->postfields[oldlen + 1 + size] = '\0';
   1452         Curl_safefree(oldpost);
   1453         Curl_safefree(postdata);
   1454         config->postfieldsize += size + 1;
   1455       }
   1456       else {
   1457         config->postfields = postdata;
   1458         config->postfieldsize = curlx_uztoso(size);
   1459       }
   1460     }
   1461     /*
   1462       We can't set the request type here, as this data might be used in
   1463       a simple GET if -G is used. Already or soon.
   1464 
   1465       if(SetHTTPrequest(HTTPREQ_SIMPLEPOST, &config->httpreq)) {
   1466         Curl_safefree(postdata);
   1467         return PARAM_BAD_USE;
   1468       }
   1469     */
   1470     break;
   1471     case 'D':
   1472       /* dump-header to given file name */
   1473       GetStr(&config->headerfile, nextarg);
   1474       break;
   1475     case 'e':
   1476     {
   1477       char *ptr = strstr(nextarg, ";auto");
   1478       if(ptr) {
   1479         /* Automatic referer requested, this may be combined with a
   1480            set initial one */
   1481         config->autoreferer = TRUE;
   1482         *ptr = 0; /* zero terminate here */
   1483       }
   1484       else
   1485         config->autoreferer = FALSE;
   1486       GetStr(&config->referer, nextarg);
   1487     }
   1488     break;
   1489     case 'E':
   1490       switch(subletter) {
   1491       case '\0': /* certificate file */
   1492         GetFileAndPassword(nextarg, &config->cert, &config->key_passwd);
   1493         break;
   1494       case 'a': /* CA info PEM file */
   1495         GetStr(&config->cacert, nextarg);
   1496         break;
   1497       case 'b': /* cert file type */
   1498         GetStr(&config->cert_type, nextarg);
   1499         break;
   1500       case 'c': /* private key file */
   1501         GetStr(&config->key, nextarg);
   1502         break;
   1503       case 'd': /* private key file type */
   1504         GetStr(&config->key_type, nextarg);
   1505         break;
   1506       case 'e': /* private key passphrase */
   1507         GetStr(&config->key_passwd, nextarg);
   1508         cleanarg(nextarg);
   1509         break;
   1510       case 'f': /* crypto engine */
   1511         GetStr(&config->engine, nextarg);
   1512         if(config->engine && curl_strequal(config->engine, "list"))
   1513           return PARAM_ENGINES_REQUESTED;
   1514         break;
   1515       case 'g': /* CA cert directory */
   1516         GetStr(&config->capath, nextarg);
   1517         break;
   1518       case 'h': /* --pubkey public key file */
   1519         GetStr(&config->pubkey, nextarg);
   1520         break;
   1521       case 'i': /* --hostpubmd5 md5 of the host public key */
   1522         GetStr(&config->hostpubmd5, nextarg);
   1523         if(!config->hostpubmd5 || strlen(config->hostpubmd5) != 32)
   1524           return PARAM_BAD_USE;
   1525         break;
   1526       case 'j': /* CRL file */
   1527         GetStr(&config->crlfile, nextarg);
   1528         break;
   1529       case 'k': /* TLS username */
   1530         if(curlinfo->features & CURL_VERSION_TLSAUTH_SRP)
   1531           GetStr(&config->tls_username, nextarg);
   1532         else
   1533           return PARAM_LIBCURL_DOESNT_SUPPORT;
   1534         break;
   1535       case 'l': /* TLS password */
   1536         if(curlinfo->features & CURL_VERSION_TLSAUTH_SRP)
   1537           GetStr(&config->tls_password, nextarg);
   1538         else
   1539           return PARAM_LIBCURL_DOESNT_SUPPORT;
   1540         break;
   1541       case 'm': /* TLS authentication type */
   1542         if(curlinfo->features & CURL_VERSION_TLSAUTH_SRP) {
   1543           GetStr(&config->tls_authtype, nextarg);
   1544           if(!curl_strequal(config->tls_authtype, "SRP"))
   1545             return PARAM_LIBCURL_DOESNT_SUPPORT; /* only support TLS-SRP */
   1546         }
   1547         else
   1548           return PARAM_LIBCURL_DOESNT_SUPPORT;
   1549         break;
   1550       case 'n': /* no empty SSL fragments, --ssl-allow-beast */
   1551         if(curlinfo->features & CURL_VERSION_SSL)
   1552           config->ssl_allow_beast = toggle;
   1553         break;
   1554 
   1555       case 'o': /* --login-options */
   1556         GetStr(&config->login_options, nextarg);
   1557         break;
   1558 
   1559       case 'p': /* Pinned public key DER file */
   1560         GetStr(&config->pinnedpubkey, nextarg);
   1561         break;
   1562 
   1563       case 'P': /* proxy pinned public key */
   1564         GetStr(&config->proxy_pinnedpubkey, nextarg);
   1565         break;
   1566 
   1567       case 'q': /* --cert-status */
   1568         config->verifystatus = TRUE;
   1569         break;
   1570 
   1571       case 'r': /* --false-start */
   1572         config->falsestart = TRUE;
   1573         break;
   1574 
   1575       case 's': /* --ssl-no-revoke */
   1576         if(curlinfo->features & CURL_VERSION_SSL)
   1577           config->ssl_no_revoke = TRUE;
   1578         break;
   1579 
   1580       case 't': /* --tcp-fastopen */
   1581         config->tcp_fastopen = TRUE;
   1582         break;
   1583 
   1584       case 'u': /* TLS username for proxy */
   1585         if(curlinfo->features & CURL_VERSION_TLSAUTH_SRP)
   1586           GetStr(&config->proxy_tls_username, nextarg);
   1587         else
   1588           return PARAM_LIBCURL_DOESNT_SUPPORT;
   1589         break;
   1590 
   1591       case 'v': /* TLS password for proxy */
   1592         if(curlinfo->features & CURL_VERSION_TLSAUTH_SRP)
   1593           GetStr(&config->proxy_tls_password, nextarg);
   1594         else
   1595           return PARAM_LIBCURL_DOESNT_SUPPORT;
   1596         break;
   1597 
   1598       case 'w': /* TLS authentication type for proxy */
   1599         if(curlinfo->features & CURL_VERSION_TLSAUTH_SRP) {
   1600           GetStr(&config->proxy_tls_authtype, nextarg);
   1601           if(!curl_strequal(config->proxy_tls_authtype, "SRP"))
   1602             return PARAM_LIBCURL_DOESNT_SUPPORT; /* only support TLS-SRP */
   1603         }
   1604         else
   1605           return PARAM_LIBCURL_DOESNT_SUPPORT;
   1606         break;
   1607 
   1608       case 'x': /* certificate file for proxy */
   1609         GetFileAndPassword(nextarg, &config->proxy_cert,
   1610                            &config->proxy_key_passwd);
   1611         break;
   1612 
   1613       case 'y': /* cert file type for proxy */
   1614         GetStr(&config->proxy_cert_type, nextarg);
   1615         break;
   1616 
   1617       case 'z': /* private key file for proxy */
   1618         GetStr(&config->proxy_key, nextarg);
   1619         break;
   1620 
   1621       case '0': /* private key file type for proxy */
   1622         GetStr(&config->proxy_key_type, nextarg);
   1623         break;
   1624 
   1625       case '1': /* private key passphrase for proxy */
   1626         GetStr(&config->proxy_key_passwd, nextarg);
   1627         cleanarg(nextarg);
   1628         break;
   1629 
   1630       case '2': /* ciphers for proxy */
   1631         GetStr(&config->proxy_cipher_list, nextarg);
   1632         break;
   1633 
   1634       case '3': /* CRL file for proxy */
   1635         GetStr(&config->proxy_crlfile, nextarg);
   1636         break;
   1637 
   1638       case '4': /* no empty SSL fragments for proxy */
   1639         if(curlinfo->features & CURL_VERSION_SSL)
   1640           config->proxy_ssl_allow_beast = toggle;
   1641         break;
   1642 
   1643       case '5': /* --login-options */
   1644         GetStr(&config->login_options, nextarg);
   1645         break;
   1646 
   1647       case '6': /* CA info PEM file for proxy */
   1648         GetStr(&config->proxy_cacert, nextarg);
   1649         break;
   1650 
   1651       case '7': /* CA cert directory for proxy */
   1652         GetStr(&config->proxy_capath, nextarg);
   1653         break;
   1654 
   1655       case '8': /* allow insecure SSL connects for proxy */
   1656         config->proxy_insecure_ok = toggle;
   1657         break;
   1658 
   1659       case '9': /* --proxy-tlsv1 */
   1660         /* TLS version 1 for proxy */
   1661         config->proxy_ssl_version = CURL_SSLVERSION_TLSv1;
   1662         break;
   1663 
   1664       case 'A':
   1665         /* --socks5-basic */
   1666         if(toggle)
   1667           config->socks5_auth |= CURLAUTH_BASIC;
   1668         else
   1669           config->socks5_auth &= ~CURLAUTH_BASIC;
   1670         break;
   1671 
   1672       case 'B':
   1673         /* --socks5-gssapi */
   1674         if(toggle)
   1675           config->socks5_auth |= CURLAUTH_GSSAPI;
   1676         else
   1677           config->socks5_auth &= ~CURLAUTH_GSSAPI;
   1678         break;
   1679 
   1680       default: /* unknown flag */
   1681         return PARAM_OPTION_UNKNOWN;
   1682       }
   1683       break;
   1684     case 'f':
   1685       switch(subletter) {
   1686       case 'a': /* --fail-early */
   1687         global->fail_early = toggle;
   1688         break;
   1689       case 'b': /* --styled-output */
   1690         global->styled_output = toggle;
   1691         break;
   1692       default: /* --fail (hard on errors)  */
   1693         config->failonerror = toggle;
   1694       }
   1695       break;
   1696     case 'F':
   1697       /* "form data" simulation, this is a little advanced so lets do our best
   1698          to sort this out slowly and carefully */
   1699       if(formparse(config,
   1700                    nextarg,
   1701                    &config->mimeroot,
   1702                    &config->mimecurrent,
   1703                    (subletter == 's')?TRUE:FALSE)) /* 's' is literal string */
   1704         return PARAM_BAD_USE;
   1705       if(SetHTTPrequest(config, HTTPREQ_MIMEPOST, &config->httpreq))
   1706         return PARAM_BAD_USE;
   1707       break;
   1708 
   1709     case 'g': /* g disables URLglobbing */
   1710       config->globoff = toggle;
   1711       break;
   1712 
   1713     case 'G': /* HTTP GET */
   1714       if(subletter == 'a') { /* --request-target */
   1715         GetStr(&config->request_target, nextarg);
   1716       }
   1717       else
   1718         config->use_httpget = TRUE;
   1719       break;
   1720 
   1721     case 'h': /* h for help */
   1722       if(toggle) {
   1723         return PARAM_HELP_REQUESTED;
   1724       }
   1725       /* we now actually support --no-help too! */
   1726       break;
   1727     case 'H':
   1728       /* A custom header to append to a list */
   1729       if(nextarg[0] == '@') {
   1730         /* read many headers from a file or stdin */
   1731         char *string;
   1732         size_t len;
   1733         bool use_stdin = !strcmp(&nextarg[1], "-");
   1734         FILE *file = use_stdin?stdin:fopen(&nextarg[1], FOPEN_READTEXT);
   1735         if(!file)
   1736           warnf(global, "Failed to open %s!\n", &nextarg[1]);
   1737         else {
   1738           err = file2memory(&string, &len, file);
   1739           if(!err && string) {
   1740             /* Allow strtok() here since this isn't used threaded */
   1741             /* !checksrc! disable BANNEDFUNC 2 */
   1742             char *h = strtok(string, "\r\n");
   1743             while(h) {
   1744               if(subletter == 'p') /* --proxy-header */
   1745                 err = add2list(&config->proxyheaders, h);
   1746               else
   1747                 err = add2list(&config->headers, h);
   1748               if(err)
   1749                 break;
   1750               h = strtok(NULL, "\r\n");
   1751             }
   1752             free(string);
   1753           }
   1754           if(!use_stdin)
   1755             fclose(file);
   1756           if(err)
   1757             return err;
   1758         }
   1759       }
   1760       else {
   1761         if(subletter == 'p') /* --proxy-header */
   1762           err = add2list(&config->proxyheaders, nextarg);
   1763         else
   1764           err = add2list(&config->headers, nextarg);
   1765         if(err)
   1766           return err;
   1767       }
   1768       break;
   1769     case 'i':
   1770       config->show_headers = toggle; /* show the headers as well in the
   1771                                         general output stream */
   1772       break;
   1773     case 'j':
   1774       config->cookiesession = toggle;
   1775       break;
   1776     case 'I': /* --head */
   1777       config->no_body = toggle;
   1778       config->show_headers = toggle;
   1779       if(SetHTTPrequest(config,
   1780                         (config->no_body)?HTTPREQ_HEAD:HTTPREQ_GET,
   1781                         &config->httpreq))
   1782         return PARAM_BAD_USE;
   1783       break;
   1784     case 'J': /* --remote-header-name */
   1785       if(config->show_headers) {
   1786         warnf(global,
   1787               "--include and --remote-header-name cannot be combined.\n");
   1788         return PARAM_BAD_USE;
   1789       }
   1790       config->content_disposition = toggle;
   1791       break;
   1792     case 'k': /* allow insecure SSL connects */
   1793       config->insecure_ok = toggle;
   1794       break;
   1795     case 'K': /* parse config file */
   1796       if(parseconfig(nextarg, global))
   1797         warnf(global, "error trying read config from the '%s' file\n",
   1798               nextarg);
   1799       break;
   1800     case 'l':
   1801       config->dirlistonly = toggle; /* only list the names of the FTP dir */
   1802       break;
   1803     case 'L':
   1804       config->followlocation = toggle; /* Follow Location: HTTP headers */
   1805       switch(subletter) {
   1806       case 't':
   1807         /* Continue to send authentication (user+password) when following
   1808          * locations, even when hostname changed */
   1809         config->unrestricted_auth = toggle;
   1810         break;
   1811       }
   1812       break;
   1813     case 'm':
   1814       /* specified max time */
   1815       err = str2udouble(&config->timeout, nextarg, LONG_MAX/1000);
   1816       if(err)
   1817         return err;
   1818       break;
   1819     case 'M': /* M for manual, huge help */
   1820       if(toggle) { /* --no-manual shows no manual... */
   1821 #ifdef USE_MANUAL
   1822         return PARAM_MANUAL_REQUESTED;
   1823 #else
   1824         warnf(global,
   1825               "built-in manual was disabled at build-time!\n");
   1826         return PARAM_OPTION_UNKNOWN;
   1827 #endif
   1828       }
   1829       break;
   1830     case 'n':
   1831       switch(subletter) {
   1832       case 'o': /* use .netrc or URL */
   1833         config->netrc_opt = toggle;
   1834         break;
   1835       case 'e': /* netrc-file */
   1836         GetStr(&config->netrc_file, nextarg);
   1837         break;
   1838       default:
   1839         /* pick info from .netrc, if this is used for http, curl will
   1840            automatically enfore user+password with the request */
   1841         config->netrc = toggle;
   1842         break;
   1843       }
   1844       break;
   1845     case 'N':
   1846       /* disable the output I/O buffering. note that the option is called
   1847          --buffer but is mostly used in the negative form: --no-buffer */
   1848       if(longopt)
   1849         config->nobuffer = (!toggle)?TRUE:FALSE;
   1850       else
   1851         config->nobuffer = toggle;
   1852       break;
   1853     case 'O': /* --remote-name */
   1854       if(subletter == 'a') { /* --remote-name-all */
   1855         config->default_node_flags = toggle?GETOUT_USEREMOTE:0;
   1856         break;
   1857       }
   1858       /* FALLTHROUGH */
   1859     case 'o': /* --output */
   1860       /* output file */
   1861     {
   1862       struct getout *url;
   1863       if(!config->url_out)
   1864         config->url_out = config->url_list;
   1865       if(config->url_out) {
   1866         /* there's a node here, if it already is filled-in continue to find
   1867            an "empty" node */
   1868         while(config->url_out && (config->url_out->flags & GETOUT_OUTFILE))
   1869           config->url_out = config->url_out->next;
   1870       }
   1871 
   1872       /* now there might or might not be an available node to fill in! */
   1873 
   1874       if(config->url_out)
   1875         /* existing node */
   1876         url = config->url_out;
   1877       else
   1878         /* there was no free node, create one! */
   1879         config->url_out = url = new_getout(config);
   1880 
   1881       if(!url)
   1882         return PARAM_NO_MEM;
   1883 
   1884       /* fill in the outfile */
   1885       if('o' == letter) {
   1886         GetStr(&url->outfile, nextarg);
   1887         url->flags &= ~GETOUT_USEREMOTE; /* switch off */
   1888       }
   1889       else {
   1890         url->outfile = NULL; /* leave it */
   1891         if(toggle)
   1892           url->flags |= GETOUT_USEREMOTE;  /* switch on */
   1893         else
   1894           url->flags &= ~GETOUT_USEREMOTE; /* switch off */
   1895       }
   1896       url->flags |= GETOUT_OUTFILE;
   1897     }
   1898     break;
   1899     case 'P':
   1900       /* This makes the FTP sessions use PORT instead of PASV */
   1901       /* use <eth0> or <192.168.10.10> style addresses. Anything except
   1902          this will make us try to get the "default" address.
   1903          NOTE: this is a changed behaviour since the released 4.1!
   1904       */
   1905       GetStr(&config->ftpport, nextarg);
   1906       break;
   1907     case 'p':
   1908       /* proxy tunnel for non-http protocols */
   1909       config->proxytunnel = toggle;
   1910       break;
   1911 
   1912     case 'q': /* if used first, already taken care of, we do it like
   1913                  this so we don't cause an error! */
   1914       break;
   1915     case 'Q':
   1916       /* QUOTE command to send to FTP server */
   1917       switch(nextarg[0]) {
   1918       case '-':
   1919         /* prefixed with a dash makes it a POST TRANSFER one */
   1920         nextarg++;
   1921         err = add2list(&config->postquote, nextarg);
   1922         break;
   1923       case '+':
   1924         /* prefixed with a plus makes it a just-before-transfer one */
   1925         nextarg++;
   1926         err = add2list(&config->prequote, nextarg);
   1927         break;
   1928       default:
   1929         err = add2list(&config->quote, nextarg);
   1930         break;
   1931       }
   1932       if(err)
   1933         return err;
   1934       break;
   1935     case 'r':
   1936       /* Specifying a range WITHOUT A DASH will create an illegal HTTP range
   1937          (and won't actually be range by definition). The man page previously
   1938          claimed that to be a good way, why this code is added to work-around
   1939          it. */
   1940       if(ISDIGIT(*nextarg) && !strchr(nextarg, '-')) {
   1941         char buffer[32];
   1942         curl_off_t off;
   1943         if(curlx_strtoofft(nextarg, NULL, 10, &off)) {
   1944           warnf(global, "unsupported range point\n");
   1945           return PARAM_BAD_USE;
   1946         }
   1947         warnf(global,
   1948               "A specified range MUST include at least one dash (-). "
   1949               "Appending one for you!\n");
   1950         msnprintf(buffer, sizeof(buffer), "%" CURL_FORMAT_CURL_OFF_T "-", off);
   1951         Curl_safefree(config->range);
   1952         config->range = strdup(buffer);
   1953         if(!config->range)
   1954           return PARAM_NO_MEM;
   1955       }
   1956       {
   1957         /* byte range requested */
   1958         char *tmp_range;
   1959         tmp_range = nextarg;
   1960         while(*tmp_range != '\0') {
   1961           if(!ISDIGIT(*tmp_range) && *tmp_range != '-' && *tmp_range != ',') {
   1962             warnf(global, "Invalid character is found in given range. "
   1963                   "A specified range MUST have only digits in "
   1964                   "\'start\'-\'stop\'. The server's response to this "
   1965                   "request is uncertain.\n");
   1966             break;
   1967           }
   1968           tmp_range++;
   1969         }
   1970         /* byte range requested */
   1971         GetStr(&config->range, nextarg);
   1972       }
   1973       break;
   1974     case 'R':
   1975       /* use remote file's time */
   1976       config->remote_time = toggle;
   1977       break;
   1978     case 's':
   1979       /* don't show progress meter, don't show errors : */
   1980       if(toggle)
   1981         global->mute = global->noprogress = TRUE;
   1982       else
   1983         global->mute = global->noprogress = FALSE;
   1984       if(global->showerror < 0)
   1985         /* if still on the default value, set showerror to the reverse of
   1986            toggle. This is to allow -S and -s to be used in an independent
   1987            order but still have the same effect. */
   1988         global->showerror = (!toggle)?TRUE:FALSE; /* toggle off */
   1989       break;
   1990     case 'S':
   1991       /* show errors */
   1992       global->showerror = toggle?1:0; /* toggle on if used with -s */
   1993       break;
   1994     case 't':
   1995       /* Telnet options */
   1996       err = add2list(&config->telnet_options, nextarg);
   1997       if(err)
   1998         return err;
   1999       break;
   2000     case 'T':
   2001       /* we are uploading */
   2002     {
   2003       struct getout *url;
   2004       if(!config->url_ul)
   2005         config->url_ul = config->url_list;
   2006       if(config->url_ul) {
   2007         /* there's a node here, if it already is filled-in continue to find
   2008            an "empty" node */
   2009         while(config->url_ul && (config->url_ul->flags & GETOUT_UPLOAD))
   2010           config->url_ul = config->url_ul->next;
   2011       }
   2012 
   2013       /* now there might or might not be an available node to fill in! */
   2014 
   2015       if(config->url_ul)
   2016         /* existing node */
   2017         url = config->url_ul;
   2018       else
   2019         /* there was no free node, create one! */
   2020         config->url_ul = url = new_getout(config);
   2021 
   2022       if(!url)
   2023         return PARAM_NO_MEM;
   2024 
   2025       url->flags |= GETOUT_UPLOAD; /* mark -T used */
   2026       if(!*nextarg)
   2027         url->flags |= GETOUT_NOUPLOAD;
   2028       else {
   2029         /* "-" equals stdin, but keep the string around for now */
   2030         GetStr(&url->infile, nextarg);
   2031       }
   2032     }
   2033     break;
   2034     case 'u':
   2035       /* user:password  */
   2036       GetStr(&config->userpwd, nextarg);
   2037       cleanarg(nextarg);
   2038       break;
   2039     case 'U':
   2040       /* Proxy user:password  */
   2041       GetStr(&config->proxyuserpwd, nextarg);
   2042       cleanarg(nextarg);
   2043       break;
   2044     case 'v':
   2045       if(toggle) {
   2046         /* the '%' thing here will cause the trace get sent to stderr */
   2047         Curl_safefree(global->trace_dump);
   2048         global->trace_dump = strdup("%");
   2049         if(!global->trace_dump)
   2050           return PARAM_NO_MEM;
   2051         if(global->tracetype && (global->tracetype != TRACE_PLAIN))
   2052           warnf(global,
   2053                 "-v, --verbose overrides an earlier trace/verbose option\n");
   2054         global->tracetype = TRACE_PLAIN;
   2055       }
   2056       else
   2057         /* verbose is disabled here */
   2058         global->tracetype = TRACE_NONE;
   2059       break;
   2060     case 'V':
   2061       if(toggle)    /* --no-version yields no output! */
   2062         return PARAM_VERSION_INFO_REQUESTED;
   2063       break;
   2064 
   2065     case 'w':
   2066       /* get the output string */
   2067       if('@' == *nextarg) {
   2068         /* the data begins with a '@' letter, it means that a file name
   2069            or - (stdin) follows */
   2070         FILE *file;
   2071         const char *fname;
   2072         nextarg++; /* pass the @ */
   2073         if(!strcmp("-", nextarg)) {
   2074           fname = "<stdin>";
   2075           file = stdin;
   2076         }
   2077         else {
   2078           fname = nextarg;
   2079           file = fopen(nextarg, FOPEN_READTEXT);
   2080         }
   2081         Curl_safefree(config->writeout);
   2082         err = file2string(&config->writeout, file);
   2083         if(file && (file != stdin))
   2084           fclose(file);
   2085         if(err)
   2086           return err;
   2087         if(!config->writeout)
   2088           warnf(global, "Failed to read %s", fname);
   2089       }
   2090       else
   2091         GetStr(&config->writeout, nextarg);
   2092       break;
   2093     case 'x':
   2094       switch(subletter) {
   2095       case 'a': /* --preproxy */
   2096         GetStr(&config->preproxy, nextarg);
   2097         break;
   2098       default:
   2099         /* --proxy */
   2100         GetStr(&config->proxy, nextarg);
   2101         config->proxyver = CURLPROXY_HTTP;
   2102         break;
   2103       }
   2104       break;
   2105     case 'X':
   2106       /* set custom request */
   2107       GetStr(&config->customrequest, nextarg);
   2108       break;
   2109     case 'y':
   2110       /* low speed time */
   2111       err = str2unum(&config->low_speed_time, nextarg);
   2112       if(err)
   2113         return err;
   2114       if(!config->low_speed_limit)
   2115         config->low_speed_limit = 1;
   2116       break;
   2117     case 'Y':
   2118       /* low speed limit */
   2119       err = str2unum(&config->low_speed_limit, nextarg);
   2120       if(err)
   2121         return err;
   2122       if(!config->low_speed_time)
   2123         config->low_speed_time = 30;
   2124       break;
   2125     case 'z': /* time condition coming up */
   2126       switch(*nextarg) {
   2127       case '+':
   2128         nextarg++;
   2129         /* FALLTHROUGH */
   2130       default:
   2131         /* If-Modified-Since: (section 14.28 in RFC2068) */
   2132         config->timecond = CURL_TIMECOND_IFMODSINCE;
   2133         break;
   2134       case '-':
   2135         /* If-Unmodified-Since:  (section 14.24 in RFC2068) */
   2136         config->timecond = CURL_TIMECOND_IFUNMODSINCE;
   2137         nextarg++;
   2138         break;
   2139       case '=':
   2140         /* Last-Modified:  (section 14.29 in RFC2068) */
   2141         config->timecond = CURL_TIMECOND_LASTMOD;
   2142         nextarg++;
   2143         break;
   2144       }
   2145       now = time(NULL);
   2146       config->condtime = (curl_off_t)curl_getdate(nextarg, &now);
   2147       if(-1 == config->condtime) {
   2148         /* now let's see if it is a file name to get the time from instead! */
   2149         curl_off_t filetime = getfiletime(nextarg, config->global->errors);
   2150         if(filetime >= 0) {
   2151           /* pull the time out from the file */
   2152           config->condtime = filetime;
   2153         }
   2154         else {
   2155           /* failed, remove time condition */
   2156           config->timecond = CURL_TIMECOND_NONE;
   2157           warnf(global,
   2158                 "Illegal date format for -z, --time-cond (and not "
   2159                 "a file name). Disabling time condition. "
   2160                 "See curl_getdate(3) for valid date syntax.\n");
   2161         }
   2162       }
   2163       break;
   2164     default: /* unknown flag */
   2165       return PARAM_OPTION_UNKNOWN;
   2166     }
   2167     hit = -1;
   2168 
   2169   } while(!longopt && !singleopt && *++parse && !*usedarg);
   2170 
   2171   return PARAM_OK;
   2172 }
   2173 
   2174 ParameterError parse_args(struct GlobalConfig *config, int argc,
   2175                           argv_item_t argv[])
   2176 {
   2177   int i;
   2178   bool stillflags;
   2179   char *orig_opt = NULL;
   2180   ParameterError result = PARAM_OK;
   2181   struct OperationConfig *operation = config->first;
   2182 
   2183   for(i = 1, stillflags = TRUE; i < argc && !result; i++) {
   2184     orig_opt = argv[i];
   2185 
   2186     if(stillflags && ('-' == argv[i][0])) {
   2187       bool passarg;
   2188       char *flag = argv[i];
   2189 
   2190       if(!strcmp("--", argv[i]))
   2191         /* This indicates the end of the flags and thus enables the
   2192            following (URL) argument to start with -. */
   2193         stillflags = FALSE;
   2194       else {
   2195         char *nextarg = (i < (argc - 1)) ? argv[i + 1] : NULL;
   2196 
   2197         result = getparameter(flag, nextarg, &passarg, config, operation);
   2198         if(result == PARAM_NEXT_OPERATION) {
   2199           /* Reset result as PARAM_NEXT_OPERATION is only used here and not
   2200              returned from this function */
   2201           result = PARAM_OK;
   2202 
   2203           if(operation->url_list && operation->url_list->url) {
   2204             /* Allocate the next config */
   2205             operation->next = malloc(sizeof(struct OperationConfig));
   2206             if(operation->next) {
   2207               /* Initialise the newly created config */
   2208               config_init(operation->next);
   2209 
   2210               /* Copy the easy handle */
   2211               operation->next->easy = config->easy;
   2212 
   2213               /* Set the global config pointer */
   2214               operation->next->global = config;
   2215 
   2216               /* Update the last operation pointer */
   2217               config->last = operation->next;
   2218 
   2219               /* Move onto the new config */
   2220               operation->next->prev = operation;
   2221               operation = operation->next;
   2222             }
   2223             else
   2224               result = PARAM_NO_MEM;
   2225           }
   2226         }
   2227         else if(!result && passarg)
   2228           i++; /* we're supposed to skip this */
   2229       }
   2230     }
   2231     else {
   2232       bool used;
   2233 
   2234       /* Just add the URL please */
   2235       result = getparameter((char *)"--url", argv[i], &used, config,
   2236                             operation);
   2237     }
   2238   }
   2239 
   2240   if(result && result != PARAM_HELP_REQUESTED &&
   2241      result != PARAM_MANUAL_REQUESTED &&
   2242      result != PARAM_VERSION_INFO_REQUESTED &&
   2243      result != PARAM_ENGINES_REQUESTED) {
   2244     const char *reason = param2text(result);
   2245 
   2246     if(orig_opt && strcmp(":", orig_opt))
   2247       helpf(config->errors, "option %s: %s\n", orig_opt, reason);
   2248     else
   2249       helpf(config->errors, "%s\n", reason);
   2250   }
   2251 
   2252   return result;
   2253 }
   2254