Skip to content
Snippets Groups Projects
main.c 32.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • Daniel Stenberg's avatar
    Daniel Stenberg committed
    /*****************************************************************************
     *                                  _   _ ____  _     
     *  Project                     ___| | | |  _ \| |    
     *                             / __| | | | |_) | |    
     *                            | (__| |_| |  _ <| |___ 
     *                             \___|\___/|_| \_\_____|
     *
     *  The contents of this file are subject to the Mozilla Public License
     *  Version 1.0 (the "License"); you may not use this file except in
     *  compliance with the License. You may obtain a copy of the License at
     *  http://www.mozilla.org/MPL/
     *
     *  Software distributed under the License is distributed on an "AS IS"
     *  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
     *  License for the specific language governing rights and limitations
     *  under the License.
     *
     *  The Original Code is Curl.
     *
     *  The Initial Developer of the Original Code is Daniel Stenberg.
     *
     *  Portions created by the Initial Developer are Copyright (C) 1998.
     *  All Rights Reserved.
     *
     * ------------------------------------------------------------
     * Main author:
     * - Daniel Stenberg <Daniel.Stenberg@haxx.nu>
     *
     * 	http://curl.haxx.nu
     *
     * $Source$
     * $Revision$
     * $Date$
     * $Author$
     * $State$
     * $Locker$
     *
     * ------------------------------------------------------------
     ****************************************************************************/
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stdarg.h>
    #include <sys/stat.h>
    #include <ctype.h>
    
    #include <curl/curl.h>
    #include <curl/mprintf.h>
    #include "../lib/getdate.h"
    #ifdef GLOBURL
    #include "urlglob.h"
    #define CURLseparator	"--_curl_--"
    #define MIMEseparator	"_curl_"
    #endif
    
    /* This is now designed to have its own local setup.h */
    #include "setup.h"
    
    #include "version.h"
    
    #ifdef HAVE_IO_H /* typical win32 habit */
    #include <io.h>
    #endif
    
    #ifdef HAVE_UNISTD_H
    #include <unistd.h>
    #endif
    
    extern void hugehelp(void);
    
    static void helpf(char *fmt, ...)
    {
      va_list ap;
      if(fmt) {
        va_start(ap, fmt);
        fputs("curl: ", stderr); /* prefix it */
        vfprintf(stderr, fmt, ap);
        va_end(ap);
      }
      fprintf(stderr, "curl: try 'curl --help' for more information\n");
    }
    
    static void help(void)
    {
      printf(CURL_ID "%s\n"
           "Usage: curl [options...] <url>\n"
           "Options: (H) means HTTP/HTTPS only, (F) means FTP only\n"
           " -a/--append        Append to target file when uploading (F)\n"
           " -A/--user-agent <string> User-Agent to send to server (H)\n"
           " -b/--cookie <name=string/file> Cookie string or file to read cookies from (H)\n"
           " -B/--ftp-ascii     Use ASCII transfer (F)\n"
           " -c/--continue      Resume a previous transfer where we left it\n"
           " -C/--continue-at <offset> Specify absolute resume offset\n"
           " -d/--data          POST data (H)\n"
           " -D/--dump-header <file> Write the headers to this file\n"
           " -e/--referer       Referer page (H)\n"
           " -E/--cert <cert:passwd> Specifies your certificate file and password (HTTPS)\n"
           " -f/--fail          Fail silently (no output at all) on errors (H)\n"
           " -F/--form <name=content> Specify HTTP POST data (H)\n"
    
           " -h/--help          This help text\n"
           " -H/--header <line> Custom header to pass to server. (H)\n"
           " -i/--include       Include the HTTP-header in the output (H)\n"
           " -I/--head          Fetch document info only (HTTP HEAD/FTP SIZE)\n"
           " -K/--config        Specify which config file to read\n"
           " -l/--list-only     List only names of an FTP directory (F)\n"
           " -L/--location      Follow Location: hints (H)\n"
           " -m/--max-time <seconds> Maximum time allowed for the transfer\n"
           " -M/--manual        Display huge help text\n"
           " -n/--netrc         Read .netrc for user name and password\n"
           " -o/--output <file> Write output to <file> instead of stdout\n"
           " -O/--remote-name   Write output to a file named as the remote file\n"
    #if 0
           " -p/--port <port>   Use port other than default for current protocol.\n"
    #endif
           " -P/--ftpport <address> Use PORT with address instead of PASV when ftping (F)\n"
           " -q                 When used as the first parameter disables .curlrc\n"
           " -Q/--quote <cmd>   Send QUOTE command to FTP before file transfer (F)\n"
           " -r/--range <range> Retrieve a byte range from a HTTP/1.1 or FTP server\n"
           " -s/--silent        Silent mode. Don't output anything\n"
           " -S/--show-error    Show error. With -s, make curl show errors when they occur\n"
           " -t/--upload        Transfer/upload stdin to remote site\n"
           " -T/--upload-file <file> Transfer/upload <file> to remote site\n"
           " -u/--user <user:password> Specify user and password to use\n"
           " -U/--proxy-user <user:password> Specify Proxy authentication\n"
           " -v/--verbose       Makes the operation more talkative\n"
           " -V/--version       Outputs version number then quits\n"
           " -x/--proxy <host>  Use proxy. (Default port is 1080)\n"
           " -X/--request <command> Specific request command to use\n"
    
           " -y/--speed-time    Time needed to trig speed-limit abort. Defaults to 30\n"
           " -Y/--speed-limit   Stop transfer if below speed-limit for 'speed-time' secs\n"
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
           " -z/--time-cond <time> Includes a time condition to the server (H)\n"
           " -2/--sslv2         Force usage of SSLv2 (H)\n"
           " -3/--sslv3         Force usage of SSLv3 (H)\n"
           " -#/--progress-bar  Display transfer progress as a progress bar\n"
           "    --crlf          Convert LF to CRLF in upload. Useful for MVS (OS/390)\n"
           "    --stderr <file> Where to redirect stderr. - means stdout.\n",
             curl_version()
             );
    }
    
    struct LongShort {
      char *letter;
      char *lname;
      bool extraparam;
    };
    
    struct Configurable {
      char *useragent;
      char *cookie;
      bool use_resume;
      int resume_from;
      char *postfields;
      char *referer;
      long timeout;
      char *outfile;
      char *headerfile;
      char remotefile;
      char *ftpport;
      unsigned short porttouse;
      char *range;
      int low_speed_limit;
      int low_speed_time;
      bool showerror;
      char *infile;
      char *userpwd;
      char *proxyuserpwd;
      char *proxy;
      bool configread;
      long conf;
      char *url;
      char *cert;
      char *cert_passwd;
      bool crlf;
      char *cookiefile;
      char *customrequest;
      bool progressmode;
    
      FILE *errors; /* if stderr redirect is requested */
    
      struct curl_slist *quote;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      struct curl_slist *postquote;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
      long ssl_version;
      TimeCond timecond;
      time_t condtime;
    
      struct HttpHeader *headers;
      struct HttpHeader *last_header;
    
      struct HttpPost *httppost;
      struct HttpPost *last_post;
    };
    
    static int parseconfig(char *filename,
    		       struct Configurable *config);
    
    static void GetStr(char **string,
    		   char *value)
    {
      if(*string)
        free(*string);
      *string = strdup(value);
    }
    
    static char *file2string(FILE *file)
    {
      char buffer[256];
      char *ptr;
      char *string=NULL;
      int len=0;
      int stringlen;
    
      if(file) {
        while(fgets(buffer, sizeof(buffer), file)) {
          ptr= strchr(buffer, '\r');
          if(ptr)
            *ptr=0;
          ptr= strchr(buffer, '\n');
          if(ptr)
            *ptr=0;
          stringlen=strlen(buffer);
          if(string)
            string = realloc(string, len+stringlen+1);
          else
            string = malloc(stringlen+1);
    
          strcpy(string+len, buffer);
    
          len+=stringlen;
        }
        return string;
      }
      else
        return NULL; /* no string */
    }
    
    static int getparameter(char *flag, /* f or -long-flag */
    			char *nextarg, /* NULL if unset */
    			bool *usedarg, /* set to TRUE if the arg has been
    					  used */
    			struct Configurable *config)
    {
      char letter;
      char *parse=NULL;
      int res;
      struct HttpHeader *head;
      int j;
      time_t now;
      int hit=-1;
    
      /* single-letter,
         long-name,
         boolean whether it takes an additional argument
         */
      struct LongShort aliases[]= {
        {"9", "crlf",        FALSE},
        {"8", "stderr",      TRUE},
    
        {"2", "sslv2",       FALSE},
        {"3", "sslv3",       FALSE},
        {"a", "append",      FALSE},
        {"A", "user-agent",  TRUE},
        {"b", "cookie",      TRUE},
        {"B", "ftp-ascii",   FALSE},
        {"c", "continue",    FALSE},
        {"C", "continue-at", TRUE},
        {"d", "data",        TRUE},
        {"D", "dump-header", TRUE},
        {"e", "referer",     TRUE},
        {"E", "cert",        TRUE},
        {"f", "fail",        FALSE},
        {"F", "form",        TRUE},
    
        {"h", "help",        FALSE},
        {"H", "header",      TRUE},
        {"i", "include",     FALSE},
        {"I", "head",        FALSE},
        {"K", "config",      TRUE},
        {"l", "list-only",   FALSE},
        {"L", "location",    FALSE},
        {"m", "max-time",    TRUE},
        {"M", "manual",      FALSE},
        {"n", "netrc",       FALSE},
        {"o", "output",      TRUE},
        {"O", "remote-name", FALSE},
    #if 0
        {"p", "port",        TRUE},
    #endif
        {"P", "ftpport",     TRUE},
        {"q", "disable",     FALSE},
        {"Q", "quote",       TRUE},
        {"r", "range",       TRUE},
        {"s", "silent",      FALSE},
        {"S", "show-error",  FALSE},
        {"t", "upload",      FALSE},
        {"T", "upload-file", TRUE},
        {"u", "user",        TRUE},
        {"U", "proxy-user",  TRUE},
        {"v", "verbose",     FALSE},
        {"V", "version",     FALSE},
        {"x", "proxy",       TRUE},
        {"X", "request",     TRUE},
        {"X", "http-request", TRUE}, /* OBSOLETE VERSION */
    
        {"Y", "speed-limit",  TRUE},
        {"y", "speed-time", TRUE},
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        {"z", "time-cond",   TRUE},
        {"#", "progress-bar",FALSE},
      };
    
      if('-' == flag[0]) {
        /* try a long name */
        int fnam=strlen(&flag[1]);
        for(j=0; j< sizeof(aliases)/sizeof(aliases[0]); j++) {
          if(strnequal(aliases[j].lname, &flag[1], fnam)) {
            if(strequal(aliases[j].lname, &flag[1])) {
              parse = aliases[j].letter;
              hit = j;
              break;
            }
    	if(parse) {
    	  /* this is the second match, we can't continue! */
    	  helpf("option --%s is ambiguous\n", &flag[1]);
    	  return URG_FAILED_INIT;
    	}
    	parse = aliases[j].letter;
    	hit = j;
          }
        }
        if(hit < 0) {
          helpf("unknown option -%s.\n", flag);
          return URG_FAILED_INIT;
        }    
      }
      else {
        hit=-1;
        parse = flag;
      }
    
      do {
        /* we can loop here if we have multiple single-letters */
    
        letter = parse?*parse:'\0';
        *usedarg = FALSE; /* default is that we don't use the arg */
    
    #if 0
        fprintf(stderr, "OPTION: %c %s\n", letter, nextarg?nextarg:"<null>");
    #endif
        if(hit < 0) {
          for(j=0; j< sizeof(aliases)/sizeof(aliases[0]); j++) {
    	if(letter == *aliases[j].letter) {
    	  hit = j;
    	  break;
    	}
          }
          if(hit < 0) {
    	helpf("unknown option -%c.\n", letter);
    	return URG_FAILED_INIT;      
          }
        }
        if(hit < 0) {
          helpf("unknown option -%c.\n", letter);
          return URG_FAILED_INIT;
        }    
        if(!nextarg && aliases[hit].extraparam) {
          helpf("option -%s/--%s requires an extra argument!\n",
    	    aliases[hit].letter,
    	    aliases[hit].lname);
          return URG_FAILED_INIT;
        }
        else if(nextarg && aliases[hit].extraparam)
          *usedarg = TRUE; /* mark it as used */
    
        switch(letter) {
        case 'z': /* time condition coming up */
          switch(*nextarg) {
          case '+':
            nextarg++;
          default:
            /* If-Modified-Since: (section 14.28 in RFC2068) */
            config->timecond = TIMECOND_IFMODSINCE;
            break;
          case '-':
            /* If-Unmodified-Since:  (section 14.24 in RFC2068) */
            config->timecond = TIMECOND_IFUNMODSINCE;
            nextarg++;
            break;
          case '=':
            /* Last-Modified:  (section 14.29 in RFC2068) */
            config->timecond = TIMECOND_LASTMOD;
            nextarg++;
            break;
          }
          now=time(NULL);
          config->condtime=get_date(nextarg, &now);
          if(-1 == config->condtime) {
            /* now let's see if it is a file name to get the time from instead! */
            struct stat statbuf;
            if(-1 == stat(nextarg, &statbuf)) {
              /* failed, remove time condition */
              config->timecond = TIMECOND_NONE;
            }
            else {
              /* pull the time out from the file */
              config->condtime = statbuf.st_mtime;
            }
          }
          break;
        case '9': /* there is no short letter for this */
          /* LF -> CRLF conversinon? */
          config->crlf = TRUE;
          break;
        case '8': /* there is no short letter for this */
          if(strcmp(nextarg, "-"))
            config->errors = fopen(nextarg, "wt");
          else
            config->errors = stdout;
          break;
        case '#': /* added 19990617 larsa */
          config->progressmode ^= CURL_PROGRESS_BAR;
          break;
        case '2': 
          /* SSL version 2 */
          config->ssl_version = 2;
          break;
        case '3': 
          /* SSL version 2 */
          config->ssl_version = 3;
          break;
        case 'a':
          /* This makes the FTP sessions use APPE instead of STOR */
          config->conf ^= CONF_FTPAPPEND;
          break;
        case 'A':
          /* This specifies the User-Agent name */
          GetStr(&config->useragent, nextarg);
          break;
        case 'b': /* cookie string coming up: */
          if(strchr(nextarg, '=')) {
            /* A cookie string must have a =-letter */
            GetStr(&config->cookie, nextarg);
          }
          else {
            /* We have a cookie file to read from! */
            GetStr(&config->cookiefile, nextarg);
          }
          break;
        case 'B':
          /* use type ASCII when transfering ftp files */
          config->conf ^= CONF_FTPASCII;
          break;
        case 'c':
          /* This makes us continue an ftp transfer */
          config->use_resume^=TRUE;
          break;
        case 'C':
          /* This makes us continue an ftp transfer at given position */
          config->resume_from= atoi(nextarg);
          config->use_resume=TRUE;
          break;
        case 'd':
          /* postfield data */
          if('@' == *nextarg) {
            /* the data begins with a '@' letter, it means that a file name
               or - (stdin) follows */
            FILE *file;
            nextarg++; /* pass the @ */
            if(strequal("-", nextarg))
              file = stdin;
            else 
              file = fopen(nextarg, "r");
            config->postfields = file2string(file);
            if(file && (file != stdin))
              fclose(stdin);
          }
          else {
            GetStr(&config->postfields, nextarg);
          }
          if(config->postfields)
            config->conf |= CONF_POST;
          break;
        case 'D':
          /* dump-header to given file name */
          GetStr(&config->headerfile, nextarg);
          break;
        case 'e':
          GetStr(&config->referer, nextarg);
          config->conf |= CONF_REFERER;
          break;
        case 'E':
          {
    	char *ptr = strchr(nextarg, ':');
    	if(ptr) {
    	  /* we have a password too */
    	  *ptr=0;
    	  ptr++;
    	  GetStr(&config->cert_passwd, ptr);
    	}
    	GetStr(&config->cert, nextarg);
          }
          break;
        case 'f':
          /* fail hard on errors  */
          config->conf ^= CONF_FAILONERROR;
          break;
        case 'F':
          /* "form data" simulation, this is a little advanced so lets do our best
    	 to sort this out slowly and carefully */
          if(curl_FormParse(nextarg,
                            &config->httppost,
                            &config->last_post))
    	return URG_FAILED_INIT;    
          config->conf |= CONF_HTTPPOST; /* no toggle, OR! */
          break;
    
        case 'h': /* h for help */
          help();
          return URG_FAILED_INIT;
        case 'H':
          head = (struct HttpHeader *)malloc(sizeof(struct HttpHeader));
          if(head) {
    	head->next = NULL;
    	head->header = NULL; /* first zero this */
    	GetStr(&head->header, nextarg); /* now get the header line */
    
    	/* point on our new one */
    	if(config->last_header)
    	  config->last_header->next = head;
    	else {
    	  config->headers = head;
    	}
    
    	config->last_header = head;
          }
          break;
        case 'i':
          config->conf ^= CONF_HEADER; /* include the HTTP header as well */
          break;
        case 'I':
          config->conf ^= CONF_HEADER; /* include the HTTP header in the output */
          config->conf ^= CONF_NOBODY; /* don't fetch the body at all */
          break;
        case 'K':
          res = parseconfig(nextarg, config);
          config->configread = TRUE;
          if(res)
    	return res;
          break;
        case 'l':
          config->conf ^= CONF_FTPLISTONLY; /* only list the names of the FTP dir */
          break;
        case 'L':
          config->conf ^= CONF_FOLLOWLOCATION; /* Follow Location: HTTP headers */
          break;
        case 'm':
          /* specified max time */
          config->timeout = atoi(nextarg);
          break;
        case 'M': /* M for manual, huge help */
          hugehelp();
          return URG_FAILED_INIT;
        case 'n':
          /* pick info from .netrc, if this is used for http, curl will
    	 automatically enfore user+password with the request */
          config->conf ^= CONF_NETRC;
          break;
        case 'o':
          /* output file */
          GetStr(&config->outfile, nextarg); /* write to this file */
          break;
        case 'O':
          /* output file */
          config->remotefile ^= TRUE;
          break;
        case 'P':
          /* This makes the FTP sessions use PORT instead of PASV */
          /* use <eth0> or <192.168.10.10> style addresses. Anything except
    	 this will make us try to get the "default" address.
    	 NOTE: this is a changed behaviour since the released 4.1!
    	 */
          config->conf |= CONF_FTPPORT;
          GetStr(&config->ftpport, nextarg);
          break;
    #if 0
        case 'p':
          /* specified port */
          fputs("You've used the -p option, it will be removed in a future version\n",
    	    stderr);
          config->porttouse = atoi(nextarg);
          config->conf |= CONF_PORT; /* changed port */
          break;
    #endif
        case 'q': /* if used first, already taken care of, we do it like
    		 this so we don't cause an error! */
          break;
        case 'Q':
          /* QUOTE command to send to FTP server */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
          if(nextarg[0] == '-') {
            /* prefixed with a dash makes it a POST TRANSFER one */
            nextarg++;
            config->postquote = curl_slist_append(config->postquote, nextarg);
          }
          else {
            config->quote = curl_slist_append(config->quote, nextarg);
          }
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
          break;
        case 'r':
          /* byte range requested */
          GetStr(&config->range, nextarg);
          config->conf |= CONF_RANGE;
          break;
        case 's':
          /* don't show progress meter, don't show errors : */
          config->conf |= (CONF_MUTE|CONF_NOPROGRESS);
          config->showerror ^= TRUE; /* toggle off */
          break;
        case 'S':
          /* show errors */
          config->showerror ^= TRUE; /* toggle on if used with -s */
          break;
        case 't':
          /* we are uploading */
          config->conf ^= CONF_UPLOAD;
          break;
        case 'T':
          /* we are uploading */
          config->conf |= CONF_UPLOAD;
          GetStr(&config->infile, nextarg);
          break;
        case 'u':
          /* user:password  */
          GetStr(&config->userpwd, nextarg);
          config->conf |= CONF_USERPWD;
          break;
        case 'U':
          /* Proxy user:password  */
          GetStr(&config->proxyuserpwd, nextarg);
          config->conf |= CONF_PROXYUSERPWD;
          break;
        case 'v':
          config->conf ^= CONF_VERBOSE; /* talk a lot */
          break;
        case 'V':
          printf(CURL_ID "%s\n", curl_version());
          return URG_FAILED_INIT;
        case 'x':
          /* proxy */
          if(!*nextarg) {
    	/* disable proxy when no proxy is given */
    	config->conf &= ~CONF_PROXY;
          }
          else { 
    	config->conf |= CONF_PROXY;
    	GetStr(&config->proxy, nextarg);
          }
          break;
        case 'X':
          /* HTTP request */
          GetStr(&config->customrequest, nextarg);
          break;
    
        case 'y':
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
          /* low speed time */
          config->low_speed_time = atoi(nextarg);
          if(!config->low_speed_limit)
    	config->low_speed_limit = 1;
          break;
    
        case 'Y':
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
          /* low speed limit */
          config->low_speed_limit = atoi(nextarg);
          if(!config->low_speed_time)
    	config->low_speed_time=30;
          break;
    
        default: /* unknown flag */
          if(letter)	
    	helpf("Unknown option '%c'\n", letter);
          else
    	helpf("Unknown option\n"); /* short help blurb */
          return URG_FAILED_INIT;
        }
        hit = -1;
    
      } while(*++parse && !*usedarg);
    
      return URG_OK;
    }
    
    
    static int parseconfig(char *filename,
    		       struct Configurable *config)
    {
      int res;
      FILE *file;
      char configbuffer[4096];
      char filebuffer[256];
      bool usedarg;
      
      if(!filename || !*filename) {
        /* NULL or no file name attempts to load .curlrc from the homedir! */
    
    #define CURLRC DOT_CHAR "curlrc"
    
        char *home = curl_GetEnv("HOME"); /* portable environment reader */
    
        if(!home || (strlen(home)>(sizeof(filebuffer)-strlen(CURLRC))))
          return URG_OK;
    
        sprintf(filebuffer, "%s%s%s", home, DIR_CHAR, CURLRC);
    
        filename = filebuffer;
      }
    
      if(strcmp(filename,"-"))
        file = fopen(filename, "r");
      else
        file = stdin;
      
      if(file) {
        char *tok;
        char *tok2;
        while(fgets(configbuffer, sizeof(configbuffer), file)) {
          /* lines with # in the fist column is a comment! */
    
    #if 0
          fprintf(stderr, "%s", configbuffer);
    #endif
          if('#' == configbuffer[0])
    	continue;
          tok = configbuffer;
    
          while(*tok && isspace((int)*tok))
    	tok++;
    /*      tok=strtok(configbuffer, " \t\n"); */
    #if 0
          fprintf(stderr, "TOK: %s\n", tok);
    #endif
          if('-' != tok[0]) {
    	char *nl;
    	if(config->url)
    	  free(config->url);
    	config->url = strdup(tok);
    	nl = strchr(config->url, '\n');
    	if(nl)
    	  *nl=0;
          }
          while(('-' == tok[0])) {
    	/* this is a flag */
    	char *firsttok = strdup(tok);
    	char *nl;
    
    	/* remove newline from firsttok */
    	nl = strchr(firsttok, '\n');
    	if(nl)
    	  *nl=0;
    
    	/* pass the -flag */
    	tok2=tok;
    	while(*tok2 && !isspace((int)*tok2))
    	  tok2++;
    
    	/* pass the following white space */
    	while(*tok2 && isspace((int)*tok2))
    	  tok2++;
    	
    	while(!*tok2 &&
    	      fgets(configbuffer, sizeof(configbuffer), file)) {
    	  /* lines with # in the fist column is a comment! */
    #if 0
    	  fprintf(stderr, "%s", configbuffer);
    #endif
    	  if('#' == configbuffer[0])
    	    continue;
    	  tok2 = configbuffer;
    	  /*	    tok2=strtok(configbuffer, " \t\n"); */
    	  /* pass white space */
    	  while(*tok2 && isspace((int)*tok2))
    	    tok2++;
    	}
    	/* remove newline from tok2 */
    	nl = strchr(tok2, '\n');
    	if(nl)
    	  *nl=0;
    
    	res = getparameter(firsttok+1,
    			   *tok2?tok2:NULL,
    			   &usedarg,
    			   config);
    	free(firsttok);
    #if 0
    	fprintf(stderr, "TOK %s TOK2: %s RES: %d\n",
    		firsttok, tok2?tok2:"NULL", res);
    #endif
    	if(res)
    	  return res;
    	if(!usedarg) {
    	  /* tok2 is unused,  */
    	  tok = tok2;
    	}
    	else 
    	  break; /* we've used both our words */
          }
        }
        if(file != stdin)
          fclose(file);
      }
      return URG_OK;
    }
    
    struct OutStruct {
      char *filename;
      FILE *stream;
    };
    
    int my_fwrite(void *buffer, size_t size, size_t nmemb, FILE *stream)
    {
      struct OutStruct *out=(struct OutStruct *)stream;
      if(out && !out->stream) {
        /* open file for writing */
        out->stream=fopen(out->filename, "wb");
        if(!out->stream)
          return -1; /* failure */
      }
      return fwrite(buffer, size, nmemb, out->stream);
    }
    
    
    int main(int argc, char *argv[])
    {
      char errorbuffer[URLGET_ERROR_SIZE];
    
      struct OutStruct outs;
    
      char *url = NULL;
    #ifdef GLOBURL
      URLGlob *urls;
      int urlnum;
      char *outfiles = NULL;
      int separator = 0;
    #endif
      
      FILE *infd = stdin;
      FILE *headerfilep = NULL;
      char *urlbuffer=NULL;
      int infilesize=-1; /* -1 means unknown */
      bool stillflags=TRUE;
    
      int res=URG_OK;
      int i;
      struct Configurable config;
    
      outs.stream = stdout;
    
      memset(&config, 0, sizeof(struct Configurable));
      
      /* set non-zero default values: */
      config.useragent= maprintf(CURL_NAME "/" CURL_VERSION " (" OS ") "
                                 "%s", curl_version());
      config.showerror=TRUE;
      config.conf=CONF_DEFAULT;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    #if 0
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      config.crlf=FALSE;
      config.quote=NULL;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    #endif
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
      if(argc>1 &&
         (!strnequal("--", argv[1], 2) && (argv[1][0] == '-')) &&
         strchr(argv[1], 'q')) {
        /*
         * The first flag, that is not a verbose name, but a shortname
         * and it includes the 'q' flag!
         */
    #if 0
        fprintf(stderr, "I TURNED OFF THE CRAP\n");
    #endif
        ;
      }
      else {
        res = parseconfig(NULL, &config);
        if(res)
          return res;
      }
    
      if ((argc < 2)  && !config.url) {
        helpf(NULL);
        return URG_FAILED_INIT;
      }
    
      /* Parse options */
      for (i = 1; i < argc; i++) {
        if(stillflags &&
           ('-' == argv[i][0])) {
          char *nextarg;
          bool passarg;
          
          char *flag = &argv[i][1];
    
          if(strequal("--", argv[i]))
    	/* this indicates the end of the flags and thus enables the
    	   following (URL) argument to start with -. */
    	stillflags=FALSE;
          else {
    	nextarg= (i < argc - 1)? argv[i+1]: NULL;
    
    	res = getparameter ( flag,
    			     nextarg,
    			     &passarg,
    			   &config );
    	if(res)
    	  return res;
    
    	if(passarg) /* we're supposed to skip this */
    	  i++;
          }
        }
        else {
          if(url) {
    	helpf("only one URL is supported!\n");
    	return URG_FAILED_INIT;
          }
          url = argv[i];
        }
      }
    
      /* if no URL was specified and there was one in the config file, get that
         one */
      if(!url && config.url)
        url = config.url;
      
      if(!url) {
        helpf("no URL specified!\n");
        return URG_FAILED_INIT;
      }
    #if 0
        fprintf(stderr, "URL: %s PROXY: %s\n", url, config.proxy?config.proxy:"none");
    #endif
    
    #ifdef GLOBURL
      urlnum = glob_url(&urls, url);	/* expand '{...}' and '[...]' expressions and return
    					   total number of URLs in pattern set */
      outfiles = config.outfile;		/* save outfile pattern befor expansion */
      if (!outfiles && !config.remotefile && urlnum > 1) {
    #ifdef CURL_SEPARATORS
        /* multiple files extracted to stdout, insert separators! */
        separator = 1;
    #endif
    #ifdef MIME_SEPARATORS
        /* multiple files extracted to stdout, insert MIME separators! */
        separator = 1;
        printf("MIME-Version: 1.0\n");
        printf("Content-Type: multipart/mixed; boundary=%s\n\n", MIMEseparator);
    #endif
      }
      for (i = 0; (url = next_url(urls)); ++i) {
        if (outfiles)
          config.outfile = strdup(outfiles);
    #endif
    
      if(config.outfile && config.infile) {
        helpf("you can't both upload and download!\n");
        return URG_FAILED_INIT;
      }
     
      if (config.outfile || config.remotefile) {
        /* 
         * We have specified a file name to store the result in, or we have
         * decided we want to use the remote file name.
         */
    
        if(config.remotefile) {
          /* Find and get the remote file name */
          config.outfile=strstr(url, "://");
          if(config.outfile)
            config.outfile+=3;
          else
            config.outfile=url;
          config.outfile = strrchr(config.outfile, '/');
          if(!config.outfile || !strlen(++config.outfile)) {
            helpf("Remote file name has no length!\n");
            return URG_WRITE_ERROR;
          }
        }
    #ifdef GLOBURL
        else	/* fill '#1' ... '#9' terms from URL pattern */
          config.outfile = match_url(config.outfile, *urls);
    #endif
    
        if((0 == config.resume_from) && config.use_resume) {
          /* we're told to continue where we are now, then we get the size of the
    	 file as it is now and open it for append instead */
          struct stat fileinfo;
    
          if(0 == stat(config.outfile, &fileinfo)) {
    	/* set offset to current file size: */
    	config.resume_from = fileinfo.st_size;
          }
          /* else let offset remain 0 */
        }
    
        if(config.resume_from) {