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