Skip to content
Snippets Groups Projects
Select Git revision
  • master-tlmsp default
  • master-tlmsp-latest-curl
  • bagder/runtests-duration
  • bagder/disable-progress-meter
  • master protected
  • bagder/parallel-transfers
  • bagder/hsts-rebased
  • QUIC
  • bagder/FOLLOW_IGNORE_CUSTOM
  • bagder/test-when-disabled
  • jay/test
  • bagder/curl-better-disabled-options
  • bagder/wolfssh
  • dfandrich/ci
  • TFO-windows
  • bagder/schannel-verifyhost
  • bagder/mbedtls-ssl_read-zero
  • bagder/wolfssl-crl
  • curl-7_65_1
  • curl-7_65_0
  • curl-7_64_1
  • curl-7_64_0
  • curl-7_63_0
  • curl-7_62_0
  • curl-7_61_1
  • curl-7_61_0
  • curl-7_60_0
  • curl-7_59_0
  • curl-7_58_0
  • curl-7_57_0
  • curl-7_56_1
  • curl-7_56_0
  • curl-7_55_1
  • curl-7_55_0
  • curl-7_54_1
  • curl-7_54_0
  • curl-7_53_1
  • curl-7_53_0
38 results

main.c

Blame
  • main.c 119.15 KiB
    /***************************************************************************
     *                                  _   _ ____  _
     *  Project                     ___| | | |  _ \| |
     *                             / __| | | | |_) | |
     *                            | (__| |_| |  _ <| |___
     *                             \___|\___/|_| \_\_____|
     *
     * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
     *
     * This software is licensed as described in the file COPYING, which
     * you should have received as part of this distribution. The terms
     * are also available at http://curl.haxx.se/docs/copyright.html.
     *
     * You may opt to use, copy, modify, merge, publish, distribute and/or sell
     * copies of the Software, and permit persons to whom the Software is
     * furnished to do so, under the terms of the COPYING file.
     *
     * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
     * KIND, either express or implied.
     *
     * $Id$
     ***************************************************************************/
    
    /* This is now designed to have its own local setup.h */
    #include "setup.h"
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stdarg.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <ctype.h>
    #include <errno.h>
    
    #include <curl/curl.h>
    
    #include "urlglob.h"
    #include "writeout.h"
    #include "getpass.h"
    #include "homedir.h"
    #ifdef USE_MANUAL
    #include "hugehelp.h"
    #endif
    #ifdef USE_ENVIRONMENT
    #include "writeenv.h"
    #endif
    
    #define CURLseparator   "--_curl_--"
    
    #if defined(WIN32)&&!defined(__CYGWIN32__)
    #include <winsock2.h>
    #endif
    
    #ifdef __NOVELL_LIBC__
    #include <screen.h>
    #endif
    
    #ifdef TIME_WITH_SYS_TIME
    /* We can include both fine */
    #include <sys/time.h>
    #include <time.h>
    #else
    #ifdef HAVE_SYS_TIME_H
    # include <sys/time.h>
    #else
    # include <time.h>
    #endif
    #endif
    
    
    #include "version.h"
    
    #ifdef HAVE_IO_H /* typical win32 habit */
    #include <io.h>
    #endif
    
    #ifdef HAVE_UNISTD_H
    #include <unistd.h>
    #endif
    
    #ifdef HAVE_FCNTL_H
    #include <fcntl.h>
    #endif
    
    #ifdef HAVE_UTIME_H
    #include <utime.h>
    #else
    #ifdef HAVE_SYS_UTIME_H
    #include <sys/utime.h>
    #endif
    
    #endif /* HAVE_UTIME_H */
    
    #ifdef HAVE_LIMITS_H
    #include <limits.h>
    #endif
    
    #ifdef HAVE_SYS_POLL_H
    #include <sys/poll.h>
    #endif
    
    #ifdef HAVE_LOCALE_H
    #include <locale.h> /* for setlocale() */
    #endif
    
    #define ENABLE_CURLX_PRINTF
    /* make the curlx header define all printf() functions to use the curlx_*
       versions instead */
    #include <curlx.h> /* header from the libcurl directory */
    
    /* The last #include file should be: */
    #ifdef CURLDEBUG
    #ifndef CURLTOOLDEBUG
    #define MEMDEBUG_NODEFINES
    #endif
    /* This is low-level hard-hacking memory leak tracking and similar. Using
       the library level code from this client-side is ugly, but we do this
       anyway for convenience. */
    #include "memdebug.h"
    #endif
    
    #define DEFAULT_MAXREDIRS  50L
    
    #ifdef __DJGPP__
    #include <dos.h>
    
    char *msdosify(char *);
    char *rename_if_dos_device_name(char *);
    
    /* we want to glob our own argv[] */
    char **__crt0_glob_function (char *arg)
    {
      (void)arg;
      return (char**)0;
    }
    #endif /* __DJGPP__ */
    
    #ifndef __cplusplus
    #ifndef typedef_bool
    typedef char bool;
    #endif
    #endif
    
    #define CURL_PROGRESS_STATS 0 /* default progress display */
    #define CURL_PROGRESS_BAR   1
    
    /**
     * @def MIN
     * standard MIN macro
     */
    #ifndef MIN
    #define MIN(X,Y)        (((X) < (Y)) ? (X) : (Y))
    #endif
    
    typedef enum {
      HTTPREQ_UNSPEC,
      HTTPREQ_GET,
      HTTPREQ_HEAD,
      HTTPREQ_POST,
      HTTPREQ_SIMPLEPOST,
      HTTPREQ_CUSTOM,
      HTTPREQ_LAST
    } HttpReq;
    
    /* Just a set of bits */
    #define CONF_DEFAULT  0
    
    #define CONF_AUTO_REFERER (1<<4) /* the automatic referer-system please! */
    #define CONF_VERBOSE  (1<<5) /* talk a lot */
    #define CONF_HEADER   (1<<8) /* throw the header out too */
    #define CONF_NOPROGRESS (1<<10) /* shut off the progress meter */
    #define CONF_NOBODY   (1<<11) /* use HEAD to get http document */
    #define CONF_FAILONERROR (1<<12) /* no output on http error codes >= 300 */
    #define CONF_FTPLISTONLY (1<<16) /* Use NLST when listing ftp dir */
    #define CONF_FTPAPPEND (1<<20) /* Append instead of overwrite on upload! */
    #define CONF_NETRC    (1<<22)  /* read user+password from .netrc */
    #define CONF_FOLLOWLOCATION (1<<23) /* use Location: Luke! */
    #define CONF_GETTEXT  (1<<24) /* use ASCII/text for transfer */
    #define CONF_MUTE     (1<<28) /* force NOPROGRESS */
    
    #define CONF_NETRC_OPT (1<<29)  /* read user+password from either
                                     * .netrc or URL*/
    #define CONF_UNRESTRICTED_AUTH (1<<30)
    /* Send authentication (user+password) when following
     * locations, even when hostname changed */
    
    #ifndef HAVE_STRDUP
    /* Ultrix doesn't have strdup(), so make a quick clone: */
    char *strdup(char *str)
    {
      int len;
      char *newstr;
    
      len = strlen(str);
      newstr = (char *) malloc((len+1)*sizeof(char));
      if (!newstr)
        return (char *)NULL;
    
      strcpy(newstr,str);
    
      return newstr;
    
    }
    #endif
    
    #ifdef WIN32
    #include <direct.h>
    #define F_OK 0
    #define mkdir(x,y) (mkdir)(x)
    #endif
    
    #ifdef  VMS
    #include "curlmsg_vms.h"
    #endif
    
    #if !defined(HAVE_FTRUNCATE) && defined(WIN32)
    /*
     * Truncate a file handle at a 64-bit position 'where'.
     * Borland doesn't even support 64-bit types.
     */
    #ifdef __BORLANDC__
    #define _lseeki64(hnd,ofs,whence) lseek(hnd,ofs,whence)
    #endif
    
    static int ftruncate (int fd, curl_off_t where)
    {
      curl_off_t curr;
      int rc = 0;
    
      if ((curr = _lseeki64(fd, 0, SEEK_CUR)) < 0)
         return -1;
    
      if (_lseeki64(fd, where, SEEK_SET) < 0)
         return -1;
    
      if (_write(fd, 0, 0) < 0)
         rc = -1;
      _lseeki64(fd, curr, SEEK_SET);
      return rc;
    }
    #endif
    
    /*
     * This is the main global constructor for the app. Call this before
     * _any_ libcurl usage. If this fails, *NO* libcurl functions may be
     * used, or havoc may be the result.
     */
    static CURLcode main_init(void)
    {
      return curl_global_init(CURL_GLOBAL_DEFAULT);
    }
    
    /*
     * This is the main global destructor for the app. Call this after
     * _all_ libcurl usage is done.
     */
    static void main_free(void)
    {
      curl_global_cleanup();
    }
    
    static int SetHTTPrequest(HttpReq req, HttpReq *store)
    {
      if((*store == HTTPREQ_UNSPEC) ||
         (*store == req)) {
        *store = req;
        return 0;
      }
      fprintf(stderr, "You can only select one HTTP request!\n");
      return 1;
    }
    
    static void helpf(const 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' "
    #ifdef USE_MANUAL
              "or 'curl --manual' "
    #endif
              "for more information\n");
    }
    
    /*
     * A chain of these nodes contain URL to get and where to put the URL's
     * contents.
     */
    struct getout {
      struct getout *next; /* next one */
      char *url;     /* the URL we deal with */
      char *outfile; /* where to store the output */
      char *infile;  /* file to upload, if GETOUT_UPLOAD is set */
      int flags;     /* options */
    };
    #define GETOUT_OUTFILE (1<<0)   /* set when outfile is deemed done */
    #define GETOUT_URL     (1<<1)   /* set when URL is deemed done */
    #define GETOUT_USEREMOTE (1<<2) /* use remote file name locally */
    #define GETOUT_UPLOAD  (1<<3)   /* if set, -T has been used */
    #define GETOUT_NOUPLOAD  (1<<4) /* if set, -T "" has been used */
    
    
    static void help(void)
    {
      int i;
      static const char *helptext[]={
        "Usage: curl [options...] <url>",
        "Options: (H) means HTTP/HTTPS only, (F) means FTP only",
        " -a/--append        Append to target file when uploading (F)",
        " -A/--user-agent <string> User-Agent to send to server (H)",
        "    --anyauth       Tell curl to choose authentication method (H)",
        " -b/--cookie <name=string/file> Cookie string or file to read cookies from (H)",
        "    --basic         Enable HTTP Basic Authentication (H)",
        " -B/--use-ascii     Use ASCII/text transfer",
        " -c/--cookie-jar <file> Write cookies to this file after operation (H)",
        " -C/--continue-at <offset> Resumed transfer offset",
        " -d/--data <data>   HTTP POST data (H)",
        "    --data-ascii <data>   HTTP POST ASCII data (H)",
        "    --data-binary <data>  HTTP POST binary data (H)",
        "    --negotiate     Enable HTTP Negotiate Authentication (H)",
        "    --digest        Enable HTTP Digest Authentication (H)",
        "    --disable-eprt  Prevent curl from using EPRT or LPRT (F)",
        "    --disable-epsv  Prevent curl from using EPSV (F)",
        " -D/--dump-header <file> Write the headers to this file",
        "    --egd-file <file> EGD socket path for random data (SSL)",
        "    --tcp-nodelay   Set the TCP_NODELAY option",
    #ifdef USE_ENVIRONMENT
        "    --environment   Write result codes to environment variables (RISC OS)",
    #endif
        " -e/--referer       Referer URL (H)",
        " -E/--cert <cert[:passwd]> Client certificate file and password (SSL)",
        "    --cert-type <type> Certificate file type (DER/PEM/ENG) (SSL)",
        "    --key <key>     Private key file name (SSL)",
        "    --key-type <type> Private key file type (DER/PEM/ENG) (SSL)",
        "    --pass  <pass>  Pass phrase for the private key (SSL)",
        "    --engine <eng>  Crypto engine to use (SSL)",
        "    --cacert <file> CA certificate to verify peer against (SSL)",
        "    --capath <directory> CA directory (made using c_rehash) to verify",
        "                    peer against (SSL)",
        "    --ciphers <list> SSL ciphers to use (SSL)",
        "    --compressed    Request compressed response (using deflate or gzip)",
        "    --connect-timeout <seconds> Maximum time allowed for connection",
        "    --create-dirs   Create necessary local directory hierarchy",
        "    --crlf          Convert LF to CRLF in upload",
        " -f/--fail          Fail silently (no output at all) on errors (H)",
        "    --ftp-create-dirs Create the remote dirs if not present (F)",
        "    --ftp-pasv      Use PASV instead of PORT (F)",
        "    --ftp-ssl       Enable SSL/TLS for the ftp transfer (F)",
        " -F/--form <name=content> Specify HTTP multipart POST data (H)",
        " -g/--globoff       Disable URL sequences and ranges using {} and []",
        " -G/--get           Send the -d data with a HTTP GET (H)",
        " -h/--help          This help text",
        " -H/--header <line> Custom header to pass to server (H)",
        " -i/--include       Include protocol headers in the output (H/F)",
        " -I/--head          Show document info only",
        " -j/--junk-session-cookies Ignore session cookies read from file (H)",
        "    --interface <interface> Specify network interface to use",
        "    --krb4 <level>  Enable krb4 with specified security level (F)",
        " -k/--insecure      Allow curl to connect to SSL sites without certs (H)",
        " -K/--config        Specify which config file to read",
        " -l/--list-only     List only names of an FTP directory (F)",
        "    --limit-rate <rate> Limit transfer speed to this rate",
        " -L/--location      Follow Location: hints (H)",
        "    --location-trusted Follow Location: and send authentication even ",
        "                    to other hostnames (H)",
        " -m/--max-time <seconds> Maximum time allowed for the transfer",
        "    --max-redirs <num> Maximum number of redirects allowed (H)",
        "    --max-filesize <bytes> Maximum file size to download (H/F)",
        " -M/--manual        Display the full manual",
        " -n/--netrc         Must read .netrc for user name and password",
        "    --netrc-optional Use either .netrc or URL; overrides -n",
        "    --ntlm          Enable HTTP NTLM authentication (H)",
        " -N/--no-buffer     Disable buffering of the output stream",
        " -o/--output <file> Write output to <file> instead of stdout",
        " -O/--remote-name   Write output to a file named as the remote file",
        " -p/--proxytunnel   Operate through a HTTP proxy tunnel (using CONNECT)",
        "    --proxy-basic   Enable Basic authentication on the proxy (H)",
        "    --proxy-digest  Enable Digest authentication on the proxy (H)",
        "    --proxy-ntlm    Enable NTLM authentication on the proxy (H)",
        " -P/--ftp-port <address> Use PORT with address instead of PASV (F)",
        " -q                 If used as the first parameter disables .curlrc",
        " -Q/--quote <cmd>   Send command(s) to server before file transfer (F)",
        " -r/--range <range> Retrieve a byte range from a HTTP/1.1 or FTP server",
        "    --random-file <file> File for reading random data from (SSL)",
        " -R/--remote-time   Set the remote file's time on the local output",
        "    --retry <num>   Retry request <num> times if transient problems occur",
        "    --retry-delay <seconds> When retrying, wait this many seconds between each",
        "    --retry-max-time <seconds> Retry only within this period",
        " -s/--silent        Silent mode. Don't output anything",
        " -S/--show-error    Show error. With -s, make curl show errors when they occur",
        "    --socks <host[:port]> Use SOCKS5 proxy on given host + port",
        "    --stderr <file> Where to redirect stderr. - means stdout",
        " -t/--telnet-option <OPT=val> Set telnet option",
        "    --trace <file>  Dump a network/debug trace to the given file",
        "    --trace-ascii <file> Like --trace but without the hex output",
        " -T/--upload-file <file> Transfer/upload <file> to remote site",
        "    --url <URL>     Another way to specify URL to work with",
        " -u/--user <user[:password]> Specify user and password to use",
        "                    Overrides -n and --netrc-optional",
        " -U/--proxy-user <user[:password]> Specify Proxy authentication",
        " -v/--verbose       Make the operation more talkative",
        " -V/--version       Show version number and quit",
    #ifdef __DJGPP__
        "    --wdebug        Turn on Watt-32 debugging under DJGPP",
    #endif
        " -w/--write-out [format] What to output after completion",
        " -x/--proxy <host[:port]> Use HTTP proxy on given port",
        " -X/--request <command> Specify request command to use",
        " -y/--speed-time    Time needed to trig speed-limit abort. Defaults to 30",
        " -Y/--speed-limit   Stop transfer if below speed-limit for 'speed-time' secs",
        " -z/--time-cond <time> Transfer based on a time condition",
        " -0/--http1.0       Use HTTP 1.0 (H)",
        " -1/--tlsv1         Use TLSv1 (SSL)",
        " -2/--sslv2         Use SSLv2 (SSL)",
        " -3/--sslv3         Use SSLv3 (SSL)",
        " -4/--ipv4          Resolve name to IPv4 address",
        " -6/--ipv6          Resolve name to IPv6 address",
        " -#/--progress-bar  Display transfer progress as a progress bar",
        NULL
      };
      for(i=0; helptext[i]; i++) {
        puts(helptext[i]);
    #ifdef __NOVELL_LIBC__
        if (i && ((i % 23) == 0))
          pressanykey();
    #endif
      }
    }
    
    struct LongShort {
      const char *letter;
      const char *lname;
      bool extraparam;
    };
    
    struct Configurable {
      bool remote_time;
      char *random_file;
      char *egd_file;
      char *useragent;
      char *cookie;     /* single line with specified cookies */
      char *cookiejar;  /* write to this file */
      char *cookiefile; /* read from this file */
      bool cookiesession; /* new session? */
      bool encoding;    /* Accept-Encoding please */
      long authtype;    /* auth bitmask */
      bool use_resume;
      bool resume_from_current;
      bool disable_epsv;
      bool disable_eprt;
      curl_off_t resume_from;
      char *postfields;
      long postfieldsize;
      char *referer;
      long timeout;
      long connecttimeout;
      long maxredirs;
      curl_off_t max_filesize;
      char *headerfile;
      char *ftpport;
      char *iface;
      unsigned short porttouse;
      char *range;
      long low_speed_limit;
      long low_speed_time;
      bool showerror;
      char *userpwd;
      char *proxyuserpwd;
      char *proxy;
      bool proxytunnel;
      long conf;
      struct getout *url_list; /* point to the first node */
      struct getout *url_last; /* point to the last/current node */
      struct getout *url_get;  /* point to the node to fill in URL */
      struct getout *url_out;  /* point to the node to fill in outfile */
      char *cipher_list;
      char *cert;
      char *cert_type;
      char *cacert;
      char *capath;
      char *key;
      char *key_type;
      char *key_passwd;
      char *engine;
      bool crlf;
      char *customrequest;
      char *krb4level;
      char *trace_dump; /* file to dump the network trace to, or NULL */
      FILE *trace_stream;
      bool trace_fopened;
      bool trace_ascii;
      long httpversion;
      bool progressmode;
      bool nobuffer;
      bool globoff;
      bool use_httpget;
      bool insecure_ok; /* set TRUE to allow insecure SSL connects */
      bool create_dirs;
      bool ftp_create_dirs;
      bool proxyntlm;
      bool proxydigest;
      bool proxybasic;
      char *writeout; /* %-styled format string to output */
      bool writeenv; /* write results to environment, if available */
      FILE *errors; /* if stderr redirect is requested */
      bool errors_fopened;
      struct curl_slist *quote;
      struct curl_slist *postquote;
      struct curl_slist *prequote;
      long ssl_version;
      long ip_version;
      curl_TimeCond timecond;
      time_t condtime;
      struct curl_slist *headers;
      struct curl_httppost *httppost;
      struct curl_httppost *last_post;
      struct curl_slist *telnet_options;
      HttpReq httpreq;
    
      /* for bandwidth limiting features: */
      curl_off_t sendpersecond; /* send to peer */
      curl_off_t recvpersecond; /* receive from peer */
      struct timeval lastsendtime;
      size_t lastsendsize;
      struct timeval lastrecvtime;
      size_t lastrecvsize;
      bool ftp_ssl;
      char *socks5proxy;
      bool tcp_nodelay;
      long req_retry;   /* number of retries */
      long retry_delay; /* delay between retries (in seconds) */
      long retry_maxtime; /* maximum time to keep retrying */
    };
    
    /* global variable to hold info about libcurl */
    static curl_version_info_data *curlinfo;
    
    static void parseconfig(const char *filename,
                            struct Configurable *config);
    static char *my_get_line(FILE *fp);
    static int create_dir_hierarchy(char *outfile);
    
    static void GetStr(char **string,
                       char *value)
    {
      if(*string)
        free(*string);
      if(value)
        *string = strdup(value);
      else
        *string = NULL;
    }
    
    static char *file2string(FILE *file)
    {
      char buffer[256];
      char *ptr;
      char *string=NULL;
      size_t len=0;
      size_t 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 char *file2memory(FILE *file, long *size)
    {
      char buffer[1024];
      char *string=NULL;
      char *newstring=NULL;
      size_t len=0;
      long stringlen=0;
    
      if(file) {
        while((len = fread(buffer, 1, sizeof(buffer), file))) {
          if(string) {
            newstring = realloc(string, len+stringlen);
            if(newstring)
              string = newstring;
            else
              break; /* no more strings attached! :-) */
          }
          else
            string = malloc(len);
          memcpy(&string[stringlen], buffer, len);
          stringlen+=len;
        }
        *size = stringlen;
        return string;
      }
      else
        return NULL; /* no string */
    }
    
    static void clean_getout(struct Configurable *config)
    {
      struct getout *node=config->url_list;
      struct getout *next;
    
      while(node) {
        next = node->next;
        if(node->url)
          free(node->url);
        if(node->outfile)
          free(node->outfile);
        if(node->infile)
          free(node->infile);
        free(node);
    
        node = next; /* GOTO next */
      }
    }
    
    static struct getout *new_getout(struct Configurable *config)
    {
      struct getout *node =malloc(sizeof(struct getout));
      struct getout *last= config->url_last;
      if(node) {
        /* clear the struct */
        memset(node, 0, sizeof(struct getout));
    
        /* append this new node last in the list */
        if(last)
          last->next = node;
        else
          config->url_list = node; /* first node */
    
        /* move the last pointer */
        config->url_last = node;
      }
      return node;
    }
    
    /* Structure for storing the information needed to build a multiple files
     * section
    */
    struct multi_files {
      struct curl_forms   form;
      struct multi_files *next;
    };
    
    /* Add a new list entry possibly with a type_name
     */
    static struct multi_files *
    AddMultiFiles (const char *file_name,
                   const char *type_name,
                   const char *show_filename,
                   struct multi_files **multi_start,
                   struct multi_files **multi_current)
    {
      struct multi_files *multi;
      struct multi_files *multi_type = NULL;
      struct multi_files *multi_name = NULL;
      multi = (struct multi_files *)malloc(sizeof(struct multi_files));
      if (multi) {
        memset(multi, 0, sizeof(struct multi_files));
        multi->form.option = CURLFORM_FILE;
        multi->form.value = file_name;
      }
      else
        return NULL;
    
      if (!*multi_start)
        *multi_start = multi;
    
      if (type_name) {
        multi_type = (struct multi_files *)malloc(sizeof(struct multi_files));
        if (multi_type) {
          memset(multi_type, 0, sizeof(struct multi_files));
          multi_type->form.option = CURLFORM_CONTENTTYPE;
          multi_type->form.value = type_name;
          multi->next = multi_type;
    
          multi = multi_type;
        }
        else {
          free (multi);
          return NULL;
        }
      }
      if (show_filename) {
        multi_name = (struct multi_files *)malloc(sizeof(struct multi_files));
        if (multi_name) {
          memset(multi_name, 0, sizeof(struct multi_files));
          multi_name->form.option = CURLFORM_FILENAME;
          multi_name->form.value = show_filename;
          multi->next = multi_name;
    
          multi = multi_name;
        }
        else {
          free (multi);
          return NULL;
        }
      }
    
      if (*multi_current)
        (*multi_current)->next = multi;
    
      *multi_current = multi;
    
      return *multi_current;
    }
    
    /* Free the items of the list.
     */
    static void FreeMultiInfo (struct multi_files *multi_start)
    {
      struct multi_files *multi;
      while (multi_start) {
        multi = multi_start;
        multi_start = multi_start->next;
        free (multi);
      }
    }
    
    /***************************************************************************
     *
     * formparse()
     *
     * Reads a 'name=value' paramter and builds the appropriate linked list.
     *
     * Specify files to upload with 'name=@filename'. Supports specified
     * given Content-Type of the files. Such as ';type=<content-type>'.
     *
     * You may specify more than one file for a single name (field). Specify
     * multiple files by writing it like:
     *
     * 'name=@filename,filename2,filename3'
     *
     * If you want content-types specified for each too, write them like:
     *
     * 'name=@filename;type=image/gif,filename2,filename3'
     *
     * If you want custom headers added for a single part, write them in a separate
     * file and do like this:
     *
     * 'name=foo;headers=@headerfile' or why not
     * 'name=@filemame;headers=@headerfile'
     *
     * To upload a file, but to fake the file name that will be included in the
     * formpost, do like this:
     *
     * 'name=@filename;filename=/dev/null'
     *
     * This function uses curl_formadd to fulfill it's job. Is heavily based on
     * the old curl_formparse code.
     *
     ***************************************************************************/
    
    #define FORM_FILE_SEPARATOR ','
    #define FORM_TYPE_SEPARATOR ';'
    
    static int formparse(char *input,
                         struct curl_httppost **httppost,
                         struct curl_httppost **last_post)
    {
      /* nextarg MUST be a string in the format 'name=contents' and we'll
         build a linked list with the info */
      char name[256];
      char *contents;
      char major[128];
      char minor[128];
      char *contp;
      const char *type = NULL;
      char *sep;
      char *sep2;
    
      if((1 == sscanf(input, "%255[^=]=", name)) &&
         (contp = strchr(input, '='))) {
        /* the input was using the correct format */
    
        /* Allocate the contents */
        contents = strdup(contp+1);
        if(!contents) {
          fprintf(stderr, "out of memory\n");
          return 1;
        }
        contp = contents;
    
        if('@' == contp[0]) {
          struct multi_files *multi_start = NULL, *multi_current = NULL;
          /* we use the @-letter to indicate file name(s) */
          contp++;
    
          multi_start = multi_current=NULL;
    
          do {
            /* since this was a file, it may have a content-type specifier
               at the end too, or a filename. Or both. */
            char *ptr;
            char *filename=NULL;
    
            sep=strchr(contp, FORM_TYPE_SEPARATOR);
            sep2=strchr(contp, FORM_FILE_SEPARATOR);
    
            /* pick the closest */
            if(sep2 && (sep2 < sep)) {
              sep = sep2;
    
              /* no type was specified! */
            }
    
            type = NULL;
    
            if(sep) {
    
              /* if we got here on a comma, don't do much */
              if(FORM_FILE_SEPARATOR == *sep)
                ptr = NULL;
              else
                ptr = sep+1;
    
              *sep=0; /* terminate file name at separator */
    
              while(ptr && (FORM_FILE_SEPARATOR!= *ptr)) {
    
                /* pass all white spaces */
                while(isspace((int)*ptr))
                  ptr++;
    
                if(curlx_strnequal("type=", ptr, 5)) {
    
                  /* set type pointer */
                  type = &ptr[5];
    
                  /* verify that this is a fine type specifier */
                  if(2 != sscanf(type, "%127[^/]/%127[^;,\n]",
                                 major, minor)) {
                    fprintf(stderr, "Illegally formatted content-type field!\n");
                    free(contents);
                    FreeMultiInfo (multi_start);
                    return 2; /* illegal content-type syntax! */
                  }
                  /* now point beyond the content-type specifier */
                  sep = (char *)type + strlen(major)+strlen(minor)+1;
    
                  *sep=0; /* zero terminate type string */
    
                  ptr=sep+1;
                }
                else if(curlx_strnequal("filename=", ptr, 9)) {
                  filename = &ptr[9];
                  ptr=strchr(filename, FORM_TYPE_SEPARATOR);
                  if(!ptr) {
                    ptr=strchr(filename, FORM_FILE_SEPARATOR);
                  }
                  if(ptr) {
                    *ptr=0; /* zero terminate */
                    ptr++;
                  }
                }
                else
                  /* confusion, bail out of loop */
                  break;
              }
              /* find the following comma */
              if(ptr)
                sep=strchr(ptr, FORM_FILE_SEPARATOR);
              else
                sep=NULL;
            }
            else {
              sep=strchr(contp, FORM_FILE_SEPARATOR);
            }
            if(sep) {
              /* the next file name starts here */
              *sep =0;
              sep++;
            }
            /* if type == NULL curl_formadd takes care of the problem */
    
            if (!AddMultiFiles (contp, type, filename, &multi_start,
                                &multi_current)) {
              fprintf(stderr, "Error building form post!\n");
              free(contents);
              FreeMultiInfo (multi_start);
              return 3;
            }
            contp = sep; /* move the contents pointer to after the separator */
    
          } while(sep && *sep); /* loop if there's another file name */
    
          /* now we add the multiple files section */
          if (multi_start) {
            struct curl_forms *forms = NULL;
            struct multi_files *ptr = multi_start;
            unsigned int i, count = 0;
            while (ptr) {
              ptr = ptr->next;
              ++count;
            }
            forms =
              (struct curl_forms *)malloc((count+1)*sizeof(struct curl_forms));
            if (!forms)
            {
              fprintf(stderr, "Error building form post!\n");
              free(contents);
              FreeMultiInfo (multi_start);
              return 4;
            }
            for (i = 0, ptr = multi_start; i < count; ++i, ptr = ptr->next)
            {
              forms[i].option = ptr->form.option;
              forms[i].value = ptr->form.value;
            }
            forms[count].option = CURLFORM_END;
            FreeMultiInfo (multi_start);
            if (curl_formadd(httppost, last_post,
                             CURLFORM_COPYNAME, name,
                             CURLFORM_ARRAY, forms, CURLFORM_END) != 0) {
              fprintf(stderr, "curl_formadd failed!\n");
              free(forms);
              free(contents);
              return 5;
            }
            free(forms);
          }
        }
        else {
          struct curl_forms info[4];
          int i = 0;
          char *ct = strstr(contp, ";type=");
    
          info[i].option = CURLFORM_COPYNAME;
          info[i].value = name;
          i++;
    
          if(ct) {
            info[i].option = CURLFORM_CONTENTTYPE;
            info[i].value = &ct[6];
            i++;
            ct[0]=0; /* zero terminate here */
          }
    
          if( contp[0]=='<' ) {
            info[i].option = CURLFORM_FILECONTENT;
            info[i].value = contp+1;
            i++;
            info[i].option = CURLFORM_END;
    
            if (curl_formadd(httppost, last_post,
                             CURLFORM_ARRAY, info, CURLFORM_END ) != 0) {
              fprintf(stderr, "curl_formadd failed, possibly the file %s is bad!\n",
                      contp+1);
              free(contents);
              return 6;
            }
          }
          else {
            info[i].option = CURLFORM_COPYCONTENTS;
            info[i].value = contp;
            i++;
            info[i].option = CURLFORM_END;
            if (curl_formadd(httppost, last_post,
                             CURLFORM_ARRAY, info, CURLFORM_END) != 0) {
              fprintf(stderr, "curl_formadd failed!\n");
              free(contents);
              return 7;
            }
          }
        }
    
      }
      else {
        fprintf(stderr, "Illegally formatted input field!\n");
        return 1;
      }
      free(contents);
      return 0;
    }
    
    
    typedef enum {
      PARAM_OK,
      PARAM_OPTION_AMBIGUOUS,
      PARAM_OPTION_UNKNOWN,
      PARAM_REQUIRES_PARAMETER,
      PARAM_BAD_USE,
      PARAM_HELP_REQUESTED,
      PARAM_GOT_EXTRA_PARAMETER,
      PARAM_BAD_NUMERIC,
      PARAM_LIBCURL_DOESNT_SUPPORT,
      PARAM_NO_MEM,
      PARAM_LAST
    } ParameterError;
    
    static const char *param2text(int res)
    {
      ParameterError error = (ParameterError)res;
      switch(error) {
      case PARAM_GOT_EXTRA_PARAMETER:
        return "had unsupported trailing garbage";
      case PARAM_OPTION_UNKNOWN:
        return "is unknown";
      case PARAM_OPTION_AMBIGUOUS:
        return "is ambiguous";
      case PARAM_REQUIRES_PARAMETER:
        return "requires parameter";
      case PARAM_BAD_USE:
        return "is badly used here";
      case PARAM_BAD_NUMERIC:
        return "expected a proper numerical parameter";
      case PARAM_LIBCURL_DOESNT_SUPPORT:
        return "the installed libcurl version doesn't support this";
      case PARAM_NO_MEM:
        return "out of memory";
      default:
        return "unknown error";
      }
    }
    
    static void cleanarg(char *str)
    {
    #ifdef HAVE_WRITABLE_ARGV
      /* now that GetStr has copied the contents of nextarg, wipe the next
       * argument out so that the username:password isn't displayed in the
       * system process list */
      if (str) {
        size_t len = strlen(str);
        memset(str, ' ', len);
      }
    #else
      (void)str;
    #endif
    }
    
    /*
     * Parse the string and write the integer in the given address. Return
     * non-zero on failure, zero on success.
     *
     * The string must start with a digit to be valid.
     */
    
    static int str2num(long *val, char *str)
    {
      int retcode = 0;
      if(isdigit((int)*str))
        *val = atoi(str);
      else
        retcode = 1; /* badness */
      return retcode;
    }
    
    /**
     * Parses the given string looking for an offset (which may be
     * a larger-than-integer value).
     *
     * @param val  the offset to populate
     * @param str  the buffer containing the offset
     * @return zero if successful, non-zero if failure.
     */
    static int str2offset(curl_off_t *val, char *str)
    {
    #if SIZEOF_CURL_OFF_T > 4
      /* Ugly, but without going through a bunch of rigmarole, we don't have the
       * definitions for LLONG_{MIN,MAX} or LONG_LONG_{MIN,MAX}.
       */
    #ifndef LLONG_MAX
    #ifdef _MSC_VER
    #define LLONG_MAX (curl_off_t)0x7FFFFFFFFFFFFFFFi64
    #define LLONG_MIN (curl_off_t)0x8000000000000000i64
    #else
    #define LLONG_MAX (curl_off_t)0x7FFFFFFFFFFFFFFFLL
    #define LLONG_MIN (curl_off_t)0x8000000000000000LL
    #endif
    #endif
    
      /* this is a duplicate of the function that is also used in libcurl */
      *val = curlx_strtoofft(str, NULL, 0);
    
      if ((*val == LLONG_MAX || *val == LLONG_MIN) && errno == ERANGE)
        return 1;
    #else
      *val = strtol(str, NULL, 0);
      if ((*val == LONG_MIN || *val == LONG_MAX) && errno == ERANGE)
        return 1;
    #endif
      return 0;
    }
    
    static void checkpasswd(const char *kind, /* for what purpose */
                            char **userpwd) /* pointer to allocated string */
    {
      char *ptr;
      if(!*userpwd)
        return;
    
      ptr = strchr(*userpwd, ':');
      if(!ptr) {
        /* no password present, prompt for one */
        char passwd[256]="";
        char prompt[256];
        size_t passwdlen;
        size_t userlen = strlen(*userpwd);
        char *passptr;
    
        /* build a nice-looking prompt */
        curlx_msnprintf(prompt, sizeof(prompt),
                       "Enter %s password for user '%s':",
                       kind, *userpwd);
    
        /* get password */
        getpass_r(prompt, passwd, sizeof(passwd));
        passwdlen = strlen(passwd);
    
        /* extend the allocated memory area to fit the password too */
        passptr = realloc(*userpwd,
                          passwdlen + 1 + /* an extra for the colon */
                          userlen + 1);   /* an extra for the zero */
    
        if(passptr) {
          /* append the password separated with a colon */
          passptr[userlen]=':';
          memcpy(&passptr[userlen+1], passwd, passwdlen+1);
          *userpwd = passptr;
        }
      }
    }
    
    static ParameterError add2list(struct curl_slist **list,
                                   char *ptr)
    {
      struct curl_slist *newlist = curl_slist_append(*list, ptr);
      if(newlist)
        *list = newlist;
      else
        return PARAM_NO_MEM;
    
      return PARAM_OK;
    }
    
    static ParameterError 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 subletter=0; /* subletters can only occur on long options */
    
      const char *parse=NULL;
      unsigned int j;
      time_t now;
      int hit=-1;
      bool longopt=FALSE;
      bool singleopt=FALSE; /* when true means '-o foo' used '-ofoo' */
      ParameterError err;
    
      /* single-letter,
         long-name,
         boolean whether it takes an additional argument
         */
      struct LongShort aliases[]= {
        /* all these ones, starting with "*" or "$" as a short-option have *no*
           short option to mention. */
        {"*", "url",         TRUE},
        {"*a", "random-file", TRUE},
        {"*b", "egd-file",   TRUE},
        {"*c", "connect-timeout", TRUE},
        {"*d", "ciphers",    TRUE},
        {"*e", "disable-epsv", FALSE},
    #ifdef USE_ENVIRONMENT
        {"*f", "environment", FALSE},
    #endif
        {"*g", "trace",      TRUE},
        {"*h", "trace-ascii", TRUE},
        {"*i", "limit-rate", TRUE},
        {"*j", "compressed",  FALSE}, /* might take an arg someday */
        {"*k", "digest",     FALSE},
        {"*l", "negotiate",  FALSE},
        {"*m", "ntlm",       FALSE},
        {"*n", "basic",      FALSE},
        {"*o", "anyauth",    FALSE},
    #ifdef __DJGPP__
        {"*p", "wdebug",     FALSE},
    #endif
        {"*q", "ftp-create-dirs", FALSE},
        {"*r", "create-dirs", FALSE},
        {"*s", "max-redirs",   TRUE},
        {"*t", "proxy-ntlm",   FALSE},
        {"*u", "crlf",        FALSE},
        {"*v", "stderr",      TRUE},
        {"*w", "interface",   TRUE},
        {"*x", "krb4",        TRUE},
        {"*y", "max-filesize", TRUE},
        {"*z", "disable-eprt", FALSE},
        {"$a", "ftp-ssl",    FALSE},
        {"$b", "ftp-pasv",   FALSE},
        {"$c", "socks5",     TRUE},
        {"$d", "tcp-nodelay",FALSE},
        {"$e", "proxy-digest", FALSE},
        {"$f", "proxy-basic", FALSE},
        {"$g", "retry",      TRUE},
        {"$h", "retry-delay", TRUE},
        {"$i", "retry-max-time", TRUE},
        {"0", "http1.0",     FALSE},
        {"1", "tlsv1",       FALSE},
        {"2", "sslv2",       FALSE},
        {"3", "sslv3",       FALSE},
        {"4", "ipv4",       FALSE},
        {"6", "ipv6",       FALSE},
        {"a", "append",      FALSE},
        {"A", "user-agent",  TRUE},
        {"b", "cookie",      TRUE},
        {"B", "use-ascii",   FALSE},
        {"c", "cookie-jar",  TRUE},
        {"C", "continue-at", TRUE},
        {"d", "data",        TRUE},
        {"da", "data-ascii", TRUE},
        {"db", "data-binary", TRUE},
        {"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},
        {"f", "fail",        FALSE},
        {"F", "form",        TRUE},
        {"g", "globoff",     FALSE},
        {"G", "get",         FALSE},
        {"h", "help",        FALSE},
        {"H", "header",      TRUE},
        {"i", "include",     FALSE},
        {"I", "head",        FALSE},
        {"j", "junk-session-cookies", FALSE},
        {"k", "insecure",    FALSE},
        {"K", "config",      TRUE},
        {"l", "list-only",   FALSE},
        {"L", "location",    FALSE},
        {"Lt", "location-trusted", FALSE},
        {"m", "max-time",    TRUE},
        {"M", "manual",      FALSE},
        {"n", "netrc",       FALSE},
        {"no", "netrc-optional", FALSE},
        {"N", "no-buffer",   FALSE},
        {"o", "output",      TRUE},
        {"O", "remote-name", FALSE},
        {"p", "proxytunnel", FALSE},
        {"P", "ftpport",     TRUE}, /* older version */
        {"P", "ftp-port",    TRUE},
        {"q", "disable",     FALSE},
        {"Q", "quote",       TRUE},
        {"r", "range",       TRUE},
        {"R", "remote-time", FALSE},
        {"s", "silent",      FALSE},
        {"S", "show-error",  FALSE},
        {"t", "telnet-options", TRUE},
        {"T", "upload-file", TRUE},
        {"u", "user",        TRUE},
        {"U", "proxy-user",  TRUE},
        {"v", "verbose",     FALSE},
        {"V", "version",     FALSE},
        {"w", "write-out",   TRUE},
        {"x", "proxy",       TRUE},
        {"X", "request",     TRUE},
        {"X", "http-request", TRUE}, /* OBSOLETE VERSION */
        {"Y", "speed-limit",  TRUE},
        {"y", "speed-time", TRUE},
        {"z", "time-cond",   TRUE},
        {"#", "progress-bar",FALSE},
      };
    
      if(('-' != flag[0]) ||
         (('-' == flag[0]) && ('-' == flag[1]))) {
        /* this should be a long name */
        char *word=('-' == flag[0])?flag+2:flag;
        size_t fnam=strlen(word);
        int numhits=0;
        for(j=0; j< sizeof(aliases)/sizeof(aliases[0]); j++) {
          if(curlx_strnequal(aliases[j].lname, word, fnam)) {
            longopt = TRUE;
            numhits++;
            if(curlx_strequal(aliases[j].lname, word)) {
              parse = aliases[j].letter;
              hit = j;
              numhits = 1; /* a single unique hit */
              break;
            }
            parse = aliases[j].letter;
            hit = j;
          }
        }
        if(numhits>1) {
          /* this is at least the second match! */
          return PARAM_OPTION_AMBIGUOUS;
        }
        if(hit < 0) {
          return PARAM_OPTION_UNKNOWN;
        }
      }
      else {
        flag++; /* prefixed with one dash, pass it */
        hit=-1;
        parse = flag;
      }
    
      do {
        /* we can loop here if we have multiple single-letters */
    
        if(!longopt)
          letter = parse?(char)*parse:'\0';
        else {
          letter = parse[0];
          subletter = parse[1];
        }
        *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]) {
              hit = j;
              break;
            }
          }
          if(hit < 0) {
            return PARAM_OPTION_UNKNOWN;
          }
        }
        if(hit < 0) {
          return PARAM_OPTION_UNKNOWN;
        }
        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) {
          return PARAM_REQUIRES_PARAMETER;
        }
        else if(nextarg && aliases[hit].extraparam)
          *usedarg = TRUE; /* mark it as used */
    
        switch(letter) {
        case '*': /* options without a short option */
          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 */
            if(str2num(&config->connecttimeout, nextarg))
              return PARAM_BAD_NUMERIC;
            break;
          case 'd': /* ciphers */
            GetStr(&config->cipher_list, nextarg);
            break;
          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;
            break;
          case 'i': /* --limit-rate */
            {
              /* We support G, M, K too */
              char *unit;
              curl_off_t value = curlx_strtoofft(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;
    
          case 'j': /* --compressed */
            config->encoding ^= TRUE;
            break;
    
          case 'k': /* --digest */
            config->authtype = CURLAUTH_DIGEST;
            break;
    
          case 'l': /* --negotiate */
            if(curlinfo->features & CURL_VERSION_GSSNEGOTIATE)
              config->authtype = CURLAUTH_GSSNEGOTIATE;
            else
              return PARAM_LIBCURL_DOESNT_SUPPORT;
            break;
    
          case 'm': /* --ntlm */
            if(curlinfo->features & CURL_VERSION_NTLM)
              config->authtype = CURLAUTH_NTLM;
            else
              return PARAM_LIBCURL_DOESNT_SUPPORT;
            break;
    
          case 'n': /* --basic for completeness */
            config->authtype = CURLAUTH_BASIC;
            break;
    
          case 'o': /* --anyauth, let libcurl pick it */
            config->authtype = CURLAUTH_ANY;
            break;
    
    #ifdef __DJGPP__
          case 'p': /* --wdebug */
            dbug_init();
            break;
    #endif
          case 'q': /* --ftp-create-dirs */
            config->ftp_create_dirs ^= TRUE;
            break;
    
          case 'r': /* --create-dirs */
            config->create_dirs = TRUE;
            break;
    
          case 's': /* --max-redirs */
            /* specified max no of redirects (http(s)) */
            if(str2num(&config->maxredirs, nextarg))
              return PARAM_BAD_NUMERIC;
            break;
    
          case 't': /* --proxy-ntlm */
            if(curlinfo->features & CURL_VERSION_NTLM)
              config->proxyntlm ^= TRUE;
            else
              return PARAM_LIBCURL_DOESNT_SUPPORT;
            break;
    
          case 'u': /* --crlf */
            /* LF -> CRLF conversinon? */
            config->crlf = TRUE;
            break;
    
          case 'v': /* --stderr */
            if(strcmp(nextarg, "-")) {
              config->errors = fopen(nextarg, "wt");
              config->errors_fopened = TRUE;
            }
            else
              config->errors = stdout;
          break;
          case 'w': /* --interface */
            /* interface */
            GetStr(&config->iface, nextarg);
            break;
          case 'x': /* --krb4 */
            /* krb4 level string */
            if(curlinfo->features & CURL_VERSION_KERBEROS4)
              GetStr(&config->krb4level, nextarg);
            else
              return PARAM_LIBCURL_DOESNT_SUPPORT;
            break;
          case 'y': /* --max-filesize */
            if(str2offset(&config->max_filesize, nextarg))
              return PARAM_BAD_NUMERIC;
            break;
          case 'z': /* --disable-eprt */
            config->disable_eprt ^= TRUE;
            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;
              }
            }
          }
          break;
        case '$': /* more options without a short option */
          switch(subletter) {
          case 'a': /* --ftp-ssl */
            config->ftp_ssl ^= TRUE;
            break;
          case 'b': /* --ftp-pasv */
            if(config->ftpport)
              free(config->ftpport);
            config->ftpport = NULL;
            break;
          case 'c': /* --socks specifies a socks5 proxy to use */
            GetStr(&config->socks5proxy, nextarg);
            break;
          case 'd': /* --tcp-nodelay option */
            config->tcp_nodelay ^= TRUE;
            break;
          case 'e': /* --proxy-digest */
            config->proxydigest ^= TRUE;
            break;
          case 'f': /* --proxy-basic */
            config->proxybasic ^= TRUE;
            break;
          case 'g': /* --retry */
            if(str2num(&config->req_retry, nextarg))
              return PARAM_BAD_NUMERIC;
            break;
          case 'h': /* --retry-delay */
            if(str2num(&config->retry_delay, nextarg))
              return PARAM_BAD_NUMERIC;
            break;
          case 'i': /* --retry-max-time */
            if(str2num(&config->retry_maxtime, nextarg))
              return PARAM_BAD_NUMERIC;
            /* break */
          }
          break;
        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;
        case '2':
          /* SSL version 2 */
          config->ssl_version = CURL_SSLVERSION_SSLv2;
          break;
        case '3':
          /* SSL version 3 */
          config->ssl_version = CURL_SSLVERSION_SSLv3;
          break;
        case '4':
          /* IPv4 */
          config->ip_version = 4;
          break;
        case '6':
          /* IPv6 */
          config->ip_version = 6;
          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, '=')) {
            /* A cookie string must have a =-letter */
            GetStr(&config->cookie, nextarg);
            break;
          }
          /* We have a cookie file to read from! */
          GetStr(&config->cookiefile, nextarg);
          break;
        case 'B':
          /* use ASCII/text when transfering */
          config->conf ^= CONF_GETTEXT;
          break;
        case 'c':
          /* get the file name to dump all cookies in */
          GetStr(&config->cookiejar, nextarg);
          break;
        case 'C':
          /* This makes us continue an ftp transfer at given position */
          if(!curlx_strequal(nextarg, "-")) {
            if(str2offset(&config->resume_from, nextarg))
              return PARAM_BAD_NUMERIC;
            config->resume_from_current = FALSE;
          }
          else {
            config->resume_from_current = TRUE;
            config->resume_from = 0;
          }
          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(curlx_strequal("-", nextarg))
                file = stdin;
              else
                file = fopen(nextarg, "rb");
    
              if(subletter == 'b') /* forced binary */
                postdata = file2memory(file, &config->postfieldsize);
              else
                postdata = file2string(file);
              if(file && (file != stdin))
                fclose(file);
            }
            else {
              GetStr(&postdata, nextarg);
            }
    
            if(config->postfields) {
              /* we already have a string, we append this one
                 with a separating &-letter */
              char *oldpost=config->postfields;
              config->postfields=aprintf("%s&%s", oldpost, postdata);
              free(oldpost);
              free(postdata);
            }
            else
              config->postfields=postdata;
          }
          /*
            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;
          */
          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);
          }
          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);
            cleanarg(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. */
    #ifdef WIN32
              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 */
    #endif
              if(ptr) {
                /* we have a password too */
                *ptr=0;
                ptr++;
                GetStr(&config->key_passwd, ptr);
              }
              GetStr(&config->cert, nextarg);
              cleanarg(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(formparse(nextarg,
                       &config->httppost,
                       &config->last_post))
            return PARAM_BAD_USE;
          if(SetHTTPrequest(HTTPREQ_POST, &config->httpreq))
            return PARAM_BAD_USE;
          break;
    
        case 'g': /* g disables URLglobbing */
          config->globoff ^= TRUE;
          break;
    
        case 'G': /* HTTP GET */
          config->use_httpget = TRUE;
          break;
    
        case 'h': /* h for help */
          help();
          return PARAM_HELP_REQUESTED;
        case 'H':
          /* A custom header to append to a list */
          err = add2list(&config->headers, nextarg);
          if(err)
            return err;
          break;
        case 'i':
          config->conf ^= CONF_HEADER; /* include the HTTP header as well */
          break;
        case 'j':
          config->cookiesession ^= TRUE;
          break;
        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;
          }
          break;
        case 'k': /* allow insecure SSL connects */
          config->insecure_ok ^= TRUE;
          break;
        case 'K': /* parse config file */
          parseconfig(nextarg, config);
          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 */
          switch (subletter) {
          case 't':
            /* Continue to send authentication (user+password) when following
             * locations, even when hostname changed */
            config->conf ^= CONF_UNRESTRICTED_AUTH;
            break;
          }
          break;
        case 'm':
          /* specified max time */
          if(str2num(&config->timeout, nextarg))
            return PARAM_BAD_NUMERIC;
          break;
        case 'M': /* M for manual, huge help */
    #ifdef USE_MANUAL
          hugehelp();
          return PARAM_HELP_REQUESTED;
    #else
          fprintf(stderr,
                  "curl: built-in manual was disabled at build-time!\n");
          return PARAM_OPTION_UNKNOWN;
    #endif
        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;
          }
          break;
        case 'N':
          /* disable the output I/O buffering */
          config->nobuffer ^= 1;
          break;
        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;
            }
          }
          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;
          break;
    
        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 */
          err = PARAM_OK;
          switch(nextarg[0]) {
          case '-':
            /* prefixed with a dash makes it a POST TRANSFER one */
            nextarg++;
            err = add2list(&config->postquote, nextarg);
            break;
          case '+':
            /* prefixed with a plus makes it a just-before-transfer one */
            nextarg++;
            err = add2list(&config->prequote, nextarg);
            break;
          default:
            err = add2list(&config->quote, nextarg);
            break;
          }
          if(err)
            return err;
          break;
        case 'r':
          /* byte range requested */
          GetStr(&config->range, nextarg);
          break;
        case 'R':
          /* use remote file's time */
          config->remote_time ^= TRUE;
          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':
          /* Telnet options */
          err = add2list(&config->telnet_options, nextarg);
          if(err)
            return err;
          break;
        case 'T':
          /* we are uploading */
          {
            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_UPLOAD))
                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) {
              url->flags |= GETOUT_UPLOAD; /* mark -T used */
              if(!*nextarg)
                url->flags |= GETOUT_NOUPLOAD;
              else {
                /* "-" equals stdin, but keep the string around for now */
                GetStr(&url->infile, nextarg);
              }
            }
          }
          break;
        case 'u':
          /* user:password  */
          GetStr(&config->userpwd, nextarg);
          cleanarg(nextarg);
          checkpasswd("host", &config->userpwd);
          break;
        case 'U':
          /* Proxy user:password  */
          GetStr(&config->proxyuserpwd, nextarg);
          cleanarg(nextarg);
          checkpasswd("proxy", &config->proxyuserpwd);
          break;
        case 'v':
          config->conf ^= CONF_VERBOSE; /* talk a lot */
          break;
        case 'V':
        {
          const char **proto;
    
          printf(CURL_ID "%s\n", curl_version());
          if (curlinfo->protocols) {
            printf("Protocols: ");
            for (proto=curlinfo->protocols; *proto; ++proto) {
              printf("%s ", *proto);
            }
            puts(""); /* newline */
          }
          if(curlinfo->features) {
            unsigned int i;
            struct feat {
              const char *name;
              int bitmask;
            };
            struct feat feats[] = {
              {"AsynchDNS", CURL_VERSION_ASYNCHDNS},
              {"Debug", CURL_VERSION_DEBUG},
              {"GSS-Negotiate", CURL_VERSION_GSSNEGOTIATE},
              {"IDN", CURL_VERSION_IDN},
              {"IPv6", CURL_VERSION_IPV6},
              {"Largefile", CURL_VERSION_LARGEFILE},
              {"NTLM", CURL_VERSION_NTLM},
              {"SPNEGO", CURL_VERSION_SPNEGO},
              {"SSL",  CURL_VERSION_SSL},
              {"krb4", CURL_VERSION_KERBEROS4},
              {"libz", CURL_VERSION_LIBZ}
            };
            printf("Features: ");
            for(i=0; i<sizeof(feats)/sizeof(feats[0]); i++) {
              if(curlinfo->features & feats[i].bitmask)
                printf("%s ", feats[i].name);
            }
            puts(""); /* newline */
          }
        }
        return PARAM_HELP_REQUESTED;
        case 'w':
          /* get the output string */
          if('@' == *nextarg) {
            /* the data begins with a '@' letter, it means that a file name
               or - (stdin) follows */
            FILE *file;
            nextarg++; /* pass the @ */
            if(curlx_strequal("-", nextarg))
              file = stdin;
            else
              file = fopen(nextarg, "r");
            config->writeout = file2string(file);
            if(file && (file != stdin))
              fclose(file);
          }
          else
            GetStr(&config->writeout, nextarg);
          break;
        case 'x':
          /* proxy */
          GetStr(&config->proxy, nextarg);
          break;
        case 'X':
          /* set custom request */
          GetStr(&config->customrequest, nextarg);
          break;
        case 'y':
          /* low speed time */
          if(str2num(&config->low_speed_time, nextarg))
            return PARAM_BAD_NUMERIC;
          if(!config->low_speed_limit)
            config->low_speed_limit = 1;
          break;
        case 'Y':
          /* low speed limit */
          if(str2num(&config->low_speed_limit, nextarg))
            return PARAM_BAD_NUMERIC;
          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;
        default: /* unknown flag */
          return PARAM_OPTION_UNKNOWN;
        }
        hit = -1;
    
      } while(!longopt && !singleopt && *++parse && !*usedarg);
    
      return PARAM_OK;
    }
    
    
    static void parseconfig(const char *filename,
                            struct Configurable *config)
    {
      int res;
      FILE *file;
      char filebuffer[512];
      bool usedarg;
      char *home;
    
      if(!filename || !*filename) {
        /* NULL or no file name attempts to load .curlrc from the homedir! */
    
    #define CURLRC DOT_CHAR "curlrc"
    
    #ifndef AMIGA
        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);
    
            filename = filebuffer;
          }
          free(home); /* we've used it, now free it */
        }
    
    # else /* AmigaOS */
      /* On AmigaOS all the config files are into env:
       */
      filename = "ENV:" CURLRC;
    
    #endif
      }
    
      if(strcmp(filename,"-"))
        file = fopen(filename, "r");
      else
        file = stdin;
    
      if(file) {
        char *line;
        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(*line && isspace((int)*line))
            line++;
    
          switch(*line) {
          case '#':
          case '/':
          case '\r':
          case '\n':
          case '*':
          case '\0':
            free(aline);
            continue;
          }
    
          /* the option keywords starts here */
          option = line;
          while(*line && !isspace((int)*line) && !isseparator(*line))
            line++;
          /* ... and has ended here */
    
          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 == '\\') {
                char out;
                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';
                  break;
                }
                *ptr++=out;
                line++;
              }
              else
                *ptr++=*line++;
            }
            *ptr=0; /* always zero terminate */
    
          }
          else {
            param=line; /* parameter starts here */
            while(*line && !isspace((int)*line))
              line++;
            *line=0; /* zero terminate */
          }
    
          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;
          }
    
    #ifdef DEBUG_CONFIG
          fprintf(stderr, "PARAM: \"%s\"\n",(param ? param : "(null)"));
    #endif
          res = getparameter(option, param, &usedarg, config);
    
          if (param && *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>";
            }
            if(PARAM_HELP_REQUESTED != res) {
              const char *reason = param2text(res);
              fprintf(stderr, "%s:%d: warning: '%s' %s\n",
                      filename, lineno, option, reason);
            }
          }
    
          if(alloced_param)
          {
            free(param);
            param = NULL;
          }
    
          free(aline);
        }
        if(file != stdin)
          fclose(file);
      }
    }
    
    static void go_sleep(long ms)
    {
    #ifdef HAVE_POLL_FINE
      /* portable subsecond "sleep" */
      poll((void *)0, 0, (int)ms);
    #else
      /* systems without poll() need other solutions */
    
    #ifdef WIN32
      /* Windows offers a millisecond sleep */
      Sleep(ms);
    #elif defined(__MSDOS__)
      delay(ms);
    #else
      /* Other systems must use select() for this */
      struct timeval timeout;
    
      timeout.tv_sec = ms/1000;
      ms = ms%1000;
      timeout.tv_usec = ms * 1000;
    
      select(0, NULL,  NULL, NULL, &timeout);
    #endif
    
    #endif
    }
    
    struct OutStruct {
      char *filename;
      FILE *stream;
      struct Configurable *config;
      curl_off_t bytes; /* amount written so far */
      curl_off_t init;  /* original size (non-zero when appending) */
    };
    
    static int my_fwrite(void *buffer, size_t sz, size_t nmemb, void *stream)
    {
      int rc;
      struct OutStruct *out=(struct OutStruct *)stream;
      struct Configurable *config = out->config;
      curl_off_t size = (curl_off_t)(sz * nmemb); /* typecast to prevent
                                                     warnings when converting from
                                                     unsigned to signed */
      if(out && !out->stream) {
        /* open file for writing */
        out->stream=fopen(out->filename, "wb");
        if(!out->stream)
          return -1; /* failure */
      }
    
      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.
         */
    
        struct timeval now;
        long timediff;
        long sleep_time;
    
        static curl_off_t addit = 0;
    
        now = curlx_tvnow();
        timediff = curlx_tvdiff(now, config->lastrecvtime); /* milliseconds */
    
        if((config->recvpersecond > CURL_MAX_WRITE_SIZE) && (timediff < 100) ) {
          /* If we allow a rather speedy transfer, add this amount for later
           * checking. Also, do not modify the lastrecvtime as we will use a
           * longer scope due to this addition.  We wait for at least 100 ms to
           * pass to get better values to do better math for the sleep. */
          addit += size;
        }
        else {
          size += addit; /* add up the possibly added bonus rounds from the
                            zero timediff calls */
          addit = 0; /* clear the addition pool */
    
          if( size*1000 > config->recvpersecond*timediff) {
            /* figure out how many milliseconds to rest */
            sleep_time = (long)(size*1000/config->recvpersecond - timediff);
    
            /*
             * 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);
    
            if(sleep_time > 0) {
              go_sleep(sleep_time);
              now = curlx_tvnow();
            }
          }
          config->lastrecvtime = now;
        }
      }
    
      rc = fwrite(buffer, sz, nmemb, out->stream);
    
      if((int)(sz * nmemb) == rc) {
        /* we added this amount of data to the output */
        out->bytes += (sz * nmemb);
      }
    
      if(config->nobuffer)
        /* disable output buffering */
        fflush(out->stream);
    
      return rc;
    }
    
    struct InStruct {
      FILE *stream;
      struct Configurable *config;
    };
    
    static curlioerr my_ioctl(CURL *handle, curliocmd cmd, void *userp)
    {
      struct InStruct *in=(struct InStruct *)userp;
      (void)handle; /* not used in here */
    
      switch(cmd) {
      case CURLIOCMD_RESTARTREAD:
        /* mr libcurl kindly asks as to rewind the read data stream to start */
        if(-1 == fseek(in->stream, 0, SEEK_SET))
          /* couldn't rewind, the reason is in errno but errno is just not
             portable enough and we don't actually care that much why we failed. */
          return CURLIOE_FAILRESTART;
    
        break;
    
      default: /* ignore unknown commands */
        return CURLIOE_UNKNOWNCMD;
      }
      return CURLIOE_OK;
    }
    
    static int my_fread(void *buffer, size_t sz, size_t nmemb, void *userp)
    {
      int rc;
      struct InStruct *in=(struct InStruct *)userp;
      struct Configurable *config = in->config;
      curl_off_t size = (curl_off_t)(sz * nmemb);  /* typecast to prevent warnings
                                                      when converting from
                                                      unsigned to signed */
    
      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!
         */
    
        struct timeval now;
        long timediff;
        long sleep_time;
    
        static curl_off_t addit = 0;
    
        now = curlx_tvnow();
        timediff = curlx_tvdiff(now, config->lastsendtime); /* milliseconds */
    
        if((config->sendpersecond > CURL_MAX_WRITE_SIZE) &&
           (timediff < 100)) {
          /*
           * We allow very fast transfers, then allow at least 100 ms between
           * each sleeping mile-stone to create more accurate long-term rates.
           */
          addit += size;
        }
        else {
          /* If 'addit' is non-zero, it contains the total amount of bytes
             uploaded during the last 'timediff' milliseconds. If it is zero,
             we use the stored previous size. */
          curl_off_t xfered = addit?addit:(curl_off_t)config->lastsendsize;
          addit = 0; /* clear it for the next round */
    
          if( xfered*1000 > config->sendpersecond*timediff) {
            /* figure out how many milliseconds to rest */
            sleep_time = (long)(xfered*1000/config->sendpersecond - timediff);
            if(sleep_time > 0) {
              go_sleep (sleep_time);
              now = curlx_tvnow();
            }
          }
          config->lastsendtime = now;
    
          if(size > config->sendpersecond) {
            /* lower the size to actually read */
            nmemb = (size_t)config->sendpersecond;
            sz = 1;
          }
        }
    
        config->lastsendsize = sz*nmemb;
      }
    
      rc = fread(buffer, sz, nmemb, in->stream);
    #if 0
      fprintf(stderr, "CALLBACK returning %d bytes data\n", (int)rc);
    #endif
      return rc;
    }
    
    struct ProgressData {
      int calls;
      curl_off_t prev;
      int width;
      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];
      double frac;
      double percent;
      int barwidth;
      int num;
      int i;
    
      struct ProgressData *bar = (struct ProgressData *)clientp;
      curl_off_t total = (curl_off_t)dltotal + (curl_off_t)ultotal +
        bar->initial_size; /* expected transfer size */
      curl_off_t point = (curl_off_t)dlnow + (curl_off_t)ulnow +
        bar->initial_size; /* we've come this far */
    
      bar->calls++; /* simply count invokes */
    
      if(total < 1) {
        curl_off_t prevblock = bar->prev / 1024;
        curl_off_t thisblock = point / 1024;
        while ( thisblock > prevblock ) {
          fprintf( bar->out, "#" );
          prevblock++;
        }
      }
      else {
        frac = (double)point / (double)total;
        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';
        snprintf( format, sizeof(format), "%%-%ds %%5.1f%%%%", barwidth );
        snprintf( outline, sizeof(outline), format, line, percent );
        fprintf( bar->out, "\r%s", outline );
      }
      fflush(bar->out);
      bar->prev = point;
    
      return 0;
    }
    
    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 = curlx_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;
    }
    
    static
    void dump(const char *text,
              FILE *stream, unsigned char *ptr, size_t size,
              bool nohex)
    {
      size_t i;
      size_t c;
    
      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) {
    
        fprintf(stream, "%04zx: ", i);
    
        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;
          }
        }
        fputc('\n', stream); /* newline */
      }
      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;
      (void)handle; /* prevent compiler warning */
    
      if(!config->trace_stream) {
        /* open for append */
        if(curlx_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;
    
      case CURLINFO_HEADER_OUT:
        text = "=> Send header";
        break;
      case CURLINFO_DATA_OUT:
        text = "=> Send data";
        break;
      case CURLINFO_HEADER_IN:
        text = "<= Recv header";
        break;
      case CURLINFO_DATA_IN:
        text = "<= Recv data";
        break;
      case CURLINFO_SSL_DATA_IN:
        text = "<= Recv SSL data";
        break;
      case CURLINFO_SSL_DATA_OUT:
        text = "<= Send SSL data";
        break;
      }
    
      dump(text, output, data, size, config->trace_ascii);
      return 0;
    }
    
    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->trace_dump)
        free(config->trace_dump);
      if(config->cipher_list)
        free(config->cipher_list);
      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);
      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
    
    #define RETRY_SLEEP_DEFAULT 1000  /* ms */
    #define RETRY_SLEEP_MAX     600000 /* ms == 10 minutes */
    
    static int
    operate(struct Configurable *config, int argc, char *argv[])
    {
      char errorbuffer[CURL_ERROR_SIZE];
      char useragent[128]; /* buah, we don't want a larger default user agent */
      struct ProgressData progressbar;
      struct getout *urlnode;
      struct getout *nextnode;
    
      struct OutStruct outs;
      struct OutStruct heads;
      struct InStruct input;
    
      char *url = NULL;
    
      URLGlob *urls=NULL;
      URLGlob *inglob=NULL;
      int urlnum;
      int infilenum;
      char *outfiles;
      char *infiles; /* might a glob pattern */
      char *uploadfile=NULL; /* a single file, never a glob */
    
      int separator = 0;
    
      FILE *infd = stdin;
      bool infdfopen;
      FILE *headerfilep = NULL;
      char *urlbuffer=NULL;
      curl_off_t uploadfilesize; /* -1 means unknown */
      bool stillflags=TRUE;
    
      bool allocuseragent=FALSE;
    
      char *httpgetfields=NULL;
    
      CURL *curl;
      int res = 0;
      int i;
      int up; /* upload file counter within a single upload glob */
      long retry_sleep_default = config->retry_delay?
        config->retry_delay*1000:RETRY_SLEEP_DEFAULT; /* ms */
      long retry_numretries;
      long retry_sleep = retry_sleep_default;
      long response;
      struct timeval retrystart;
    
      char *env;
    #ifdef CURLDEBUG
      /* this sends all memory debug messages to a logfile named memdump */
      env = curlx_getenv("CURL_MEMDEBUG");
      if(env) {
        curl_free(env);
        curl_memdebug("memdump");
      }
      env = curlx_getenv("CURL_MEMLIMIT");
      if(env) {
        curl_memlimit(atoi(env));
        curl_free(env);
      }
    #endif
    
      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 */
    
      /* setup proper locale from environment */
    #ifdef HAVE_SETLOCALE
      setlocale(LC_ALL, "");
    #endif
    
      /* inits */
      if (main_init() != CURLE_OK) {
        helpf("error initializing curl library\n");
        return CURLE_FAILED_INIT;
      }
      config->postfieldsize = -1;
      config->showerror=TRUE;
      config->conf=CONF_DEFAULT;
      config->use_httpget=FALSE;
      config->create_dirs=FALSE;
      config->lastrecvtime = curlx_tvnow();
      config->lastsendtime = curlx_tvnow();
    
      if(argc>1 &&
         (!curlx_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!
         */
        ;
      }
      else {
        parseconfig(NULL, config);
      }
    
      if ((argc < 2)  && !config->url_list) {
        helpf(NULL);
        return CURLE_FAILED_INIT;
      }
    
      /* Parse options */
      for (i = 1; i < argc; i++) {
        if(stillflags &&
           ('-' == argv[i][0])) {
          char *nextarg;
          bool passarg;
          char *origopt=argv[i];
    
          char *flag = argv[i];
    
          if(curlx_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) {
              const char *reason = param2text(res);
              if(res != PARAM_HELP_REQUESTED)
                helpf("option %s: %s\n", origopt, reason);
              clean_getout(config);
              return CURLE_FAILED_INIT;
            }
    
            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) {
        clean_getout(config);
        helpf("no URL specified!\n");
        return CURLE_FAILED_INIT;
      }
      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;
    
      /* 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 = curlx_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");
    #endif
      }
    
      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
       * when all transfers are done.
       */
      curl = curl_easy_init();
      if(!curl) {
        clean_getout(config);
        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 */
      while(urlnode) {
        char *dourl;
    
        /* get the full URL (it might be NULL) */
        dourl=urlnode->url;
    
        url = dourl;
    
        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;
        outs.bytes = 0; /* nothing written yet */
    
        /* 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);
            if(outfiles)
              free(outfiles);
            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 || curlx_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 && !curlx_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;
                }
    #if defined(__DJGPP__)
                {
                  /* This is for DOS, and then we do some major replacing of
                     bad characters in the file name before using it */
                  char file1 [PATH_MAX];
    
                  strcpy(file1, msdosify(outfile));
                  free (outfile);
                  outfile = strdup (rename_if_dos_device_name(file1));
                }
    #endif /* __DJGPP__ */
              }
              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 &&
                 (-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) {
                outs.init = 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 && !curlx_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;
              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.*/
    
                /* 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)
                  filep++;
                else
                  filep = uploadfile;
    
                /* URL encode the file name */
                filep = curl_escape(filep, 0 /* use strlen */);
    
                if(filep) {
    
                  urlbuffer=(char *)malloc(strlen(url) + strlen(filep) + 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, filep);
                  else
                    /* thers is no trailing slash on the URL */
                    sprintf(urlbuffer, "%s/%s", url, filep);
    
                  curl_free(filep);
    
                  free(url);
                  url = urlbuffer; /* use our new URL instead! */
                }
              }
              /* VMS Note:
               *
               * Reading binary from files can be a problem...  Only FIXED, VAR
               * etc WITHOUT implied CC will work Others need a \n appended to a
               * line
               *
               * - Stat gives a size but this is UNRELIABLE in VMS As a f.e. a
               * fixed file with implied CC needs to have a byte added for every
               * record processed, this can by derived from Filesize & recordsize
               * for VARiable record files the records need to be counted!  for
               * every record add 1 for linefeed and subtract 2 for the record
               * header for VARIABLE header files only the bare record data needs
               * to be considered with one appended if implied CC
               */
    
              infd=(FILE *) fopen(uploadfile, "rb");
              if (!infd || stat(uploadfile, &fileinfo)) {
                helpf("Can't open '%s'!\n", uploadfile);
                return CURLE_READ_ERROR;
              }
              infdfopen=TRUE;
              uploadfilesize=fileinfo.st_size;
    
            }
            else if(uploadfile && curlx_strequal(uploadfile, "-")) {
              infd = stdin;
            }
    
            if(uploadfile && config->resume_from_current)
              config->resume_from = -1; /* -1 will then force get-it-yourself */
    
            if(outs.stream && isatty(fileno(outs.stream)))
              /* we send the output to a tty, therefore we switch off the progress
                 meter */
              config->conf |= CONF_NOPROGRESS;
    
            if (urlnum > 1 && !(config->conf&CONF_MUTE)) {
              fprintf(stderr, "\n[%d/%d]: %s --> %s\n",
                      i+1, urlnum, url, outfile ? outfile : "<stdout>");
              if (separator)
                printf("%s%s\n", CURLseparator, url);
            }
            if (httpgetfields) {
              /* Find out whether the url contains a file name */
              const char *pc =strstr(url, "://");
              char sep='?';
              if(pc)
                pc+=3;
              else
                pc=url;
    
              pc = strrchr(pc, '/'); /* check for a slash */
    
              if(pc) {
                /* there is a slash present in the URL */
    
                if(strchr(pc, '?'))
                  /* Ouch, there's already a question mark in the URL string, we
                     then append the data with an ampersand separator instead! */
                  sep='&';
              }
              /*
               * Then append ? followed by the get fields to the url.
               */
              urlbuffer=(char *)malloc(strlen(url) + strlen(httpgetfields) + 2);
              if(!urlbuffer) {
                helpf("out of memory\n");
                return CURLE_OUT_OF_MEMORY;
              }
              if (pc)
                sprintf(urlbuffer, "%s%c%s", url, sep, httpgetfields);
              else
                /* Append  / before the ? to create a well-formed url
                   if the url contains a hostname only
                */
                sprintf(urlbuffer, "%s/?%s", url, httpgetfields);
    
              free(url); /* free previous URL */
              url = urlbuffer; /* use our new URL instead! */
            }
    
            if(!config->errors)
              config->errors = stderr;
    
    #ifdef O_BINARY
            if(!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( fileno(stdout), O_BINARY );
            }
    #endif
    
            if(1 == config->tcp_nodelay)
              curl_easy_setopt(curl, CURLOPT_TCP_NODELAY, 1);
    
            curl_easy_setopt(curl, CURLOPT_SSLENGINE, config->engine);
            curl_easy_setopt(curl, CURLOPT_SSLENGINE_DEFAULT, 1);
    
            /* where to store */
            curl_easy_setopt(curl, CURLOPT_WRITEDATA, (FILE *)&outs);
            /* what call to write */
            curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_fwrite);
    
            /* for uploads */
            input.stream = infd;
            input.config = config;
            curl_easy_setopt(curl, CURLOPT_READDATA, &input);
            /* what call to read */
            curl_easy_setopt(curl, CURLOPT_READFUNCTION, my_fread);
    
            /* libcurl 7.12.3 business: */
            curl_easy_setopt(curl, CURLOPT_IOCTLDATA, &input);
            curl_easy_setopt(curl, CURLOPT_IOCTLFUNCTION, my_ioctl);
    
            if(config->recvpersecond) {
              /* tell libcurl to use a smaller sized buffer as it allows us to
                 make better sleeps! 7.9.9 stuff! */
              curl_easy_setopt(curl, CURLOPT_BUFFERSIZE, config->recvpersecond);
            }
    
            /* size of uploaded file: */
            curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, uploadfilesize);
            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_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, uploadfile?TRUE:FALSE);
            curl_easy_setopt(curl, CURLOPT_FTPLISTONLY,
                             config->conf&CONF_FTPLISTONLY);
            curl_easy_setopt(curl, CURLOPT_FTPAPPEND, config->conf&CONF_FTPAPPEND);
    
            if (config->conf&CONF_NETRC_OPT)
              curl_easy_setopt(curl, CURLOPT_NETRC, CURL_NETRC_OPTIONAL);
            else if (config->conf&CONF_NETRC)
              curl_easy_setopt(curl, CURLOPT_NETRC, CURL_NETRC_REQUIRED);
            else
              curl_easy_setopt(curl, CURLOPT_NETRC, CURL_NETRC_IGNORED);
    
            curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION,
                             config->conf&CONF_FOLLOWLOCATION);
            curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH,
                             config->conf&CONF_UNRESTRICTED_AUTH);
            curl_easy_setopt(curl, CURLOPT_TRANSFERTEXT, config->conf&CONF_GETTEXT);
            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);
    
            switch(config->httpreq) {
            case HTTPREQ_SIMPLEPOST:
              curl_easy_setopt(curl, CURLOPT_POSTFIELDS, config->postfields);
              curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, config->postfieldsize);
              break;
            case HTTPREQ_POST:
              curl_easy_setopt(curl, CURLOPT_HTTPPOST, config->httppost);
              break;
            default:
              break;
            }
            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_LARGE,
                             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_SSLCERT, config->cert);
            curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE, config->cert_type);
            curl_easy_setopt(curl, CURLOPT_SSLKEY, config->key);
            curl_easy_setopt(curl, CURLOPT_SSLKEYTYPE, config->key_type);
            curl_easy_setopt(curl, CURLOPT_SSLKEYPASSWD, config->key_passwd);
    
            /* default to strict verifyhost */
            curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2);
            if(config->cacert || config->capath) {
              if (config->cacert)
                curl_easy_setopt(curl, CURLOPT_CAINFO, config->cacert);
    
              if (config->capath)
                curl_easy_setopt(curl, CURLOPT_CAPATH, config->capath);
              curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, TRUE);
            }
            if(config->insecure_ok) {
              /* new stuff needed for libcurl 7.10 */
              curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, FALSE);
              curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1);
            }
    
            if((config->conf&CONF_NOBODY) ||
               config->remote_time) {
              /* no body or use remote time */
              curl_easy_setopt(curl, CURLOPT_FILETIME, TRUE);
            }
    
            if (config->maxredirs)
              curl_easy_setopt(curl, CURLOPT_MAXREDIRS, config->maxredirs);
            else
              curl_easy_setopt(curl, CURLOPT_MAXREDIRS, DEFAULT_MAXREDIRS);
    
            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);
            /* cookie jar was added in 7.9 */
            if(config->cookiejar)
              curl_easy_setopt(curl, CURLOPT_COOKIEJAR, config->cookiejar);
            /* cookie session added in 7.9.7 */
            curl_easy_setopt(curl, CURLOPT_COOKIESESSION, config->cookiesession);
    
            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);
    
            progressbarinit(&progressbar, config);
            if((config->progressmode == CURL_PROGRESS_BAR) &&
               !(config->conf&(CONF_NOPROGRESS|CONF_MUTE))) {
              /* we want the alternative style, then we have to implement it
                 ourselves! */
              curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, myprogress);
              curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, &progressbar);
            }
    
            /* new in libcurl 7.6.2: */
            curl_easy_setopt(curl, CURLOPT_TELNETOPTIONS, config->telnet_options);
    
            /* new in libcurl 7.7: */
            curl_easy_setopt(curl, CURLOPT_RANDOM_FILE, config->random_file);
            curl_easy_setopt(curl, CURLOPT_EGDSOCKET, config->egd_file);
            curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, config->connecttimeout);
    
            if(config->cipher_list)
              curl_easy_setopt(curl, CURLOPT_SSL_CIPHER_LIST, config->cipher_list);
    
            if(config->httpversion)
              curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, config->httpversion);
    
            /* new in libcurl 7.9.2: */
            if(config->disable_epsv)
              /* disable it */
              curl_easy_setopt(curl, CURLOPT_FTP_USE_EPSV, FALSE);
    
            /* new in libcurl 7.10.5 */
            if(config->disable_eprt)
              /* disable it */
              curl_easy_setopt(curl, CURLOPT_FTP_USE_EPRT, FALSE);
    
            /* new in libcurl 7.10.6 (default is Basic) */
            if(config->authtype)
              curl_easy_setopt(curl, CURLOPT_HTTPAUTH, config->authtype);
    
            /* new in curl 7.9.7 */
            if(config->trace_dump) {
              curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, my_trace);
              curl_easy_setopt(curl, CURLOPT_DEBUGDATA, config);
              config->conf |= CONF_VERBOSE; /* force verbose */
            }
            curl_easy_setopt(curl, CURLOPT_VERBOSE, config->conf&CONF_VERBOSE);
    
            /* new in curl 7.10 */
            curl_easy_setopt(curl, CURLOPT_ENCODING,
                             (config->encoding) ? "" : NULL);
    
            /* new in curl 7.10.7 */
            curl_easy_setopt(curl, CURLOPT_FTP_CREATE_MISSING_DIRS,
                             config->ftp_create_dirs);
            if(config->proxyntlm)
              curl_easy_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_NTLM);
            else if(config->proxydigest)
              curl_easy_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_DIGEST);
            else if(config->proxybasic)
              curl_easy_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_BASIC);
    
            /* new in curl 7.10.8 */
            if(config->max_filesize)
              curl_easy_setopt(curl, CURLOPT_MAXFILESIZE_LARGE,
                               config->max_filesize);
    
            if(4 == config->ip_version)
              curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
            else if(6 == config->ip_version)
              curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6);
            else
              curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER);
    
            /* new in curl 7.11.0 */
            if(config->ftp_ssl)
              curl_easy_setopt(curl, CURLOPT_FTP_SSL, CURLFTPSSL_TRY);
    
            /* new in curl 7.11.1 */
            if(config->socks5proxy) {
              curl_easy_setopt(curl, CURLOPT_PROXY, config->socks5proxy);
              curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
            }
    
            retry_numretries = config->req_retry;
    
            retrystart = curlx_tvnow();
    
            do {
              res = curl_easy_perform(curl);
    
              /* if retry-max-time is non-zero, make sure we haven't exceeded the
                 time */
              if(retry_numretries &&
                 (!config->retry_maxtime ||
                  (curlx_tvdiff(curlx_tvnow(), retrystart)<
                   config->retry_maxtime*1000)) ) {
                enum {
                  RETRY_NO,
                  RETRY_TIMEOUT,
                  RETRY_HTTP,
                  RETRY_FTP,
                  RETRY_LAST /* not used */
                } retry = RETRY_NO;
                if(CURLE_OPERATION_TIMEDOUT == res)
                  /* retry timeout always */
                  retry = RETRY_TIMEOUT;
                else if(CURLE_OK == res) {
                  /* Check for HTTP transient errors */
                  char *url=NULL;
                  curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url);
                  if(url &&
                     curlx_strnequal(url, "http", 4)) {
                    /* This was HTTP(S) */
                    curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response);
    
                    switch(response) {
                    case 500: /* Internal Server Error */
                    case 502: /* Bad Gateway */
                    case 503: /* Service Unavailable */
                    case 504: /* Gateway Timeout */
                      retry = RETRY_HTTP;
                      /*
                       * At this point, we have already written data to the output
                       * file (or terminal). If we write to a file, we must rewind
                       * or close/re-open the file so that the next attempt starts
                       * over from the beginning.
                       *
                       * TODO: similar action for the upload case. We might need
                       * to start over reading from a previous point if we have
                       * uploaded something when this was returned.
                       */
                      break;
                    }
                  }
                } /* if CURLE_OK */
                else if(CURLE_FTP_USER_PASSWORD_INCORRECT == res) {
                  curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response);
    
                  if(response/100 == 5)
                    /*
                     * This is typically when the FTP server only allows a certain
                     * amount of users and we are not one of them. It mostly
                     * returns 530 in this case, but all 5xx codes are transient.
                     */
                    retry = RETRY_FTP;
                }
    
                if(retry) {
                  if(!(config->conf&CONF_MUTE)) {
                    static const char *m[]={NULL,
                                            "timeout",
                                            "HTTP error",
                                            "FTP error"
                    };
                    fprintf(stderr, "Transient problem: %s\n"
                            "Will retry in %ld seconds. "
                            "%ld retries left.\n",
                            m[retry],
                            retry_sleep/1000,
                            retry_numretries);
                  }
                  go_sleep(retry_sleep);
                  retry_numretries--;
                  if(!config->retry_delay) {
                    retry_sleep *= 2;
                    if(retry_sleep > RETRY_SLEEP_MAX)
                      retry_sleep = RETRY_SLEEP_MAX;
                  }
                  if(outs.bytes && outs.filename) {
                    /* We have written data to a output file, we truncate file
                     */
                    if(!(config->conf&CONF_MUTE))
                      fprintf(stderr, "Throwing away " CURL_FORMAT_OFF_T
                              " bytes\n", outs.bytes);
                    fflush(outs.stream);
                    /* truncate file at the position where we started appending */
                    ftruncate( fileno(outs.stream), outs.init);
                    /* now seek to the end of the file, the position where we
                       just truncated the file */
                    fseek(outs.stream, 0, SEEK_END);
                    outs.bytes = 0; /* clear for next round */
                  }
                  continue;
                }
              } /* if retry_numretries */
    
              /* In all ordinary cases, just break out of loop here */
              retry_sleep = retry_sleep_default;
              break;
    
            } while(1);
    
            if((config->progressmode == CURL_PROGRESS_BAR) &&
               progressbar.calls) {
              /* if the custom progress bar has been displayed, we output a
                 newline here */
              fputs("\n", progressbar.out);
            }
    
            if(config->writeout) {
              ourWriteOut(curl, config->writeout);
            }
    #ifdef USE_ENVIRONMENT
            if (config->writeenv)
              ourWriteEnv(curl);
    #endif
    
    #ifdef  VMS
            if (!config->showerror)  {
              vms_show = VMSSTS_HIDE;
            }
    #else
            if((res!=CURLE_OK) && config->showerror) {
              fprintf(config->errors, "curl: (%d) %s\n", (int)res,
                      errorbuffer[0]? errorbuffer:
                      curl_easy_strerror((CURLcode)res));
              if(CURLE_SSL_CACERT == res) {
    #define CURL_CA_CERT_ERRORMSG1 \
    "More details here: http://curl.haxx.se/docs/sslcerts.html\n\n" \
    "curl performs SSL certificate verification by default, using a \"bundle\"\n" \
    " of Certificate Authority (CA) public keys (CA certs). The default\n" \
    " bundle is named curl-ca-bundle.crt; you can specify an alternate file\n" \
    " using the --cacert option.\n"
    
    #define CURL_CA_CERT_ERRORMSG2 \
    "If this HTTPS server uses a certificate signed by a CA represented in\n" \
    " the bundle, the certificate verification probably failed due to a\n" \
    " problem with the certificate (it might be expired, or the name might\n" \
    " not match the domain name in the URL).\n" \
    "If you'd like to turn off curl's verification of the certificate, use\n" \
    " the -k (or --insecure) option.\n"
    
                fprintf(config->errors, "%s%s",
                        CURL_CA_CERT_ERRORMSG1,
                        CURL_CA_CERT_ERRORMSG2 );
              }
            }
    #endif
    
            if (outfile && !curlx_strequal(outfile, "-") && outs.stream)
              fclose(outs.stream);
    
    #ifdef HAVE_UTIME
            /* Important that we set the time _after_ the file has been
               closed, as is done above here */
            if(config->remote_time && outs.filename) {
              /* as libcurl if we got a time. Pretty please */
              long filetime;
              curl_easy_getinfo(curl, CURLINFO_FILETIME, &filetime);
              if(filetime >= 0) {
                struct utimbuf times;
                times.actime = (time_t)filetime;
                times.modtime = (time_t)filetime;
                utime(outs.filename, &times); /* set the time we got */
              }
            }
    #endif
    #ifdef AMIGA
            /* Set the url as comment for the file. (up to 80 chars are allowed)
             */
            if( strlen(url) > 78 )
              url[79] = '\0';
    
            SetComment( outs.filename, url);
    #endif
    
            if(headerfilep)
              fclose(headerfilep);
    
            if (httpgetfields)
              free(httpgetfields);
    
            if(url)
              free(url);
    
            if(outfile)
              free(outfile);
    
            if(infdfopen)
              fclose(infd);
    
          } /* loop to the next URL */
    
          if(urls)
            /* cleanup memory used for URL globbing patterns */
            glob_cleanup(urls);
    
          if(uploadfile)
            free(uploadfile);
    
        } /* loop to the next globbed upload file */
    
        if(inglob)
          glob_cleanup(inglob);
    
        if(outfiles)
          free(outfiles);
    
        /* empty this urlnode struct */
        if(urlnode->url)
          free(urlnode->url);
        if(urlnode->outfile)
          free(urlnode->outfile);
        if(urlnode->infile)
          free(urlnode->infile);
    
        /* move on to the next URL */
        nextnode=urlnode->next;
        free(urlnode); /* free the node */
        urlnode = nextnode;
    
      } /* while-loop through all URLs */
    
      if(config->headerfile && !headerfilep && heads.stream)
        fclose(heads.stream);
    
      if(allocuseragent)
        free(config->useragent);
    
      /* cleanup the curl handle! */
      curl_easy_cleanup(curl);
    
      if(config->trace_fopened && config->trace_stream)
        fclose(config->trace_stream);
    
      if(config->errors_fopened)
        fclose(config->errors);
    
      main_free(); /* cleanup */
    
      return res;
    }
    
    static void checkfds(void);
    
    static void checkfds(void)
    {
    #ifdef HAVE_PIPE
      int fd[2] = { STDIN_FILENO, STDIN_FILENO };
      while( fd[0] == STDIN_FILENO ||
             fd[0] == STDOUT_FILENO ||
             fd[0] == STDERR_FILENO ||
             fd[1] == STDIN_FILENO ||
             fd[1] == STDOUT_FILENO ||
             fd[1] == STDERR_FILENO )
        pipe(fd);
    
      close(fd[0]);
      close(fd[1]);
    #endif
    }
    
    
    
    int main(int argc, char *argv[])
    {
      int res;
      struct Configurable config;
      memset(&config, 0, sizeof(struct Configurable));
    
      checkfds();
    
      res = operate(&config, argc, argv);
      free_config_fields(&config);
    
    #ifdef __NOVELL_LIBC__
      pressanykey();
    #endif
    #ifdef  VMS
      if (res > CURL_LAST) res = CURL_LAST; /* If CURL_LAST exceeded then */
      return (vms_cond[res]|vms_show);      /* curlmsg.h is out of sync.  */
    #else
      return res;
    #endif
    }
    
    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;
    }
    
    
    /* Create the needed directory hierarchy recursively in order to save
       multi-GETs in file output, ie:
       curl "http://my.site/dir[1-5]/file[1-5].txt" -o "dir#1/file#2.txt"
       should create all the dir* automagically
    */
    static int create_dir_hierarchy(char *outfile)
    {
      char *tempdir;
      char *tempdir2;
      char *outdup;
      char *dirbuildup;
      int result=0;
    
      outdup = strdup(outfile);
      dirbuildup = malloc(sizeof(char) * strlen(outfile));
      if(!dirbuildup)
        return -1;
      dirbuildup[0] = '\0';
    
      tempdir = strtok(outdup, DIR_CHAR);
    
      while (tempdir != NULL) {
        tempdir2 = strtok(NULL, DIR_CHAR);
        /* since strtok returns a token for the last word even
           if not ending with DIR_CHAR, we need to prune it */
        if (tempdir2 != NULL) {
          if (strlen(dirbuildup) > 0)
            sprintf(dirbuildup,"%s%s%s",dirbuildup, DIR_CHAR, tempdir);
          else {
            if (0 != strncmp(outdup, DIR_CHAR, 1))
              sprintf(dirbuildup,"%s",tempdir);
            else
              sprintf(dirbuildup,"%s%s", DIR_CHAR, tempdir);
          }
          if (access(dirbuildup, F_OK) == -1) {
            result = mkdir(dirbuildup,(mode_t)0000750);
            if (-1 == result) {
              switch (errno) {
    #ifdef EACCES
              case EACCES:
                fprintf(stderr,"You don't have permission to create %s.\n",
                        dirbuildup);
                break;
    #endif
    #ifdef ENAMETOOLONG
              case ENAMETOOLONG:
                fprintf(stderr,"The directory name %s is too long.\n",
                        dirbuildup);
                break;
    #endif
    #ifdef EROFS
              case EROFS:
                fprintf(stderr,"%s resides on a read-only file system.\n",
                        dirbuildup);
                break;
    #endif
    #ifdef ENOSPC
              case ENOSPC:
                fprintf(stderr,"No space left on the file system that will "
                        "contain the directory %s.\n", dirbuildup);
                break;
    #endif
    #ifdef EDQUOT
              case EDQUOT:
                fprintf(stderr,"Cannot create directory %s because you "
                        "exceeded your quota.\n", dirbuildup);
                break;
    #endif
              default :
                fprintf(stderr,"Error creating directory %s.\n", dirbuildup);
                break;
              }
              break; /* get out of loop */
            }
          }
        }
        tempdir = tempdir2;
      }
      free(dirbuildup);
      free(outdup);
    
      return result; /* 0 is fine, -1 is badness */
    }
    
    #ifdef __DJGPP__
    /* The following functions are taken with modification from the DJGPP
     * port of tar 1.12. They use algorithms originally from DJTAR. */
    
    char *
    msdosify (char *file_name)
    {
      static char dos_name[PATH_MAX];
      static char illegal_chars_dos[] = ".+, ;=[]|<>\\\":?*";
      static char *illegal_chars_w95 = &illegal_chars_dos[8];
      int idx, dot_idx;
      char *s = file_name, *d = dos_name;
      char *illegal_aliens = illegal_chars_dos;
      size_t len = sizeof (illegal_chars_dos) - 1;
      int lfn = 0;
    
      /* Support for Windows 9X VFAT systems, when available.  */
      if (_use_lfn (file_name))
        lfn = 1;
      if (lfn) {
        illegal_aliens = illegal_chars_w95;
        len -= (illegal_chars_w95 - illegal_chars_dos);
      }
    
      /* Get past the drive letter, if any. */
      if (s[0] >= 'A' && s[0] <= 'z' && s[1] == ':') {
        *d++ = *s++;
        *d++ = *s++;
      }
    
      for (idx = 0, dot_idx = -1; *s; s++, d++) {
        if (memchr (illegal_aliens, *s, len)) {
          /* Dots are special: DOS doesn't allow them as the leading character,
             and a file name cannot have more than a single dot.  We leave the
             first non-leading dot alone, unless it comes too close to the
             beginning of the name: we want sh.lex.c to become sh_lex.c, not
             sh.lex-c.  */
          if (*s == '.') {
            if (idx == 0 && (s[1] == '/' || (s[1] == '.' && s[2] == '/'))) {
              /* Copy "./" and "../" verbatim.  */
              *d++ = *s++;
              if (*s == '.')
                *d++ = *s++;
              *d = *s;
            }
            else if (idx == 0)
              *d = '_';
            else if (dot_idx >= 0) {
              if (dot_idx < 5) { /* 5 is a heuristic ad-hoc'ery */
                d[dot_idx - idx] = '_'; /* replace previous dot */
                *d = '.';
              }
              else
                *d = '-';
            }
            else
              *d = '.';
    
            if (*s == '.')
              dot_idx = idx;
          }
          else if (*s == '+' && s[1] == '+') {
            if (idx - 2 == dot_idx) { /* .c++, .h++ etc. */
              *d++ = 'x';
              *d   = 'x';
            }
            else {
              /* libg++ etc.  */
              memcpy (d, "plus", 4);
              d += 3;
            }
            s++;
            idx++;
          }
          else
            *d = '_';
        }
        else
          *d = *s;
        if (*s == '/') {
          idx = 0;
          dot_idx = -1;
        }
        else
          idx++;
      }
    
      *d = '\0';
      return dos_name;
    }
    
    char *
    rename_if_dos_device_name (char *file_name)
    {
      /* We could have a file whose name is a device on MS-DOS.  Trying to
       * retrieve such a file would fail at best and wedge us at worst.  We need
       * to rename such files. */
      char *base;
      struct stat st_buf;
      char fname[PATH_MAX];
    
      strcpy (fname, file_name);
      base = basename (fname);
      if (((stat(base, &st_buf)) == 0) && (S_ISCHR(st_buf.st_mode))) {
        size_t blen = strlen (base);
    
        /* Prepend a '_'.  */
        memmove (base + 1, base, blen + 1);
        base[0] = '_';
        strcpy (file_name, fname);
      }
      return file_name;
    }
    
    #endif /* __DJGPP__ */