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