Skip to content
Snippets Groups Projects
main.c 49.4 KiB
Newer Older

    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="<stdin>";
        }
        if(PARAM_HELP_REQUESTED != res) {
          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);
  }
  if(home)
    free(home);
Daniel Stenberg's avatar
Daniel Stenberg committed
}

struct OutStruct {
  char *filename;
  FILE *stream;
};

/* having this global is a bit dirty, but hey, who said we weren't? ;-) */
struct Configurable config;

Daniel Stenberg's avatar
Daniel Stenberg committed
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 */
    if(config.nobuffer) {
      /* disable output buffering */
#ifdef HAVE_SETVBUF
      setvbuf(out->stream, NULL, _IONBF, 0);
#endif
    }
Daniel Stenberg's avatar
Daniel Stenberg committed
  }
  return fwrite(buffer, size, nmemb, out->stream);
}

struct ProgressData {
  size_t total;
  size_t prev;
  size_t point;
  int width;
};

int myprogress (void *clientp,
                size_t dltotal,
                size_t dlnow,
                size_t ultotal,
                size_t ulnow)
{
  /* The original progress-bar source code was written for curl by Lars Aas,
     and this new edition inherites some of his concepts. */
  
  char line[256];
  char outline[256];
  char format[40];
  float frac;
  float percent;
  int barwidth;
  int num;
  int i;

  struct ProgressData *bar = (struct ProgressData *)clientp;
  size_t total = dltotal + ultotal;

  bar->point = dlnow + ulnow; /* we've come this far */

  if(0 == total) {
    int prevblock = bar->prev / 1024;
    int thisblock = bar->point / 1024;
    while ( thisblock > prevblock ) {
      fprintf( stderr, "#" );
      prevblock++;
    }
  }
  else {
    frac = (float) bar->point / (float) total;
    percent = frac * 100.0f;
    barwidth = bar->width - 7;
    num = (int) (((float)barwidth) * frac);
    i = 0;
    for ( i = 0; i < num; i++ ) {
      line[i] = '#';
    }
    line[i] = '\0';
    sprintf( format, "%%-%ds %%5.1f%%%%", barwidth );
    sprintf( outline, format, line, percent );
    fprintf( stderr, "\r%s", outline );
  }
  bar->prev = bar->point;

  return 0;
}

void progressbarinit(struct ProgressData *bar)
{
#ifdef __EMX__
  /* 20000318 mgs */
  int scr_size [2];
#endif
  char *colp;

  memset(bar, 0, sizeof(struct ProgressData));

/* TODO: get terminal width through ansi escapes or something similar.
         try to update width when xterm is resized... - 19990617 larsa */
#ifndef __EMX__
  /* 20000318 mgs
   * OS/2 users most likely won't have this env var set, and besides that
   * we're using our own way to determine screen width */
  colp = curl_getenv("COLUMNS");
  if (colp != NULL) {
    bar->width = atoi(colp);
    free(colp);
  }
  else
    bar->width = 79;
#else
  /* 20000318 mgs
   * We use this emx library call to get the screen width, and subtract
   * one from what we got in order to avoid a problem with the cursor
   * advancing to the next line if we print a string that is as long as
   * the screen is wide. */
 
  _scrsize(scr_size);
  bar->width = scr_size[0] - 1;
#endif

}
Daniel Stenberg's avatar
Daniel Stenberg committed

void free_config_fields(struct Configurable *confp)
{
  if(confp->url)
    free(confp->url);
  if(confp->userpwd)
    free(confp->userpwd);
  if(confp->postfields)
    free(confp->postfields);
  if(confp->proxy)
    free(confp->proxy);
  if(confp->proxyuserpwd)
    free(confp->proxyuserpwd);
  if(confp->cookie)
    free(confp->cookie);
  if(confp->cookiefile)
    free(confp->cookiefile);
  if(confp->krb4level)
    free(confp->krb4level);
  if(confp->headerfile)
    free(confp->headerfile);
  if(confp->outfile)
    free(confp->outfile);
  if(confp->infile)
    free(confp->infile);
  if(confp->range)
    free(confp->range);
  if(confp->customrequest)
    free(confp->customrequest);
  if(confp->writeout)
    free(confp->writeout);
  if(confp->httppost)
    curl_formfree(confp->httppost);

  curl_slist_free_all(confp->quote); /* the checks for confp->quote == NULL */
  curl_slist_free_all(confp->postquote); /*  */
  curl_slist_free_all(confp->headers); /*  */
}


static int 
operate(int argc, char *argv[])
Daniel Stenberg's avatar
Daniel Stenberg committed
{
  char errorbuffer[CURL_ERROR_SIZE];
  char useragent[128]; /* buah, we don't want a larger default user agent */
Daniel Stenberg's avatar
Daniel Stenberg committed

  struct OutStruct outs;
Daniel Stenberg's avatar
Daniel Stenberg committed

  char *url = NULL;
Daniel Stenberg's avatar
Daniel Stenberg committed
  URLGlob *urls;
  int urlnum;
Daniel Stenberg's avatar
Daniel Stenberg committed
  int separator = 0;
  
  FILE *infd = stdin;
  FILE *headerfilep = NULL;
  char *urlbuffer=NULL;
  int infilesize=-1; /* -1 means unknown */
  bool stillflags=TRUE;

  bool allocuseragent=FALSE;

  CURL *curl;
Daniel Stenberg's avatar
Daniel Stenberg committed
  int i;

  outs.stream = stdout;

#ifdef MALLOCDEBUG
  /* this sends all memory debug messages to a logfile named memdump */
  curl_memdebug("memdump");
#endif

Daniel Stenberg's avatar
Daniel Stenberg committed
  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 CURLE_FAILED_INIT;
Daniel Stenberg's avatar
Daniel Stenberg committed
  }

  /* Parse options */
  for (i = 1; i < argc; i++) {
    if(stillflags &&
       ('-' == argv[i][0])) {
      char *nextarg;
      bool passarg;
Daniel Stenberg's avatar
Daniel Stenberg committed
      
Daniel Stenberg's avatar
Daniel Stenberg committed

      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) {
          switch(res) {
          case PARAM_OPTION_AMBIGUOUS:
            helpf("option %s is ambiguous\n", origopt);
            break;
          case PARAM_OPTION_UNKNOWN:
            helpf("option %s is unknown\n", origopt);
            break;
          case PARAM_REQUIRES_PARAMETER:
            helpf("option %s requires an extra argument!\n", origopt);
            break;
          case PARAM_BAD_USE:
            helpf("option %s was wrongly used!\n", origopt);
            break;
          case PARAM_HELP_REQUESTED:
            /* no text */
            break;
          }
	  return CURLE_FAILED_INIT;
        }
Daniel Stenberg's avatar
Daniel Stenberg committed

	if(passarg) /* we're supposed to skip this */
	  i++;
      }
    }
    else {
      if(url) {
	helpf("only one URL is supported!\n");
	return CURLE_FAILED_INIT;
Daniel Stenberg's avatar
Daniel Stenberg committed
      }
      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 CURLE_FAILED_INIT;
Daniel Stenberg's avatar
Daniel Stenberg committed
  }
  if(NULL == config.useragent) {
    /* set non-zero default values: */
    snprintf(useragent, sizeof(useragent),
             CURL_NAME "/" CURL_VERSION " (" OS ") " "%s", curl_version());
    config.useragent= useragent;
  }
  else
    allocuseragent = TRUE;
Daniel Stenberg's avatar
Daniel Stenberg committed
#if 0
  fprintf(stderr, "URL: %s PROXY: %s\n", url, config.proxy?config.proxy:"none");
Daniel Stenberg's avatar
Daniel Stenberg committed
#endif

  /* expand '{...}' and '[...]' expressions and return total number of URLs
     in pattern set */
  res = glob_url(&urls, url, &urlnum);
  if(res != CURLE_OK)
  /* save outfile pattern befor expansion */
  outfiles = config.outfile?strdup(config.outfile):NULL;
Daniel Stenberg's avatar
Daniel Stenberg committed
  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) {
      free(config.outfile);
      config.outfile = outfiles;
    }
Daniel Stenberg's avatar
Daniel Stenberg committed
 
    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.outfile && 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 = strdup(strrchr(config.outfile, '/'));
        if(!config.outfile || !strlen(++config.outfile)) {
          helpf("Remote file name has no length!\n");
          return CURLE_WRITE_ERROR;
        }
      }
      else {
	/* fill '#1' ... '#9' terms from URL pattern */
        char *outfile = config.outfile;
        config.outfile = match_url(config.outfile, *urls);
        free(outfile);
      }
      
      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) {
        /* open file for output: */
        outs.stream=(FILE *) fopen(config.outfile, config.resume_from?"ab":"wb");
        if (!outs.stream) {
          helpf("Can't open '%s'!\n", config.outfile);
          return CURLE_WRITE_ERROR;
        }
      }
      else {
        outs.filename = config.outfile;
        outs.stream = NULL; /* open when needed */
    if (config.infile) {
      /*
       * We have specified a file to upload
       */
Daniel Stenberg's avatar
Daniel Stenberg committed
      struct stat fileinfo;

      /* If no file name part is given in the URL, we add this file name */
      char *ptr=strstr(url, "://");
      if(ptr)
        ptr+=3;
      else
        ptr=url;
      ptr = strrchr(ptr, '/');
      if(!ptr || !strlen(++ptr)) {
        /* The URL has no file name part, add the local file name. In order to
           be able to do so, we have to create a new URL in another buffer.*/

        urlbuffer=(char *)malloc(strlen(url) + strlen(config.infile) + 3);
        if(!urlbuffer) {
          helpf("out of memory\n");
          return CURLE_OUT_OF_MEMORY;
        }
        if(ptr)
          /* there is a trailing slash on the URL */
          sprintf(urlbuffer, "%s%s", url, config.infile);
        else
          /* thers is no trailing slash on the URL */
          sprintf(urlbuffer, "%s/%s", url, config.infile);
        
        url = urlbuffer; /* use our new URL instead! */
      infd=(FILE *) fopen(config.infile, "rb");
      if (!infd || stat(config.infile, &fileinfo)) {
        helpf("Can't open '%s'!\n", config.infile);
        return CURLE_READ_ERROR;
Daniel Stenberg's avatar
Daniel Stenberg committed
      }
      infilesize=fileinfo.st_size;
      
Daniel Stenberg's avatar
Daniel Stenberg committed
    }
    if((config.conf&CONF_UPLOAD) &&
       config.use_resume &&
       (0==config.resume_from)) {
      config.resume_from = -1; /* -1 will then force get-it-yourself */
Daniel Stenberg's avatar
Daniel Stenberg committed
    }
    if(config.headerfile) {
      /* open file for output: */
      if(strcmp(config.headerfile,"-")) {
        heads.filename = config.headerfile;
        headerfilep=NULL;
Daniel Stenberg's avatar
Daniel Stenberg committed
      }
      else
        headerfilep=stdout;
      heads.stream = headerfilep;
Daniel Stenberg's avatar
Daniel Stenberg committed
    }
    
    if(outs.stream && isatty(fileno(outs.stream)) &&
       !(config.conf&(CONF_UPLOAD|CONF_HTTPPOST)))
      /* we send the output to a tty and it isn't an upload operation,
         therefore we switch off the progress meter */
      config.conf |= CONF_NOPROGRESS;
    
    if (urlnum > 1) {
      fprintf(stderr, "\n[%d/%d]: %s --> %s\n",
              i+1, urlnum, url, config.outfile ? config.outfile : "<stdout>");
      if (separator) {
Daniel Stenberg's avatar
Daniel Stenberg committed
#ifdef CURL_SEPARATORS
        printf("%s%s\n", CURLseparator, url);
Daniel Stenberg's avatar
Daniel Stenberg committed
#endif
#ifdef MIME_SEPARATORS
        printf("--%s\n", MIMEseparator);
        printf("Content-ID: %s\n\n", url); 
Daniel Stenberg's avatar
Daniel Stenberg committed
#endif
    if(!config.errors)
      config.errors = stderr;
Daniel Stenberg's avatar
Daniel Stenberg committed

    if(!config.outfile && !(config.conf & CONF_GETTEXT)) {
      /* We get the output to stdout and we have not got the ASCII/text flag,
         then set stdout to be binary */
      setmode( 1, O_BINARY );
    main_init();

    /* The new, v7-style easy-interface! */
    curl = curl_easy_init();
    if(curl) {
      curl_easy_setopt(curl, CURLOPT_FILE, (FILE *)&outs); /* where to store */
      /* what call to write: */
      curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_fwrite);
      curl_easy_setopt(curl, CURLOPT_INFILE, infd); /* for uploads */
      /* size of uploaded file: */
      curl_easy_setopt(curl, CURLOPT_INFILESIZE, infilesize);
      curl_easy_setopt(curl, CURLOPT_URL, url);     /* what to fetch */
      curl_easy_setopt(curl, CURLOPT_PROXY, config.proxy); /* proxy to use */
      curl_easy_setopt(curl, CURLOPT_VERBOSE, config.conf&CONF_VERBOSE);
      curl_easy_setopt(curl, CURLOPT_HEADER, config.conf&CONF_HEADER);
      curl_easy_setopt(curl, CURLOPT_NOPROGRESS, config.conf&CONF_NOPROGRESS);
      curl_easy_setopt(curl, CURLOPT_NOBODY, config.conf&CONF_NOBODY);
      curl_easy_setopt(curl, CURLOPT_FAILONERROR,
                       config.conf&CONF_FAILONERROR);
      curl_easy_setopt(curl, CURLOPT_UPLOAD, config.conf&CONF_UPLOAD);
      curl_easy_setopt(curl, CURLOPT_POST, config.conf&CONF_POST);
      curl_easy_setopt(curl, CURLOPT_FTPLISTONLY,
                       config.conf&CONF_FTPLISTONLY);
      curl_easy_setopt(curl, CURLOPT_FTPAPPEND, config.conf&CONF_FTPAPPEND);
      curl_easy_setopt(curl, CURLOPT_NETRC, config.conf&CONF_NETRC);
      curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION,
                       config.conf&CONF_FOLLOWLOCATION);
      curl_easy_setopt(curl, CURLOPT_TRANSFERTEXT, config.conf&CONF_GETTEXT);
      curl_easy_setopt(curl, CURLOPT_PUT, config.conf&CONF_PUT);
      curl_easy_setopt(curl, CURLOPT_MUTE, config.conf&CONF_MUTE);
      curl_easy_setopt(curl, CURLOPT_USERPWD, config.userpwd);
      curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, config.proxyuserpwd);
      curl_easy_setopt(curl, CURLOPT_RANGE, config.range);
      curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errorbuffer);
      curl_easy_setopt(curl, CURLOPT_TIMEOUT, config.timeout);
      curl_easy_setopt(curl, CURLOPT_POSTFIELDS, config.postfields);
      
      /* new in libcurl 7.2: */
      curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, config.postfieldsize);

      curl_easy_setopt(curl, CURLOPT_REFERER, config.referer);
      curl_easy_setopt(curl, CURLOPT_AUTOREFERER,
                       config.conf&CONF_AUTO_REFERER);
      curl_easy_setopt(curl, CURLOPT_USERAGENT, config.useragent);
      curl_easy_setopt(curl, CURLOPT_FTPPORT, config.ftpport);
      curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, config.low_speed_limit);
      curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, config.low_speed_time);
      curl_easy_setopt(curl, CURLOPT_RESUME_FROM,
                       config.use_resume?config.resume_from:0);
      curl_easy_setopt(curl, CURLOPT_COOKIE, config.cookie);
      curl_easy_setopt(curl, CURLOPT_HTTPHEADER, config.headers);
      curl_easy_setopt(curl, CURLOPT_HTTPPOST, config.httppost);
      curl_easy_setopt(curl, CURLOPT_SSLCERT, config.cert);
      curl_easy_setopt(curl, CURLOPT_SSLCERTPASSWD, config.cert_passwd);
      curl_easy_setopt(curl, CURLOPT_CRLF, config.crlf);
      curl_easy_setopt(curl, CURLOPT_QUOTE, config.quote);
      curl_easy_setopt(curl, CURLOPT_POSTQUOTE, config.postquote);
      curl_easy_setopt(curl, CURLOPT_WRITEHEADER,
                       config.headerfile?&heads:NULL);
      curl_easy_setopt(curl, CURLOPT_COOKIEFILE, config.cookiefile);
      curl_easy_setopt(curl, CURLOPT_SSLVERSION, config.ssl_version);
      curl_easy_setopt(curl, CURLOPT_TIMECONDITION, config.timecond);
      curl_easy_setopt(curl, CURLOPT_TIMEVALUE, config.condtime);
      curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, config.customrequest);
      curl_easy_setopt(curl, CURLOPT_STDERR, config.errors);
      
      /* three new ones in libcurl 7.3: */
      curl_easy_setopt(curl, CURLOPT_HTTPPROXYTUNNEL, config.proxytunnel);
      curl_easy_setopt(curl, CURLOPT_INTERFACE, config.iface);
      curl_easy_setopt(curl, CURLOPT_KRB4LEVEL, config.krb4level);

      if((config.progressmode == CURL_PROGRESS_BAR) &&
         !(config.conf&(CONF_NOPROGRESS|CONF_MUTE))) {
        /* we want the alternative style, then we have to implement it
           ourselves! */
        progressbarinit(&progressbar);
        curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, myprogress);
        curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, &progressbar);
      }
      res = curl_easy_perform(curl);
      if(config.writeout) {
        ourWriteOut(curl, config.writeout);
      }
Daniel Stenberg's avatar
Daniel Stenberg committed

      /* always cleanup */
      curl_easy_cleanup(curl);
Daniel Stenberg's avatar
Daniel Stenberg committed

      if((res!=CURLE_OK) && config.showerror)
        fprintf(config.errors, "curl: (%d) %s\n", res, errorbuffer);
    }
    else
      fprintf(config.errors, "curl: failed to init libcurl!\n");
    main_free();
Daniel Stenberg's avatar
Daniel Stenberg committed

    if((config.errors != stderr) &&
       (config.errors != stdout))
      /* it wasn't directed to stdout or stderr so close the file! */
      fclose(config.errors);
    
    if(config.headerfile && !headerfilep && heads.stream)
      fclose(heads.stream);

    if(urlbuffer)
      free(urlbuffer);
    if (config.outfile && outs.stream)
      fclose(outs.stream);
    if (config.infile)
      fclose(infd);
    if(headerfilep)
      fclose(headerfilep);
    
    if(url)
      free(url);
Daniel Stenberg's avatar
Daniel Stenberg committed

  }
#ifdef MIME_SEPARATORS
  if (separator)
    printf("--%s--\n", MIMEseparator);
#endif


  if(allocuseragent)
    free(config.useragent);

Daniel Stenberg's avatar
Daniel Stenberg committed
  /* cleanup memory used for URL globbing patterns */
  glob_cleanup(urls);

  return res;
}


int main(int argc, char *argv[])
{
  int res;
  memset(&config, 0, sizeof(struct Configurable));
  
  res = operate(argc, argv);
  free_config_fields(&config);
Daniel Stenberg's avatar
Daniel Stenberg committed

  return res;
Daniel Stenberg's avatar
Daniel Stenberg committed
}

static char *my_get_line(FILE *fp)
{
   char buf[4096];
   char *nl = NULL;
   char *retval = NULL;

   do
   {
      if (NULL == fgets(buf, sizeof(buf), fp))
         break;
      if (NULL == retval)
         retval = strdup(buf);
      else
      {
         if (NULL == (retval = realloc(retval,
                                       strlen(retval) + strlen(buf) + 1)))
            break;
         strcat(retval, buf);
      }
   }
   while (NULL == (nl = strchr(retval, '\n')));

   if (NULL != nl)
     *nl = '\0';

   return retval;
}