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