Skip to content
Snippets Groups Projects
main.c 106 KiB
Newer Older
  • Learn to ignore specific revisions
  •         else {
              /* pull the time out from the file */
              config->condtime = statbuf.st_mtime;
            }
          }
          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 void parseconfig(const char *filename,
                            struct Configurable *config)
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    {
      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 = homedir();    /* portable homedir finder */
    
        if(home) {
          if(strlen(home)<(sizeof(filebuffer)-strlen(CURLRC))) {
            snprintf(filebuffer, sizeof(filebuffer),
                     "%s%s%s", home, DIR_CHAR, CURLRC);
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
          free(home); /* we've used it, now free it */
    
        
    # else /* AmigaOS */
      /* On AmigaOS all the config files are into env:
       */
      filename = "ENV:" CURLRC;
    
    #endif
    
    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! */
    
            line++;
    
          switch(*line) {
          case '#':
          case '/':
          case '\r':
          case '\n':
          case '*':
          case '\0':
    
            free(aline);
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
          /* the option keywords starts here */
          option = line;
    
          while(*line && !isspace((int)*line) && !isseparator(*line))
    
          if(*line)
            *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(*line && (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
          }
    
    
          if (param && !*param) {
            /* do this so getparameter can check for required parameters.
               Otherwise it always thinks there's a parameter. */
            if (alloced_param)
              free(param);
            param = NULL;
          }
    
    
          fprintf(stderr, "PARAM: \"%s\"\n",(param ? param : "(null)"));
    
    #endif
          res = getparameter(option, param, &usedarg, config);
    
            /* 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 = param2text(res);
    
              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;
    
    static int my_fwrite(void *buffer, size_t size, size_t nmemb, void *stream)
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    {
    
    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->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;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
        now = time(NULL);
        timediff = now - config->lastrecvtime;
        if( size*nmemb > config->recvpersecond*timediff) {
          /* figure out how many milliseconds to rest */
    
          sleep_time = (size*nmemb)*1000/config->recvpersecond - timediff*1000;
    
          /*
           * Make sure we don't sleep for so long that we trigger the speed limit.
           * This won't limit the bandwidth quite the way we've been asked to, but
           * at least the transfer has a chance.
           */
          if (config->low_speed_time > 0)
            sleep_time = MIN(sleep_time,(config->low_speed_time * 1000) / 2);
    
          go_sleep (sleep_time);
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
          now = time(NULL);
        }
        config->lastrecvtime = now;
      }
    
    
      rc = fwrite(buffer, size, nmemb, out->stream);
      
      if(config->nobuffer)
        /* disable output buffering */
        fflush(out->stream);
      
      return rc;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    struct InStruct {
      FILE *stream;
      struct Configurable *config;
    };
    
    
    static int my_fread(void *buffer, size_t size, size_t nmemb, void *userp)
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    {
      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 */
    
      curl_off_t initial_size;
    
    static int myprogress (void *clientp,
                           double dltotal,
                           double dlnow,
                           double ultotal,
                           double ulnow)
    
    {
      /* The original progress-bar source code was written for curl by Lars Aas,
    
         and this new edition inherits some of his concepts. */
    
      
      char line[256];
      char outline[256];
      char format[40];
    
      int barwidth;
      int num;
      int i;
    
      struct ProgressData *bar = (struct ProgressData *)clientp;
    
      double total = dltotal + ultotal + bar->initial_size;
      double point = dlnow + ulnow + bar->initial_size; /* we've come this far */
    
        int prevblock = (int)bar->prev / 1024;
    
          fprintf( bar->out, "#" );
    
        percent = frac * 100.0f;
        barwidth = bar->width - 7;
    
        num = (int) (((double)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( bar->out, "\r%s", outline );
    
    static
    void progressbarinit(struct ProgressData *bar,
                         struct Configurable *config)
    
    {
    #ifdef __EMX__
      /* 20000318 mgs */
      int scr_size [2];
    #endif
      char *colp;
    
      memset(bar, 0, sizeof(struct ProgressData));
    
    
      /* pass this through to progress function so
       * it can display progress towards total file
       * not just the part that's left. (21-may-03, dbyron) */
      if (config->use_resume)
        bar->initial_size = config->resume_from;
    
    
    /* 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);
    
        curl_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
    
    
      bar->out = config->errors;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
              FILE *stream, unsigned char *ptr, size_t size,
              bool nohex)
    
      unsigned int width=0x10;
    
      if(nohex)
        /* without the hex output, we can fit more on screen */
        width = 0x40;
    
      fprintf(stream, "%s, %zd bytes (0x%zx)\n", text, size, size);
    
      for(i=0; i<size; i+= width) {
    
        if(!nohex) {
          /* hex not disabled, show it */
          for(c = 0; c < width; c++)
            if(i+c < size)
              fprintf(stream, "%02x ", ptr[i+c]);
            else
              fputs("   ", stream);
        }
    
    
        for(c = 0; (c < width) && (i+c < size); c++) {
          /* check for 0D0A; if found, skip past and start a new line of output */
          if (nohex && (i+c+1 < size) && ptr[i+c]==0x0D && ptr[i+c+1]==0x0A) {
            i+=(c+2-width);
            break;
          }
    
          fprintf(stream, "%c",
                  (ptr[i+c]>=0x20) && (ptr[i+c]<0x80)?ptr[i+c]:'.');
    
          /* check again for 0D0A, to avoid an extra \n if it's at width */
          if (nohex && (i+c+2 < size) && ptr[i+c+1]==0x0D && ptr[i+c+2]==0x0A) {
            i+=(c+3-width);
            break;
          }
        }
    
      fflush(stream);
    
    }
    
    static
    int my_trace(CURL *handle, curl_infotype type,
                 unsigned char *data, size_t size,
                 void *userp)
    {
      struct Configurable *config = (struct Configurable *)userp;
      FILE *output=config->errors;
    
      const char *text;
    
      if(!config->trace_stream) {
    
        if(curl_strequal("-", config->trace_dump))
    
          config->trace_stream = stdout;
        else {
          config->trace_stream = fopen(config->trace_dump, "w");
          config->trace_fopened = TRUE;
        }
      }
    
    
      if(config->trace_stream)
        output = config->trace_stream;
    
      switch (type) {
      case CURLINFO_TEXT:
        fprintf(output, "== Info: %s", data);
    
      default: /* in case a new one is introduced to shock us */
        return 0;
    
    
        text = "=> Send header";
    
        text = "=> Send data";
    
        text = "<= Recv header";
    
        text = "<= Recv data";
    
    
      dump(text, output, data, size, config->trace_ascii);
    
    static void free_config_fields(struct Configurable *config)
    
      if(config->random_file)
        free(config->random_file);
      if(config->egd_file)
        free(config->egd_file);
    
      if(config->userpwd)
        free(config->userpwd);
      if(config->postfields)
        free(config->postfields);
      if(config->proxy)
        free(config->proxy);
      if(config->proxyuserpwd)
        free(config->proxyuserpwd);
      if(config->cookie)
        free(config->cookie);
      if(config->cookiefile)
        free(config->cookiefile);
      if(config->krb4level)
        free(config->krb4level);
      if(config->headerfile)
        free(config->headerfile);
      if(config->ftpport)
        free(config->ftpport);
      if(config->range)
        free(config->range);
      if(config->customrequest)
        free(config->customrequest);
      if(config->writeout)
        free(config->writeout);
      if(config->httppost)
        curl_formfree(config->httppost);
      if(config->cacert)
        free(config->cacert);
    
      if(config->capath)
        free(config->capath);
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      if(config->cookiejar)
        free(config->cookiejar);
    
    
      curl_slist_free_all(config->quote); /* checks for config->quote == NULL */
      curl_slist_free_all(config->postquote); /*  */
      curl_slist_free_all(config->headers); /*  */
    
    #if defined(WIN32) && !defined(__CYGWIN32__)
    
    /* Function to find CACert bundle on a Win32 platform using SearchPath.
     * (SearchPath is defined in windows.h, which is #included into libcurl)
     * (Use the ASCII version instead of the unicode one!)
     * The order of the directories it searches is:
     *  1. application's directory
     *  2. current working directory
     *  3. Windows System directory (e.g. C:\windows\system32)
     *  4. Windows Directory (e.g. C:\windows)
     *  5. all directories along %PATH%
     */
    static void FindWin32CACert(struct Configurable *config, 
                                const char *bundle_file)
    {
      /* only check for cert file if "we" support SSL */
    
      if(curlinfo->features & CURL_VERSION_SSL) {
    
        DWORD buflen;
        char *ptr = NULL;
        char *retval = (char *) malloc(sizeof (TCHAR) * (MAX_PATH + 1));
        if (!retval)
          return;
        retval[0] = '\0';
        buflen = SearchPathA(NULL, bundle_file, NULL, MAX_PATH+2, retval, &ptr);
        if (buflen > 0) {
          GetStr(&config->cacert, retval);
        }
        free(retval);
      }
    }
    
    #endif
    
    operate(struct Configurable *config, 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 */
    
      struct getout *urlnode;
      struct getout *nextnode;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
      struct OutStruct outs;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      struct InStruct input;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
      char *url = NULL;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      int urlnum;
    
      char *infiles; /* might a glob pattern */
      char *uploadfile=NULL; /* a single file, never a glob */
    
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      int separator = 0;
      
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      FILE *headerfilep = NULL;
      char *urlbuffer=NULL;
    
      curl_off_t uploadfilesize; /* -1 means unknown */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      bool stillflags=TRUE;
    
    
      bool allocuseragent=FALSE;
    
    
      CURL *curl;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      int i;
    
      int up; /* upload file counter within a single upload glob */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
    #ifdef CURLDEBUG
    
      /* this sends all memory debug messages to a logfile named memdump */
    
      env = curl_getenv("CURL_MEMDEBUG");
      if(env) {
    
        curl_free(env);
    
      env = curl_getenv("CURL_MEMLIMIT");
      if(env) {
        curl_memlimit(atoi(env));
        curl_free(env);
      }
    
      memset(&outs,0,sizeof(outs));
    
    
      /* we get libcurl info right away */
      curlinfo = curl_version_info(CURLVERSION_NOW);
    
    
      errorbuffer[0]=0; /* prevent junk from being output */
    
    
      /* inits */
      if (main_init() != CURLE_OK) {
        helpf("error initializing curl library\n");
        return CURLE_FAILED_INIT;
      }
    
      config->showerror=TRUE;
      config->conf=CONF_DEFAULT;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
      if(argc>1 &&
    
         (!curl_strnequal("--", argv[1], 2) && (argv[1][0] == '-')) &&
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
         strchr(argv[1], 'q')) {
        /*
         * The first flag, that is not a verbose name, but a shortname
         * and it includes the 'q' flag!
         */
        ;
      }
      else {
    
        parseconfig(NULL, config);
    
      if ((argc < 2)  && !config->url_list) {
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        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
    
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    	/* 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);
    
              const char *reason = param2text(res);
              if(res != PARAM_HELP_REQUESTED)
                helpf("option %s: %s\n", origopt, reason);
    
              clean_getout(config);
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    	if(passarg) /* we're supposed to skip this */
    	  i++;
          }
        }
        else {
    
          bool used;
          /* just add the URL please */
    
          res = getparameter((char *)"--url", argv[i], &used, config);
    
          if(res)
            return res;
    
      if(!config->url_list || !config->url_list->url) {
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        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
    
    
      /* On WIN32 (non-cygwin), we can't set the path to curl-ca-bundle.crt
       * at compile time. So we look here for the file in two ways:
       * 1: look at the environment variable CURL_CA_BUNDLE for a path
       * 2: if #1 isn't found, use the windows API function SearchPath()
       *    to find it along the app's path (includes app's dir and CWD)
       *
       * We support the environment variable thing for non-Windows platforms
       * too. Just for the sake of it.
       */
    
      if (!config->cacert &&
          !config->capath &&
          !config->insecure_ok) {
    
        env = curl_getenv("CURL_CA_BUNDLE");
        if(env) {
          GetStr(&config->cacert, env);
    
          curl_free(env);
    
        }
    #if defined(WIN32) && !defined(__CYGWIN32__)
    
        else
          FindWin32CACert(config, "curl-ca-bundle.crt");
    
      if (config->postfields) {
        if (config->use_httpget) {
          /* Use the postfields data for a http get */
          httpgetfields = strdup(config->postfields);
          free(config->postfields);
          config->postfields = NULL;
    
          if(SetHTTPrequest((config->conf&CONF_NOBODY?HTTPREQ_HEAD:HTTPREQ_GET),
                            &config->httpreq)) {
    
            free(httpgetfields);
            return PARAM_BAD_USE;
          }
        }
        else {
          if(SetHTTPrequest(HTTPREQ_SIMPLEPOST, &config->httpreq))
            return PARAM_BAD_USE;
        }
      }
    
    
      /*
       * Get a curl handle to use for all forthcoming curl transfers.  Cleanup
    
       */
      curl = curl_easy_init();
    
        return CURLE_FAILED_INIT;
    
      /* After this point, we should call curl_easy_cleanup() if we decide to bail
       * out from this function! */
    
    
      urlnode = config->url_list;
    
      if(config->headerfile) {
        /* open file for output: */
        if(strcmp(config->headerfile,"-")) {
          heads.filename = config->headerfile;
          headerfilep=NULL;
        }
        else
          headerfilep=stdout;
        heads.stream = headerfilep;
        heads.config = config;
      }
    
    
      /* loop through the list of given URLs */
    
        /* get the full URL (it might be NULL) */
    
        if(NULL == url) {
          /* This node had no URL, skip it and continue to the next */
          if(urlnode->outfile)
            free(urlnode->outfile);
        
          /* move on to the next URL */
          nextnode=urlnode->next;
          free(urlnode); /* free the node */
          urlnode = nextnode;
          continue; /* next please */
        }
    
    
        /* default output stream is stdout */
        outs.stream = stdout;
        outs.config = config;
    
        /* save outfile pattern before expansion */
        outfiles = urlnode->outfile?strdup(urlnode->outfile):NULL;
    
        infiles = urlnode->infile;
    
        if(!config->globoff && infiles) {
          /* Unless explicitly shut off */
          res = glob_url(&inglob, infiles, &infilenum,
    
                         config->showerror?
                         (config->errors?config->errors:stderr):NULL);
    
          if(res != CURLE_OK) {
            clean_getout(config);
            break;
          }
    
        /* Here's the loop for uploading multiple files within the same
           single globbed string. If no upload, we enter the loop once anyway. */
        for(up = 0;
            (!up && !infiles) ||
              (uploadfile = inglob?
               glob_next_url(inglob):
               (!up?strdup(infiles):NULL));
            up++) {
          uploadfilesize=-1;
    
          if(!config->globoff) {
            /* Unless explicitly shut off, we expand '{...}' and '[...]'
               expressions and return total number of URLs in pattern set */
            res = glob_url(&urls, dourl, &urlnum,
                           config->showerror?
                           (config->errors?config->errors:stderr):NULL);
            if(res != CURLE_OK) {
              break;
            }
          }
    
          else
            urlnum = 1; /* without globbing, this is a single URL */
    
          /* if multiple files extracted to stdout, insert separators! */
          separator= ((!outfiles || curl_strequal(outfiles, "-")) && urlnum > 1);
    
          /* Here's looping around each globbed URL */
          for(i = 0;
              (url = urls?glob_next_url(urls):(i?NULL:strdup(url)));
              i++) {
            char *outfile;
            outfile = outfiles?strdup(outfiles):NULL;
            
            if((urlnode->flags&GETOUT_USEREMOTE) ||
               (outfile && !curl_strequal("-", outfile)) ) {
              
              /* 
               * We have specified a file name to store the result in, or we have
               * decided we want to use the remote file name.
               */
    
              if(!outfile) {
                /* Find and get the remote file name */
                char * pc =strstr(url, "://");
                if(pc)
                  pc+=3;
                else
                  pc=url;
                pc = strrchr(pc, '/');
    
                if(pc) {
                  /* duplicate the string beyond the slash */
                  pc++;
                  outfile = *pc ? strdup(pc): NULL;
                }
                if(!outfile || !*outfile) {
                  helpf("Remote file name has no length!\n");
                  res = CURLE_WRITE_ERROR;
                  free(url);
                  break;
                }
    
                {
                  /* This is for DOS, and then we do some major replacing of 
                     bad characters in the file name before using it */
                  char *file1=xmalloc(PATH_MAX);
                  strcpy(file1, msdosify(outfile));
                  strcpy(outfile, rename_if_dos_device_name(file1));
                  xfree(file1);
                }
    
              else if(urls) {
                /* fill '#1' ... '#9' terms from URL pattern */
                char *storefile = outfile;
                outfile = glob_match_url(storefile, urls);
                free(storefile);
                if(!outfile) {
                  /* bad globbing */
                  fprintf(stderr, "bad output glob!\n");
                  free(url);
                  res = CURLE_FAILED_INIT;
                  break;
                }
    
              
              /* Create the directory hierarchy, if not pre-existant to a multiple
                 file output call */
    
              if(config->create_dirs)
                if (-1 == create_dir_hierarchy(outfile)) {
                  return CURLE_WRITE_ERROR;
                }
    
              if(config->resume_from_current) {
                /* We're told to continue from where we are now. Get the
                   size of the file as it is now and open it for append instead */
                
                struct stat fileinfo;
    
                /*VMS?? -- Danger, the filesize is only valid for stream files */
                if(0 == stat(outfile, &fileinfo))
                  /* set offset to current file size: */
                  config->resume_from = fileinfo.st_size;
                else
                  /* let offset be 0 */
                  config->resume_from = 0;
              }
    
    	  outs.filename = outfile;
    
    
              if(config->resume_from) {
                /* open file for output: */
                outs.stream=(FILE *) fopen(outfile, config->resume_from?"ab":"wb");
                if (!outs.stream) {
                  helpf("Can't open '%s'!\n", outfile);
                  return CURLE_WRITE_ERROR;
                }
              }
              else {
                outs.stream = NULL; /* open when needed */
    
            infdfopen=FALSE;
            if(uploadfile && !curl_strequal(uploadfile, "-")) {
              /*
               * We have specified a file to upload and it isn't "-".
               */
              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;
    
                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.*/
    
                /* We only want the part of the local path that is on the right
                   side of the rightmost slash and backslash. */
                char *filep = strrchr(uploadfile, '/');
                char *file2 = strrchr(filep?filep:uploadfile, '\\');
    
                if(file2)
                  filep = file2+1;
                else if(filep)