]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/curl/src/main.c
hello world
[icculus/iodoom3.git] / neo / curl / src / main.c
1 /***************************************************************************
2  *                                  _   _ ____  _     
3  *  Project                     ___| | | |  _ \| |    
4  *                             / __| | | | |_) | |    
5  *                            | (__| |_| |  _ <| |___ 
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at http://curl.haxx.se/docs/copyright.html.
13  * 
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  * $Id: main.c,v 1.247 2004/03/17 12:46:48 bagder Exp $
22  ***************************************************************************/
23
24 /* This is now designed to have its own local setup.h */
25 #include "setup.h"
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <stdarg.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <ctype.h>
34 #include <errno.h>
35
36 #include <curl/curl.h>
37
38 #define _MPRINTF_REPLACE /* we want curl-functions instead of native ones */
39 #include <curl/mprintf.h>
40
41 #include "urlglob.h"
42 #include "writeout.h"
43 #include "getpass.h"
44 #include "homedir.h"
45 #ifdef USE_MANUAL
46 #include "hugehelp.h"
47 #endif
48 #ifdef USE_ENVIRONMENT
49 #include "writeenv.h"
50 #endif
51
52 #define CURLseparator   "--_curl_--"
53
54 #if defined(WIN32)&&!defined(__CYGWIN32__)
55 #include <winsock2.h>
56 #endif
57
58 #ifdef __NOVELL_LIBC__
59 #include <screen.h>
60 #endif
61
62 #ifdef TIME_WITH_SYS_TIME
63 /* We can include both fine */
64 #include <sys/time.h>
65 #include <time.h>
66 #else
67 #ifdef HAVE_SYS_TIME_H
68 # include <sys/time.h>
69 #else
70 # include <time.h>
71 #endif
72 #endif
73
74
75 #include "version.h"
76
77 #ifdef HAVE_IO_H /* typical win32 habit */
78 #include <io.h>
79 #endif
80
81 #ifdef HAVE_UNISTD_H
82 #include <unistd.h>
83 #endif
84
85 #ifdef HAVE_FCNTL_H
86 #include <fcntl.h>
87 #endif
88
89 #ifdef HAVE_UTIME_H
90 #include <utime.h>
91 #else
92 #ifdef HAVE_SYS_UTIME_H
93 #include <sys/utime.h>
94 #endif
95
96 #endif
97
98 #ifdef HAVE_LIMITS_H
99 #include <limits.h>
100 #endif
101
102 #ifdef HAVE_SYS_POLL_H
103 #include <sys/poll.h>
104 #endif
105
106 #include <strtoofft.h> /* header from the libcurl directory */
107
108 /* The last #include file should be: */
109 #ifdef CURLDEBUG
110 /* This is low-level hard-hacking memory leak tracking and similar. Using
111    the library level code from this client-side is ugly, but we do this
112    anyway for convenience. */
113 #include "memdebug.h"
114 #endif
115
116 #define DEFAULT_MAXREDIRS  50L
117
118 #ifdef __DJGPP__
119 void *xmalloc(size_t);
120 char *msdosify(char *);
121 char *rename_if_dos_device_name(char *);
122 void xfree(void *);
123 struct pollfd {
124        int fd;
125        int events;     /* in param: what to poll for */
126        int revents;    /* out param: what events occured */
127      };
128 int poll (struct pollfd *, int, int);
129 #endif /* __DJGPP__ */
130
131 #ifndef __cplusplus
132 #ifndef typedef_bool
133 typedef char bool;
134 #endif
135 #endif
136
137 #define CURL_PROGRESS_STATS 0 /* default progress display */
138 #define CURL_PROGRESS_BAR   1
139
140 /**
141  * @def MIN
142  * standard MIN macro
143  */
144 #ifndef MIN
145 #define MIN(X,Y)        (((X) < (Y)) ? (X) : (Y))
146 #endif
147
148 typedef enum {
149   HTTPREQ_UNSPEC,
150   HTTPREQ_GET,
151   HTTPREQ_HEAD,
152   HTTPREQ_POST,
153   HTTPREQ_SIMPLEPOST,
154   HTTPREQ_CUSTOM,
155   HTTPREQ_LAST
156 } HttpReq;
157
158 /* Just a set of bits */
159 #define CONF_DEFAULT  0
160
161 #define CONF_AUTO_REFERER (1<<4) /* the automatic referer-system please! */
162 #define CONF_VERBOSE  (1<<5) /* talk a lot */
163 #define CONF_HEADER   (1<<8) /* throw the header out too */
164 #define CONF_NOPROGRESS (1<<10) /* shut off the progress meter */
165 #define CONF_NOBODY   (1<<11) /* use HEAD to get http document */
166 #define CONF_FAILONERROR (1<<12) /* no output on http error codes >= 300 */
167 #define CONF_FTPLISTONLY (1<<16) /* Use NLST when listing ftp dir */
168 #define CONF_FTPAPPEND (1<<20) /* Append instead of overwrite on upload! */
169 #define CONF_NETRC    (1<<22)  /* read user+password from .netrc */
170 #define CONF_FOLLOWLOCATION (1<<23) /* use Location: Luke! */
171 #define CONF_GETTEXT  (1<<24) /* use ASCII/text for transfer */
172 #define CONF_HTTPPOST (1<<25) /* multipart/form-data HTTP POST */
173 #define CONF_MUTE     (1<<28) /* force NOPROGRESS */
174
175 #define CONF_NETRC_OPT (1<<29)  /* read user+password from either
176                                  * .netrc or URL*/
177 #define CONF_UNRESTRICTED_AUTH (1<<30)
178 /* Send authentication (user+password) when following
179  * locations, even when hostname changed */
180
181 #ifndef HAVE_STRDUP
182 /* Ultrix doesn't have strdup(), so make a quick clone: */
183 char *strdup(char *str)
184 {
185   int len;
186   char *newstr;
187
188   len = strlen(str);
189   newstr = (char *) malloc((len+1)*sizeof(char));
190   if (!newstr)
191     return (char *)NULL;
192
193   strcpy(newstr,str);
194
195   return newstr;
196
197 }
198 #endif 
199
200 #ifdef WIN32
201 #include <direct.h>
202 #define F_OK 0
203 #define mkdir(x,y) (mkdir)(x)
204 #endif
205
206 #ifdef  VMS
207 #include "curlmsg_vms.h"
208 #endif
209
210 /*
211  * This is the main global constructor for the app. Call this before
212  * _any_ libcurl usage. If this fails, *NO* libcurl functions may be
213  * used, or havoc may be the result.
214  */
215 static CURLcode main_init(void)
216 {
217   return curl_global_init(CURL_GLOBAL_DEFAULT);
218 }
219
220 /*
221  * This is the main global destructor for the app. Call this after
222  * _all_ libcurl usage is done.
223  */
224 static void main_free(void)
225 {
226   curl_global_cleanup();
227 }
228
229 static int SetHTTPrequest(HttpReq req, HttpReq *store)
230 {
231   if((*store == HTTPREQ_UNSPEC) ||
232      (*store == req)) {
233     *store = req;
234     return 0;
235   }
236   fprintf(stderr, "You can only select one HTTP request!\n");
237   return 1;
238 }
239
240 static void helpf(const char *fmt, ...)
241 {
242   va_list ap;
243   if(fmt) {
244     va_start(ap, fmt);
245     fputs("curl: ", stderr); /* prefix it */
246     vfprintf(stderr, fmt, ap);
247     va_end(ap);
248   }
249   fprintf(stderr, "curl: try 'curl --help' "
250 #ifdef USE_MANUAL
251           "or 'curl --manual' "
252 #endif
253           "for more information\n");
254 }
255
256 /*
257  * A chain of these nodes contain URL to get and where to put the URL's
258  * contents.
259  */
260 struct getout {
261   struct getout *next; /* next one */
262   char *url;     /* the URL we deal with */
263   char *outfile; /* where to store the output */
264   char *infile;  /* file to upload, if GETOUT_UPLOAD is set */
265   int flags;     /* options */
266 };
267 #define GETOUT_OUTFILE (1<<0)   /* set when outfile is deemed done */
268 #define GETOUT_URL     (1<<1)   /* set when URL is deemed done */
269 #define GETOUT_USEREMOTE (1<<2) /* use remote file name locally */
270 #define GETOUT_UPLOAD  (1<<3)   /* if set, -T has been used */
271 #define GETOUT_NOUPLOAD  (1<<4) /* if set, -T "" has been used */
272
273
274 static void help(void)
275 {
276   int i;
277   static const char *helptext[]={
278     "Usage: curl [options...] <url>",
279     "Options: (H) means HTTP/HTTPS only, (F) means FTP only",
280     " -a/--append        Append to target file when uploading (F)",
281     " -A/--user-agent <string> User-Agent to send to server (H)",
282     "    --anyauth       Tell curl to choose authentication method (H)",
283     " -b/--cookie <name=string/file> Cookie string or file to read cookies from (H)",
284     "    --basic         Enable HTTP Basic Authentication (H)",
285     " -B/--use-ascii     Use ASCII/text transfer",
286     " -c/--cookie-jar <file> Write cookies to this file after operation (H)",
287     " -C/--continue-at <offset> Resumed transfer offset",
288     " -d/--data <data>   HTTP POST data (H)",
289     "    --data-ascii <data>   HTTP POST ASCII data (H)",
290     "    --data-binary <data>  HTTP POST binary data (H)",
291     "    --negotiate     Enable HTTP Negotiate Authentication (H)",
292     "    --digest        Enable HTTP Digest Authentication (H)",
293     "    --disable-eprt  Prevent curl from using EPRT or LPRT (F)",
294     "    --disable-epsv  Prevent curl from using EPSV (F)",
295     " -D/--dump-header <file> Write the headers to this file",
296     "    --egd-file <file> EGD socket path for random data (SSL)",
297 #ifdef USE_ENVIRONMENT
298     "    --environment   Write result codes to environment variables (RISC OS)",
299 #endif
300     " -e/--referer       Referer URL (H)",
301     " -E/--cert <cert[:passwd]> Client certificate file and password (SSL)",
302     "    --cert-type <type> Certificate file type (DER/PEM/ENG) (SSL)",
303     "    --key <key>     Private key file name (SSL)",
304     "    --key-type <type> Private key file type (DER/PEM/ENG) (SSL)",
305     "    --pass  <pass>  Pass phrase for the private key (SSL)",
306     "    --engine <eng>  Crypto engine to use (SSL)",
307     "    --cacert <file> CA certificate to verify peer against (SSL)",
308     "    --capath <directory> CA directory (made using c_rehash) to verify",
309     "                    peer against (SSL)",
310     "    --ciphers <list> SSL ciphers to use (SSL)",
311     "    --compressed    Request compressed response (using deflate or gzip)",
312     "    --connect-timeout <seconds> Maximum time allowed for connection",
313     "    --create-dirs   Create necessary local directory hierarchy",
314     "    --crlf          Convert LF to CRLF in upload",
315     " -f/--fail          Fail silently (no output at all) on errors (H)",
316     "    --ftp-create-dirs Create the remote dirs if not present (F)",
317     "    --ftp-pasv      Use PASV instead of PORT (F)",
318     "    --ftp-ssl       Enable SSL/TLS for the ftp transfer (F)",
319     " -F/--form <name=content> Specify HTTP multipart POST data (H)",
320     " -g/--globoff       Disable URL sequences and ranges using {} and []",
321     " -G/--get           Send the -d data with a HTTP GET (H)",
322     " -h/--help          This help text",
323     " -H/--header <line> Custom header to pass to server (H)",
324     " -i/--include       Include protocol headers in the output (H/F)",
325     " -I/--head          Show document info only",
326     " -j/--junk-session-cookies Ignore session cookies read from file (H)",
327     "    --interface <interface> Specify network interface to use",
328     "    --krb4 <level>  Enable krb4 with specified security level (F)",
329     " -k/--insecure      Allow curl to connect to SSL sites without certs (H)",
330     " -K/--config        Specify which config file to read",
331     " -l/--list-only     List only names of an FTP directory (F)",
332     "    --limit-rate <rate> Limit transfer speed to this rate",
333     " -L/--location      Follow Location: hints (H)",
334     "    --location-trusted Follow Location: and send authentication even ",
335     "                    to other hostnames (H)",
336     " -m/--max-time <seconds> Maximum time allowed for the transfer",
337     "    --max-redirs <num> Maximum number of redirects allowed (H)",
338     "    --max-filesize <bytes> Maximum file size to download (H/F)",
339     " -M/--manual        Display the full manual",
340     " -n/--netrc         Must read .netrc for user name and password",
341     "    --netrc-optional Use either .netrc or URL; overrides -n",
342     "    --ntlm          Enable HTTP NTLM authentication (H)",
343     " -N/--no-buffer     Disable buffering of the output stream",
344     " -o/--output <file> Write output to <file> instead of stdout",
345     " -O/--remote-name   Write output to a file named as the remote file",
346     " -p/--proxytunnel   Operate through a HTTP proxy tunnel (using CONNECT)",
347     "    --proxy-ntlm    Enable NTLM authentication on the proxy (H)",
348     " -P/--ftp-port <address> Use PORT with address instead of PASV (F)",
349     " -q                 If used as the first parameter disables .curlrc",
350     " -Q/--quote <cmd>   Send command(s) to server before file transfer (F)",
351     " -r/--range <range> Retrieve a byte range from a HTTP/1.1 or FTP server",
352     "    --random-file <file> File for reading random data from (SSL)",
353     " -R/--remote-time   Set the remote file's time on the local output",
354     " -s/--silent        Silent mode. Don't output anything",
355     " -S/--show-error    Show error. With -s, make curl show errors when they occur",
356     "    --socks <host[:port]> Use SOCKS5 proxy on given host + port",
357     "    --stderr <file> Where to redirect stderr. - means stdout",
358     " -t/--telnet-option <OPT=val> Set telnet option",
359     "    --trace <file>  Dump a network/debug trace to the given file",
360     "    --trace-ascii <file> Like --trace but without the hex output",
361     " -T/--upload-file <file> Transfer/upload <file> to remote site",
362     "    --url <URL>     Another way to specify URL to work with",
363     " -u/--user <user[:password]> Specify user and password to use",
364     "                    Overrides -n and --netrc-optional",
365     " -U/--proxy-user <user[:password]> Specify Proxy authentication",
366     " -v/--verbose       Make the operation more talkative",
367     " -V/--version       Show version number and quit",
368 #ifdef __DJGPP__
369     "    --wdebug        Turn on WATT-32 debugging under DJGPP",
370 #endif
371     " -w/--write-out [format] What to output after completion",
372     " -x/--proxy <host[:port]> Use HTTP proxy on given port",
373     " -X/--request <command> Specify request command to use",
374     " -y/--speed-time    Time needed to trig speed-limit abort. Defaults to 30",
375     " -Y/--speed-limit   Stop transfer if below speed-limit for 'speed-time' secs",
376     " -z/--time-cond <time> Transfer based on a time condition",
377     " -0/--http1.0       Use HTTP 1.0 (H)",
378     " -1/--tlsv1         Use TLSv1 (SSL)",
379     " -2/--sslv2         Use SSLv2 (SSL)",
380     " -3/--sslv3         Use SSLv3 (SSL)",
381     " -4/--ipv4          Resolve name to IPv4 address",
382     " -6/--ipv6          Resolve name to IPv6 address",
383     " -#/--progress-bar  Display transfer progress as a progress bar",
384     NULL
385   };
386   for(i=0; helptext[i]; i++) {
387     puts(helptext[i]);
388 #ifdef __NOVELL_LIBC__
389     if (i && ((i % 23) == 0))
390       pressanykey();
391 #endif
392   }
393 }
394
395 struct LongShort {
396   const char *letter;
397   const char *lname;
398   bool extraparam;
399 };
400
401 struct Configurable {
402   bool remote_time;
403   char *random_file;
404   char *egd_file;
405   char *useragent;
406   char *cookie;     /* single line with specified cookies */
407   char *cookiejar;  /* write to this file */
408   char *cookiefile; /* read from this file */
409   bool cookiesession; /* new session? */
410   bool encoding;    /* Accept-Encoding please */
411   long authtype;    /* auth bitmask */  
412   bool use_resume;
413   bool resume_from_current;
414   bool disable_epsv;
415   bool disable_eprt;
416   curl_off_t resume_from;
417   char *postfields;
418   long postfieldsize;
419   char *referer;
420   long timeout;
421   long connecttimeout;
422   long maxredirs;
423   curl_off_t max_filesize;
424   char *headerfile;
425   char *ftpport;
426   char *iface;
427   unsigned short porttouse;
428   char *range;
429   long low_speed_limit;
430   long low_speed_time;
431   bool showerror;
432   char *userpwd;
433   char *proxyuserpwd;
434   char *proxy;
435   bool proxytunnel;
436   long conf;
437
438   struct getout *url_list; /* point to the first node */
439   struct getout *url_last; /* point to the last/current node */
440
441   struct getout *url_get;  /* point to the node to fill in URL */
442   struct getout *url_out;  /* point to the node to fill in outfile */
443
444   char *cipher_list;
445   char *cert;
446   char *cert_type;
447   char *cacert;
448   char *capath;
449   char *key;
450   char *key_type;
451   char *key_passwd;
452   char *engine;
453   bool crlf;
454   char *customrequest;
455   char *krb4level;
456   char *trace_dump; /* file to dump the network trace to, or NULL */
457   FILE *trace_stream;
458   bool trace_fopened;
459   bool trace_ascii;
460
461   long httpversion;
462   bool progressmode;
463   bool nobuffer;
464   bool globoff;
465   bool use_httpget;
466   bool insecure_ok; /* set TRUE to allow insecure SSL connects */
467   bool create_dirs;
468   bool ftp_create_dirs;
469   bool proxyntlm;
470
471   char *writeout; /* %-styled format string to output */
472   bool writeenv; /* write results to environment, if available */
473
474   FILE *errors; /* if stderr redirect is requested */
475   bool errors_fopened;
476
477   struct curl_slist *quote;
478   struct curl_slist *postquote;
479   struct curl_slist *prequote;
480
481   long ssl_version;
482   long ip_version;
483   curl_TimeCond timecond;
484   time_t condtime;
485
486   struct curl_slist *headers;
487
488   struct curl_httppost *httppost;
489   struct curl_httppost *last_post;
490
491   struct curl_slist *telnet_options;
492         
493   HttpReq httpreq;
494
495   /* for bandwidth limiting features: */
496
497   size_t sendpersecond; /* send to peer */
498   size_t recvpersecond; /* receive from peer */
499
500   time_t lastsendtime;
501   size_t lastsendsize;
502
503   time_t lastrecvtime;
504   size_t lastrecvsize;
505
506   bool ftp_ssl;
507
508   char *socks5proxy;
509 };
510
511 /* global variable to hold info about libcurl */
512 static curl_version_info_data *curlinfo;
513
514 static void parseconfig(const char *filename,
515                         struct Configurable *config);
516 static char *my_get_line(FILE *fp);
517 static int create_dir_hierarchy(char *outfile);
518
519 static void GetStr(char **string,
520                    char *value)
521 {
522   if(*string)
523     free(*string);
524   if(value)
525     *string = strdup(value);
526   else
527     *string = NULL;
528 }
529
530 static char *file2string(FILE *file)
531 {
532   char buffer[256];
533   char *ptr;
534   char *string=NULL;
535   size_t len=0;
536   size_t stringlen;
537
538   if(file) {
539     while(fgets(buffer, sizeof(buffer), file)) {
540       ptr= strchr(buffer, '\r');
541       if(ptr)
542         *ptr=0;
543       ptr= strchr(buffer, '\n');
544       if(ptr)
545         *ptr=0;
546       stringlen=strlen(buffer);
547       if(string)
548         string = realloc(string, len+stringlen+1);
549       else
550         string = malloc(stringlen+1);
551
552       strcpy(string+len, buffer);
553
554       len+=stringlen;
555     }
556     return string;
557   }
558   else
559     return NULL; /* no string */
560 }
561
562 static char *file2memory(FILE *file, long *size)
563 {
564   char buffer[1024];
565   char *string=NULL;
566   char *newstring=NULL;
567   size_t len=0;
568   long stringlen=0;
569
570   if(file) {
571     while((len = fread(buffer, 1, sizeof(buffer), file))) {
572       if(string) {
573         newstring = realloc(string, len+stringlen);
574         if(newstring)
575           string = newstring;
576         else
577           break; /* no more strings attached! :-) */
578       }
579       else
580         string = malloc(len);
581       memcpy(&string[stringlen], buffer, len);
582       stringlen+=len;
583     }
584     *size = stringlen;
585     return string;
586   }
587   else
588     return NULL; /* no string */
589 }
590
591 static void clean_getout(struct Configurable *config)
592 {
593   struct getout *node=config->url_list;
594   struct getout *next;
595
596   while(node) {
597     next = node->next;
598     if(node->url)
599       free(node->url);
600     if(node->outfile)
601       free(node->outfile);
602     if(node->infile)
603       free(node->infile);
604     free(node);
605
606     node = next; /* GOTO next */
607   }
608 }
609
610 static struct getout *new_getout(struct Configurable *config)
611 {
612   struct getout *node =malloc(sizeof(struct getout));
613   struct getout *last= config->url_last;
614   if(node) {
615     /* clear the struct */
616     memset(node, 0, sizeof(struct getout));
617         
618     /* append this new node last in the list */
619     if(last)
620       last->next = node;
621     else
622       config->url_list = node; /* first node */
623             
624     /* move the last pointer */
625     config->url_last = node;
626   }
627   return node;
628 }
629
630 /* Structure for storing the information needed to build a multiple files
631  * section
632 */
633 struct multi_files {
634   struct curl_forms   form;
635   struct multi_files *next;
636 };
637
638 /* Add a new list entry possibly with a type_name
639  */
640 static struct multi_files *
641 AddMultiFiles (const char *file_name,
642                const char *type_name,
643                const char *show_filename,
644                struct multi_files **multi_start,
645                struct multi_files **multi_current)
646 {
647   struct multi_files *multi;
648   struct multi_files *multi_type = NULL;
649   struct multi_files *multi_name = NULL;
650   multi = (struct multi_files *)malloc(sizeof(struct multi_files));
651   if (multi) {
652     memset(multi, 0, sizeof(struct multi_files));
653     multi->form.option = CURLFORM_FILE;
654     multi->form.value = file_name;
655   }
656   else
657     return NULL;
658
659   if (!*multi_start)
660     *multi_start = multi;
661
662   if (type_name) {
663     multi_type = (struct multi_files *)malloc(sizeof(struct multi_files));
664     if (multi_type) {
665       memset(multi_type, 0, sizeof(struct multi_files));
666       multi_type->form.option = CURLFORM_CONTENTTYPE;
667       multi_type->form.value = type_name;
668       multi->next = multi_type;
669
670       multi = multi_type;
671     }
672     else {
673       free (multi);
674       return NULL;
675     }
676   }
677   if (show_filename) {
678     multi_name = (struct multi_files *)malloc(sizeof(struct multi_files));
679     if (multi_name) {
680       memset(multi_name, 0, sizeof(struct multi_files));
681       multi_name->form.option = CURLFORM_FILENAME;
682       multi_name->form.value = show_filename;
683       multi->next = multi_name;
684
685       multi = multi_name;
686     }
687     else {
688       free (multi);
689       return NULL;
690     }
691   }
692
693   if (*multi_current)
694     (*multi_current)->next = multi;
695
696   *multi_current = multi;
697
698   return *multi_current;
699 }
700
701 /* Free the items of the list.
702  */
703 static void FreeMultiInfo (struct multi_files *multi_start)
704 {
705   struct multi_files *multi;
706   while (multi_start) {
707     multi = multi_start;
708     multi_start = multi_start->next;
709     free (multi);
710   }
711 }
712
713 /***************************************************************************
714  *
715  * formparse()
716  *      
717  * Reads a 'name=value' paramter and builds the appropriate linked list.
718  *
719  * Specify files to upload with 'name=@filename'. Supports specified
720  * given Content-Type of the files. Such as ';type=<content-type>'.
721  *
722  * You may specify more than one file for a single name (field). Specify
723  * multiple files by writing it like:
724  *
725  * 'name=@filename,filename2,filename3'
726  *
727  * If you want content-types specified for each too, write them like:
728  *
729  * 'name=@filename;type=image/gif,filename2,filename3'
730  *
731  * If you want custom headers added for a single part, write them in a separate
732  * file and do like this:
733  *
734  * 'name=foo;headers=@headerfile' or why not
735  * 'name=@filemame;headers=@headerfile'
736  *
737  * To upload a file, but to fake the file name that will be included in the
738  * formpost, do like this:
739  *
740  * 'name=@filename;filename=/dev/null'
741  *
742  * This function uses curl_formadd to fulfill it's job. Is heavily based on
743  * the old curl_formparse code.
744  *
745  ***************************************************************************/
746
747 #define FORM_FILE_SEPARATOR ','
748 #define FORM_TYPE_SEPARATOR ';'
749
750 static int formparse(char *input,
751                      struct curl_httppost **httppost,
752                      struct curl_httppost **last_post)
753 {
754   /* nextarg MUST be a string in the format 'name=contents' and we'll
755      build a linked list with the info */
756   char name[256];
757   char *contents;
758   char major[128];
759   char minor[128];
760   char *contp;
761   const char *type = NULL;
762   char *sep;
763   char *sep2;
764
765   /* Preallocate contents to the length of input to make sure we don't
766      overwrite anything. */
767   contents = malloc(strlen(input));
768   contents[0] = '\000';
769  
770   if(1 <= sscanf(input, "%255[^=]=%[^\n]", name, contents)) {
771     /* the input was using the correct format */
772     contp = contents;
773
774     if('@' == contp[0]) {
775       struct multi_files *multi_start = NULL, *multi_current = NULL;
776       /* we use the @-letter to indicate file name(s) */
777       contp++;
778
779       multi_start = multi_current=NULL;
780
781       do {
782         /* since this was a file, it may have a content-type specifier
783            at the end too, or a filename. Or both. */
784         char *ptr;
785         char *filename=NULL;
786
787         sep=strchr(contp, FORM_TYPE_SEPARATOR);
788         sep2=strchr(contp, FORM_FILE_SEPARATOR);
789
790         /* pick the closest */
791         if(sep2 && (sep2 < sep)) {
792           sep = sep2;
793
794           /* no type was specified! */
795         }
796
797         type = NULL;
798
799         if(sep) {
800
801           /* if we got here on a comma, don't do much */
802           if(FORM_FILE_SEPARATOR == *sep)
803             ptr = NULL;
804           else
805             ptr = sep+1;
806
807           *sep=0; /* terminate file name at separator */
808
809           while(ptr && (FORM_FILE_SEPARATOR!= *ptr)) {
810
811             /* pass all white spaces */
812             while(isspace((int)*ptr))
813               ptr++;
814
815             if(curl_strnequal("type=", ptr, 5)) {
816
817               /* set type pointer */
818               type = &ptr[5];
819             
820               /* verify that this is a fine type specifier */
821               if(2 != sscanf(type, "%127[^/]/%127[^;,\n]",
822                              major, minor)) {
823                 fprintf(stderr, "Illegally formatted content-type field!\n");
824                 free(contents);
825                 FreeMultiInfo (multi_start);
826                 return 2; /* illegal content-type syntax! */
827               }
828               /* now point beyond the content-type specifier */
829               sep = (char *)type + strlen(major)+strlen(minor)+1;
830
831               *sep=0; /* zero terminate type string */
832
833               ptr=sep+1;
834             }
835             else if(curl_strnequal("filename=", ptr, 9)) {
836               filename = &ptr[9];
837               ptr=strchr(filename, FORM_TYPE_SEPARATOR);
838               if(!ptr) {
839                 ptr=strchr(filename, FORM_FILE_SEPARATOR);
840               }
841               if(ptr) {
842                 *ptr=0; /* zero terminate */
843                 ptr++;
844               }
845             }
846             else
847               /* confusion, bail out of loop */
848               break;
849           }
850           /* find the following comma */
851           if(ptr)
852             sep=strchr(ptr, FORM_FILE_SEPARATOR);
853           else
854             sep=NULL;
855         }
856         else {
857           sep=strchr(contp, FORM_FILE_SEPARATOR);
858         }
859         if(sep) {
860           /* the next file name starts here */
861           *sep =0;
862           sep++;
863         }
864         /* if type == NULL curl_formadd takes care of the problem */
865
866         if (!AddMultiFiles (contp, type, filename, &multi_start,
867                             &multi_current)) {
868           fprintf(stderr, "Error building form post!\n");
869           free(contents);
870           FreeMultiInfo (multi_start);
871           return 3;
872         }
873         contp = sep; /* move the contents pointer to after the separator */
874
875       } while(sep && *sep); /* loop if there's another file name */
876
877       /* now we add the multiple files section */
878       if (multi_start) {
879         struct curl_forms *forms = NULL;
880         struct multi_files *ptr = multi_start;
881         unsigned int i, count = 0;
882         while (ptr) {
883           ptr = ptr->next;
884           ++count;
885         }
886         forms =
887           (struct curl_forms *)malloc((count+1)*sizeof(struct curl_forms));
888         if (!forms)
889         {
890           fprintf(stderr, "Error building form post!\n");
891           free(contents);
892           FreeMultiInfo (multi_start);
893           return 4;
894         }
895         for (i = 0, ptr = multi_start; i < count; ++i, ptr = ptr->next)
896         {
897           forms[i].option = ptr->form.option;
898           forms[i].value = ptr->form.value;
899         }
900         forms[count].option = CURLFORM_END;
901         FreeMultiInfo (multi_start);
902         if (curl_formadd (httppost, last_post,
903                           CURLFORM_COPYNAME, name,
904                           CURLFORM_ARRAY, forms, CURLFORM_END) != 0) {
905           fprintf(stderr, "curl_formadd failed!\n");
906           free(forms);
907           free(contents);
908           return 5;
909         }
910         free(forms);
911       }
912     }
913     else {
914       if( contp[0]=='<' ) {
915         if (curl_formadd (httppost, last_post,
916                           CURLFORM_COPYNAME, name,
917                           CURLFORM_FILECONTENT, contp+1, CURLFORM_END) != 0) {
918           fprintf(stderr, "curl_formadd failed!\n");
919           free(contents);
920           return 6;
921         }
922       }
923       else {
924         if (curl_formadd (httppost, last_post,
925                           CURLFORM_COPYNAME, name,
926                           CURLFORM_COPYCONTENTS, contp, CURLFORM_END) != 0) {
927           fprintf(stderr, "curl_formadd failed!\n");
928           free(contents);
929           return 7;
930         }
931       }
932     }
933
934   }
935   else {
936     fprintf(stderr, "Illegally formatted input field!\n");
937     free(contents);
938     return 1;
939   }
940   free(contents);
941   return 0;
942 }
943
944
945 typedef enum {
946   PARAM_OK,
947   PARAM_OPTION_AMBIGUOUS,
948   PARAM_OPTION_UNKNOWN,
949   PARAM_REQUIRES_PARAMETER,  
950   PARAM_BAD_USE,
951   PARAM_HELP_REQUESTED,
952   PARAM_GOT_EXTRA_PARAMETER,
953   PARAM_BAD_NUMERIC,
954   PARAM_LIBCURL_DOESNT_SUPPORT,
955   PARAM_LAST
956 } ParameterError;
957
958 static const char *param2text(int res)
959 {
960   ParameterError error = (ParameterError)res;
961   switch(error) {
962   case PARAM_GOT_EXTRA_PARAMETER:
963     return "had unsupported trailing garbage";
964   case PARAM_OPTION_UNKNOWN:
965     return "is unknown";
966   case PARAM_OPTION_AMBIGUOUS:
967     return "is ambiguous";
968   case PARAM_REQUIRES_PARAMETER:
969     return "requires parameter";
970   case PARAM_BAD_USE:
971     return "is badly used here";
972   case PARAM_BAD_NUMERIC:
973     return "expected a proper numerical parameter";
974   case PARAM_LIBCURL_DOESNT_SUPPORT:
975     return "the installed libcurl version doesn't support this";
976   default:
977     return "unknown error";
978   }
979 }
980
981 static void cleanarg(char *str)
982 {
983 #ifdef HAVE_WRITABLE_ARGV
984   /* now that GetStr has copied the contents of nextarg, wipe the next
985    * argument out so that the username:password isn't displayed in the
986    * system process list */
987   if (str) {
988     size_t len = strlen(str);
989     memset(str, ' ', len);
990   }
991 #else
992   (void)str;
993 #endif
994 }
995
996 /*
997  * Parse the string and write the integer in the given address. Return
998  * non-zero on failure, zero on success.
999  *
1000  * The string must start with a digit to be valid.
1001  */
1002
1003 static int str2num(long *val, char *str)
1004 {
1005   int retcode = 0;
1006   if(isdigit((int)*str))
1007     *val = atoi(str);
1008   else
1009     retcode = 1; /* badness */
1010   return retcode;  
1011 }
1012
1013 /**
1014  * Parses the given string looking for an offset (which may be
1015  * a larger-than-integer value).
1016  *
1017  * @param val  the offset to populate
1018  * @param str  the buffer containing the offset
1019  * @return zero if successful, non-zero if failure.
1020  */
1021 static int str2offset(curl_off_t *val, char *str)
1022 {
1023 #if SIZEOF_CURL_OFF_T > 4
1024   /* Ugly, but without going through a bunch of rigmarole, we don't have the
1025    * definitions for LLONG_{MIN,MAX} or LONG_LONG_{MIN,MAX}.
1026    */
1027 #ifndef LLONG_MAX
1028 #ifdef _MSC_VER
1029 #define LLONG_MAX (curl_off_t)0x7FFFFFFFFFFFFFFFi64
1030 #define LLONG_MIN (curl_off_t)0x8000000000000000i64
1031 #else
1032 #define LLONG_MAX (curl_off_t)0x7FFFFFFFFFFFFFFFLL
1033 #define LLONG_MIN (curl_off_t)0x8000000000000000LL
1034 #endif
1035 #endif
1036
1037   /* this is a duplicate of the function that is also used in libcurl */
1038   *val = strtoofft(str, NULL, 0);
1039
1040   if ((*val == LLONG_MAX || *val == LLONG_MIN) && errno == ERANGE)
1041     return 1;
1042 #else
1043   *val = strtol(str, NULL, 0);
1044   if ((*val == LONG_MIN || *val == LONG_MAX) && errno == ERANGE)
1045     return 1;
1046 #endif
1047   return 0;
1048 }
1049
1050 static void checkpasswd(const char *kind, /* for what purpose */
1051                         char **userpwd) /* pointer to allocated string */
1052 {
1053   char *ptr = strchr(*userpwd, ':');
1054   if(!ptr) {
1055     /* no password present, prompt for one */
1056     char passwd[256]="";
1057     char prompt[256];
1058     size_t passwdlen;
1059     size_t userlen = strlen(*userpwd);
1060     char *passptr;
1061
1062     /* build a nice-looking prompt */
1063     curl_msnprintf(prompt, sizeof(prompt),
1064                    "Enter %s password for user '%s':",
1065                    kind, *userpwd);
1066
1067     /* get password */
1068     getpass_r(prompt, passwd, sizeof(passwd));
1069     passwdlen = strlen(passwd);
1070
1071     /* extend the allocated memory area to fit the password too */
1072     passptr = realloc(*userpwd,
1073                       passwdlen + 1 + /* an extra for the colon */
1074                       userlen + 1);   /* an extra for the zero */
1075
1076     if(passptr) {
1077       /* append the password separated with a colon */
1078       passptr[userlen]=':';
1079       memcpy(&passptr[userlen+1], passwd, passwdlen+1);
1080       *userpwd = passptr;
1081     }
1082   }
1083 }
1084
1085 static ParameterError getparameter(char *flag, /* f or -long-flag */
1086                                    char *nextarg, /* NULL if unset */
1087                                    bool *usedarg, /* set to TRUE if the arg
1088                                                      has been used */
1089                                    struct Configurable *config)
1090 {
1091   char letter;
1092   char subletter=0; /* subletters can only occur on long options */
1093
1094   const char *parse=NULL;
1095   unsigned int j;
1096   time_t now;
1097   int hit=-1;
1098   bool longopt=FALSE;
1099   bool singleopt=FALSE; /* when true means '-o foo' used '-ofoo' */
1100
1101
1102   /* single-letter,
1103      long-name,
1104      boolean whether it takes an additional argument
1105      */
1106   struct LongShort aliases[]= {
1107     /* all these ones, starting with "*" or "$" as a short-option have *no*
1108        short option to mention. */
1109     {"*", "url",         TRUE},
1110     {"*a", "random-file", TRUE},
1111     {"*b", "egd-file",   TRUE},
1112     {"*c", "connect-timeout", TRUE},
1113     {"*d", "ciphers",    TRUE},
1114     {"*e", "disable-epsv", FALSE},
1115 #ifdef USE_ENVIRONMENT
1116     {"*f", "environment", FALSE},
1117 #endif
1118     {"*g", "trace",      TRUE},
1119     {"*h", "trace-ascii", TRUE},
1120     {"*i", "limit-rate", TRUE},
1121     {"*j", "compressed",  FALSE}, /* might take an arg someday */
1122     {"*k", "digest",     FALSE},
1123     {"*l", "negotiate",  FALSE},
1124     {"*m", "ntlm",       FALSE},
1125     {"*n", "basic",      FALSE},
1126     {"*o", "anyauth",    FALSE},
1127 #ifdef __DJGPP__
1128     {"*p", "wdebug",     FALSE},
1129 #endif
1130     {"*q", "ftp-create-dirs", FALSE},
1131     {"*r", "create-dirs", FALSE},
1132     {"*s", "max-redirs",   TRUE},
1133     {"*t", "proxy-ntlm",   FALSE},
1134     {"*u", "crlf",        FALSE},
1135     {"*v", "stderr",      TRUE},
1136     {"*w", "interface",   TRUE},
1137     {"*x", "krb4",        TRUE},
1138     {"*y", "max-filesize", TRUE},
1139     {"*z", "disable-eprt", FALSE},
1140     {"$a", "ftp-ssl",    FALSE},
1141     {"$b", "ftp-pasv",   FALSE},
1142     {"$c", "socks5",     TRUE},
1143     {"0", "http1.0",     FALSE},
1144     {"1", "tlsv1",       FALSE},
1145     {"2", "sslv2",       FALSE},
1146     {"3", "sslv3",       FALSE},
1147     {"4", "ipv4",       FALSE},
1148     {"6", "ipv6",       FALSE},
1149     {"a", "append",      FALSE},
1150     {"A", "user-agent",  TRUE},
1151     {"b", "cookie",      TRUE},
1152     {"B", "use-ascii",   FALSE},
1153     {"c", "cookie-jar",  TRUE},
1154     {"C", "continue-at", TRUE},
1155     {"d", "data",        TRUE},
1156     {"da", "data-ascii", TRUE},
1157     {"db", "data-binary", TRUE},
1158     {"D", "dump-header", TRUE},
1159     {"e", "referer",     TRUE},
1160     {"E", "cert",        TRUE},
1161     {"Ea", "cacert",     TRUE},
1162     {"Eb","cert-type",   TRUE},
1163     {"Ec","key",         TRUE},
1164     {"Ed","key-type",    TRUE},
1165     {"Ee","pass",        TRUE},
1166     {"Ef","engine",      TRUE},
1167     {"Eg","capath ",     TRUE},
1168     {"f", "fail",        FALSE},
1169     {"F", "form",        TRUE},
1170     {"g", "globoff",     FALSE},
1171     {"G", "get",         FALSE},
1172     {"h", "help",        FALSE},
1173     {"H", "header",      TRUE},
1174     {"i", "include",     FALSE},
1175     {"I", "head",        FALSE},
1176     {"j", "junk-session-cookies", FALSE},
1177     {"k", "insecure",    FALSE},
1178     {"K", "config",      TRUE},
1179     {"l", "list-only",   FALSE},
1180     {"L", "location",    FALSE},
1181     {"Lt", "location-trusted", FALSE},
1182     {"m", "max-time",    TRUE},
1183     {"M", "manual",      FALSE},
1184     {"n", "netrc",       FALSE},
1185     {"no", "netrc-optional", FALSE},
1186     {"N", "no-buffer",   FALSE},
1187     {"o", "output",      TRUE},
1188     {"O", "remote-name", FALSE},
1189     {"p", "proxytunnel", FALSE},
1190     {"P", "ftpport",     TRUE}, /* older version */
1191     {"P", "ftp-port",    TRUE},
1192     {"q", "disable",     FALSE},
1193     {"Q", "quote",       TRUE},
1194     {"r", "range",       TRUE},
1195     {"R", "remote-time", FALSE},
1196     {"s", "silent",      FALSE},
1197     {"S", "show-error",  FALSE},
1198     {"t", "telnet-options", TRUE},
1199     {"T", "upload-file", TRUE},
1200     {"u", "user",        TRUE},
1201     {"U", "proxy-user",  TRUE},
1202     {"v", "verbose",     FALSE},
1203     {"V", "version",     FALSE},
1204     {"w", "write-out",   TRUE},
1205     {"x", "proxy",       TRUE},
1206     {"X", "request",     TRUE},
1207     {"X", "http-request", TRUE}, /* OBSOLETE VERSION */
1208     {"Y", "speed-limit",  TRUE},
1209     {"y", "speed-time", TRUE},
1210     {"z", "time-cond",   TRUE},
1211     {"#", "progress-bar",FALSE},
1212   };
1213
1214   if(('-' != flag[0]) ||
1215      (('-' == flag[0]) && ('-' == flag[1]))) {
1216     /* this should be a long name */
1217     char *word=('-' == flag[0])?flag+2:flag;
1218     size_t fnam=strlen(word);
1219     int numhits=0;
1220     for(j=0; j< sizeof(aliases)/sizeof(aliases[0]); j++) {
1221       if(curl_strnequal(aliases[j].lname, word, fnam)) {
1222         longopt = TRUE;
1223         numhits++;
1224         if(curl_strequal(aliases[j].lname, word)) {
1225           parse = aliases[j].letter;
1226           hit = j;
1227           numhits = 1; /* a single unique hit */
1228           break;
1229         }
1230         parse = aliases[j].letter;
1231         hit = j;
1232       }
1233     }
1234     if(numhits>1) {
1235       /* this is at least the second match! */
1236       return PARAM_OPTION_AMBIGUOUS;
1237     }
1238     if(hit < 0) {
1239       return PARAM_OPTION_UNKNOWN;
1240     }    
1241   }
1242   else {
1243     flag++; /* prefixed with one dash, pass it */
1244     hit=-1;
1245     parse = flag;
1246   }
1247
1248   do {
1249     /* we can loop here if we have multiple single-letters */
1250
1251     if(!longopt)
1252       letter = parse?(char)*parse:'\0';
1253     else {
1254       letter = parse[0];
1255       subletter = parse[1];
1256     }
1257     *usedarg = FALSE; /* default is that we don't use the arg */
1258
1259 #if 0
1260     fprintf(stderr, "OPTION: %c %s\n", letter, nextarg?nextarg:"<null>");
1261 #endif
1262     if(hit < 0) {
1263       for(j=0; j< sizeof(aliases)/sizeof(aliases[0]); j++) {
1264         if(letter == aliases[j].letter[0]) {
1265           hit = j;
1266           break;
1267         }
1268       }
1269       if(hit < 0) {
1270         return PARAM_OPTION_UNKNOWN;
1271       }
1272     }
1273     if(hit < 0) {
1274       return PARAM_OPTION_UNKNOWN;
1275     }    
1276     if(!longopt && aliases[hit].extraparam && parse[1]) {
1277       nextarg=(char *)&parse[1]; /* this is the actual extra parameter */
1278       singleopt=TRUE;   /* don't loop anymore after this */
1279     }
1280     else if(!nextarg && aliases[hit].extraparam) {
1281       return PARAM_REQUIRES_PARAMETER;
1282     }
1283     else if(nextarg && aliases[hit].extraparam)
1284       *usedarg = TRUE; /* mark it as used */
1285
1286     switch(letter) {
1287     case '*': /* options without a short option */
1288       switch(subletter) {
1289       case 'a': /* random-file */
1290         GetStr(&config->random_file, nextarg);
1291         break;
1292       case 'b': /* egd-file */
1293         GetStr(&config->egd_file, nextarg);
1294         break;
1295       case 'c': /* connect-timeout */
1296         if(str2num(&config->connecttimeout, nextarg))
1297           return PARAM_BAD_NUMERIC;
1298         break;
1299       case 'd': /* ciphers */
1300         GetStr(&config->cipher_list, nextarg);
1301         break;
1302       case 'e': /* --disable-epsv */
1303         config->disable_epsv ^= TRUE;
1304         break;
1305 #ifdef USE_ENVIRONMENT
1306       case 'f':
1307         config->writeenv ^= TRUE;
1308         break;
1309 #endif
1310       case 'g': /* --trace */
1311         GetStr(&config->trace_dump, nextarg);
1312         break;
1313       case 'h': /* --trace-ascii */
1314         GetStr(&config->trace_dump, nextarg);
1315         config->trace_ascii = TRUE;
1316         break;
1317       case 'i': /* --limit-rate */
1318         {
1319           /* We support G, M, K too */
1320           char *unit;
1321           unsigned long value = strtol(nextarg, &unit, 0);
1322           switch(nextarg[strlen(nextarg)-1]) {
1323           case 'G':
1324           case 'g':
1325             value *= 1024*1024*1024;
1326             break;
1327           case 'M':
1328           case 'm':
1329             value *= 1024*1024;
1330             break;
1331           case 'K':
1332           case 'k':
1333             value *= 1024;
1334             break;
1335           }
1336           config->recvpersecond = value;
1337           config->sendpersecond = value;
1338         }
1339         break;
1340
1341       case 'j': /* --compressed */
1342         config->encoding ^= TRUE;
1343         break;
1344
1345       case 'k': /* --digest */
1346         config->authtype = CURLAUTH_DIGEST;
1347         break;
1348
1349       case 'l': /* --negotiate */
1350         if(curlinfo->features & CURL_VERSION_GSSNEGOTIATE)
1351           config->authtype = CURLAUTH_GSSNEGOTIATE;
1352         else
1353           return PARAM_LIBCURL_DOESNT_SUPPORT;
1354         break;
1355
1356       case 'm': /* --ntlm */
1357         if(curlinfo->features & CURL_VERSION_NTLM)
1358           config->authtype = CURLAUTH_NTLM;
1359         else
1360           return PARAM_LIBCURL_DOESNT_SUPPORT;
1361         break;
1362
1363       case 'n': /* --basic for completeness */
1364         config->authtype = CURLAUTH_BASIC;
1365         break;
1366
1367       case 'o': /* --anyauth, let libcurl pick it */
1368         config->authtype = CURLAUTH_ANY;
1369         break;
1370
1371 #ifdef __DJGPP__
1372       case 'p': /* --wdebug */
1373         dbug_init();
1374         break;
1375 #endif
1376       case 'q': /* --ftp-create-dirs */
1377         config->ftp_create_dirs ^= TRUE;
1378         break;
1379
1380       case 'r': /* --create-dirs */
1381         config->create_dirs = TRUE;
1382         break;
1383
1384       case 's': /* --max-redirs */
1385         /* specified max no of redirects (http(s)) */
1386         if(str2num(&config->maxredirs, nextarg))
1387           return PARAM_BAD_NUMERIC;
1388         break;
1389
1390       case 't': /* --proxy-ntlm */
1391         config->proxyntlm ^= TRUE;
1392         break;
1393
1394       case 'u': /* --crlf */
1395         /* LF -> CRLF conversinon? */
1396         config->crlf = TRUE;
1397         break;
1398
1399       case 'v': /* --stderr */
1400         if(strcmp(nextarg, "-")) {
1401           config->errors = fopen(nextarg, "wt");
1402           config->errors_fopened = TRUE;
1403         }
1404         else
1405           config->errors = stdout;
1406       break;
1407       case 'w': /* --interface */
1408         /* interface */
1409         GetStr(&config->iface, nextarg);
1410         break;
1411       case 'x': /* --krb4 */
1412         /* krb4 level string */
1413         if(curlinfo->features & CURL_VERSION_KERBEROS4)
1414           GetStr(&config->krb4level, nextarg);
1415         else
1416           return PARAM_LIBCURL_DOESNT_SUPPORT;
1417         break;
1418       case 'y': /* --max-filesize */
1419         if(str2offset(&config->max_filesize, nextarg))
1420           return PARAM_BAD_NUMERIC;
1421         break;
1422       case 'z': /* --disable-eprt */
1423         config->disable_eprt ^= TRUE;
1424         break;
1425
1426       default: /* the URL! */
1427         {
1428           struct getout *url;
1429           if(config->url_get || (config->url_get=config->url_list)) {
1430             /* there's a node here, if it already is filled-in continue to find
1431                an "empty" node */
1432             while(config->url_get && (config->url_get->flags&GETOUT_URL))
1433               config->url_get = config->url_get->next;
1434           }
1435
1436           /* now there might or might not be an available node to fill in! */
1437
1438           if(config->url_get)
1439             /* existing node */
1440             url = config->url_get;
1441           else
1442             /* there was no free node, create one! */
1443             url=new_getout(config);
1444           
1445           if(url) {
1446             /* fill in the URL */
1447             GetStr(&url->url, nextarg);
1448             url->flags |= GETOUT_URL;
1449           }
1450         }
1451       }
1452       break;
1453     case '$': /* more options without a short option */
1454       switch(subletter) {
1455       case 'a': /* --ftp-ssl */
1456         config->ftp_ssl ^= TRUE;
1457         break;
1458       case 'b': /* --ftp-pasv */
1459         if(config->ftpport)
1460           free(config->ftpport);
1461         config->ftpport = NULL;
1462         break;
1463       case 'c': /* --socks specifies a socks5 proxy to use */
1464         GetStr(&config->socks5proxy, nextarg);
1465         break;
1466       }
1467       break;
1468     case '#': /* added 19990617 larsa */
1469       config->progressmode ^= CURL_PROGRESS_BAR;
1470       break;
1471     case '0': 
1472       /* HTTP version 1.0 */
1473       config->httpversion = CURL_HTTP_VERSION_1_0;
1474       break;
1475     case '1':
1476       /* TLS version 1 */
1477       config->ssl_version = CURL_SSLVERSION_TLSv1;
1478       break;
1479     case '2': 
1480       /* SSL version 2 */
1481       config->ssl_version = CURL_SSLVERSION_SSLv2;
1482       break;
1483     case '3': 
1484       /* SSL version 3 */
1485       config->ssl_version = CURL_SSLVERSION_SSLv3;
1486       break;
1487     case '4': 
1488       /* IPv4 */
1489       config->ip_version = 4;
1490       break;
1491     case '6': 
1492       /* IPv6 */
1493       config->ip_version = 6;
1494       break;
1495     case 'a':
1496       /* This makes the FTP sessions use APPE instead of STOR */
1497       config->conf ^= CONF_FTPAPPEND;
1498       break;
1499     case 'A':
1500       /* This specifies the User-Agent name */
1501       GetStr(&config->useragent, nextarg);
1502       break;
1503     case 'b': /* cookie string coming up: */
1504       if(nextarg[0] == '@') {
1505         nextarg++;
1506       }
1507       else if(strchr(nextarg, '=')) {
1508         /* A cookie string must have a =-letter */
1509         GetStr(&config->cookie, nextarg);
1510         break;
1511       }
1512       /* We have a cookie file to read from! */
1513       GetStr(&config->cookiefile, nextarg);
1514       break;
1515     case 'B':
1516       /* use ASCII/text when transfering */
1517       config->conf ^= CONF_GETTEXT;
1518       break;
1519     case 'c':
1520       /* get the file name to dump all cookies in */
1521       GetStr(&config->cookiejar, nextarg);
1522       break;
1523     case 'C':
1524       /* This makes us continue an ftp transfer at given position */
1525       if(!curl_strequal(nextarg, "-")) {
1526         if(str2offset(&config->resume_from, nextarg))
1527           return PARAM_BAD_NUMERIC;
1528         config->resume_from_current = FALSE;
1529       }
1530       else {
1531         config->resume_from_current = TRUE;
1532         config->resume_from = 0;
1533       }
1534       config->use_resume=TRUE;
1535       break;
1536     case 'd':
1537       /* postfield data */
1538       {
1539         char *postdata=NULL;
1540
1541         if('@' == *nextarg) {
1542           /* the data begins with a '@' letter, it means that a file name
1543              or - (stdin) follows */
1544           FILE *file;
1545
1546           nextarg++; /* pass the @ */
1547
1548           if(curl_strequal("-", nextarg))
1549             file = stdin;
1550           else 
1551             file = fopen(nextarg, "rb");
1552
1553           if(subletter == 'b') /* forced binary */
1554             postdata = file2memory(file, &config->postfieldsize);
1555           else
1556             postdata = file2string(file);
1557           if(file && (file != stdin))
1558             fclose(file);
1559         }
1560         else {
1561           GetStr(&postdata, nextarg);
1562         }
1563
1564         if(config->postfields) {
1565           /* we already have a string, we append this one
1566              with a separating &-letter */
1567           char *oldpost=config->postfields;
1568           config->postfields=aprintf("%s&%s", oldpost, postdata);
1569           free(oldpost);
1570           free(postdata);
1571         }
1572         else
1573           config->postfields=postdata;
1574       }
1575       /*
1576         We can't set the request type here, as this data might be used in
1577         a simple GET if -G is used. Already or soon.
1578
1579         if(SetHTTPrequest(HTTPREQ_SIMPLEPOST, &config->httpreq))
1580           return PARAM_BAD_USE;
1581       */
1582       break;
1583     case 'D':
1584       /* dump-header to given file name */
1585       GetStr(&config->headerfile, nextarg);
1586       break;
1587     case 'e':
1588       {
1589         char *ptr = strstr(nextarg, ";auto");
1590         if(ptr) {
1591           /* Automatic referer requested, this may be combined with a
1592              set initial one */
1593           config->conf |= CONF_AUTO_REFERER;
1594           *ptr = 0; /* zero terminate here */
1595         }
1596         GetStr(&config->referer, nextarg);
1597       }
1598       break;
1599     case 'E':
1600       switch(subletter) {
1601       case 'a': /* CA info PEM file */
1602         /* CA info PEM file */
1603         GetStr(&config->cacert, nextarg);
1604         break;
1605       case 'b': /* cert file type */
1606         GetStr(&config->cert_type, nextarg);
1607         break;
1608       case 'c': /* private key file */
1609         GetStr(&config->key, nextarg);
1610         break;
1611       case 'd': /* private key file type */
1612         GetStr(&config->key_type, nextarg);
1613         break;
1614       case 'e': /* private key passphrase */
1615         GetStr(&config->key_passwd, nextarg);
1616         cleanarg(nextarg);
1617         break;
1618       case 'f': /* crypto engine */
1619         GetStr(&config->engine, nextarg);
1620         break;
1621       case 'g': /* CA info PEM file */
1622         /* CA cert directory */
1623         GetStr(&config->capath, nextarg);
1624         break;
1625       default: /* certificate file */
1626         {
1627           char *ptr = strchr(nextarg, ':');
1628           /* Since we live in a world of weirdness and confusion, the win32
1629              dudes can use : when using drive letters and thus
1630              c:\file:password needs to work. In order not to break
1631              compatibility, we still use : as separator, but we try to detect
1632              when it is used for a file name! On windows. */
1633 #ifdef WIN32
1634           if(ptr &&
1635              (ptr == &nextarg[1]) &&
1636              (nextarg[2] == '\\') &&
1637              (isalpha((int)nextarg[0])) )
1638              /* colon in the second column, followed by a backslash, and the
1639                 first character is an alphabetic letter:
1640
1641                 this is a drive letter colon */
1642             ptr = strchr(&nextarg[3], ':'); /* find the next one instead */
1643 #endif
1644           if(ptr) {
1645             /* we have a password too */
1646             *ptr=0;
1647             ptr++;
1648             GetStr(&config->key_passwd, ptr);
1649           }
1650           GetStr(&config->cert, nextarg);
1651           cleanarg(nextarg);
1652         }
1653       }
1654       break;
1655     case 'f':
1656       /* fail hard on errors  */
1657       config->conf ^= CONF_FAILONERROR;
1658       break;
1659     case 'F':
1660       /* "form data" simulation, this is a little advanced so lets do our best
1661          to sort this out slowly and carefully */
1662       if(formparse(nextarg,
1663                    &config->httppost,
1664                    &config->last_post))
1665         return PARAM_BAD_USE;
1666       if(SetHTTPrequest(HTTPREQ_POST, &config->httpreq))
1667         return PARAM_BAD_USE;
1668       break;
1669
1670     case 'g': /* g disables URLglobbing */
1671       config->globoff ^= TRUE;
1672       break;
1673
1674     case 'G': /* HTTP GET */
1675       config->use_httpget = TRUE;
1676       break;
1677
1678     case 'h': /* h for help */
1679       help();
1680       return PARAM_HELP_REQUESTED;
1681     case 'H':
1682       /* A custom header to append to a list */
1683       config->headers = curl_slist_append(config->headers, nextarg);
1684       break;
1685     case 'i':
1686       config->conf ^= CONF_HEADER; /* include the HTTP header as well */
1687       break;
1688     case 'j':
1689       config->cookiesession ^= TRUE;
1690       break;
1691     case 'I':
1692       /*
1693        * This is a bit tricky. We either SET both bits, or we clear both
1694        * bits. Let's not make any other outcomes from this.
1695        */
1696       if((CONF_HEADER|CONF_NOBODY) !=
1697          (config->conf&(CONF_HEADER|CONF_NOBODY)) ) {
1698         /* one of them weren't set, set both */
1699         config->conf |= (CONF_HEADER|CONF_NOBODY);
1700         if(SetHTTPrequest(HTTPREQ_HEAD, &config->httpreq))
1701           return PARAM_BAD_USE;
1702       }
1703       else {
1704         /* both were set, clear both */
1705         config->conf &= ~(CONF_HEADER|CONF_NOBODY);
1706         if(SetHTTPrequest(HTTPREQ_GET, &config->httpreq))
1707           return PARAM_BAD_USE;
1708       }
1709       break;
1710     case 'k': /* allow insecure SSL connects */
1711       config->insecure_ok ^= TRUE;
1712       break;
1713     case 'K': /* parse config file */
1714       parseconfig(nextarg, config);
1715       break;
1716     case 'l':
1717       config->conf ^= CONF_FTPLISTONLY; /* only list the names of the FTP dir */
1718       break;
1719     case 'L':
1720       config->conf ^= CONF_FOLLOWLOCATION; /* Follow Location: HTTP headers */
1721       switch (subletter) {
1722       case 't':
1723         /* Continue to send authentication (user+password) when following
1724          * locations, even when hostname changed */
1725         config->conf ^= CONF_UNRESTRICTED_AUTH;
1726         break;
1727       }
1728       break;
1729     case 'm':
1730       /* specified max time */
1731       if(str2num(&config->timeout, nextarg))
1732         return PARAM_BAD_NUMERIC;
1733       break;
1734     case 'M': /* M for manual, huge help */
1735 #ifdef USE_MANUAL
1736       hugehelp();
1737       return PARAM_HELP_REQUESTED;
1738 #else
1739       fprintf(stderr,
1740               "curl: built-in manual was disabled at build-time!\n");
1741       return PARAM_OPTION_UNKNOWN;
1742 #endif
1743     case 'n':
1744       switch(subletter) {
1745       case 'o': /* CA info PEM file */
1746         /* use .netrc or URL */
1747         config->conf ^= CONF_NETRC_OPT;
1748         break;
1749       default:
1750         /* pick info from .netrc, if this is used for http, curl will
1751            automatically enfore user+password with the request */
1752         config->conf ^= CONF_NETRC;
1753         break;
1754       }
1755       break;
1756     case 'N':
1757       /* disable the output I/O buffering */
1758       config->nobuffer ^= 1;
1759       break;
1760     case 'o':
1761     case 'O':
1762       /* output file */
1763       {
1764         struct getout *url;
1765         if(config->url_out || (config->url_out=config->url_list)) {
1766           /* there's a node here, if it already is filled-in continue to find
1767              an "empty" node */
1768           while(config->url_out && (config->url_out->flags&GETOUT_OUTFILE))
1769             config->url_out = config->url_out->next;
1770         }
1771
1772         /* now there might or might not be an available node to fill in! */
1773
1774         if(config->url_out)
1775           /* existing node */
1776           url = config->url_out;
1777         else
1778           /* there was no free node, create one! */
1779           url=new_getout(config);
1780
1781         if(url) {
1782           /* fill in the outfile */
1783           if('o' == letter)
1784             GetStr(&url->outfile, nextarg);
1785           else {
1786             url->outfile=NULL; /* leave it */
1787             url->flags |= GETOUT_USEREMOTE;
1788           }
1789           url->flags |= GETOUT_OUTFILE;
1790         }
1791       }
1792       break;
1793     case 'P':
1794       /* This makes the FTP sessions use PORT instead of PASV */
1795       /* use <eth0> or <192.168.10.10> style addresses. Anything except
1796          this will make us try to get the "default" address.
1797          NOTE: this is a changed behaviour since the released 4.1!
1798          */
1799       GetStr(&config->ftpport, nextarg);
1800       break;
1801     case 'p':
1802       /* proxy tunnel for non-http protocols */
1803       config->proxytunnel ^= TRUE;
1804       break;
1805
1806     case 'q': /* if used first, already taken care of, we do it like
1807                  this so we don't cause an error! */
1808       break;
1809     case 'Q':
1810       /* QUOTE command to send to FTP server */
1811       switch(nextarg[0]) {
1812       case '-':
1813         /* prefixed with a dash makes it a POST TRANSFER one */
1814         nextarg++;
1815         config->postquote = curl_slist_append(config->postquote, nextarg);
1816         break;
1817       case '+':
1818         /* prefixed with a plus makes it a just-before-transfer one */
1819         nextarg++;
1820         config->prequote = curl_slist_append(config->prequote, nextarg);
1821         break;
1822       default:
1823         config->quote = curl_slist_append(config->quote, nextarg);
1824       }
1825       break;
1826     case 'r':
1827       /* byte range requested */
1828       GetStr(&config->range, nextarg);
1829       break;
1830     case 'R':
1831       /* use remote file's time */
1832       config->remote_time ^= TRUE;
1833       break;
1834     case 's':
1835       /* don't show progress meter, don't show errors : */
1836       config->conf |= (CONF_MUTE|CONF_NOPROGRESS);
1837       config->showerror ^= TRUE; /* toggle off */
1838       break;
1839     case 'S':
1840       /* show errors */
1841       config->showerror ^= TRUE; /* toggle on if used with -s */
1842       break;
1843     case 't':
1844       /* Telnet options */
1845       config->telnet_options =
1846         curl_slist_append(config->telnet_options, nextarg);
1847       break;
1848     case 'T':
1849       /* we are uploading */
1850       {
1851         struct getout *url;
1852         if(config->url_out || (config->url_out=config->url_list)) {
1853           /* there's a node here, if it already is filled-in continue to find
1854              an "empty" node */
1855           while(config->url_out && (config->url_out->flags&GETOUT_UPLOAD))
1856             config->url_out = config->url_out->next;
1857         }
1858
1859         /* now there might or might not be an available node to fill in! */
1860
1861         if(config->url_out)
1862           /* existing node */
1863           url = config->url_out;
1864         else
1865           /* there was no free node, create one! */
1866           url=new_getout(config);
1867
1868         if(url) {
1869           url->flags |= GETOUT_UPLOAD; /* mark -T used */
1870           if(!*nextarg)
1871             url->flags |= GETOUT_NOUPLOAD;
1872           else {
1873             /* "-" equals stdin, but keep the string around for now */
1874             GetStr(&url->infile, nextarg);
1875           }
1876         }
1877       }
1878       break;
1879     case 'u':
1880       /* user:password  */
1881       GetStr(&config->userpwd, nextarg);
1882       cleanarg(nextarg);
1883       checkpasswd("host", &config->userpwd);
1884       break;
1885     case 'U':
1886       /* Proxy user:password  */
1887       GetStr(&config->proxyuserpwd, nextarg);
1888       cleanarg(nextarg);
1889       checkpasswd("proxy", &config->proxyuserpwd);
1890       break;
1891     case 'v':
1892       config->conf ^= CONF_VERBOSE; /* talk a lot */
1893       break;
1894     case 'V':
1895     {
1896       const char **proto;
1897
1898       printf(CURL_ID "%s\n", curl_version());
1899       if (curlinfo->protocols) {
1900         printf("Protocols: ");
1901         for (proto=curlinfo->protocols; *proto; ++proto) {
1902           printf("%s ", *proto);
1903         }
1904         puts(""); /* newline */
1905       }
1906       if(curlinfo->features) {
1907         unsigned int i;
1908         struct feat {
1909           const char *name;
1910           int bitmask;
1911         };
1912         struct feat feats[] = {
1913           {"IPv6", CURL_VERSION_IPV6},
1914           {"krb4", CURL_VERSION_KERBEROS4},
1915           {"SSL",  CURL_VERSION_SSL},
1916           {"libz", CURL_VERSION_LIBZ},
1917           {"NTLM", CURL_VERSION_NTLM},
1918           {"GSS-Negotiate", CURL_VERSION_GSSNEGOTIATE},
1919           {"Debug", CURL_VERSION_DEBUG},
1920           {"AsynchDNS", CURL_VERSION_ASYNCHDNS},
1921           {"SPNEGO", CURL_VERSION_SPNEGO},
1922           {"Largefile", CURL_VERSION_LARGEFILE}
1923         };
1924         printf("Features: ");
1925         for(i=0; i<sizeof(feats)/sizeof(feats[0]); i++) {
1926           if(curlinfo->features & feats[i].bitmask)
1927             printf("%s ", feats[i].name);
1928         }
1929         puts(""); /* newline */
1930       }
1931     }
1932     return PARAM_HELP_REQUESTED;
1933     case 'w':
1934       /* get the output string */
1935       if('@' == *nextarg) {
1936         /* the data begins with a '@' letter, it means that a file name
1937            or - (stdin) follows */
1938         FILE *file;
1939         nextarg++; /* pass the @ */
1940         if(curl_strequal("-", nextarg))
1941           file = stdin;
1942         else 
1943           file = fopen(nextarg, "r");
1944         config->writeout = file2string(file);
1945         if(file && (file != stdin))
1946           fclose(file);
1947       }
1948       else 
1949         GetStr(&config->writeout, nextarg);
1950       break;
1951     case 'x':
1952       /* proxy */
1953       GetStr(&config->proxy, nextarg);
1954       break;
1955     case 'X':
1956       /* set custom request */
1957       GetStr(&config->customrequest, nextarg);
1958       break;
1959     case 'y':
1960       /* low speed time */
1961       if(str2num(&config->low_speed_time, nextarg))
1962         return PARAM_BAD_NUMERIC;
1963       if(!config->low_speed_limit)
1964         config->low_speed_limit = 1;
1965       break;
1966     case 'Y':
1967       /* low speed limit */
1968       if(str2num(&config->low_speed_limit, nextarg))
1969         return PARAM_BAD_NUMERIC;
1970       if(!config->low_speed_time)
1971         config->low_speed_time=30;
1972       break;
1973     case 'z': /* time condition coming up */
1974       switch(*nextarg) {
1975       case '+':
1976         nextarg++;
1977       default:
1978         /* If-Modified-Since: (section 14.28 in RFC2068) */
1979         config->timecond = CURL_TIMECOND_IFMODSINCE;
1980         break;
1981       case '-':
1982         /* If-Unmodified-Since:  (section 14.24 in RFC2068) */
1983         config->timecond = CURL_TIMECOND_IFUNMODSINCE;
1984         nextarg++;
1985         break;
1986       case '=':
1987         /* Last-Modified:  (section 14.29 in RFC2068) */
1988         config->timecond = CURL_TIMECOND_LASTMOD;
1989         nextarg++;
1990         break;
1991       }
1992       now=time(NULL);
1993       config->condtime=curl_getdate(nextarg, &now);
1994       if(-1 == (int)config->condtime) {
1995         /* now let's see if it is a file name to get the time from instead! */
1996         struct stat statbuf;
1997         if(-1 == stat(nextarg, &statbuf)) {
1998           /* failed, remove time condition */
1999           config->timecond = CURL_TIMECOND_NONE;
2000         }
2001         else {
2002           /* pull the time out from the file */
2003           config->condtime = statbuf.st_mtime;
2004         }
2005       }
2006       break;
2007     default: /* unknown flag */
2008       return PARAM_OPTION_UNKNOWN;
2009     }
2010     hit = -1;
2011
2012   } while(!longopt && !singleopt && *++parse && !*usedarg);
2013
2014   return PARAM_OK;
2015 }
2016
2017
2018 static void parseconfig(const char *filename,
2019                         struct Configurable *config)
2020 {
2021   int res;
2022   FILE *file;
2023   char filebuffer[512];
2024   bool usedarg;
2025   char *home;
2026   
2027   if(!filename || !*filename) {
2028     /* NULL or no file name attempts to load .curlrc from the homedir! */
2029
2030 #define CURLRC DOT_CHAR "curlrc"
2031
2032 #ifndef AMIGA
2033     filename = CURLRC;   /* sensible default */
2034     home = homedir();    /* portable homedir finder */
2035     if(home) {
2036       if(strlen(home)<(sizeof(filebuffer)-strlen(CURLRC))) {
2037         snprintf(filebuffer, sizeof(filebuffer),
2038                  "%s%s%s", home, DIR_CHAR, CURLRC);
2039
2040         filename = filebuffer;
2041       }
2042       free(home); /* we've used it, now free it */
2043     }
2044     
2045 # else /* AmigaOS */
2046   /* On AmigaOS all the config files are into env:
2047    */
2048   filename = "ENV:" CURLRC;
2049
2050 #endif
2051   }
2052
2053   if(strcmp(filename,"-"))
2054     file = fopen(filename, "r");
2055   else
2056     file = stdin;
2057   
2058   if(file) {
2059     char *line;
2060     char *aline;
2061     char *option;
2062     char *param;
2063     int lineno=0;
2064     bool alloced_param;
2065
2066 #define isseparator(x) (((x)=='=') || ((x) == ':'))
2067
2068     while (NULL != (aline = my_get_line(file))) {
2069       lineno++;
2070       line = aline;
2071       alloced_param=FALSE;
2072
2073       /* lines with # in the fist column is a comment! */
2074       while(*line && isspace((int)*line))
2075         line++;
2076
2077       switch(*line) {
2078       case '#':
2079       case '/':
2080       case '\r':
2081       case '\n':
2082       case '*':
2083       case '\0':
2084         free(aline);
2085         continue;
2086       }
2087
2088       /* the option keywords starts here */
2089       option = line;
2090       while(*line && !isspace((int)*line) && !isseparator(*line))
2091         line++;
2092       /* ... and has ended here */
2093
2094       if(*line)
2095         *line++=0; /* zero terminate, we have a local copy of the data */
2096
2097 #ifdef DEBUG_CONFIG
2098       fprintf(stderr, "GOT: %s\n", option);
2099 #endif
2100
2101       /* pass spaces and separator(s) */
2102       while(*line && (isspace((int)*line) || isseparator(*line)))
2103         line++;
2104       
2105       /* the parameter starts here (unless quoted) */
2106       if(*line == '\"') {
2107         char *ptr;
2108         /* quoted parameter, do the qoute dance */
2109         line++;
2110         param=strdup(line); /* parameter */
2111         alloced_param=TRUE;
2112
2113         ptr=param;
2114         while(*line && (*line != '\"')) {
2115           if(*line == '\\') {
2116             char out;
2117             line++;
2118
2119             /* default is to output the letter after the backslah */
2120             switch(out = *line) {
2121             case '\0':
2122               continue; /* this'll break out of the loop */
2123             case 't':
2124               out='\t';
2125               break;
2126             case 'n':
2127               out='\n';
2128               break;
2129             case 'r':
2130               out='\r';
2131               break;
2132             case 'v':
2133               out='\v';
2134               break;
2135             }
2136             *ptr++=out;
2137             line++;
2138           }
2139           else
2140             *ptr++=*line++;
2141         }
2142         *ptr=0; /* always zero terminate */
2143
2144       }
2145       else {
2146         param=line; /* parameter starts here */
2147         while(*line && !isspace((int)*line))
2148           line++;
2149         *line=0; /* zero terminate */
2150       }
2151
2152       if (param && !*param) {
2153         /* do this so getparameter can check for required parameters.
2154            Otherwise it always thinks there's a parameter. */
2155         if (alloced_param)
2156           free(param);
2157         param = NULL;
2158       }
2159
2160 #ifdef DEBUG_CONFIG
2161       fprintf(stderr, "PARAM: \"%s\"\n",(param ? param : "(null)"));
2162 #endif
2163       res = getparameter(option, param, &usedarg, config);
2164
2165       if (param && *param && !usedarg)
2166         /* we passed in a parameter that wasn't used! */
2167         res = PARAM_GOT_EXTRA_PARAMETER;
2168
2169       if(res != PARAM_OK) {
2170         /* the help request isn't really an error */
2171         if(!strcmp(filename, "-")) {
2172           filename=(char *)"<stdin>";
2173         }
2174         if(PARAM_HELP_REQUESTED != res) {
2175           const char *reason = param2text(res);
2176           fprintf(stderr, "%s:%d: warning: '%s' %s\n",
2177                   filename, lineno, option, reason);
2178         }
2179       }
2180
2181       if(alloced_param)
2182       {
2183         free(param);
2184         param = NULL;
2185       }
2186
2187       free(aline);
2188     }
2189     if(file != stdin)
2190       fclose(file);
2191   }
2192 }
2193
2194 static void go_sleep(long ms)
2195 {
2196 #ifdef HAVE_POLL
2197   /* portable subsecond "sleep" */
2198   poll((void *)0, 0, ms);
2199 #else
2200   /* systems without poll() need other solutions */
2201
2202 #ifdef WIN32
2203   /* Windows offers a millisecond sleep */
2204   Sleep(ms);
2205 #else
2206   /* Other systems must use select() for this */
2207   struct timeval timeout;
2208
2209   timeout.tv_sec = 0;
2210   timeout.tv_usec = ms * 1000;
2211
2212   select(0, NULL,  NULL, NULL, &timeout);
2213 #endif
2214
2215 #endif
2216 }
2217
2218 struct OutStruct {
2219   char *filename;
2220   FILE *stream;
2221   struct Configurable *config;
2222 };
2223
2224 static int my_fwrite(void *buffer, size_t size, size_t nmemb, void *stream)
2225 {
2226   int rc;
2227   struct OutStruct *out=(struct OutStruct *)stream;
2228   struct Configurable *config = out->config;
2229   if(out && !out->stream) {
2230     /* open file for writing */
2231     out->stream=fopen(out->filename, "wb");
2232     if(!out->stream)
2233       return -1; /* failure */
2234   }
2235
2236   if(config->recvpersecond) {
2237     /*
2238      * We know when we received data the previous time. We know how much data
2239      * we get now. Make sure that this is not faster than we are told to run.
2240      * If we're faster, sleep a while *before* doing the fwrite() here.
2241      */
2242
2243     time_t timediff;
2244     time_t now;
2245     time_t sleep_time;
2246
2247     now = time(NULL);
2248     timediff = now - config->lastrecvtime;
2249     if( size*nmemb > config->recvpersecond*timediff) {
2250       /* figure out how many milliseconds to rest */
2251       sleep_time = (size*nmemb)*1000/config->recvpersecond - timediff*1000;
2252
2253       /*
2254        * Make sure we don't sleep for so long that we trigger the speed limit.
2255        * This won't limit the bandwidth quite the way we've been asked to, but
2256        * at least the transfer has a chance.
2257        */
2258       if (config->low_speed_time > 0)
2259         sleep_time = MIN(sleep_time,(config->low_speed_time * 1000) / 2);
2260
2261       go_sleep (sleep_time);
2262       now = time(NULL);
2263     }
2264     config->lastrecvtime = now;
2265   }
2266
2267   rc = fwrite(buffer, size, nmemb, out->stream);
2268   
2269   if(config->nobuffer)
2270     /* disable output buffering */
2271     fflush(out->stream);
2272   
2273   return rc;
2274 }
2275
2276 struct InStruct {
2277   FILE *stream;
2278   struct Configurable *config;
2279 };
2280
2281 static int my_fread(void *buffer, size_t size, size_t nmemb, void *userp)
2282 {
2283   struct InStruct *in=(struct InStruct *)userp;
2284
2285   struct Configurable *config = in->config;
2286
2287   if(config->sendpersecond) {
2288     /*
2289      * We know when we sent data the previous time. We know how much data
2290      * we sent. Make sure that this was not faster than we are told to run.
2291      * If we're faster, sleep a while *before* doing the fread() here.
2292      * Also, make no larger fread() than should be sent this second!
2293      */
2294
2295     time_t timediff;
2296     time_t now;
2297
2298     now = time(NULL);
2299     timediff = now - config->lastsendtime;
2300     if( config->lastsendsize > config->sendpersecond*timediff) {
2301       /* figure out how many milliseconds to rest */
2302       go_sleep ( config->lastsendsize*1000/config->sendpersecond -
2303                  timediff*1000 );
2304       now = time(NULL);
2305     }
2306     config->lastsendtime = now;
2307
2308     if(size*nmemb > config->sendpersecond) {
2309       /* lower the size to actually read */
2310       nmemb = config->sendpersecond;
2311       size = 1;
2312     }
2313     config->lastsendsize = size*nmemb;    
2314   }
2315
2316
2317   return fread(buffer, size, nmemb, in->stream);
2318 }
2319
2320 struct ProgressData {
2321   int calls;
2322   double prev;
2323   int width;
2324   FILE *out; /* where to write everything to */
2325   curl_off_t initial_size;
2326 };
2327
2328 static int myprogress (void *clientp,
2329                        double dltotal,
2330                        double dlnow,
2331                        double ultotal,
2332                        double ulnow)
2333 {
2334   /* The original progress-bar source code was written for curl by Lars Aas,
2335      and this new edition inherits some of his concepts. */
2336   
2337   char line[256];
2338   char outline[256];
2339   char format[40];
2340   double frac;
2341   double percent;
2342   int barwidth;
2343   int num;
2344   int i;
2345
2346   struct ProgressData *bar = (struct ProgressData *)clientp;
2347   double total = dltotal + ultotal + bar->initial_size;
2348   double point = dlnow + ulnow + bar->initial_size; /* we've come this far */
2349
2350   bar->calls++; /* simply count invokes */
2351
2352   if(total < 1) {
2353     int prevblock = (int)bar->prev / 1024;
2354     int thisblock = (int)point / 1024;
2355     while ( thisblock > prevblock ) {
2356       fprintf( bar->out, "#" );
2357       prevblock++;
2358     }
2359   }
2360   else {
2361     frac = point / total;
2362     percent = frac * 100.0f;
2363     barwidth = bar->width - 7;
2364     num = (int) (((double)barwidth) * frac);
2365     i = 0;
2366     for ( i = 0; i < num; i++ ) {
2367       line[i] = '#';
2368     }
2369     line[i] = '\0';
2370     sprintf( format, "%%-%ds %%5.1f%%%%", barwidth );
2371     sprintf( outline, format, line, percent );
2372     fprintf( bar->out, "\r%s", outline );
2373   }
2374   fflush(bar->out);
2375   bar->prev = point;
2376
2377   return 0;
2378 }
2379
2380 static
2381 void progressbarinit(struct ProgressData *bar,
2382                      struct Configurable *config)
2383 {
2384 #ifdef __EMX__
2385   /* 20000318 mgs */
2386   int scr_size [2];
2387 #endif
2388   char *colp;
2389
2390   memset(bar, 0, sizeof(struct ProgressData));
2391
2392   /* pass this through to progress function so
2393    * it can display progress towards total file
2394    * not just the part that's left. (21-may-03, dbyron) */
2395   if (config->use_resume)
2396     bar->initial_size = config->resume_from;
2397
2398 /* TODO: get terminal width through ansi escapes or something similar.
2399          try to update width when xterm is resized... - 19990617 larsa */
2400 #ifndef __EMX__
2401   /* 20000318 mgs
2402    * OS/2 users most likely won't have this env var set, and besides that
2403    * we're using our own way to determine screen width */
2404   colp = curl_getenv("COLUMNS");
2405   if (colp != NULL) {
2406     bar->width = atoi(colp);
2407     curl_free(colp);
2408   }
2409   else
2410     bar->width = 79;
2411 #else
2412   /* 20000318 mgs
2413    * We use this emx library call to get the screen width, and subtract
2414    * one from what we got in order to avoid a problem with the cursor
2415    * advancing to the next line if we print a string that is as long as
2416    * the screen is wide. */
2417  
2418   _scrsize(scr_size);
2419   bar->width = scr_size[0] - 1;
2420 #endif
2421
2422   bar->out = config->errors;
2423 }
2424
2425 static
2426 void dump(const char *text,
2427           FILE *stream, unsigned char *ptr, size_t size,
2428           bool nohex)
2429 {
2430   size_t i;
2431   size_t c;
2432
2433   unsigned int width=0x10;
2434
2435   if(nohex)
2436     /* without the hex output, we can fit more on screen */
2437     width = 0x40;
2438
2439   fprintf(stream, "%s, %zd bytes (0x%zx)\n", text, size, size);
2440
2441   for(i=0; i<size; i+= width) {
2442
2443     fprintf(stream, "%04zx: ", i);
2444
2445     if(!nohex) {
2446       /* hex not disabled, show it */
2447       for(c = 0; c < width; c++)
2448         if(i+c < size)
2449           fprintf(stream, "%02x ", ptr[i+c]);
2450         else
2451           fputs("   ", stream);
2452     }
2453
2454     for(c = 0; (c < width) && (i+c < size); c++) {
2455       /* check for 0D0A; if found, skip past and start a new line of output */
2456       if (nohex && (i+c+1 < size) && ptr[i+c]==0x0D && ptr[i+c+1]==0x0A) {
2457         i+=(c+2-width);
2458         break;
2459       }
2460       fprintf(stream, "%c",
2461               (ptr[i+c]>=0x20) && (ptr[i+c]<0x80)?ptr[i+c]:'.');
2462       /* check again for 0D0A, to avoid an extra \n if it's at width */
2463       if (nohex && (i+c+2 < size) && ptr[i+c+1]==0x0D && ptr[i+c+2]==0x0A) {
2464         i+=(c+3-width);
2465         break;
2466       }
2467     }
2468     fputc('\n', stream); /* newline */
2469   }
2470   fflush(stream);
2471 }
2472
2473 static
2474 int my_trace(CURL *handle, curl_infotype type,
2475              unsigned char *data, size_t size,
2476              void *userp)
2477 {
2478   struct Configurable *config = (struct Configurable *)userp;
2479   FILE *output=config->errors;
2480   const char *text;
2481
2482   (void)handle; /* prevent compiler warning */
2483
2484   if(!config->trace_stream) {
2485     /* open for append */
2486     if(curl_strequal("-", config->trace_dump))
2487       config->trace_stream = stdout;
2488     else {
2489       config->trace_stream = fopen(config->trace_dump, "w");
2490       config->trace_fopened = TRUE;
2491     }
2492   }
2493
2494   if(config->trace_stream)
2495     output = config->trace_stream;
2496
2497   switch (type) {
2498   case CURLINFO_TEXT:
2499     fprintf(output, "== Info: %s", data);
2500   default: /* in case a new one is introduced to shock us */
2501     return 0;
2502
2503   case CURLINFO_HEADER_OUT:
2504     text = "=> Send header";
2505     break;
2506   case CURLINFO_DATA_OUT:
2507     text = "=> Send data";
2508     break;
2509   case CURLINFO_HEADER_IN:
2510     text = "<= Recv header";
2511     break;
2512   case CURLINFO_DATA_IN:
2513     text = "<= Recv data";
2514     break;
2515   }
2516
2517   dump(text, output, data, size, config->trace_ascii);
2518   return 0;
2519 }
2520
2521 static void free_config_fields(struct Configurable *config)
2522 {
2523   if(config->random_file)
2524     free(config->random_file);
2525   if(config->egd_file)
2526     free(config->egd_file);
2527   if(config->userpwd)
2528     free(config->userpwd);
2529   if(config->postfields)
2530     free(config->postfields);
2531   if(config->proxy)
2532     free(config->proxy);
2533   if(config->proxyuserpwd)
2534     free(config->proxyuserpwd);
2535   if(config->cookie)
2536     free(config->cookie);
2537   if(config->cookiefile)
2538     free(config->cookiefile);
2539   if(config->krb4level)
2540     free(config->krb4level);
2541   if(config->headerfile)
2542     free(config->headerfile);
2543   if(config->ftpport)
2544     free(config->ftpport);
2545   if(config->range)
2546     free(config->range);
2547   if(config->customrequest)
2548     free(config->customrequest);
2549   if(config->writeout)
2550     free(config->writeout);
2551   if(config->httppost)
2552     curl_formfree(config->httppost);
2553   if(config->cacert)
2554     free(config->cacert);
2555   if(config->capath)
2556     free(config->capath);
2557   if(config->cookiejar)
2558     free(config->cookiejar);
2559
2560   curl_slist_free_all(config->quote); /* checks for config->quote == NULL */
2561   curl_slist_free_all(config->postquote); /*  */
2562   curl_slist_free_all(config->headers); /*  */
2563 }
2564
2565 #if defined(WIN32) && !defined(__CYGWIN32__)
2566
2567 /* Function to find CACert bundle on a Win32 platform using SearchPath.
2568  * (SearchPath is defined in windows.h, which is #included into libcurl)
2569  * (Use the ASCII version instead of the unicode one!)
2570  * The order of the directories it searches is:
2571  *  1. application's directory
2572  *  2. current working directory
2573  *  3. Windows System directory (e.g. C:\windows\system32)
2574  *  4. Windows Directory (e.g. C:\windows)
2575  *  5. all directories along %PATH%
2576  */
2577 static void FindWin32CACert(struct Configurable *config, 
2578                             const char *bundle_file)
2579 {
2580   /* only check for cert file if "we" support SSL */
2581   if(curlinfo->features & CURL_VERSION_SSL) {
2582     DWORD buflen;
2583     char *ptr = NULL;
2584     char *retval = (char *) malloc(sizeof (TCHAR) * (MAX_PATH + 1));
2585     if (!retval)
2586       return;
2587     retval[0] = '\0';
2588     buflen = SearchPathA(NULL, bundle_file, NULL, MAX_PATH+2, retval, &ptr);
2589     if (buflen > 0) {
2590       GetStr(&config->cacert, retval);
2591     }
2592     free(retval);
2593   }
2594 }
2595
2596 #endif
2597
2598 static int 
2599 operate(struct Configurable *config, int argc, char *argv[])
2600 {
2601   char errorbuffer[CURL_ERROR_SIZE];
2602   char useragent[128]; /* buah, we don't want a larger default user agent */
2603   struct ProgressData progressbar;
2604   struct getout *urlnode;
2605   struct getout *nextnode;
2606
2607   struct OutStruct outs;
2608   struct OutStruct heads;
2609   struct InStruct input;
2610
2611   char *url = NULL;
2612
2613   URLGlob *urls=NULL;
2614   URLGlob *inglob=NULL;
2615   int urlnum;
2616   int infilenum;
2617   char *outfiles;
2618   char *infiles; /* might a glob pattern */
2619   char *uploadfile=NULL; /* a single file, never a glob */
2620
2621   int separator = 0;
2622   
2623   FILE *infd = stdin;
2624   bool infdfopen;
2625   FILE *headerfilep = NULL;
2626   char *urlbuffer=NULL;
2627   curl_off_t uploadfilesize; /* -1 means unknown */
2628   bool stillflags=TRUE;
2629
2630   bool allocuseragent=FALSE;
2631
2632   char *httpgetfields=NULL;
2633
2634   CURL *curl;
2635   int res = 0;
2636   int i;
2637   int up; /* upload file counter within a single upload glob */
2638
2639   char *env;
2640 #ifdef CURLDEBUG
2641   /* this sends all memory debug messages to a logfile named memdump */
2642   env = curl_getenv("CURL_MEMDEBUG");
2643   if(env) {
2644     curl_free(env);
2645     curl_memdebug("memdump");
2646   }
2647   env = curl_getenv("CURL_MEMLIMIT");
2648   if(env) {
2649     curl_memlimit(atoi(env));
2650     curl_free(env);
2651   }
2652 #endif
2653
2654   memset(&outs,0,sizeof(outs));
2655
2656   /* we get libcurl info right away */
2657   curlinfo = curl_version_info(CURLVERSION_NOW);
2658
2659   errorbuffer[0]=0; /* prevent junk from being output */
2660
2661   /* inits */
2662   if (main_init() != CURLE_OK) {
2663     helpf("error initializing curl library\n");
2664     return CURLE_FAILED_INIT;
2665   }
2666   config->showerror=TRUE;
2667   config->conf=CONF_DEFAULT;
2668   config->use_httpget=FALSE;
2669   config->create_dirs=FALSE;
2670
2671   if(argc>1 &&
2672      (!curl_strnequal("--", argv[1], 2) && (argv[1][0] == '-')) &&
2673      strchr(argv[1], 'q')) {
2674     /*
2675      * The first flag, that is not a verbose name, but a shortname
2676      * and it includes the 'q' flag!
2677      */
2678     ;
2679   }
2680   else {
2681     parseconfig(NULL, config);
2682   }
2683
2684   if ((argc < 2)  && !config->url_list) {
2685     helpf(NULL);
2686     return CURLE_FAILED_INIT;
2687   }
2688
2689   /* Parse options */
2690   for (i = 1; i < argc; i++) {
2691     if(stillflags &&
2692        ('-' == argv[i][0])) {
2693       char *nextarg;
2694       bool passarg;
2695       char *origopt=argv[i];
2696       
2697       char *flag = argv[i];
2698
2699       if(curl_strequal("--", argv[i]))
2700         /* this indicates the end of the flags and thus enables the
2701            following (URL) argument to start with -. */
2702         stillflags=FALSE;
2703       else {
2704         nextarg= (i < argc - 1)? argv[i+1]: NULL;
2705
2706         res = getparameter(flag, nextarg, &passarg, config);
2707         if(res) {
2708           const char *reason = param2text(res);
2709           if(res != PARAM_HELP_REQUESTED)
2710             helpf("option %s: %s\n", origopt, reason);
2711           clean_getout(config);
2712           return CURLE_FAILED_INIT;
2713         }
2714
2715         if(passarg) /* we're supposed to skip this */
2716           i++;
2717       }
2718     }
2719     else {
2720       bool used;
2721       /* just add the URL please */
2722       res = getparameter((char *)"--url", argv[i], &used, config);
2723       if(res)
2724         return res;
2725     }
2726   }
2727
2728   if(!config->url_list || !config->url_list->url) {
2729     clean_getout(config);
2730     helpf("no URL specified!\n");
2731     return CURLE_FAILED_INIT;
2732   }
2733   if(NULL == config->useragent) {
2734     /* set non-zero default values: */
2735     snprintf(useragent, sizeof(useragent),
2736              CURL_NAME "/" CURL_VERSION " (" OS ") " "%s", curl_version());
2737     config->useragent= useragent;
2738   }
2739   else
2740     allocuseragent = TRUE;
2741
2742   /* On WIN32 (non-cygwin), we can't set the path to curl-ca-bundle.crt
2743    * at compile time. So we look here for the file in two ways:
2744    * 1: look at the environment variable CURL_CA_BUNDLE for a path
2745    * 2: if #1 isn't found, use the windows API function SearchPath()
2746    *    to find it along the app's path (includes app's dir and CWD)
2747    *
2748    * We support the environment variable thing for non-Windows platforms
2749    * too. Just for the sake of it.
2750    */
2751   if (!config->cacert &&
2752       !config->capath &&
2753       !config->insecure_ok) {
2754     env = curl_getenv("CURL_CA_BUNDLE");
2755     if(env) {
2756       GetStr(&config->cacert, env);
2757       curl_free(env);
2758     }
2759 #if defined(WIN32) && !defined(__CYGWIN32__)
2760     else
2761       FindWin32CACert(config, "curl-ca-bundle.crt");
2762 #endif
2763   }
2764
2765   if (config->postfields) {
2766     if (config->use_httpget) {
2767       /* Use the postfields data for a http get */
2768       httpgetfields = strdup(config->postfields);
2769       free(config->postfields);
2770       config->postfields = NULL;
2771       if(SetHTTPrequest((config->conf&CONF_NOBODY?HTTPREQ_HEAD:HTTPREQ_GET),
2772                         &config->httpreq)) {
2773         free(httpgetfields);
2774         return PARAM_BAD_USE;
2775       }
2776     }
2777     else {
2778       if(SetHTTPrequest(HTTPREQ_SIMPLEPOST, &config->httpreq))
2779         return PARAM_BAD_USE;
2780     }
2781   }
2782
2783   /*
2784    * Get a curl handle to use for all forthcoming curl transfers.  Cleanup
2785    * when all transfers are done.
2786    */
2787   curl = curl_easy_init();
2788   if(!curl) {
2789     clean_getout(config);
2790     return CURLE_FAILED_INIT;
2791   }
2792
2793   /* After this point, we should call curl_easy_cleanup() if we decide to bail
2794    * out from this function! */
2795
2796   urlnode = config->url_list;
2797
2798   if(config->headerfile) {
2799     /* open file for output: */
2800     if(strcmp(config->headerfile,"-")) {
2801       heads.filename = config->headerfile;
2802       headerfilep=NULL;
2803     }
2804     else
2805       headerfilep=stdout;
2806     heads.stream = headerfilep;
2807     heads.config = config;
2808   }
2809
2810   /* loop through the list of given URLs */
2811   while(urlnode && !res) {
2812     char *dourl;
2813
2814     /* get the full URL (it might be NULL) */
2815     dourl=urlnode->url;
2816
2817     url = dourl;
2818
2819     if(NULL == url) {
2820       /* This node had no URL, skip it and continue to the next */
2821       if(urlnode->outfile)
2822         free(urlnode->outfile);
2823     
2824       /* move on to the next URL */
2825       nextnode=urlnode->next;
2826       free(urlnode); /* free the node */
2827       urlnode = nextnode;
2828       continue; /* next please */
2829     }
2830
2831     /* default output stream is stdout */
2832     outs.stream = stdout;
2833     outs.config = config;
2834
2835     /* save outfile pattern before expansion */
2836     outfiles = urlnode->outfile?strdup(urlnode->outfile):NULL;
2837
2838     infiles = urlnode->infile;
2839
2840     if(!config->globoff && infiles) {
2841       /* Unless explicitly shut off */
2842       res = glob_url(&inglob, infiles, &infilenum,
2843                      config->showerror?
2844                      (config->errors?config->errors:stderr):NULL);
2845       if(res != CURLE_OK) {
2846         clean_getout(config);
2847         break;
2848       }
2849     }
2850
2851     /* Here's the loop for uploading multiple files within the same
2852        single globbed string. If no upload, we enter the loop once anyway. */
2853     for(up = 0;
2854         (!up && !infiles) ||
2855           (uploadfile = inglob?
2856            glob_next_url(inglob):
2857            (!up?strdup(infiles):NULL));
2858         up++) {
2859       uploadfilesize=-1;
2860
2861       if(!config->globoff) {
2862         /* Unless explicitly shut off, we expand '{...}' and '[...]'
2863            expressions and return total number of URLs in pattern set */
2864         res = glob_url(&urls, dourl, &urlnum,
2865                        config->showerror?
2866                        (config->errors?config->errors:stderr):NULL);
2867         if(res != CURLE_OK) {
2868           break;
2869         }
2870       }
2871       else
2872         urlnum = 1; /* without globbing, this is a single URL */
2873
2874       /* if multiple files extracted to stdout, insert separators! */
2875       separator= ((!outfiles || curl_strequal(outfiles, "-")) && urlnum > 1);
2876
2877       /* Here's looping around each globbed URL */
2878       for(i = 0;
2879           (url = urls?glob_next_url(urls):(i?NULL:strdup(url)));
2880           i++) {
2881         char *outfile;
2882         outfile = outfiles?strdup(outfiles):NULL;
2883         
2884         if((urlnode->flags&GETOUT_USEREMOTE) ||
2885            (outfile && !curl_strequal("-", outfile)) ) {
2886           
2887           /* 
2888            * We have specified a file name to store the result in, or we have
2889            * decided we want to use the remote file name.
2890            */
2891       
2892           if(!outfile) {
2893             /* Find and get the remote file name */
2894             char * pc =strstr(url, "://");
2895             if(pc)
2896               pc+=3;
2897             else
2898               pc=url;
2899             pc = strrchr(pc, '/');
2900
2901             if(pc) {
2902               /* duplicate the string beyond the slash */
2903               pc++;
2904               outfile = *pc ? strdup(pc): NULL;
2905             }
2906             if(!outfile || !*outfile) {
2907               helpf("Remote file name has no length!\n");
2908               res = CURLE_WRITE_ERROR;
2909               free(url);
2910               break;
2911             }
2912 #if defined(__DJGPP__)
2913             {
2914               /* This is for DOS, and then we do some major replacing of 
2915                  bad characters in the file name before using it */
2916               char *file1=xmalloc(PATH_MAX);
2917               strcpy(file1, msdosify(outfile));
2918               strcpy(outfile, rename_if_dos_device_name(file1));
2919               xfree(file1);
2920             }
2921 #endif /* __DJGPP__ */
2922           }
2923           else if(urls) {
2924             /* fill '#1' ... '#9' terms from URL pattern */
2925             char *storefile = outfile;
2926             outfile = glob_match_url(storefile, urls);
2927             free(storefile);
2928             if(!outfile) {
2929               /* bad globbing */
2930               fprintf(stderr, "bad output glob!\n");
2931               free(url);
2932               res = CURLE_FAILED_INIT;
2933               break;
2934             }
2935           }
2936           
2937           /* Create the directory hierarchy, if not pre-existant to a multiple
2938              file output call */
2939         
2940           if(config->create_dirs)
2941             if (-1 == create_dir_hierarchy(outfile)) {
2942               return CURLE_WRITE_ERROR;
2943             }
2944           
2945           if(config->resume_from_current) {
2946             /* We're told to continue from where we are now. Get the
2947                size of the file as it is now and open it for append instead */
2948             
2949             struct stat fileinfo;
2950
2951             /*VMS?? -- Danger, the filesize is only valid for stream files */
2952             if(0 == stat(outfile, &fileinfo))
2953               /* set offset to current file size: */
2954               config->resume_from = fileinfo.st_size;
2955             else
2956               /* let offset be 0 */
2957               config->resume_from = 0;
2958           }
2959         
2960           outs.filename = outfile;
2961
2962           if(config->resume_from) {
2963             /* open file for output: */
2964             outs.stream=(FILE *) fopen(outfile, config->resume_from?"ab":"wb");
2965             if (!outs.stream) {
2966               helpf("Can't open '%s'!\n", outfile);
2967               return CURLE_WRITE_ERROR;
2968             }
2969           }
2970           else {
2971             outs.stream = NULL; /* open when needed */
2972           }
2973         }
2974         infdfopen=FALSE;
2975         if(uploadfile && !curl_strequal(uploadfile, "-")) {
2976           /*
2977            * We have specified a file to upload and it isn't "-".
2978            */
2979           struct stat fileinfo;
2980
2981           /* If no file name part is given in the URL, we add this file name */
2982           char *ptr=strstr(url, "://");
2983           if(ptr)
2984             ptr+=3;
2985           else
2986             ptr=url;
2987           ptr = strrchr(ptr, '/');
2988           if(!ptr || !strlen(++ptr)) {
2989             /* The URL has no file name part, add the local file name. In order
2990                to be able to do so, we have to create a new URL in another
2991                buffer.*/
2992
2993             /* We only want the part of the local path that is on the right
2994                side of the rightmost slash and backslash. */
2995             char *filep = strrchr(uploadfile, '/');
2996             char *file2 = strrchr(filep?filep:uploadfile, '\\');
2997
2998             if(file2)
2999               filep = file2+1;
3000             else if(filep)
3001               filep++;
3002             else
3003               filep = uploadfile;
3004
3005             /* URL encode the file name */
3006             filep = curl_escape(filep, 0 /* use strlen */);
3007
3008             if(filep) {
3009
3010               urlbuffer=(char *)malloc(strlen(url) + strlen(filep) + 3);
3011               if(!urlbuffer) {
3012                 helpf("out of memory\n");
3013                 return CURLE_OUT_OF_MEMORY;
3014               }
3015               if(ptr)
3016                 /* there is a trailing slash on the URL */
3017                 sprintf(urlbuffer, "%s%s", url, filep);
3018               else
3019                 /* thers is no trailing slash on the URL */
3020                 sprintf(urlbuffer, "%s/%s", url, filep);
3021             
3022               curl_free(filep);
3023
3024               free(url);
3025               url = urlbuffer; /* use our new URL instead! */
3026             }
3027           }
3028 /*VMS??-- Reading binary from files can be a problem... */
3029 /*VMS??   Only FIXED, VAR etc WITHOUT implied CC will work */
3030 /*VMS??   Others need a \n appended to a line */
3031 /*VMS??-- Stat gives a size but this is UNRELIABLE in VMS */
3032 /*VMS??   As a f.e. a fixed file with implied CC needs to have a byte added */
3033 /*VMS??   for every record processed, this can by derived from Filesize & recordsize */
3034 /*VMS??   for VARiable record files the records need to be counted! */
3035 /*VMS??   for every record add 1 for linefeed and subtract 2 for the record header */
3036 /*VMS??   for VARIABLE header files only the bare record data needs to be considered with one appended if implied CC */
3037
3038           infd=(FILE *) fopen(uploadfile, "rb");
3039           if (!infd || stat(uploadfile, &fileinfo)) {
3040             helpf("Can't open '%s'!\n", uploadfile);
3041             return CURLE_READ_ERROR;
3042           }
3043           infdfopen=TRUE;
3044           uploadfilesize=fileinfo.st_size;
3045       
3046         }
3047         else if(uploadfile && curl_strequal(uploadfile, "-")) {
3048           infd = stdin;
3049         }
3050
3051         if(uploadfile && config->resume_from_current)
3052           config->resume_from = -1; /* -1 will then force get-it-yourself */
3053
3054         if(outs.stream && isatty(fileno(outs.stream)))
3055           /* we send the output to a tty, therefore we switch off the progress
3056              meter */
3057           config->conf |= CONF_NOPROGRESS;
3058
3059         if (urlnum > 1 && !(config->conf&CONF_MUTE)) {
3060           fprintf(stderr, "\n[%d/%d]: %s --> %s\n",
3061                   i+1, urlnum, url, outfile ? outfile : "<stdout>");
3062           if (separator)
3063             printf("%s%s\n", CURLseparator, url);
3064         }
3065         if (httpgetfields) {
3066           /* Find out whether the url contains a file name */
3067           const char *pc =strstr(url, "://");
3068           char sep='?';
3069           if(pc)
3070             pc+=3;
3071           else
3072             pc=url;
3073
3074           pc = strrchr(pc, '/'); /* check for a slash */
3075
3076           if(pc) {
3077             /* there is a slash present in the URL */
3078
3079             if(strchr(pc, '?'))
3080               /* Ouch, there's already a question mark in the URL string, we
3081                  then append the data with an ampersand separator instead! */
3082               sep='&';
3083           }
3084           /*
3085            * Then append ? followed by the get fields to the url.
3086            */
3087           urlbuffer=(char *)malloc(strlen(url) + strlen(httpgetfields) + 2);
3088           if(!urlbuffer) {
3089             helpf("out of memory\n");
3090             return CURLE_OUT_OF_MEMORY;
3091           }
3092           if (pc)
3093             sprintf(urlbuffer, "%s%c%s", url, sep, httpgetfields);
3094           else
3095             /* Append  / before the ? to create a well-formed url
3096                if the url contains a hostname only
3097             */
3098             sprintf(urlbuffer, "%s/?%s", url, httpgetfields);
3099  
3100           free(url); /* free previous URL */
3101           url = urlbuffer; /* use our new URL instead! */
3102         }
3103
3104         if(!config->errors)
3105           config->errors = stderr;
3106
3107 #ifdef O_BINARY
3108         if(!outfile && !(config->conf & CONF_GETTEXT)) {
3109           /* We get the output to stdout and we have not got the ASCII/text flag,
3110              then set stdout to be binary */
3111           setmode( fileno(stdout), O_BINARY );
3112         }
3113 #endif
3114
3115         curl_easy_setopt(curl, CURLOPT_SSLENGINE, config->engine);
3116         curl_easy_setopt(curl, CURLOPT_SSLENGINE_DEFAULT, 1);
3117
3118         /* where to store */
3119         curl_easy_setopt(curl, CURLOPT_WRITEDATA, (FILE *)&outs);
3120         /* what call to write */
3121         curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_fwrite);
3122
3123         /* for uploads */
3124         input.stream = infd;
3125         input.config = config;
3126         curl_easy_setopt(curl, CURLOPT_READDATA, &input);
3127         /* what call to read */
3128         curl_easy_setopt(curl, CURLOPT_READFUNCTION, my_fread);
3129
3130         if(config->recvpersecond) {
3131           /* tell libcurl to use a smaller sized buffer as it allows us to
3132              make better sleeps! 7.9.9 stuff! */
3133           curl_easy_setopt(curl, CURLOPT_BUFFERSIZE, config->recvpersecond);
3134         }
3135
3136         /* size of uploaded file: */
3137         curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, uploadfilesize);
3138         curl_easy_setopt(curl, CURLOPT_URL, url);     /* what to fetch */
3139         curl_easy_setopt(curl, CURLOPT_PROXY, config->proxy); /* proxy to use */
3140         curl_easy_setopt(curl, CURLOPT_HEADER, config->conf&CONF_HEADER);
3141         curl_easy_setopt(curl, CURLOPT_NOPROGRESS, config->conf&CONF_NOPROGRESS);
3142         curl_easy_setopt(curl, CURLOPT_NOBODY, config->conf&CONF_NOBODY);
3143         curl_easy_setopt(curl, CURLOPT_FAILONERROR,
3144                          config->conf&CONF_FAILONERROR);
3145         curl_easy_setopt(curl, CURLOPT_UPLOAD, uploadfile?TRUE:FALSE);
3146         curl_easy_setopt(curl, CURLOPT_FTPLISTONLY,
3147                          config->conf&CONF_FTPLISTONLY);
3148         curl_easy_setopt(curl, CURLOPT_FTPAPPEND, config->conf&CONF_FTPAPPEND);
3149
3150         if (config->conf&CONF_NETRC_OPT)
3151           curl_easy_setopt(curl, CURLOPT_NETRC, CURL_NETRC_OPTIONAL);
3152         else if (config->conf&CONF_NETRC)
3153           curl_easy_setopt(curl, CURLOPT_NETRC, CURL_NETRC_REQUIRED);
3154         else
3155           curl_easy_setopt(curl, CURLOPT_NETRC, CURL_NETRC_IGNORED);
3156
3157         curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION,
3158                          config->conf&CONF_FOLLOWLOCATION);
3159         curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH,
3160                          config->conf&CONF_UNRESTRICTED_AUTH);
3161         curl_easy_setopt(curl, CURLOPT_TRANSFERTEXT, config->conf&CONF_GETTEXT);
3162         curl_easy_setopt(curl, CURLOPT_USERPWD, config->userpwd);
3163         curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, config->proxyuserpwd);
3164         curl_easy_setopt(curl, CURLOPT_RANGE, config->range);
3165         curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errorbuffer);
3166         curl_easy_setopt(curl, CURLOPT_TIMEOUT, config->timeout);
3167         curl_easy_setopt(curl, CURLOPT_POSTFIELDS, config->postfields);
3168
3169         /* new in libcurl 7.2: */
3170         curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, config->postfieldsize);
3171         
3172         curl_easy_setopt(curl, CURLOPT_REFERER, config->referer);
3173         curl_easy_setopt(curl, CURLOPT_AUTOREFERER,
3174                          config->conf&CONF_AUTO_REFERER);
3175         curl_easy_setopt(curl, CURLOPT_USERAGENT, config->useragent);
3176         curl_easy_setopt(curl, CURLOPT_FTPPORT, config->ftpport);
3177         curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, config->low_speed_limit);
3178         curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, config->low_speed_time);
3179         curl_easy_setopt(curl, CURLOPT_RESUME_FROM_LARGE,
3180                          config->use_resume?config->resume_from:0);
3181         curl_easy_setopt(curl, CURLOPT_COOKIE, config->cookie);
3182         curl_easy_setopt(curl, CURLOPT_HTTPHEADER, config->headers);
3183         curl_easy_setopt(curl, CURLOPT_HTTPPOST, config->httppost);
3184         curl_easy_setopt(curl, CURLOPT_SSLCERT, config->cert);
3185         curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE, config->cert_type);
3186         curl_easy_setopt(curl, CURLOPT_SSLKEY, config->key);
3187         curl_easy_setopt(curl, CURLOPT_SSLKEYTYPE, config->key_type);
3188         curl_easy_setopt(curl, CURLOPT_SSLKEYPASSWD, config->key_passwd);
3189
3190         /* default to strict verifyhost */
3191         curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2);
3192         if(config->cacert || config->capath) {
3193           if (config->cacert)
3194             curl_easy_setopt(curl, CURLOPT_CAINFO, config->cacert);
3195
3196           if (config->capath)
3197             curl_easy_setopt(curl, CURLOPT_CAPATH, config->capath);
3198           curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, TRUE);
3199         }
3200         if(config->insecure_ok) {
3201           /* new stuff needed for libcurl 7.10 */
3202           curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, FALSE);
3203           curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1);
3204         }
3205       
3206         if((config->conf&CONF_NOBODY) ||
3207            config->remote_time) {
3208           /* no body or use remote time */
3209           curl_easy_setopt(curl, CURLOPT_FILETIME, TRUE);
3210         }
3211       
3212         if (config->maxredirs) 
3213           curl_easy_setopt(curl, CURLOPT_MAXREDIRS, config->maxredirs); 
3214         else 
3215           curl_easy_setopt(curl, CURLOPT_MAXREDIRS, DEFAULT_MAXREDIRS); 
3216  
3217         curl_easy_setopt(curl, CURLOPT_CRLF, config->crlf);
3218         curl_easy_setopt(curl, CURLOPT_QUOTE, config->quote);
3219         curl_easy_setopt(curl, CURLOPT_POSTQUOTE, config->postquote);
3220         curl_easy_setopt(curl, CURLOPT_WRITEHEADER,
3221                          config->headerfile?&heads:NULL);
3222         curl_easy_setopt(curl, CURLOPT_COOKIEFILE, config->cookiefile);
3223         /* cookie jar was added in 7.9 */
3224         if(config->cookiejar)
3225           curl_easy_setopt(curl, CURLOPT_COOKIEJAR, config->cookiejar);
3226         /* cookie session added in 7.9.7 */
3227         curl_easy_setopt(curl, CURLOPT_COOKIESESSION, config->cookiesession);
3228
3229         curl_easy_setopt(curl, CURLOPT_SSLVERSION, config->ssl_version);
3230         curl_easy_setopt(curl, CURLOPT_TIMECONDITION, config->timecond);
3231         curl_easy_setopt(curl, CURLOPT_TIMEVALUE, config->condtime);
3232         curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, config->customrequest);
3233         curl_easy_setopt(curl, CURLOPT_STDERR, config->errors);
3234       
3235         /* three new ones in libcurl 7.3: */
3236         curl_easy_setopt(curl, CURLOPT_HTTPPROXYTUNNEL, config->proxytunnel);
3237         curl_easy_setopt(curl, CURLOPT_INTERFACE, config->iface);
3238         curl_easy_setopt(curl, CURLOPT_KRB4LEVEL, config->krb4level);
3239       
3240         progressbarinit(&progressbar, config);
3241         if((config->progressmode == CURL_PROGRESS_BAR) &&
3242            !(config->conf&(CONF_NOPROGRESS|CONF_MUTE))) {
3243           /* we want the alternative style, then we have to implement it
3244              ourselves! */
3245           curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, myprogress);
3246           curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, &progressbar);
3247         }
3248         
3249         /* new in libcurl 7.6.2: */
3250         curl_easy_setopt(curl, CURLOPT_TELNETOPTIONS, config->telnet_options);
3251
3252         /* new in libcurl 7.7: */
3253         curl_easy_setopt(curl, CURLOPT_RANDOM_FILE, config->random_file);
3254         curl_easy_setopt(curl, CURLOPT_EGDSOCKET, config->egd_file);
3255         curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, config->connecttimeout);
3256
3257         if(config->cipher_list)
3258           curl_easy_setopt(curl, CURLOPT_SSL_CIPHER_LIST, config->cipher_list);
3259
3260         if(config->httpversion)
3261           curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, config->httpversion);
3262
3263         /* new in libcurl 7.9.2: */
3264         if(config->disable_epsv)
3265           /* disable it */
3266           curl_easy_setopt(curl, CURLOPT_FTP_USE_EPSV, FALSE);
3267
3268         /* new in libcurl 7.10.5 */
3269         if(config->disable_eprt)
3270           /* disable it */
3271           curl_easy_setopt(curl, CURLOPT_FTP_USE_EPRT, FALSE);
3272
3273         /* new in libcurl 7.10.6 (default is Basic) */
3274         if(config->authtype)
3275           curl_easy_setopt(curl, CURLOPT_HTTPAUTH, config->authtype);
3276       
3277         /* new in curl 7.9.7 */
3278         if(config->trace_dump) {
3279           curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, my_trace);
3280           curl_easy_setopt(curl, CURLOPT_DEBUGDATA, config);
3281           config->conf |= CONF_VERBOSE; /* force verbose */
3282         }
3283         curl_easy_setopt(curl, CURLOPT_VERBOSE, config->conf&CONF_VERBOSE);
3284
3285         /* new in curl 7.10 */
3286         curl_easy_setopt(curl, CURLOPT_ENCODING, 
3287                          (config->encoding) ? "" : NULL);
3288
3289         /* new in curl 7.10.7 */
3290         curl_easy_setopt(curl, CURLOPT_FTP_CREATE_MISSING_DIRS,
3291                          config->ftp_create_dirs);
3292         if(config->proxyntlm)
3293           curl_easy_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_NTLM);
3294
3295         /* new in curl 7.10.8 */
3296         if(config->max_filesize)
3297           curl_easy_setopt(curl, CURLOPT_MAXFILESIZE_LARGE,
3298                            config->max_filesize);
3299
3300         /* new in curl 7.11.0 */
3301         if(config->ftp_ssl)
3302           curl_easy_setopt(curl, CURLOPT_FTP_SSL, CURLFTPSSL_TRY);
3303
3304         /* new in curl 7.11.1 */
3305         if(config->socks5proxy) {
3306           curl_easy_setopt(curl, CURLOPT_PROXY, config->socks5proxy);
3307           curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
3308         }
3309
3310         res = curl_easy_perform(curl);
3311         
3312         if((config->progressmode == CURL_PROGRESS_BAR) &&
3313            progressbar.calls) {
3314           /* if the custom progress bar has been displayed, we output a
3315              newline here */
3316           fputs("\n", progressbar.out);
3317         }
3318
3319         if(config->writeout) {
3320           ourWriteOut(curl, config->writeout);
3321         }
3322 #ifdef USE_ENVIRONMENT
3323         if (config->writeenv)
3324           ourWriteEnv(curl);
3325 #endif
3326
3327 #ifdef  VMS
3328         if (!config->showerror)  {
3329           vms_show = VMSSTS_HIDE;
3330         }
3331 #else
3332         if((res!=CURLE_OK) && config->showerror) {
3333           if(CURLE_SSL_CACERT == res) {
3334             fprintf(config->errors, "curl: (%d) %s\n\n", res, errorbuffer);
3335 #define CURL_CA_CERT_ERRORMSG1 \
3336 "More details here: http://curl.haxx.se/docs/sslcerts.html\n\n" \
3337 "curl performs SSL certificate verification by default, using a \"bundle\"\n" \
3338 " of Certificate Authority (CA) public keys (CA certs). The default\n" \
3339 " bundle is named curl-ca-bundle.crt; you can specify an alternate file\n" \
3340 " using the --cacert option.\n"
3341
3342 #define CURL_CA_CERT_ERRORMSG2 \
3343 "If this HTTPS server uses a certificate signed by a CA represented in\n" \
3344 " the bundle, the certificate verification probably failed due to a\n" \
3345 " problem with the certificate (it might be expired, or the name might\n" \
3346 " not match the domain name in the URL).\n" \
3347 "If you'd like to turn off curl's verification of the certificate, use\n" \
3348 " the -k (or --insecure) option.\n"
3349
3350             fprintf(config->errors, "%s%s",
3351                     CURL_CA_CERT_ERRORMSG1,
3352                     CURL_CA_CERT_ERRORMSG2 );
3353           }
3354           else
3355             fprintf(config->errors, "curl: (%d) %s\n", res, errorbuffer);
3356         }
3357 #endif
3358
3359         if (outfile && !curl_strequal(outfile, "-") && outs.stream)
3360           fclose(outs.stream);
3361
3362 #ifdef HAVE_UTIME
3363         /* Important that we set the time _after_ the file has been 
3364            closed, as is done above here */
3365         if(config->remote_time && outs.filename) {
3366           /* as libcurl if we got a time. Pretty please */
3367           long filetime;
3368           curl_easy_getinfo(curl, CURLINFO_FILETIME, &filetime);
3369           if(filetime >= 0) {
3370             struct utimbuf times;
3371             times.actime = filetime;
3372             times.modtime = filetime;
3373             utime(outs.filename, &times); /* set the time we got */
3374           }
3375         }
3376 #endif
3377 #ifdef AMIGA
3378         /* Set the url as comment for the file. (up to 80 chars are allowed)
3379          */
3380         if( strlen(url) > 78 )
3381           url[79] = '\0';
3382         
3383         SetComment( outs.filename, url);
3384 #endif
3385
3386         if(headerfilep)
3387           fclose(headerfilep);
3388       
3389         if (httpgetfields)
3390           free(httpgetfields);
3391
3392         if(url)
3393           free(url);
3394
3395         if(outfile)
3396           free(outfile);
3397
3398         if(infdfopen)
3399           fclose(infd);
3400
3401       } /* loop to the next URL */
3402
3403       if(urls)
3404         /* cleanup memory used for URL globbing patterns */
3405         glob_cleanup(urls);
3406      
3407       if(uploadfile)
3408         free(uploadfile);
3409  
3410     } /* loop to the next globbed upload file */
3411
3412     if(inglob)
3413       glob_cleanup(inglob);
3414
3415     if(outfiles)
3416       free(outfiles);
3417
3418     /* empty this urlnode struct */
3419     if(urlnode->url)
3420       free(urlnode->url);
3421     if(urlnode->outfile)
3422       free(urlnode->outfile);
3423     if(urlnode->infile)
3424       free(urlnode->infile);
3425     
3426     /* move on to the next URL */
3427     nextnode=urlnode->next;
3428     free(urlnode); /* free the node */
3429     urlnode = nextnode;
3430
3431   } /* while-loop through all URLs */
3432
3433   if(config->headerfile && !headerfilep && heads.stream)
3434     fclose(heads.stream);
3435
3436   if(config->trace_fopened && config->trace_stream)
3437     fclose(config->trace_stream);
3438
3439   if(allocuseragent)
3440     free(config->useragent);
3441
3442   /* cleanup the curl handle! */
3443   curl_easy_cleanup(curl);
3444
3445   if(config->errors_fopened)
3446     fclose(config->errors);
3447
3448   main_free(); /* cleanup */
3449
3450   return res;
3451 }
3452
3453 int main(int argc, char *argv[])
3454 {
3455   int res;
3456   struct Configurable config;
3457   memset(&config, 0, sizeof(struct Configurable));
3458
3459   res = operate(&config, argc, argv);
3460   free_config_fields(&config);
3461
3462 #ifdef __NOVELL_LIBC__
3463   pressanykey();
3464 #endif
3465 #ifdef  VMS
3466   if (res > CURL_LAST) res = CURL_LAST; /* If CURL_LAST exceeded then */
3467   return (vms_cond[res]|vms_show);      /* curlmsg.h is out of sync.  */
3468 #else
3469   return res;
3470 #endif
3471 }
3472
3473 static char *my_get_line(FILE *fp)
3474 {
3475    char buf[4096];
3476    char *nl = NULL;
3477    char *retval = NULL;
3478
3479    do {
3480      if (NULL == fgets(buf, sizeof(buf), fp))
3481        break;
3482      if (NULL == retval)
3483        retval = strdup(buf);
3484      else {
3485        if (NULL == (retval = realloc(retval,
3486                                      strlen(retval) + strlen(buf) + 1)))
3487          break;
3488        strcat(retval, buf);
3489      }
3490    }
3491    while (NULL == (nl = strchr(retval, '\n')));
3492
3493    if (NULL != nl)
3494      *nl = '\0';
3495
3496    return retval;
3497 }
3498
3499
3500 /* Create the needed directory hierarchy recursively in order to save
3501    multi-GETs in file output, ie:
3502    curl "http://my.site/dir[1-5]/file[1-5].txt" -o "dir#1/file#2.txt"
3503    should create all the dir* automagically
3504 */
3505 static int create_dir_hierarchy(char *outfile)
3506 {
3507   char *tempdir;
3508   char *tempdir2;
3509   char *outdup;
3510   char *dirbuildup;
3511   int result=0;
3512   
3513   outdup = strdup(outfile);
3514   dirbuildup = malloc(sizeof(char) * strlen(outfile));
3515   if(!dirbuildup)
3516     return -1;
3517   dirbuildup[0] = '\0';
3518
3519   tempdir = strtok(outdup, DIR_CHAR);
3520
3521   while (tempdir != NULL) {
3522     tempdir2 = strtok(NULL, DIR_CHAR);
3523     /* since strtok returns a token for the last word even
3524        if not ending with DIR_CHAR, we need to prune it */
3525     if (tempdir2 != NULL) {
3526       if (strlen(dirbuildup) > 0)
3527         sprintf(dirbuildup,"%s%s%s",dirbuildup, DIR_CHAR, tempdir);
3528       else {
3529         if (0 != strncmp(outdup, DIR_CHAR, 1))
3530           sprintf(dirbuildup,"%s",tempdir);
3531         else
3532           sprintf(dirbuildup,"%s%s", DIR_CHAR, tempdir);
3533       }
3534       if (access(dirbuildup, F_OK) == -1) {
3535         result = mkdir(dirbuildup,(mode_t)0000750);
3536         if (-1 == result) {
3537           switch (errno) {
3538 #ifdef EACCES
3539           case EACCES:
3540             fprintf(stderr,"You don't have permission to create %s.\n",
3541                     dirbuildup);
3542             break;
3543 #endif
3544 #ifdef ENAMETOOLONG
3545           case ENAMETOOLONG:
3546             fprintf(stderr,"The directory name %s is too long.\n",
3547                     dirbuildup);
3548             break;
3549 #endif
3550 #ifdef EROFS
3551           case EROFS:
3552             fprintf(stderr,"%s resides on a read-only file system.\n",
3553                     dirbuildup);
3554             break;
3555 #endif
3556 #ifdef ENOSPC
3557           case ENOSPC:
3558             fprintf(stderr,"No space left on the file system that will "
3559                     "contain the directory %s.\n", dirbuildup);
3560             break;
3561 #endif
3562 #ifdef EDQUOT
3563           case EDQUOT:
3564             fprintf(stderr,"Cannot create directory %s because you "
3565                     "exceeded your quota.\n", dirbuildup);
3566             break;
3567 #endif
3568           default :
3569             fprintf(stderr,"Error creating directory %s.\n", dirbuildup);
3570             break;
3571           }
3572           break; /* get out of loop */
3573         }
3574       }
3575     }
3576     tempdir = tempdir2;
3577   }
3578   free(dirbuildup);
3579   free(outdup);
3580
3581   return result; /* 0 is fine, -1 is badness */
3582 }
3583
3584 #ifdef __DJGPP__
3585 /* The following functions are taken with modification from the DJGPP
3586  * port of tar 1.12. They use algorithms originally from DJTAR. */
3587
3588 char *
3589 msdosify (char *file_name)
3590 {
3591   static char dos_name[PATH_MAX];
3592   static char illegal_chars_dos[] = ".+, ;=[]|<>\\\":?*";
3593   static char *illegal_chars_w95 = &illegal_chars_dos[8];
3594   int idx, dot_idx;
3595   char *s = file_name, *d = dos_name;
3596   char *illegal_aliens = illegal_chars_dos;
3597   size_t len = sizeof (illegal_chars_dos) - 1;
3598   int lfn = 0;
3599
3600   /* Support for Windows 9X VFAT systems, when available.  */
3601   if (_use_lfn (file_name))
3602     lfn = 1;
3603   if (lfn) {
3604     illegal_aliens = illegal_chars_w95;
3605     len -= (illegal_chars_w95 - illegal_chars_dos);
3606   }
3607   
3608   /* Get past the drive letter, if any. */
3609   if (s[0] >= 'A' && s[0] <= 'z' && s[1] == ':') {
3610     *d++ = *s++;
3611     *d++ = *s++;
3612   }
3613
3614   for (idx = 0, dot_idx = -1; *s; s++, d++) {
3615     if (memchr (illegal_aliens, *s, len)) {
3616       /* Dots are special: DOS doesn't allow them as the leading character,
3617          and a file name cannot have more than a single dot.  We leave the
3618          first non-leading dot alone, unless it comes too close to the
3619          beginning of the name: we want sh.lex.c to become sh_lex.c, not
3620          sh.lex-c.  */
3621       if (*s == '.') {
3622         if (idx == 0 && (s[1] == '/' || (s[1] == '.' && s[2] == '/'))) {
3623           /* Copy "./" and "../" verbatim.  */
3624           *d++ = *s++;
3625           if (*s == '.')
3626             *d++ = *s++;
3627           *d = *s;
3628         }
3629         else if (idx == 0)
3630           *d = '_';
3631         else if (dot_idx >= 0) {
3632           if (dot_idx < 5) { /* 5 is a heuristic ad-hoc'ery */
3633             d[dot_idx - idx] = '_'; /* replace previous dot */
3634             *d = '.';
3635           }
3636           else
3637             *d = '-';
3638         }
3639         else
3640           *d = '.';
3641
3642         if (*s == '.')
3643           dot_idx = idx;
3644       }
3645       else if (*s == '+' && s[1] == '+') {
3646         if (idx - 2 == dot_idx) { /* .c++, .h++ etc. */
3647           *d++ = 'x';
3648           *d   = 'x';
3649         }
3650         else {
3651           /* libg++ etc.  */
3652           memcpy (d, "plus", 4);
3653           d += 3;
3654         }
3655         s++;
3656         idx++;
3657       }
3658       else
3659         *d = '_';
3660     }
3661     else
3662       *d = *s;
3663     if (*s == '/') {
3664       idx = 0;
3665       dot_idx = -1;
3666     }
3667     else
3668       idx++;
3669   }
3670
3671   *d = '\0';
3672   return dos_name;
3673 }
3674
3675 char *
3676 rename_if_dos_device_name (char *file_name)
3677 {
3678   /* We could have a file whose name is a device on MS-DOS.  Trying to
3679    * retrieve such a file would fail at best and wedge us at worst.  We need
3680    * to rename such files. */
3681   extern char *basename (const char *);
3682   char *base;
3683   struct stat st_buf;
3684   char fname[PATH_MAX];
3685
3686   strcpy (fname, file_name);
3687   base = basename (fname);
3688   if (((stat(base, &st_buf)) == 0) && (S_ISCHR(st_buf.st_mode))) {
3689     size_t blen = strlen (base);
3690
3691     /* Prepend a '_'.  */
3692     memmove (base + 1, base, blen + 1);
3693     base[0] = '_';
3694     strcpy (file_name, fname);
3695   }
3696   return file_name;
3697 }
3698
3699 #endif /* __DJGPP__ */