Initial revision
[platform/upstream/curl.git] / src / main.c
1 /*****************************************************************************
2  *                                  _   _ ____  _     
3  *  Project                     ___| | | |  _ \| |    
4  *                             / __| | | | |_) | |    
5  *                            | (__| |_| |  _ <| |___ 
6  *                             \___|\___/|_| \_\_____|
7  *
8  *  The contents of this file are subject to the Mozilla Public License
9  *  Version 1.0 (the "License"); you may not use this file except in
10  *  compliance with the License. You may obtain a copy of the License at
11  *  http://www.mozilla.org/MPL/
12  *
13  *  Software distributed under the License is distributed on an "AS IS"
14  *  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
15  *  License for the specific language governing rights and limitations
16  *  under the License.
17  *
18  *  The Original Code is Curl.
19  *
20  *  The Initial Developer of the Original Code is Daniel Stenberg.
21  *
22  *  Portions created by the Initial Developer are Copyright (C) 1998.
23  *  All Rights Reserved.
24  *
25  * ------------------------------------------------------------
26  * Main author:
27  * - Daniel Stenberg <Daniel.Stenberg@haxx.nu>
28  *
29  *      http://curl.haxx.nu
30  *
31  * $Source$
32  * $Revision$
33  * $Date$
34  * $Author$
35  * $State$
36  * $Locker$
37  *
38  * ------------------------------------------------------------
39  ****************************************************************************/
40
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <stdarg.h>
45 #include <sys/stat.h>
46 #include <ctype.h>
47
48 #include <curl/curl.h>
49 #include <curl/mprintf.h>
50 #include "../lib/getdate.h"
51 #ifdef GLOBURL
52 #include "urlglob.h"
53 #define CURLseparator   "--_curl_--"
54 #define MIMEseparator   "_curl_"
55 #endif
56
57 /* This is now designed to have its own local setup.h */
58 #include "setup.h"
59
60 #include "version.h"
61
62 #ifdef HAVE_IO_H /* typical win32 habit */
63 #include <io.h>
64 #endif
65
66 #ifdef HAVE_UNISTD_H
67 #include <unistd.h>
68 #endif
69
70 extern void hugehelp(void);
71
72 static void helpf(char *fmt, ...)
73 {
74   va_list ap;
75   if(fmt) {
76     va_start(ap, fmt);
77     fputs("curl: ", stderr); /* prefix it */
78     vfprintf(stderr, fmt, ap);
79     va_end(ap);
80   }
81   fprintf(stderr, "curl: try 'curl --help' for more information\n");
82 }
83
84 static void help(void)
85 {
86   printf(CURL_ID "%s\n"
87        "Usage: curl [options...] <url>\n"
88        "Options: (H) means HTTP/HTTPS only, (F) means FTP only\n"
89        " -a/--append        Append to target file when uploading (F)\n"
90        " -A/--user-agent <string> User-Agent to send to server (H)\n"
91        " -b/--cookie <name=string/file> Cookie string or file to read cookies from (H)\n"
92        " -B/--ftp-ascii     Use ASCII transfer (F)\n"
93        " -c/--continue      Resume a previous transfer where we left it\n"
94        " -C/--continue-at <offset> Specify absolute resume offset\n"
95        " -d/--data          POST data (H)\n"
96        " -D/--dump-header <file> Write the headers to this file\n"
97        " -e/--referer       Referer page (H)\n"
98        " -E/--cert <cert:passwd> Specifies your certificate file and password (HTTPS)\n"
99        " -f/--fail          Fail silently (no output at all) on errors (H)\n"
100        " -F/--form <name=content> Specify HTTP POST data (H)\n"
101
102        " -h/--help          This help text\n"
103        " -H/--header <line> Custom header to pass to server. (H)\n"
104        " -i/--include       Include the HTTP-header in the output (H)\n"
105        " -I/--head          Fetch document info only (HTTP HEAD/FTP SIZE)\n"
106        " -K/--config        Specify which config file to read\n"
107        " -l/--list-only     List only names of an FTP directory (F)\n"
108        " -L/--location      Follow Location: hints (H)\n"
109        " -m/--max-time <seconds> Maximum time allowed for the transfer\n"
110        " -M/--manual        Display huge help text\n"
111        " -n/--netrc         Read .netrc for user name and password\n"
112        " -o/--output <file> Write output to <file> instead of stdout\n"
113        " -O/--remote-name   Write output to a file named as the remote file\n"
114 #if 0
115        " -p/--port <port>   Use port other than default for current protocol.\n"
116 #endif
117        " -P/--ftpport <address> Use PORT with address instead of PASV when ftping (F)\n"
118        " -q                 When used as the first parameter disables .curlrc\n"
119        " -Q/--quote <cmd>   Send QUOTE command to FTP before file transfer (F)\n"
120        " -r/--range <range> Retrieve a byte range from a HTTP/1.1 or FTP server\n"
121        " -s/--silent        Silent mode. Don't output anything\n"
122        " -S/--show-error    Show error. With -s, make curl show errors when they occur\n"
123        " -t/--upload        Transfer/upload stdin to remote site\n"
124        " -T/--upload-file <file> Transfer/upload <file> to remote site\n"
125        " -u/--user <user:password> Specify user and password to use\n"
126        " -U/--proxy-user <user:password> Specify Proxy authentication\n"
127        " -v/--verbose       Makes the operation more talkative\n"
128        " -V/--version       Outputs version number then quits\n"
129        " -x/--proxy <host>  Use proxy. (Default port is 1080)\n"
130        " -X/--request <command> Specific request command to use\n"
131        " -y/--speed-limit   Stop transfer if below speed-limit for 'speed-time' secs\n"
132        " -Y/--speed-time    Time needed to trig speed-limit abort. Defaults to 30\n"
133        " -z/--time-cond <time> Includes a time condition to the server (H)\n"
134        " -2/--sslv2         Force usage of SSLv2 (H)\n"
135        " -3/--sslv3         Force usage of SSLv3 (H)\n"
136        " -#/--progress-bar  Display transfer progress as a progress bar\n"
137        "    --crlf          Convert LF to CRLF in upload. Useful for MVS (OS/390)\n"
138        "    --stderr <file> Where to redirect stderr. - means stdout.\n",
139          curl_version()
140          );
141 }
142
143 struct LongShort {
144   char *letter;
145   char *lname;
146   bool extraparam;
147 };
148
149 struct Configurable {
150   char *useragent;
151   char *cookie;
152   bool use_resume;
153   int resume_from;
154   char *postfields;
155   char *referer;
156   long timeout;
157   char *outfile;
158   char *headerfile;
159   char remotefile;
160   char *ftpport;
161   unsigned short porttouse;
162   char *range;
163   int low_speed_limit;
164   int low_speed_time;
165   bool showerror;
166   char *infile;
167   char *userpwd;
168   char *proxyuserpwd;
169   char *proxy;
170   bool configread;
171   long conf;
172   char *url;
173   char *cert;
174   char *cert_passwd;
175   bool crlf;
176   char *cookiefile;
177   char *customrequest;
178   bool progressmode;
179
180   FILE *errors; /* if stderr redirect is requested */
181
182   struct curl_slist *quote;
183
184   long ssl_version;
185   TimeCond timecond;
186   time_t condtime;
187
188   struct HttpHeader *headers;
189   struct HttpHeader *last_header;
190
191   struct HttpPost *httppost;
192   struct HttpPost *last_post;
193 };
194
195 static int parseconfig(char *filename,
196                        struct Configurable *config);
197
198 static void GetStr(char **string,
199                    char *value)
200 {
201   if(*string)
202     free(*string);
203   *string = strdup(value);
204 }
205
206 static char *file2string(FILE *file)
207 {
208   char buffer[256];
209   char *ptr;
210   char *string=NULL;
211   int len=0;
212   int stringlen;
213
214   if(file) {
215     while(fgets(buffer, sizeof(buffer), file)) {
216       ptr= strchr(buffer, '\r');
217       if(ptr)
218         *ptr=0;
219       ptr= strchr(buffer, '\n');
220       if(ptr)
221         *ptr=0;
222       stringlen=strlen(buffer);
223       if(string)
224         string = realloc(string, len+stringlen+1);
225       else
226         string = malloc(stringlen+1);
227
228       strcpy(string+len, buffer);
229
230       len+=stringlen;
231     }
232     return string;
233   }
234   else
235     return NULL; /* no string */
236 }
237
238 static int getparameter(char *flag, /* f or -long-flag */
239                         char *nextarg, /* NULL if unset */
240                         bool *usedarg, /* set to TRUE if the arg has been
241                                           used */
242                         struct Configurable *config)
243 {
244   char letter;
245   char *parse=NULL;
246   int res;
247   struct HttpHeader *head;
248   int j;
249   time_t now;
250   int hit=-1;
251
252   /* single-letter,
253      long-name,
254      boolean whether it takes an additional argument
255      */
256   struct LongShort aliases[]= {
257     {"9", "crlf",        FALSE},
258     {"8", "stderr",      TRUE},
259
260     {"2", "sslv2",       FALSE},
261     {"3", "sslv3",       FALSE},
262     {"a", "append",      FALSE},
263     {"A", "user-agent",  TRUE},
264     {"b", "cookie",      TRUE},
265     {"B", "ftp-ascii",   FALSE},
266     {"c", "continue",    FALSE},
267     {"C", "continue-at", TRUE},
268     {"d", "data",        TRUE},
269     {"D", "dump-header", TRUE},
270     {"e", "referer",     TRUE},
271     {"E", "cert",        TRUE},
272     {"f", "fail",        FALSE},
273     {"F", "form",        TRUE},
274
275     {"h", "help",        FALSE},
276     {"H", "header",      TRUE},
277     {"i", "include",     FALSE},
278     {"I", "head",        FALSE},
279     {"K", "config",      TRUE},
280     {"l", "list-only",   FALSE},
281     {"L", "location",    FALSE},
282     {"m", "max-time",    TRUE},
283     {"M", "manual",      FALSE},
284     {"n", "netrc",       FALSE},
285     {"o", "output",      TRUE},
286     {"O", "remote-name", FALSE},
287 #if 0
288     {"p", "port",        TRUE},
289 #endif
290     {"P", "ftpport",     TRUE},
291     {"q", "disable",     FALSE},
292     {"Q", "quote",       TRUE},
293     {"r", "range",       TRUE},
294     {"s", "silent",      FALSE},
295     {"S", "show-error",  FALSE},
296     {"t", "upload",      FALSE},
297     {"T", "upload-file", TRUE},
298     {"u", "user",        TRUE},
299     {"U", "proxy-user",  TRUE},
300     {"v", "verbose",     FALSE},
301     {"V", "version",     FALSE},
302     {"x", "proxy",       TRUE},
303     {"X", "request",     TRUE},
304     {"X", "http-request", TRUE}, /* OBSOLETE VERSION */
305     {"y", "speed-time",  TRUE},
306     {"Y", "speed-limit", TRUE},
307     {"z", "time-cond",   TRUE},
308     {"#", "progress-bar",FALSE},
309   };
310
311   if('-' == flag[0]) {
312     /* try a long name */
313     int fnam=strlen(&flag[1]);
314     for(j=0; j< sizeof(aliases)/sizeof(aliases[0]); j++) {
315       if(strnequal(aliases[j].lname, &flag[1], fnam)) {
316         if(strequal(aliases[j].lname, &flag[1])) {
317           parse = aliases[j].letter;
318           hit = j;
319           break;
320         }
321         if(parse) {
322           /* this is the second match, we can't continue! */
323           helpf("option --%s is ambiguous\n", &flag[1]);
324           return URG_FAILED_INIT;
325         }
326         parse = aliases[j].letter;
327         hit = j;
328       }
329     }
330     if(hit < 0) {
331       helpf("unknown option -%s.\n", flag);
332       return URG_FAILED_INIT;
333     }    
334   }
335   else {
336     hit=-1;
337     parse = flag;
338   }
339
340   do {
341     /* we can loop here if we have multiple single-letters */
342
343     letter = parse?*parse:'\0';
344     *usedarg = FALSE; /* default is that we don't use the arg */
345
346 #if 0
347     fprintf(stderr, "OPTION: %c %s\n", letter, nextarg?nextarg:"<null>");
348 #endif
349     if(hit < 0) {
350       for(j=0; j< sizeof(aliases)/sizeof(aliases[0]); j++) {
351         if(letter == *aliases[j].letter) {
352           hit = j;
353           break;
354         }
355       }
356       if(hit < 0) {
357         helpf("unknown option -%c.\n", letter);
358         return URG_FAILED_INIT;      
359       }
360     }
361     if(hit < 0) {
362       helpf("unknown option -%c.\n", letter);
363       return URG_FAILED_INIT;
364     }    
365     if(!nextarg && aliases[hit].extraparam) {
366       helpf("option -%s/--%s requires an extra argument!\n",
367             aliases[hit].letter,
368             aliases[hit].lname);
369       return URG_FAILED_INIT;
370     }
371     else if(nextarg && aliases[hit].extraparam)
372       *usedarg = TRUE; /* mark it as used */
373
374     switch(letter) {
375     case 'z': /* time condition coming up */
376       switch(*nextarg) {
377       case '+':
378         nextarg++;
379       default:
380         /* If-Modified-Since: (section 14.28 in RFC2068) */
381         config->timecond = TIMECOND_IFMODSINCE;
382         break;
383       case '-':
384         /* If-Unmodified-Since:  (section 14.24 in RFC2068) */
385         config->timecond = TIMECOND_IFUNMODSINCE;
386         nextarg++;
387         break;
388       case '=':
389         /* Last-Modified:  (section 14.29 in RFC2068) */
390         config->timecond = TIMECOND_LASTMOD;
391         nextarg++;
392         break;
393       }
394       now=time(NULL);
395       config->condtime=get_date(nextarg, &now);
396       if(-1 == config->condtime) {
397         /* now let's see if it is a file name to get the time from instead! */
398         struct stat statbuf;
399         if(-1 == stat(nextarg, &statbuf)) {
400           /* failed, remove time condition */
401           config->timecond = TIMECOND_NONE;
402         }
403         else {
404           /* pull the time out from the file */
405           config->condtime = statbuf.st_mtime;
406         }
407       }
408       break;
409     case '9': /* there is no short letter for this */
410       /* LF -> CRLF conversinon? */
411       config->crlf = TRUE;
412       break;
413     case '8': /* there is no short letter for this */
414       if(strcmp(nextarg, "-"))
415         config->errors = fopen(nextarg, "wt");
416       else
417         config->errors = stdout;
418       break;
419     case '#': /* added 19990617 larsa */
420       config->progressmode ^= CURL_PROGRESS_BAR;
421       break;
422     case '2': 
423       /* SSL version 2 */
424       config->ssl_version = 2;
425       break;
426     case '3': 
427       /* SSL version 2 */
428       config->ssl_version = 3;
429       break;
430     case 'a':
431       /* This makes the FTP sessions use APPE instead of STOR */
432       config->conf ^= CONF_FTPAPPEND;
433       break;
434     case 'A':
435       /* This specifies the User-Agent name */
436       GetStr(&config->useragent, nextarg);
437       break;
438     case 'b': /* cookie string coming up: */
439       if(strchr(nextarg, '=')) {
440         /* A cookie string must have a =-letter */
441         GetStr(&config->cookie, nextarg);
442       }
443       else {
444         /* We have a cookie file to read from! */
445         GetStr(&config->cookiefile, nextarg);
446       }
447       break;
448     case 'B':
449       /* use type ASCII when transfering ftp files */
450       config->conf ^= CONF_FTPASCII;
451       break;
452     case 'c':
453       /* This makes us continue an ftp transfer */
454       config->use_resume^=TRUE;
455       break;
456     case 'C':
457       /* This makes us continue an ftp transfer at given position */
458       config->resume_from= atoi(nextarg);
459       config->use_resume=TRUE;
460       break;
461     case 'd':
462       /* postfield data */
463       if('@' == *nextarg) {
464         /* the data begins with a '@' letter, it means that a file name
465            or - (stdin) follows */
466         FILE *file;
467         nextarg++; /* pass the @ */
468         if(strequal("-", nextarg))
469           file = stdin;
470         else 
471           file = fopen(nextarg, "r");
472         config->postfields = file2string(file);
473         if(file && (file != stdin))
474           fclose(stdin);
475       }
476       else {
477         GetStr(&config->postfields, nextarg);
478       }
479       if(config->postfields)
480         config->conf |= CONF_POST;
481       break;
482     case 'D':
483       /* dump-header to given file name */
484       GetStr(&config->headerfile, nextarg);
485       break;
486     case 'e':
487       GetStr(&config->referer, nextarg);
488       config->conf |= CONF_REFERER;
489       break;
490     case 'E':
491       {
492         char *ptr = strchr(nextarg, ':');
493         if(ptr) {
494           /* we have a password too */
495           *ptr=0;
496           ptr++;
497           GetStr(&config->cert_passwd, ptr);
498         }
499         GetStr(&config->cert, nextarg);
500       }
501       break;
502     case 'f':
503       /* fail hard on errors  */
504       config->conf ^= CONF_FAILONERROR;
505       break;
506     case 'F':
507       /* "form data" simulation, this is a little advanced so lets do our best
508          to sort this out slowly and carefully */
509       if(curl_FormParse(nextarg,
510                         &config->httppost,
511                         &config->last_post))
512         return URG_FAILED_INIT;    
513       config->conf |= CONF_HTTPPOST; /* no toggle, OR! */
514       break;
515
516     case 'h': /* h for help */
517       help();
518       return URG_FAILED_INIT;
519     case 'H':
520       head = (struct HttpHeader *)malloc(sizeof(struct HttpHeader));
521       if(head) {
522         head->next = NULL;
523         head->header = NULL; /* first zero this */
524         GetStr(&head->header, nextarg); /* now get the header line */
525
526         /* point on our new one */
527         if(config->last_header)
528           config->last_header->next = head;
529         else {
530           config->headers = head;
531         }
532
533         config->last_header = head;
534       }
535       break;
536     case 'i':
537       config->conf ^= CONF_HEADER; /* include the HTTP header as well */
538       break;
539     case 'I':
540       config->conf ^= CONF_HEADER; /* include the HTTP header in the output */
541       config->conf ^= CONF_NOBODY; /* don't fetch the body at all */
542       break;
543     case 'K':
544       res = parseconfig(nextarg, config);
545       config->configread = TRUE;
546       if(res)
547         return res;
548       break;
549     case 'l':
550       config->conf ^= CONF_FTPLISTONLY; /* only list the names of the FTP dir */
551       break;
552     case 'L':
553       config->conf ^= CONF_FOLLOWLOCATION; /* Follow Location: HTTP headers */
554       break;
555     case 'm':
556       /* specified max time */
557       config->timeout = atoi(nextarg);
558       break;
559     case 'M': /* M for manual, huge help */
560       hugehelp();
561       return URG_FAILED_INIT;
562     case 'n':
563       /* pick info from .netrc, if this is used for http, curl will
564          automatically enfore user+password with the request */
565       config->conf ^= CONF_NETRC;
566       break;
567     case 'o':
568       /* output file */
569       GetStr(&config->outfile, nextarg); /* write to this file */
570       break;
571     case 'O':
572       /* output file */
573       config->remotefile ^= TRUE;
574       break;
575     case 'P':
576       /* This makes the FTP sessions use PORT instead of PASV */
577       /* use <eth0> or <192.168.10.10> style addresses. Anything except
578          this will make us try to get the "default" address.
579          NOTE: this is a changed behaviour since the released 4.1!
580          */
581       config->conf |= CONF_FTPPORT;
582       GetStr(&config->ftpport, nextarg);
583       break;
584 #if 0
585     case 'p':
586       /* specified port */
587       fputs("You've used the -p option, it will be removed in a future version\n",
588             stderr);
589       config->porttouse = atoi(nextarg);
590       config->conf |= CONF_PORT; /* changed port */
591       break;
592 #endif
593     case 'q': /* if used first, already taken care of, we do it like
594                  this so we don't cause an error! */
595       break;
596     case 'Q':
597       /* QUOTE command to send to FTP server */
598       config->quote = curl_slist_append(config->quote, nextarg);
599       break;
600     case 'r':
601       /* byte range requested */
602       GetStr(&config->range, nextarg);
603       config->conf |= CONF_RANGE;
604       break;
605     case 's':
606       /* don't show progress meter, don't show errors : */
607       config->conf |= (CONF_MUTE|CONF_NOPROGRESS);
608       config->showerror ^= TRUE; /* toggle off */
609       break;
610     case 'S':
611       /* show errors */
612       config->showerror ^= TRUE; /* toggle on if used with -s */
613       break;
614     case 't':
615       /* we are uploading */
616       config->conf ^= CONF_UPLOAD;
617       break;
618     case 'T':
619       /* we are uploading */
620       config->conf |= CONF_UPLOAD;
621       GetStr(&config->infile, nextarg);
622       break;
623     case 'u':
624       /* user:password  */
625       GetStr(&config->userpwd, nextarg);
626       config->conf |= CONF_USERPWD;
627       break;
628     case 'U':
629       /* Proxy user:password  */
630       GetStr(&config->proxyuserpwd, nextarg);
631       config->conf |= CONF_PROXYUSERPWD;
632       break;
633     case 'v':
634       config->conf ^= CONF_VERBOSE; /* talk a lot */
635       break;
636     case 'V':
637       printf(CURL_ID "%s\n", curl_version());
638       return URG_FAILED_INIT;
639     case 'x':
640       /* proxy */
641       if(!*nextarg) {
642         /* disable proxy when no proxy is given */
643         config->conf &= ~CONF_PROXY;
644       }
645       else { 
646         config->conf |= CONF_PROXY;
647         GetStr(&config->proxy, nextarg);
648       }
649       break;
650     case 'X':
651       /* HTTP request */
652       GetStr(&config->customrequest, nextarg);
653       break;
654     case 'Y':
655       /* low speed time */
656       config->low_speed_time = atoi(nextarg);
657       if(!config->low_speed_limit)
658         config->low_speed_limit = 1;
659       break;
660     case 'y':
661       /* low speed limit */
662       config->low_speed_limit = atoi(nextarg);
663       if(!config->low_speed_time)
664         config->low_speed_time=30;
665       break;
666
667     default: /* unknown flag */
668       if(letter)        
669         helpf("Unknown option '%c'\n", letter);
670       else
671         helpf("Unknown option\n"); /* short help blurb */
672       return URG_FAILED_INIT;
673     }
674     hit = -1;
675
676   } while(*++parse && !*usedarg);
677
678   return URG_OK;
679 }
680
681
682 static int parseconfig(char *filename,
683                        struct Configurable *config)
684 {
685   int res;
686   FILE *file;
687   char configbuffer[4096];
688   char filebuffer[256];
689   bool usedarg;
690   
691   if(!filename || !*filename) {
692     /* NULL or no file name attempts to load .curlrc from the homedir! */
693
694 #define CURLRC DOT_CHAR "curlrc"
695
696     char *home = curl_GetEnv("HOME"); /* portable environment reader */
697
698     if(!home || (strlen(home)>(sizeof(filebuffer)-strlen(CURLRC))))
699       return URG_OK;
700
701     sprintf(filebuffer, "%s%s%s", home, DIR_CHAR, CURLRC);
702
703     filename = filebuffer;
704   }
705
706   if(strcmp(filename,"-"))
707     file = fopen(filename, "r");
708   else
709     file = stdin;
710   
711   if(file) {
712     char *tok;
713     char *tok2;
714     while(fgets(configbuffer, sizeof(configbuffer), file)) {
715       /* lines with # in the fist column is a comment! */
716
717 #if 0
718       fprintf(stderr, "%s", configbuffer);
719 #endif
720       if('#' == configbuffer[0])
721         continue;
722       tok = configbuffer;
723
724       while(*tok && isspace((int)*tok))
725         tok++;
726 /*      tok=strtok(configbuffer, " \t\n"); */
727 #if 0
728       fprintf(stderr, "TOK: %s\n", tok);
729 #endif
730       if('-' != tok[0]) {
731         char *nl;
732         if(config->url)
733           free(config->url);
734         config->url = strdup(tok);
735         nl = strchr(config->url, '\n');
736         if(nl)
737           *nl=0;
738       }
739       while(('-' == tok[0])) {
740         /* this is a flag */
741         char *firsttok = strdup(tok);
742         char *nl;
743
744         /* remove newline from firsttok */
745         nl = strchr(firsttok, '\n');
746         if(nl)
747           *nl=0;
748
749         /* pass the -flag */
750         tok2=tok;
751         while(*tok2 && !isspace((int)*tok2))
752           tok2++;
753
754         /* pass the following white space */
755         while(*tok2 && isspace((int)*tok2))
756           tok2++;
757         
758         while(!*tok2 &&
759               fgets(configbuffer, sizeof(configbuffer), file)) {
760           /* lines with # in the fist column is a comment! */
761 #if 0
762           fprintf(stderr, "%s", configbuffer);
763 #endif
764           if('#' == configbuffer[0])
765             continue;
766           tok2 = configbuffer;
767           /*        tok2=strtok(configbuffer, " \t\n"); */
768           /* pass white space */
769           while(*tok2 && isspace((int)*tok2))
770             tok2++;
771         }
772         /* remove newline from tok2 */
773         nl = strchr(tok2, '\n');
774         if(nl)
775           *nl=0;
776
777         res = getparameter(firsttok+1,
778                            *tok2?tok2:NULL,
779                            &usedarg,
780                            config);
781         free(firsttok);
782 #if 0
783         fprintf(stderr, "TOK %s TOK2: %s RES: %d\n",
784                 firsttok, tok2?tok2:"NULL", res);
785 #endif
786         if(res)
787           return res;
788         if(!usedarg) {
789           /* tok2 is unused,  */
790           tok = tok2;
791         }
792         else 
793           break; /* we've used both our words */
794       }
795     }
796     if(file != stdin)
797       fclose(file);
798   }
799   return URG_OK;
800 }
801
802 struct OutStruct {
803   char *filename;
804   FILE *stream;
805 };
806
807 int my_fwrite(void *buffer, size_t size, size_t nmemb, FILE *stream)
808 {
809   struct OutStruct *out=(struct OutStruct *)stream;
810   if(out && !out->stream) {
811     /* open file for writing */
812     out->stream=fopen(out->filename, "wb");
813     if(!out->stream)
814       return -1; /* failure */
815   }
816   return fwrite(buffer, size, nmemb, out->stream);
817 }
818
819
820 int main(int argc, char *argv[])
821 {
822   char errorbuffer[URLGET_ERROR_SIZE];
823
824   struct OutStruct outs;
825
826   char *url = NULL;
827 #ifdef GLOBURL
828   URLGlob *urls;
829   int urlnum;
830   char *outfiles = NULL;
831   int separator = 0;
832 #endif
833   
834   FILE *infd = stdin;
835   FILE *headerfilep = NULL;
836   char *urlbuffer=NULL;
837   int infilesize=-1; /* -1 means unknown */
838   bool stillflags=TRUE;
839
840   int res=URG_OK;
841   int i;
842   struct Configurable config;
843
844   outs.stream = stdout;
845
846   memset(&config, 0, sizeof(struct Configurable));
847   
848   /* set non-zero default values: */
849   config.useragent= maprintf(CURL_NAME "/" CURL_VERSION " (" OS ") "
850                              "%s", curl_version());
851   config.showerror=TRUE;
852   config.conf=CONF_DEFAULT;
853   config.crlf=FALSE;
854   config.quote=NULL;
855
856   if(argc>1 &&
857      (!strnequal("--", argv[1], 2) && (argv[1][0] == '-')) &&
858      strchr(argv[1], 'q')) {
859     /*
860      * The first flag, that is not a verbose name, but a shortname
861      * and it includes the 'q' flag!
862      */
863 #if 0
864     fprintf(stderr, "I TURNED OFF THE CRAP\n");
865 #endif
866     ;
867   }
868   else {
869     res = parseconfig(NULL, &config);
870     if(res)
871       return res;
872   }
873
874   if ((argc < 2)  && !config.url) {
875     helpf(NULL);
876     return URG_FAILED_INIT;
877   }
878
879   /* Parse options */
880   for (i = 1; i < argc; i++) {
881     if(stillflags &&
882        ('-' == argv[i][0])) {
883       char *nextarg;
884       bool passarg;
885       
886       char *flag = &argv[i][1];
887
888       if(strequal("--", argv[i]))
889         /* this indicates the end of the flags and thus enables the
890            following (URL) argument to start with -. */
891         stillflags=FALSE;
892       else {
893         nextarg= (i < argc - 1)? argv[i+1]: NULL;
894
895         res = getparameter ( flag,
896                              nextarg,
897                              &passarg,
898                            &config );
899         if(res)
900           return res;
901
902         if(passarg) /* we're supposed to skip this */
903           i++;
904       }
905     }
906     else {
907       if(url) {
908         helpf("only one URL is supported!\n");
909         return URG_FAILED_INIT;
910       }
911       url = argv[i];
912     }
913   }
914
915   /* if no URL was specified and there was one in the config file, get that
916      one */
917   if(!url && config.url)
918     url = config.url;
919   
920   if(!url) {
921     helpf("no URL specified!\n");
922     return URG_FAILED_INIT;
923   }
924 #if 0
925     fprintf(stderr, "URL: %s PROXY: %s\n", url, config.proxy?config.proxy:"none");
926 #endif
927
928 #ifdef GLOBURL
929   urlnum = glob_url(&urls, url);        /* expand '{...}' and '[...]' expressions and return
930                                            total number of URLs in pattern set */
931   outfiles = config.outfile;            /* save outfile pattern befor expansion */
932   if (!outfiles && !config.remotefile && urlnum > 1) {
933 #ifdef CURL_SEPARATORS
934     /* multiple files extracted to stdout, insert separators! */
935     separator = 1;
936 #endif
937 #ifdef MIME_SEPARATORS
938     /* multiple files extracted to stdout, insert MIME separators! */
939     separator = 1;
940     printf("MIME-Version: 1.0\n");
941     printf("Content-Type: multipart/mixed; boundary=%s\n\n", MIMEseparator);
942 #endif
943   }
944   for (i = 0; (url = next_url(urls)); ++i) {
945     if (outfiles)
946       config.outfile = strdup(outfiles);
947 #endif
948
949   if(config.outfile && config.infile) {
950     helpf("you can't both upload and download!\n");
951     return URG_FAILED_INIT;
952   }
953  
954   if (config.outfile || config.remotefile) {
955     /* 
956      * We have specified a file name to store the result in, or we have
957      * decided we want to use the remote file name.
958      */
959
960     if(config.remotefile) {
961       /* Find and get the remote file name */
962       config.outfile=strstr(url, "://");
963       if(config.outfile)
964         config.outfile+=3;
965       else
966         config.outfile=url;
967       config.outfile = strrchr(config.outfile, '/');
968       if(!config.outfile || !strlen(++config.outfile)) {
969         helpf("Remote file name has no length!\n");
970         return URG_WRITE_ERROR;
971       }
972     }
973 #ifdef GLOBURL
974     else        /* fill '#1' ... '#9' terms from URL pattern */
975       config.outfile = match_url(config.outfile, *urls);
976 #endif
977
978     if((0 == config.resume_from) && config.use_resume) {
979       /* we're told to continue where we are now, then we get the size of the
980          file as it is now and open it for append instead */
981       struct stat fileinfo;
982
983       if(0 == stat(config.outfile, &fileinfo)) {
984         /* set offset to current file size: */
985         config.resume_from = fileinfo.st_size;
986       }
987       /* else let offset remain 0 */
988     }
989
990     if(config.resume_from) {
991       /* open file for output: */
992       outs.stream=(FILE *) fopen(config.outfile, config.resume_from?"ab":"wb");
993       if (!outs.stream) {
994         helpf("Can't open '%s'!\n", config.outfile);
995         return URG_WRITE_ERROR;
996       }
997     }
998     else {
999       outs.filename = config.outfile;
1000       outs.stream = NULL; /* open when needed */
1001     }
1002   }
1003   if (config.infile) {
1004     /*
1005      * We have specified a file to upload
1006      */
1007     struct stat fileinfo;
1008
1009     /* If no file name part is given in the URL, we add this file name */
1010     char *ptr=strstr(url, "://");
1011     if(ptr)
1012       ptr+=3;
1013     else
1014       ptr=url;
1015     ptr = strrchr(ptr, '/');
1016     if(!ptr || !strlen(++ptr)) {
1017       /* The URL has no file name part, add the local file name. In order
1018          to be able to do so, we have to create a new URL in another buffer.*/
1019       urlbuffer=(char *)malloc(strlen(url) + strlen(config.infile) + 3);
1020       if(!urlbuffer) {
1021         helpf("out of memory\n");
1022         return URG_OUT_OF_MEMORY;
1023       }
1024       if(ptr)
1025         /* there is a trailing slash on the URL */
1026         sprintf(urlbuffer, "%s%s", url, config.infile);
1027       else
1028         /* thers is no trailing slash on the URL */
1029         sprintf(urlbuffer, "%s/%s", url, config.infile);
1030
1031       url = urlbuffer; /* use our new URL instead! */
1032     }
1033
1034     infd=(FILE *) fopen(config.infile, "rb");
1035     if (!infd || stat(config.infile, &fileinfo)) {
1036       helpf("Can't open '%s'!\n", config.infile);
1037       return URG_READ_ERROR;
1038     }
1039     infilesize=fileinfo.st_size;
1040
1041   }
1042   if((config.conf&CONF_UPLOAD) &&
1043      config.use_resume &&
1044      (0==config.resume_from)) {
1045     config.resume_from = -1; /* -1 will then force get-it-yourself */
1046   }
1047   if(config.headerfile) {
1048     /* open file for output: */
1049     if(strcmp(config.headerfile,"-"))
1050     {
1051       headerfilep=(FILE *) fopen(config.headerfile, "wb");
1052       if (!headerfilep) {
1053         helpf("Can't open '%s'!\n", config.headerfile);
1054         return URG_WRITE_ERROR;
1055       }
1056     }
1057     else
1058       headerfilep=stdout;
1059   }
1060
1061   /* This was previously done in urlget, but that was wrong place to do it */
1062   if(outs.stream && isatty(fileno(outs.stream)))
1063     /* we send the output to a tty, and therefor we switch off the progress
1064        meter right away */
1065     config.conf |= CONF_NOPROGRESS;
1066
1067 #ifdef GLOBURL
1068   if (urlnum > 1) {
1069     fprintf(stderr, "\n[%d/%d]: %s --> %s\n", i+1, urlnum, url, config.outfile ? config.outfile : "<stdout>");
1070     if (separator) {
1071 #ifdef CURL_SEPARATORS
1072       printf("%s%s\n", CURLseparator, url);
1073 #endif
1074 #ifdef MIME_SEPARATORS
1075       printf("--%s\n", MIMEseparator);
1076       printf("Content-ID: %s\n\n", url); 
1077 #endif
1078     }
1079   }
1080 #endif
1081
1082   if(!config.errors)
1083     config.errors = stderr;
1084
1085   res = curl_urlget(URGTAG_FILE, (FILE *)&outs,  /* where to store */
1086                     URGTAG_WRITEFUNCTION, my_fwrite, /* what call to write */
1087                     URGTAG_INFILE, infd, /* for uploads */
1088                     URGTAG_INFILESIZE, infilesize, /* size of uploaded file */
1089                     URGTAG_URL, url,     /* what to fetch */
1090                     URGTAG_PROXY, config.proxy, /* proxy to use */
1091                     URGTAG_FLAGS, config.conf, /* flags */
1092                     URGTAG_USERPWD, config.userpwd, /* user + passwd */
1093                     URGTAG_PROXYUSERPWD, config.proxyuserpwd, /* Proxy user + passwd */
1094                     URGTAG_RANGE, config.range, /* range of document */
1095                     URGTAG_ERRORBUFFER, errorbuffer,
1096                     URGTAG_TIMEOUT, config.timeout,
1097                     URGTAG_POSTFIELDS, config.postfields,
1098                     URGTAG_REFERER, config.referer,
1099                     URGTAG_USERAGENT, config.useragent,
1100                     URGTAG_FTPPORT, config.ftpport,
1101                     URGTAG_LOW_SPEED_LIMIT, config.low_speed_limit,
1102                     URGTAG_LOW_SPEED_TIME, config.low_speed_time,
1103                     URGTAG_RESUME_FROM, config.use_resume?config.resume_from:0,
1104                     URGTAG_COOKIE, config.cookie,
1105                     URGTAG_HTTPHEADER, config.headers,
1106                     URGTAG_HTTPPOST, config.httppost,
1107                     URGTAG_SSLCERT, config.cert,
1108                     URGTAG_SSLCERTPASSWD, config.cert_passwd,
1109                     URGTAG_CRLF, config.crlf,
1110                     URGTAG_QUOTE, config.quote,
1111                     URGTAG_WRITEHEADER, headerfilep,
1112                     URGTAG_COOKIEFILE, config.cookiefile,
1113                     URGTAG_SSLVERSION, config.ssl_version,
1114                     URGTAG_TIMECONDITION, config.timecond,
1115                     URGTAG_TIMEVALUE, config.condtime,
1116                     URGTAG_CUSTOMREQUEST, config.customrequest,
1117                     URGTAG_STDERR, config.errors,
1118                     URGTAG_DONE); /* always terminate the list of tags */
1119   if((res!=URG_OK) && config.showerror)
1120     fprintf(config.errors, "curl: (%d) %s\n", res, errorbuffer);
1121
1122   if((config.errors != stderr) &&
1123      (config.errors != stdout))
1124     /* it wasn't directed to stdout or stderr so close the file! */
1125     fclose(config.errors);
1126
1127   if(urlbuffer)
1128     free(urlbuffer);
1129   if (config.outfile && outs.stream)
1130     fclose(outs.stream);
1131   if (config.infile)
1132     fclose(infd);
1133   if(headerfilep)
1134     fclose(headerfilep);
1135
1136   if(config.url)
1137     free(config.url);
1138
1139 #ifdef GLOBURL
1140   if(url)
1141     free(url);
1142   if(config.outfile && !config.remotefile)
1143     free(config.outfile);
1144   }
1145 #ifdef MIME_SEPARATORS
1146   if (separator)
1147     printf("--%s--\n", MIMEseparator);
1148 #endif
1149 #endif
1150
1151   curl_slist_free_all(config.quote); /* the checks for config.quote == NULL */
1152
1153   return(res);
1154 }