Skip to content
Snippets Groups Projects
main.c 83.7 KiB
Newer Older
    {"1", "tlsv1",       FALSE},
Daniel Stenberg's avatar
Daniel Stenberg committed
    {"2", "sslv2",       FALSE},
    {"3", "sslv3",       FALSE},
    {"a", "append",      FALSE},
    {"A", "user-agent",  TRUE},
    {"b", "cookie",      TRUE},
    {"B", "ftp-ascii",   FALSE}, /* this long format is OBSOLETE now! */
    {"B", "use-ascii",   FALSE},
    {"c", "cookie-jar",  TRUE},
Daniel Stenberg's avatar
Daniel Stenberg committed
    {"C", "continue-at", TRUE},
    {"d", "data",        TRUE},
    {"da", "data-ascii", TRUE},
    {"db", "data-binary", TRUE},
Daniel Stenberg's avatar
Daniel Stenberg committed
    {"D", "dump-header", TRUE},
    {"e", "referer",     TRUE},
    {"E", "cert",        TRUE},
    {"Ea", "cacert",     TRUE},
    {"Eb","cert-type",   TRUE},
    {"Ec","key",         TRUE},
    {"Ed","key-type",    TRUE},
    {"Ee","pass",        TRUE},
    {"Ef","engine",      TRUE},
    {"Eg","capath ",     TRUE},
Daniel Stenberg's avatar
Daniel Stenberg committed
    {"f", "fail",        FALSE},
    {"F", "form",        TRUE},
Daniel Stenberg's avatar
Daniel Stenberg committed
    {"h", "help",        FALSE},
    {"H", "header",      TRUE},
    {"i", "include",     FALSE},
    {"I", "head",        FALSE},
    {"j", "junk-session-cookies", FALSE},
Daniel Stenberg's avatar
Daniel Stenberg committed
    {"K", "config",      TRUE},
    {"l", "list-only",   FALSE},
    {"L", "location",    FALSE},
    {"m", "max-time",    TRUE},
    {"M", "manual",      FALSE},
    {"n", "netrc",       FALSE},
Daniel Stenberg's avatar
Daniel Stenberg committed
    {"o", "output",      TRUE},
    {"O", "remote-name", FALSE},
    {"p", "proxytunnel", FALSE},
Daniel Stenberg's avatar
Daniel Stenberg committed
    {"P", "ftpport",     TRUE},
    {"q", "disable",     FALSE},
    {"Q", "quote",       TRUE},
    {"r", "range",       TRUE},
Daniel Stenberg's avatar
Daniel Stenberg committed
    {"s", "silent",      FALSE},
    {"S", "show-error",  FALSE},
    {"t", "telnet-options", TRUE},
Daniel Stenberg's avatar
Daniel Stenberg committed
    {"T", "upload-file", TRUE},
    {"u", "user",        TRUE},
    {"U", "proxy-user",  TRUE},
    {"v", "verbose",     FALSE},
    {"V", "version",     FALSE},
Daniel Stenberg's avatar
Daniel Stenberg committed
    {"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},
    {"Z", "max-redirs",   TRUE},
Daniel Stenberg's avatar
Daniel Stenberg committed
    {"#", "progress-bar",FALSE},
  };

  if(('-' != flag[0]) ||
     (('-' == flag[0]) && ('-' == flag[1]))) {
    /* this should be a long name */
    char *word=('-' == flag[0])?flag+2:flag;
    int fnam=strlen(word);
    int numhits=0;
Daniel Stenberg's avatar
Daniel Stenberg committed
    for(j=0; j< sizeof(aliases)/sizeof(aliases[0]); j++) {
      if(strnequal(aliases[j].lname, word, fnam)) {
        longopt = TRUE;
        if(strequal(aliases[j].lname, word)) {
Daniel Stenberg's avatar
Daniel Stenberg committed
          parse = aliases[j].letter;
          hit = j;
          numhits = 1; /* a single unique hit */
Daniel Stenberg's avatar
Daniel Stenberg committed
          break;
        }
	parse = aliases[j].letter;
	hit = j;
      }
    }
    if(numhits>1) {
      /* this is at least the second match! */
Daniel Stenberg's avatar
Daniel Stenberg committed
    if(hit < 0) {
Daniel Stenberg's avatar
Daniel Stenberg committed
    }    
  }
  else {
    flag++; /* prefixed with one dash, pass it */
Daniel Stenberg's avatar
Daniel Stenberg committed
    hit=-1;
    parse = flag;
  }

  do {
    /* we can loop here if we have multiple single-letters */

    if(!longopt)
      letter = parse?*parse:'\0';
    else {
      letter = parse[0];
      subletter = parse[1];
    }
Daniel Stenberg's avatar
Daniel Stenberg committed
    *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[0]) {
Daniel Stenberg's avatar
Daniel Stenberg committed
	  hit = j;
	  break;
	}
      }
      if(hit < 0) {
Daniel Stenberg's avatar
Daniel Stenberg committed
      }
    }
    if(hit < 0) {
Daniel Stenberg's avatar
Daniel Stenberg committed
    }    
    if(!longopt && aliases[hit].extraparam && parse[1]) {
      nextarg=(char *)&parse[1]; /* this is the actual extra parameter */
      singleopt=TRUE;   /* don't loop anymore after this */
    }
    else if(!nextarg && aliases[hit].extraparam) {
Daniel Stenberg's avatar
Daniel Stenberg committed
    }
    else if(nextarg && aliases[hit].extraparam)
      *usedarg = TRUE; /* mark it as used */

    switch(letter) {
    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 */
Daniel Stenberg's avatar
Daniel Stenberg committed
        config->errors = fopen(nextarg, "wt");
Daniel Stenberg's avatar
Daniel Stenberg committed
      else
        config->errors = stdout;
      break;
    case '7': /* there is no short letter for this */
      /* interface */
      GetStr(&config->iface, nextarg);
      break;
    case '6': /* there is no short letter for this */
      /* krb4 level string */
      GetStr(&config->krb4level, nextarg);
      break;
      switch(subletter) {
      case 'a': /* random-file */
        GetStr(&config->random_file, nextarg);
        break;
      case 'b': /* egd-file */
        GetStr(&config->egd_file, nextarg);
        break;
      case 'c': /* connect-timeout */
        config->connecttimeout=atoi(nextarg);
        break;
      case 'd': /* ciphers */
        GetStr(&config->cipher_list, nextarg);
        break;
Daniel Stenberg's avatar
Daniel Stenberg committed
      case 'e': /* --disable-epsv */
        config->disable_epsv ^= TRUE;
        break;
#ifdef USE_ENVIRONMENT
      case 'f':
        config->writeenv ^= TRUE;
        break;
#endif
      case 'g': /* --trace */
        GetStr(&config->trace_dump, nextarg);
        break;
      case 'h': /* --trace-ascii */
        GetStr(&config->trace_dump, nextarg);
        config->trace_ascii = TRUE;
Daniel Stenberg's avatar
Daniel Stenberg committed
      case 'i': /* --limit-rate */
        {
          /* We support G, M, K too */
          char *unit;
          unsigned long value = strtol(nextarg, &unit, 0);
          switch(nextarg[strlen(nextarg)-1]) {
          case 'G':
          case 'g':
            value *= 1024*1024*1024;
            break;
          case 'M':
          case 'm':
            value *= 1024*1024;
            break;
          case 'K':
          case 'k':
            value *= 1024;
            break;
          }
          config->recvpersecond = value;
          config->sendpersecond = value;
        }
        break;
      default: /* the URL! */
        {
          struct getout *url;
          if(config->url_get || (config->url_get=config->url_list)) {
            /* there's a node here, if it already is filled-in continue to find
               an "empty" node */
            while(config->url_get && (config->url_get->flags&GETOUT_URL))
              config->url_get = config->url_get->next;
          }
          /* now there might or might not be an available node to fill in! */
          if(config->url_get)
            /* existing node */
            url = config->url_get;
          else
            /* there was no free node, create one! */
            url=new_getout(config);
          
          if(url) {
            /* fill in the URL */
            GetStr(&url->url, nextarg);
            url->flags |= GETOUT_URL;
          }
Daniel Stenberg's avatar
Daniel Stenberg committed
    case '#': /* added 19990617 larsa */
      config->progressmode ^= CURL_PROGRESS_BAR;
      break;
    case '0': 
      /* HTTP version 1.0 */
      config->httpversion = CURL_HTTP_VERSION_1_0;
      break;
    case '1':
      /* TLS version 1 */
      config->ssl_version = CURL_SSLVERSION_TLSv1;
      break;
Daniel Stenberg's avatar
Daniel Stenberg committed
    case '2': 
      /* SSL version 2 */
      config->ssl_version = CURL_SSLVERSION_SSLv2;
Daniel Stenberg's avatar
Daniel Stenberg committed
      break;
    case '3': 
      /* SSL version 3 */
      config->ssl_version = CURL_SSLVERSION_SSLv3;
Daniel Stenberg's avatar
Daniel Stenberg committed
      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(nextarg[0] == '@') {
        nextarg++;
      }
      else if(strchr(nextarg, '=')) {
Daniel Stenberg's avatar
Daniel Stenberg committed
        /* A cookie string must have a =-letter */
        GetStr(&config->cookie, nextarg);
Daniel Stenberg's avatar
Daniel Stenberg committed
      }
      /* We have a cookie file to read from! */
      GetStr(&config->cookiefile, nextarg);
Daniel Stenberg's avatar
Daniel Stenberg committed
      break;
    case 'B':
      /* use ASCII/text when transfering */
      config->conf ^= CONF_GETTEXT;
Daniel Stenberg's avatar
Daniel Stenberg committed
      break;
    case 'c':
      /* get the file name to dump all cookies in */
      GetStr(&config->cookiejar, nextarg);
Daniel Stenberg's avatar
Daniel Stenberg committed
      break;
    case 'C':
      /* This makes us continue an ftp transfer at given position */
        config->resume_from= atoi(nextarg);
        config->resume_from_current = FALSE;
      }
      else {
        config->resume_from_current = TRUE;
        config->resume_from = 0;
      }
Daniel Stenberg's avatar
Daniel Stenberg committed
      config->use_resume=TRUE;
      break;
    case 'd':
      /* postfield data */
      {
        char *postdata=NULL;

        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 

          if(subletter == 'b') /* forced binary */
            postdata = file2memory(file, &config->postfieldsize);
          else
            postdata = file2string(file);
          if(file && (file != stdin))
            fclose(stdin);
        }
        else {
          GetStr(&postdata, nextarg);
        }
          /* we already have a string, we append this one
             with a separating &-letter */
          char *oldpost=config->postfields;
          config->postfields=aprintf("%s&%s", oldpost, postdata);
Daniel Stenberg's avatar
Daniel Stenberg committed
      }
      /*
        We can't set the request type here, as this data might be used in
        a simple GET if -G is used. Already or soon.
        if(SetHTTPrequest(HTTPREQ_SIMPLEPOST, &config->httpreq))
          return PARAM_BAD_USE;
      */
Daniel Stenberg's avatar
Daniel Stenberg committed
      break;
    case 'D':
      /* dump-header to given file name */
      GetStr(&config->headerfile, nextarg);
      break;
    case 'e':
      {
        char *ptr = strstr(nextarg, ";auto");
        if(ptr) {
          /* Automatic referer requested, this may be combined with a
             set initial one */
          config->conf |= CONF_AUTO_REFERER;
          *ptr = 0; /* zero terminate here */
        }
        GetStr(&config->referer, nextarg);
      }
Daniel Stenberg's avatar
Daniel Stenberg committed
      break;
    case 'E':
      switch(subletter) {
      case 'a': /* CA info PEM file */
        /* CA info PEM file */
        GetStr(&config->cacert, nextarg);
        break;
      case 'b': /* cert file type */
        GetStr(&config->cert_type, nextarg);
        break;
      case 'c': /* private key file */
        GetStr(&config->key, nextarg);
        break;
      case 'd': /* private key file type */
        GetStr(&config->key_type, nextarg);
        break;
      case 'e': /* private key passphrase */
        GetStr(&config->key_passwd, nextarg);
        break;
      case 'f': /* crypto engine */
        GetStr(&config->engine, nextarg);
        break;
      case 'g': /* CA info PEM file */
        /* CA cert directory */
        GetStr(&config->capath, nextarg);
        break;
      default: /* certificate file */
        {
          char *ptr = strchr(nextarg, ':');
          /* Since we live in a world of weirdness and confusion, the win32
             dudes can use : when using drive letters and thus
             c:\file:password needs to work. In order not to break
             compatibility, we still use : as separator, but we try to detect
             when it is used for a file name! On windows. */
          if(ptr &&
             (ptr == &nextarg[1]) &&
             (nextarg[2] == '\\') &&
             (isalpha((int)nextarg[0])) )
             /* colon in the second column, followed by a backslash, and the
                first character is an alphabetic letter:

                this is a drive letter colon */
            ptr = strchr(&nextarg[3], ':'); /* find the next one instead */
          if(ptr) {
            /* we have a password too */
            *ptr=0;
            ptr++;
            GetStr(&config->key_passwd, ptr);
          }
          GetStr(&config->cert, nextarg);
        }
Daniel Stenberg's avatar
Daniel Stenberg committed
      }
      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(formparse(nextarg,
                   &config->httppost,
                   &config->last_post))
      if(SetHTTPrequest(HTTPREQ_POST, &config->httpreq))
Daniel Stenberg's avatar
Daniel Stenberg committed
      break;

    case 'g': /* g disables URLglobbing */
      config->globoff ^= TRUE;
      break;

    case 'G': /* HTTP GET */
      config->use_httpget = TRUE;
      break;

Daniel Stenberg's avatar
Daniel Stenberg committed
    case 'h': /* h for help */
      help();
Daniel Stenberg's avatar
Daniel Stenberg committed
    case 'H':
      /* A custom header to append to a list */
      config->headers = curl_slist_append(config->headers, nextarg);
Daniel Stenberg's avatar
Daniel Stenberg committed
      break;
    case 'i':
      config->conf ^= CONF_HEADER; /* include the HTTP header as well */
      break;
    case 'j':
      config->cookiesession ^= TRUE;
      break;
Daniel Stenberg's avatar
Daniel Stenberg committed
    case 'I':
      /*
       * This is a bit tricky. We either SET both bits, or we clear both
       * bits. Let's not make any other outcomes from this.
       */
      if((CONF_HEADER|CONF_NOBODY) !=
         (config->conf&(CONF_HEADER|CONF_NOBODY)) ) {
        /* one of them weren't set, set both */
        config->conf |= (CONF_HEADER|CONF_NOBODY);
        if(SetHTTPrequest(HTTPREQ_HEAD, &config->httpreq))
          return PARAM_BAD_USE;
      }
      else {
        /* both were set, clear both */
        config->conf &= ~(CONF_HEADER|CONF_NOBODY);
        if(SetHTTPrequest(HTTPREQ_GET, &config->httpreq))
          return PARAM_BAD_USE;
      }
Daniel Stenberg's avatar
Daniel Stenberg committed
      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();
Daniel Stenberg's avatar
Daniel Stenberg committed
    case 'n':
      switch(subletter) {
      case 'o': /* CA info PEM file */
        /* use .netrc or URL */
        config->conf ^= CONF_NETRC_OPT;
        break;
      default:
        /* pick info from .netrc, if this is used for http, curl will
           automatically enfore user+password with the request */
        config->conf ^= CONF_NETRC;
        break;
      }
Daniel Stenberg's avatar
Daniel Stenberg committed
      break;
    case 'N':
      /* disable the output I/O buffering */
      config->nobuffer ^= 1;
      break;
Daniel Stenberg's avatar
Daniel Stenberg committed
    case 'o':
    case 'O':
      /* output file */
      {
        struct getout *url;
        if(config->url_out || (config->url_out=config->url_list)) {
          /* there's a node here, if it already is filled-in continue to find
             an "empty" node */
          while(config->url_out && (config->url_out->flags&GETOUT_OUTFILE))
            config->url_out = config->url_out->next;
        }

        /* now there might or might not be an available node to fill in! */

        if(config->url_out)
          /* existing node */
          url = config->url_out;
        else
          /* there was no free node, create one! */
          url=new_getout(config);

        if(url) {
          /* fill in the outfile */
          if('o' == letter)
            GetStr(&url->outfile, nextarg);
          else {
            url->outfile=NULL; /* leave it */
            url->flags |= GETOUT_USEREMOTE;
          }
          url->flags |= GETOUT_OUTFILE;
        }
      }
Daniel Stenberg's avatar
Daniel Stenberg committed
      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!
	 */
      GetStr(&config->ftpport, nextarg);
      break;
    case 'p':
      /* proxy tunnel for non-http protocols */
      config->proxytunnel ^= TRUE;
Daniel Stenberg's avatar
Daniel Stenberg committed
      break;
Daniel Stenberg's avatar
Daniel Stenberg committed
    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 */
      switch(nextarg[0]) {
      case '-':
Daniel Stenberg's avatar
Daniel Stenberg committed
        /* prefixed with a dash makes it a POST TRANSFER one */
        nextarg++;
        config->postquote = curl_slist_append(config->postquote, nextarg);
        break;
      case '+':
        /* prefixed with a plus makes it a just-before-transfer one */
        nextarg++;
        config->prequote = curl_slist_append(config->prequote, nextarg);
        break;
      default:
Daniel Stenberg's avatar
Daniel Stenberg committed
        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);
      break;
    case 'R':
      /* use remote file's time */
      config->remote_time ^= TRUE;
      break;
Daniel Stenberg's avatar
Daniel Stenberg committed
    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':
      /* Telnet options */
      config->telnet_options =
        curl_slist_append(config->telnet_options, nextarg);
Daniel Stenberg's avatar
Daniel Stenberg committed
      break;
    case 'T':
      /* we are uploading */
      config->conf |= CONF_UPLOAD;
      if(!strequal("-", nextarg))
        /* make - equal stdin */
        GetStr(&config->infile, nextarg);
Daniel Stenberg's avatar
Daniel Stenberg committed
      break;
    case 'u':
      /* user:password  */
      GetStr(&config->userpwd, nextarg);
      break;
    case 'U':
      /* Proxy user:password  */
      GetStr(&config->proxyuserpwd, nextarg);
      break;
    case 'v':
      config->conf ^= CONF_VERBOSE; /* talk a lot */
      break;
    case 'V':
      printf(CURL_ID "%s\n", curl_version());
      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->writeout = file2string(file);
        if(file && (file != stdin))
          fclose(stdin);
      }
      else 
        GetStr(&config->writeout, nextarg);
Daniel Stenberg's avatar
Daniel Stenberg committed
    case 'x':
      /* proxy */
      GetStr(&config->proxy, nextarg);
Daniel Stenberg's avatar
Daniel Stenberg committed
      break;
    case 'X':
Daniel Stenberg's avatar
Daniel Stenberg committed
      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;
    case 'z': /* time condition coming up */
      switch(*nextarg) {
      case '+':
        nextarg++;
      default:
        /* If-Modified-Since: (section 14.28 in RFC2068) */
        config->timecond = CURL_TIMECOND_IFMODSINCE;
        break;
      case '-':
        /* If-Unmodified-Since:  (section 14.24 in RFC2068) */
        config->timecond = CURL_TIMECOND_IFUNMODSINCE;
        nextarg++;
        break;
      case '=':
        /* Last-Modified:  (section 14.29 in RFC2068) */
        config->timecond = CURL_TIMECOND_LASTMOD;
        nextarg++;
        break;
      }
      now=time(NULL);
      config->condtime=curl_getdate(nextarg, &now);
      if(-1 == (int)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 = CURL_TIMECOND_NONE;
        }
        else {
          /* pull the time out from the file */
          config->condtime = statbuf.st_mtime;
        }
      }
      break;
    case 'Z':
      /* specified max no of redirects (http(s)) */
      config->maxredirs = atoi(nextarg);
      break;
Daniel Stenberg's avatar
Daniel Stenberg committed

    default: /* unknown flag */
Daniel Stenberg's avatar
Daniel Stenberg committed
    }
    hit = -1;

  } while(!longopt && !singleopt && *++parse && !*usedarg);
Daniel Stenberg's avatar
Daniel Stenberg committed

static int parseconfig(const char *filename,
Daniel Stenberg's avatar
Daniel Stenberg committed
		       struct Configurable *config)
{
  int res;
  FILE *file;
Daniel Stenberg's avatar
Daniel Stenberg committed
  bool usedarg;
Daniel Stenberg's avatar
Daniel Stenberg committed
  
  if(!filename || !*filename) {
    /* NULL or no file name attempts to load .curlrc from the homedir! */

#define CURLRC DOT_CHAR "curlrc"

    filename = CURLRC;          /* sensible default */
    home = curl_getenv("HOME"); /* portable environment reader */
    if(home) {
      if(strlen(home)<(sizeof(filebuffer)-strlen(CURLRC))) {
Daniel Stenberg's avatar
Daniel Stenberg committed

        snprintf(filebuffer, sizeof(filebuffer),
                 "%s%s%s", home, DIR_CHAR, CURLRC);
Daniel Stenberg's avatar
Daniel Stenberg committed

        filename = filebuffer;
      }
      free(home); /* we've used it, now free it */
    }
Daniel Stenberg's avatar
Daniel Stenberg committed
  }

  if(strcmp(filename,"-"))
    file = fopen(filename, "r");
  else
    file = stdin;
  
    char *aline;
    char *option;
    char *param;
    int lineno=0;
    bool alloced_param;

#define isseparator(x) (((x)=='=') || ((x) == ':'))

    while (NULL != (aline = my_get_line(file))) {
      lineno++;
      line = aline;
      alloced_param=FALSE;

      /* lines with # in the fist column is a comment! */
      while(isspace((int)*line))
        line++;

      switch(*line) {
      case '#':
      case '/':
      case '\r':
      case '\n':
      case '*':
      case '\0':
Daniel Stenberg's avatar
Daniel Stenberg committed

      /* the option keywords starts here */
      option = line;
      while(*line && !isspace((int)*line) && !isseparator(*line))
        line++;
      /* ... and has ended here */

      *line++=0; /* zero terminate, we have a local copy of the data */

#ifdef DEBUG_CONFIG
      fprintf(stderr, "GOT: %s\n", option);
#endif

      /* pass spaces and separator(s) */
      while(isspace((int)*line) || isseparator(*line))
        line++;
      
      /* the parameter starts here (unless quoted) */
      if(*line == '\"') {
        char *ptr;
        /* quoted parameter, do the qoute dance */
        line++;
        param=strdup(line); /* parameter */
        alloced_param=TRUE;

        ptr=param;
        while(*line && (*line != '\"')) {
          if(*line == '\\') {

            /* default is to output the letter after the backslah */
            switch(out = *line) {
            case '\0':
              continue; /* this'll break out of the loop */
            case 't':
              out='\t';
              break;
            case 'n':
              out='\n';
              break;
            case 'r':
              out='\r';
              break;
            case 'v':
              out='\v';
Daniel Stenberg's avatar
Daniel Stenberg committed
      }
      else {
        param=line; /* parameter starts here */
        while(*line && !isspace((int)*line))
Daniel Stenberg's avatar
Daniel Stenberg committed
      }
#ifdef DEBUG_CONFIG
      fprintf(stderr, "PARAM: \"%s\"\n", param);
#endif
      res = getparameter(option, param, &usedarg, config);
      if(*param && !usedarg)
        /* we passed in a parameter that wasn't used! */
        res = PARAM_GOT_EXTRA_PARAMETER;
      if(res != PARAM_OK) {
        /* the help request isn't really an error */
        if(!strcmp(filename, "-")) {
          filename=(char *)"<stdin>";
          const char *reason;
          switch(res) {
          default:
          case PARAM_GOT_EXTRA_PARAMETER:
            reason = "had unsupported trailing garbage";
            break;
          case PARAM_OPTION_UNKNOWN:
            reason = "is unknown";
            break;
          case PARAM_OPTION_AMBIGUOUS:
            reason = "is ambiguous";
            break;
          case PARAM_REQUIRES_PARAMETER:
            reason = "requires parameter";
            break;
          case PARAM_BAD_USE:
            reason = "is badly used here";
          fprintf(stderr, "%s:%d: warning: '%s' %s\n",
                  filename, lineno, option, reason);
Daniel Stenberg's avatar
Daniel Stenberg committed
    }
    if(file != stdin)
      fclose(file);
  }
Daniel Stenberg's avatar
Daniel Stenberg committed
static void go_sleep(long ms)
{
Daniel Stenberg's avatar
Daniel Stenberg committed
  /* portable subsecond "sleep" */
  poll((void *)0, 0, ms);
#else
  /* systems without poll() need other solutions */

#ifdef WIN32
  /* Windows offers a millisecond sleep */
  Sleep(ms);
#else
  /* Other systems must use select() for this */
  struct timeval timeout;

  timeout.tv_sec = 0;
  timeout.tv_usec = ms * 1000;

  select(0, NULL,  NULL, NULL, &timeout);
#endif

#endif
Daniel Stenberg's avatar
Daniel Stenberg committed
struct OutStruct {
  char *filename;
  FILE *stream;
  struct Configurable *config;
int my_fwrite(void *buffer, size_t size, size_t nmemb, void *stream)
Daniel Stenberg's avatar
Daniel Stenberg committed
{
  struct OutStruct *out=(struct OutStruct *)stream;
Daniel Stenberg's avatar
Daniel Stenberg committed
  struct Configurable *config = out->config;
Daniel Stenberg's avatar
Daniel Stenberg committed
  if(out && !out->stream) {
    /* open file for writing */
    out->stream=fopen(out->filename, "wb");
    if(!out->stream)
      return -1; /* failure */
Daniel Stenberg's avatar
Daniel Stenberg committed
    if(config->nobuffer) {
      /* disable output buffering */
#ifdef HAVE_SETVBUF
      setvbuf(out->stream, NULL, _IONBF, 0);
#endif
    }
Daniel Stenberg's avatar
Daniel Stenberg committed
  }
Daniel Stenberg's avatar
Daniel Stenberg committed

  if(config->recvpersecond) {
    /*
     * We know when we received data the previous time. We know how much data
     * we get now. Make sure that this is not faster than we are told to run.
     * If we're faster, sleep a while *before* doing the fwrite() here.
     */

    time_t timediff;
    time_t now;

    now = time(NULL);
    timediff = now - config->lastrecvtime;
    if( size*nmemb > config->recvpersecond*timediff) {
      /* figure out how many milliseconds to rest */
      go_sleep ( (size*nmemb)*1000/config->recvpersecond - timediff*1000 );
      now = time(NULL);
    }
    config->lastrecvtime = now;
  }

Daniel Stenberg's avatar
Daniel Stenberg committed
  return fwrite(buffer, size, nmemb, out->stream);
}

Daniel Stenberg's avatar
Daniel Stenberg committed
struct InStruct {
  FILE *stream;
  struct Configurable *config;
};

int my_fread(void *buffer, size_t size, size_t nmemb, void *userp)
{
  struct InStruct *in=(struct InStruct *)userp;

  struct Configurable *config = in->config;

  if(config->sendpersecond) {
    /*
     * We know when we sent data the previous time. We know how much data
     * we sent. Make sure that this was not faster than we are told to run.
     * If we're faster, sleep a while *before* doing the fread() here.
     * Also, make no larger fread() than should be sent this second!
     */

    time_t timediff;
    time_t now;

    now = time(NULL);
    timediff = now - config->lastsendtime;
    if( config->lastsendsize > config->sendpersecond*timediff) {
      /* figure out how many milliseconds to rest */
      go_sleep ( config->lastsendsize*1000/config->sendpersecond -
                 timediff*1000 );
      now = time(NULL);
    }
    config->lastsendtime = now;

    if(size*nmemb > config->sendpersecond) {
      /* lower the size to actually read */
      nmemb = config->sendpersecond;
      size = 1;
    }
    config->lastsendsize = size*nmemb;    
  }


  return fread(buffer, size, nmemb, in->stream);
}

  FILE *out; /* where to write everything to */