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