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