fetch.c revision 63501
162216Sdes/*- 262216Sdes * Copyright (c) 2000 Dag-Erling Co�dan Sm�rgrav 362216Sdes * All rights reserved. 462216Sdes * 562216Sdes * Redistribution and use in source and binary forms, with or without 662216Sdes * modification, are permitted provided that the following conditions 762216Sdes * are met: 862216Sdes * 1. Redistributions of source code must retain the above copyright 962216Sdes * notice, this list of conditions and the following disclaimer 1062216Sdes * in this position and unchanged. 1162216Sdes * 2. Redistributions in binary form must reproduce the above copyright 1262216Sdes * notice, this list of conditions and the following disclaimer in the 1362216Sdes * documentation and/or other materials provided with the distribution. 1462216Sdes * 3. The name of the author may not be used to endorse or promote products 1562216Sdes * derived from this software without specific prior written permission 1662216Sdes * 1762216Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1862216Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1962216Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2062216Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2162216Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2262216Sdes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2362216Sdes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2462216Sdes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2562216Sdes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2662216Sdes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2762216Sdes * 2862216Sdes * $FreeBSD: head/usr.bin/fetch/fetch.c 63501 2000-07-19 09:12:36Z des $ 2962216Sdes */ 3062216Sdes 3162216Sdes#include <sys/param.h> 3262216Sdes#include <sys/stat.h> 3362216Sdes#include <sys/socket.h> 3462216Sdes 3562216Sdes#include <ctype.h> 3662216Sdes#include <err.h> 3762216Sdes#include <errno.h> 3863235Sdes#include <signal.h> 3962216Sdes#include <stdio.h> 4062216Sdes#include <stdlib.h> 4162216Sdes#include <string.h> 4262216Sdes#include <sysexits.h> 4362216Sdes#include <unistd.h> 4462216Sdes 4562216Sdes#include <fetch.h> 4662216Sdes 4762216Sdes#define MINBUFSIZE 4096 4862216Sdes 4962216Sdes/* Option flags */ 5062216Sdesint A_flag; /* -A: do not follow 302 redirects */ 5162216Sdesint a_flag; /* -a: auto retry */ 5262216Sdessize_t B_size; /* -B: buffer size */ 5362216Sdesint b_flag; /*! -b: workaround TCP bug */ 5462254Sdeschar *c_dirname; /* -c: remote directory */ 5562216Sdesint d_flag; /* -d: direct connection */ 5662216Sdesint F_flag; /* -F: restart without checking mtime */ 5762216Sdeschar *f_filename; /* -f: file to fetch */ 5862216Sdesint H_flag; /* -H: use high port */ 5962216Sdeschar *h_hostname; /* -h: host to fetch from */ 6062216Sdesint l_flag; /* -l: link rather than copy file: URLs */ 6162815Sdesint m_flag; /* -[Mm]: mirror mode */ 6262815Sdesint n_flag; /* -n: do not preserve modification time */ 6362216Sdesint o_flag; /* -o: specify output file */ 6462216Sdesint o_directory; /* output file is a directory */ 6562216Sdeschar *o_filename; /* name of output file */ 6662216Sdesint o_stdout; /* output file is stdout */ 6762216Sdesint once_flag; /* -1: stop at first successful file */ 6863501Sdesint p_flag; /* -[Pp]: use passive FTP */ 6962216Sdesint R_flag; /* -R: don't delete partially transferred files */ 7062216Sdesint r_flag; /* -r: restart previously interrupted transfer */ 7162216Sdesu_int T_secs = 0; /* -T: transfer timeout in seconds */ 7262216Sdesint s_flag; /* -s: show size, don't fetch */ 7362216Sdesoff_t S_size; /* -S: require size to match */ 7462216Sdesint t_flag; /*! -t: workaround TCP bug */ 7562216Sdesint v_level = 1; /* -v: verbosity level */ 7662216Sdesint v_tty; /* stdout is a tty */ 7762216Sdesu_int w_secs; /* -w: retry delay */ 7862216Sdesint family = PF_UNSPEC; /* -[46]: address family to use */ 7962216Sdes 8063015Sdesint sigalrm; /* SIGALRM received */ 8163015Sdesint sigint; /* SIGINT received */ 8262216Sdes 8362216Sdesu_int ftp_timeout; /* default timeout for FTP transfers */ 8462216Sdesu_int http_timeout; /* default timeout for HTTP transfers */ 8562216Sdesu_char *buf; /* transfer buffer */ 8662216Sdes 8762216Sdes 8862216Sdesvoid 8962216Sdessig_handler(int sig) 9062216Sdes{ 9163015Sdes switch (sig) { 9263015Sdes case SIGALRM: 9363015Sdes sigalrm = 1; 9463015Sdes break; 9563015Sdes case SIGINT: 9663015Sdes sigint = 1; 9763015Sdes break; 9863015Sdes } 9962216Sdes} 10062216Sdes 10162216Sdesstruct xferstat { 10262216Sdes char name[40]; 10362216Sdes struct timeval start; 10462216Sdes struct timeval end; 10562216Sdes struct timeval last; 10662216Sdes off_t size; 10762216Sdes off_t offset; 10862216Sdes off_t rcvd; 10962216Sdes}; 11062216Sdes 11162216Sdesvoid 11263046Sdesstat_display(struct xferstat *xs, int force) 11362216Sdes{ 11462216Sdes struct timeval now; 11562216Sdes 11663046Sdes if (!v_tty) 11762216Sdes return; 11862216Sdes 11962216Sdes gettimeofday(&now, NULL); 12063046Sdes if (!force && now.tv_sec <= xs->last.tv_sec) 12162216Sdes return; 12262216Sdes xs->last = now; 12362216Sdes 12462216Sdes fprintf(stderr, "\rReceiving %s", xs->name); 12562216Sdes if (xs->size == -1) 12663046Sdes fprintf(stderr, ": %lld bytes", xs->rcvd); 12762216Sdes else 12863005Sdes fprintf(stderr, " (%lld bytes): %d%%", xs->size, 12963067Sdes (int)((100.0 * xs->rcvd) / xs->size)); 13062216Sdes} 13162216Sdes 13262216Sdesvoid 13363046Sdesstat_start(struct xferstat *xs, char *name, off_t size, off_t offset) 13463046Sdes{ 13563046Sdes snprintf(xs->name, sizeof xs->name, "%s", name); 13663046Sdes gettimeofday(&xs->start, NULL); 13763046Sdes xs->last.tv_sec = xs->last.tv_usec = 0; 13863046Sdes xs->end = xs->last; 13963046Sdes xs->size = size; 14063046Sdes xs->offset = offset; 14163067Sdes xs->rcvd = offset; 14263046Sdes stat_display(xs, 1); 14363046Sdes} 14463046Sdes 14563046Sdesvoid 14663046Sdesstat_update(struct xferstat *xs, off_t rcvd, int force) 14763046Sdes{ 14863046Sdes xs->rcvd = rcvd; 14963046Sdes stat_display(xs, 0); 15063046Sdes} 15163046Sdes 15263046Sdesvoid 15362216Sdesstat_end(struct xferstat *xs) 15462216Sdes{ 15562216Sdes double delta; 15662216Sdes double bps; 15762216Sdes 15862216Sdes gettimeofday(&xs->end, NULL); 15962216Sdes 16063046Sdes stat_display(xs, 1); 16162216Sdes fputc('\n', stderr); 16262216Sdes delta = (xs->end.tv_sec + (xs->end.tv_usec / 1.e6)) 16362216Sdes - (xs->start.tv_sec + (xs->start.tv_usec / 1.e6)); 16462216Sdes fprintf(stderr, "%lld bytes transferred in %.1f seconds ", 16563005Sdes xs->rcvd - xs->offset, delta); 16663005Sdes bps = (xs->rcvd - xs->offset) / delta; 16762216Sdes if (bps > 1024*1024) 16862216Sdes fprintf(stderr, "(%.2f MBps)\n", bps / (1024*1024)); 16962216Sdes else if (bps > 1024) 17062216Sdes fprintf(stderr, "(%.2f kBps)\n", bps / 1024); 17162216Sdes else 17262216Sdes fprintf(stderr, "(%.2f Bps)\n", bps); 17362216Sdes} 17462216Sdes 17562216Sdesint 17662216Sdesfetch(char *URL, char *path) 17762216Sdes{ 17862216Sdes struct url *url; 17962216Sdes struct url_stat us; 18062216Sdes struct stat sb; 18162216Sdes struct xferstat xs; 18262216Sdes FILE *f, *of; 18362216Sdes size_t size; 18462216Sdes off_t count; 18562216Sdes char flags[8]; 18663046Sdes int n, r; 18762216Sdes u_int timeout; 18862216Sdes 18962216Sdes f = of = NULL; 19062216Sdes 19162216Sdes /* parse URL */ 19262216Sdes if ((url = fetchParseURL(URL)) == NULL) { 19362216Sdes warnx("%s: parse error", URL); 19462216Sdes goto failure; 19562216Sdes } 19662216Sdes 19762216Sdes timeout = 0; 19862216Sdes *flags = 0; 19963345Sdes count = 0; 20062216Sdes 20162216Sdes /* common flags */ 20263353Sdes if (v_level > 1) 20362216Sdes strcat(flags, "v"); 20462216Sdes switch (family) { 20562216Sdes case PF_INET: 20662216Sdes strcat(flags, "4"); 20762216Sdes break; 20862216Sdes case PF_INET6: 20962216Sdes strcat(flags, "6"); 21062216Sdes break; 21162216Sdes } 21262216Sdes 21362216Sdes /* FTP specific flags */ 21462216Sdes if (strcmp(url->scheme, "ftp") == 0) { 21562216Sdes if (p_flag) 21662216Sdes strcat(flags, "p"); 21762216Sdes if (d_flag) 21862216Sdes strcat(flags, "d"); 21962216Sdes if (H_flag) 22062216Sdes strcat(flags, "h"); 22162216Sdes timeout = T_secs ? T_secs : ftp_timeout; 22262216Sdes } 22362216Sdes 22462216Sdes /* HTTP specific flags */ 22562216Sdes if (strcmp(url->scheme, "http") == 0) { 22662216Sdes if (d_flag) 22762216Sdes strcat(flags, "d"); 22862216Sdes if (A_flag) 22962216Sdes strcat(flags, "A"); 23062216Sdes timeout = T_secs ? T_secs : http_timeout; 23162216Sdes } 23262216Sdes 23363015Sdes /* set the protocol timeout. */ 23462216Sdes fetchTimeout = timeout; 23562216Sdes 23662216Sdes /* just print size */ 23762216Sdes if (s_flag) { 23863345Sdes if (fetchStat(url, &us, flags) == -1) 23963345Sdes goto failure; 24062216Sdes if (us.size == -1) 24162216Sdes printf("Unknown\n"); 24262216Sdes else 24362216Sdes printf("%lld\n", us.size); 24462216Sdes goto success; 24562216Sdes } 24662216Sdes 24763345Sdes /* 24863345Sdes * If the -r flag was specified, we have to compare the local and 24963345Sdes * remote files, so we should really do a fetchStat() first, but I 25063345Sdes * know of at least one HTTP server that only sends the content 25163345Sdes * size in response to GET requests, and leaves it out of replies 25263345Sdes * to HEAD requests. Also, in the (frequent) case that the local 25363345Sdes * and remote files match but the local file is truncated, we have 25463345Sdes * sufficient information *before* the compare to issue a correct 25563345Sdes * request. Therefore, we always issue a GET request as if we were 25663345Sdes * sure the local file was a truncated copy of the remote file; we 25763345Sdes * can drop the connection later if we change our minds. 25863345Sdes */ 25963345Sdes if (r_flag && !o_stdout && stat(path, &sb) != -1) 26063345Sdes url->offset = sb.st_size; 26163345Sdes 26263345Sdes /* start the transfer */ 26363345Sdes if ((f = fetchXGet(url, &us, flags)) == NULL) { 26463345Sdes warnx("%s: %s", path, fetchLastErrString); 26562216Sdes goto failure; 26662216Sdes } 26763345Sdes if (sigint) 26863345Sdes goto signal; 26962216Sdes 27063345Sdes /* check that size is as expected */ 27163345Sdes if (S_size) { 27263345Sdes if (us.size == -1) { 27363345Sdes warnx("%s: size unknown", path); 27463345Sdes goto failure; 27563345Sdes } else if (us.size != S_size) { 27663345Sdes warnx("%s: size mismatch: expected %lld, actual %lld", 27763345Sdes path, S_size, us.size); 27863345Sdes goto failure; 27963345Sdes } 28063345Sdes } 28163345Sdes 28262216Sdes /* symlink instead of copy */ 28362216Sdes if (l_flag && strcmp(url->scheme, "file") == 0 && !o_stdout) { 28462216Sdes if (symlink(url->doc, path) == -1) { 28562216Sdes warn("%s: symlink()", path); 28662216Sdes goto failure; 28762216Sdes } 28862216Sdes goto success; 28962216Sdes } 29062216Sdes 29163345Sdes /* open output file */ 29262216Sdes if (o_stdout) { 29362216Sdes /* output to stdout */ 29462216Sdes of = stdout; 29563345Sdes } else if (url->offset) { 29663345Sdes /* resume mode, local file exists */ 29763345Sdes if (!F_flag && us.mtime && sb.st_mtime != us.mtime) { 29863345Sdes /* no match! have to refetch */ 29963345Sdes fclose(f); 30063345Sdes url->offset = 0; 30163345Sdes if ((f = fetchXGet(url, &us, flags)) == NULL) { 30263345Sdes warnx("%s: %s", path, fetchLastErrString); 30363345Sdes goto failure; 30463345Sdes } 30563345Sdes if (sigint) 30663345Sdes goto signal; 30763345Sdes } else { 30863345Sdes us.size += url->offset; 30963345Sdes } 31062216Sdes if (us.size == sb.st_size) 31163345Sdes /* nothing to do */ 31262216Sdes goto success; 31363345Sdes if (sb.st_size > us.size) { 31463345Sdes /* local file too long! */ 31563345Sdes warnx("%s: local file (%lld bytes) is longer " 31663345Sdes "than remote file (%lld bytes)", 31763345Sdes path, sb.st_size, us.size); 31862216Sdes goto failure; 31962216Sdes } 32063345Sdes /* we got through, open local file in append mode */ 32163345Sdes /* 32263345Sdes * XXX there's a race condition here - the file we open is not 32363345Sdes * necessarily the same as the one we stat()'ed earlier... 32463345Sdes */ 32562216Sdes if ((of = fopen(path, "a")) == NULL) { 32662216Sdes warn("%s: open()", path); 32762216Sdes goto failure; 32862216Sdes } 32963345Sdes } 33063345Sdes if (m_flag && stat(path, &sb) != -1) { 33163345Sdes /* mirror mode, local file exists */ 33262216Sdes if (sb.st_size == us.size && sb.st_mtime == us.mtime) 33363345Sdes goto success; 33463345Sdes } 33563345Sdes if (!of) { 33663345Sdes /* 33763345Sdes * We don't yet have an output file; either this is a vanilla 33863345Sdes * run with no special flags, or the local and remote files 33963345Sdes * didn't match. 34063345Sdes */ 34162216Sdes if ((of = fopen(path, "w")) == NULL) { 34262216Sdes warn("%s: open()", path); 34362216Sdes goto failure; 34462216Sdes } 34562216Sdes } 34662216Sdes count = url->offset; 34762216Sdes 34862216Sdes /* start the counter */ 34962216Sdes stat_start(&xs, path, us.size, count); 35062216Sdes 35163015Sdes sigint = sigalrm = 0; 35263046Sdes 35363046Sdes /* suck in the data */ 35463046Sdes for (n = 0; !sigint && !sigalrm; ++n) { 35563046Sdes if (us.size != -1 && us.size - count < B_size) 35663046Sdes size = us.size - count; 35763046Sdes else 35863046Sdes size = B_size; 35963046Sdes if (timeout) 36063046Sdes alarm(timeout); 36163046Sdes if ((size = fread(buf, 1, size, f)) <= 0) 36263046Sdes break; 36363046Sdes stat_update(&xs, count += size, 0); 36463046Sdes if (fwrite(buf, size, 1, of) != 1) 36563046Sdes break; 36662216Sdes } 36763015Sdes 36862216Sdes if (timeout) 36962216Sdes alarm(0); 37062216Sdes 37162216Sdes stat_end(&xs); 37263015Sdes 37362216Sdes /* Set mtime of local file */ 37463046Sdes if (!n_flag && us.mtime && !o_stdout) { 37562216Sdes struct timeval tv[2]; 37662216Sdes 37763046Sdes fflush(of); 37863046Sdes tv[0].tv_sec = (long)(us.atime ? us.atime : us.mtime); 37962216Sdes tv[1].tv_sec = (long)us.mtime; 38062216Sdes tv[0].tv_usec = tv[1].tv_usec = 0; 38162216Sdes if (utimes(path, tv)) 38262216Sdes warn("%s: utimes()", path); 38362216Sdes } 38462216Sdes 38563046Sdes /* timed out or interrupted? */ 38663345Sdes signal: 38763015Sdes if (sigalrm) 38863015Sdes warnx("transfer timed out"); 38963046Sdes if (sigint) 39063015Sdes warnx("transfer interrupted"); 39163046Sdes 39263235Sdes if (!sigalrm && !sigint) { 39363235Sdes /* check the status of our files */ 39463235Sdes if (ferror(f)) 39563235Sdes warn("%s", URL); 39663235Sdes if (ferror(of)) 39763235Sdes warn("%s", path); 39863235Sdes if (ferror(f) || ferror(of)) 39963235Sdes goto failure; 40063235Sdes } 40163046Sdes 40263046Sdes /* did the transfer complete normally? */ 40363046Sdes if (us.size != -1 && count < us.size) { 40462815Sdes warnx("%s appears to be truncated: %lld/%lld bytes", 40562815Sdes path, count, us.size); 40663046Sdes goto failure_keep; 40762815Sdes } 40862815Sdes 40962216Sdes success: 41063046Sdes r = 0; 41162216Sdes goto done; 41262216Sdes failure: 41363046Sdes if (of && of != stdout && !R_flag && !r_flag) 41463046Sdes unlink(path); 41563046Sdes failure_keep: 41662216Sdes r = -1; 41762216Sdes goto done; 41862216Sdes done: 41962216Sdes if (f) 42062216Sdes fclose(f); 42162216Sdes if (of && of != stdout) 42262216Sdes fclose(of); 42362837Sdes if (url) 42462837Sdes fetchFreeURL(url); 42562216Sdes return r; 42662216Sdes} 42762216Sdes 42862216Sdesvoid 42962216Sdesusage(void) 43062216Sdes{ 43162216Sdes /* XXX badly out of synch */ 43262216Sdes fprintf(stderr, 43362216Sdes "Usage: fetch [-1AFHMPRabdlmnpqrstv] [-o outputfile] [-S bytes]\n" 43462216Sdes " [-B bytes] [-T seconds] [-w seconds]\n" 43562216Sdes " [-f file -h host [-c dir] | URL ...]\n" 43662216Sdes ); 43762216Sdes} 43862216Sdes 43962216Sdes 44062216Sdes#define PARSENUM(NAME, TYPE) \ 44162216Sdesint \ 44262216SdesNAME(char *s, TYPE *v) \ 44362216Sdes{ \ 44462216Sdes *v = 0; \ 44562216Sdes for (*v = 0; *s; s++) \ 44662216Sdes if (isdigit(*s)) \ 44762216Sdes *v = *v * 10 + *s - '0'; \ 44862216Sdes else \ 44962216Sdes return -1; \ 45062216Sdes return 0; \ 45162216Sdes} 45262216Sdes 45362216SdesPARSENUM(parseint, u_int) 45462216SdesPARSENUM(parsesize, size_t) 45562216SdesPARSENUM(parseoff, off_t) 45662216Sdes 45762216Sdesint 45862216Sdesmain(int argc, char *argv[]) 45962216Sdes{ 46062216Sdes struct stat sb; 46163235Sdes struct sigaction sa; 46262216Sdes char *p, *q, *s; 46362216Sdes int c, e, r; 46462216Sdes 46562216Sdes while ((c = getopt(argc, argv, 46662254Sdes "146AaB:bc:dFf:h:lHMmnPpo:qRrS:sT:tvw:")) != EOF) 46762216Sdes switch (c) { 46862216Sdes case '1': 46962216Sdes once_flag = 1; 47062216Sdes break; 47162216Sdes case '4': 47262216Sdes family = PF_INET; 47362216Sdes break; 47462216Sdes case '6': 47562216Sdes family = PF_INET6; 47662216Sdes break; 47762216Sdes case 'A': 47862216Sdes A_flag = 1; 47962216Sdes break; 48062216Sdes case 'a': 48162216Sdes a_flag = 1; 48262216Sdes break; 48362216Sdes case 'B': 48462216Sdes if (parsesize(optarg, &B_size) == -1) 48562216Sdes errx(1, "invalid buffer size"); 48662216Sdes break; 48762216Sdes case 'b': 48862216Sdes warnx("warning: the -b option is deprecated"); 48962216Sdes b_flag = 1; 49062216Sdes break; 49162254Sdes case 'c': 49262254Sdes c_dirname = optarg; 49362254Sdes break; 49462216Sdes case 'd': 49562216Sdes d_flag = 1; 49662216Sdes break; 49762216Sdes case 'F': 49862216Sdes F_flag = 1; 49962216Sdes break; 50062216Sdes case 'f': 50162216Sdes f_filename = optarg; 50262216Sdes break; 50362216Sdes case 'H': 50462216Sdes H_flag = 1; 50562216Sdes break; 50662216Sdes case 'h': 50762216Sdes h_hostname = optarg; 50862216Sdes break; 50962216Sdes case 'l': 51062216Sdes l_flag = 1; 51162216Sdes break; 51262216Sdes case 'o': 51362216Sdes o_flag = 1; 51462216Sdes o_filename = optarg; 51562216Sdes break; 51662216Sdes case 'M': 51762216Sdes case 'm': 51863345Sdes if (r_flag) 51963345Sdes errx(1, "the -m and -r flags are mutually exclusive"); 52062216Sdes m_flag = 1; 52162216Sdes break; 52262216Sdes case 'n': 52362815Sdes n_flag = 1; 52462216Sdes break; 52562216Sdes case 'P': 52662216Sdes case 'p': 52762216Sdes p_flag = 1; 52862216Sdes break; 52962216Sdes case 'q': 53062216Sdes v_level = 0; 53162216Sdes break; 53262216Sdes case 'R': 53362216Sdes R_flag = 1; 53462216Sdes break; 53562216Sdes case 'r': 53663345Sdes if (m_flag) 53763345Sdes errx(1, "the -m and -r flags are mutually exclusive"); 53862216Sdes r_flag = 1; 53962216Sdes break; 54062216Sdes case 'S': 54162216Sdes if (parseoff(optarg, &S_size) == -1) 54262216Sdes errx(1, "invalid size"); 54362216Sdes break; 54462216Sdes case 's': 54562216Sdes s_flag = 1; 54662216Sdes break; 54762216Sdes case 'T': 54862216Sdes if (parseint(optarg, &T_secs) == -1) 54962216Sdes errx(1, "invalid timeout"); 55062216Sdes break; 55162216Sdes case 't': 55262216Sdes t_flag = 1; 55362216Sdes warnx("warning: the -t option is deprecated"); 55462216Sdes break; 55562216Sdes case 'v': 55662216Sdes v_level++; 55762216Sdes break; 55862216Sdes case 'w': 55962216Sdes a_flag = 1; 56062216Sdes if (parseint(optarg, &w_secs) == -1) 56162216Sdes errx(1, "invalid delay"); 56262216Sdes break; 56362216Sdes default: 56462216Sdes usage(); 56562216Sdes exit(EX_USAGE); 56662216Sdes } 56762216Sdes 56862216Sdes argc -= optind; 56962216Sdes argv += optind; 57062216Sdes 57162254Sdes if (h_hostname || f_filename || c_dirname) { 57262216Sdes if (!h_hostname || !f_filename || argc) { 57362216Sdes usage(); 57462216Sdes exit(EX_USAGE); 57562216Sdes } 57662216Sdes /* XXX this is a hack. */ 57762216Sdes if (strcspn(h_hostname, "@:/") != strlen(h_hostname)) 57862216Sdes errx(1, "invalid hostname"); 57962254Sdes if (asprintf(argv, "ftp://%s/%s/%s", h_hostname, 58062254Sdes c_dirname ? c_dirname : "", f_filename) == -1) 58162216Sdes errx(1, strerror(ENOMEM)); 58262216Sdes argc++; 58362216Sdes } 58463345Sdes 58562216Sdes if (!argc) { 58662216Sdes usage(); 58762216Sdes exit(EX_USAGE); 58862216Sdes } 58962216Sdes 59062216Sdes /* allocate buffer */ 59162216Sdes if (B_size < MINBUFSIZE) 59262216Sdes B_size = MINBUFSIZE; 59362216Sdes if ((buf = malloc(B_size)) == NULL) 59462216Sdes errx(1, strerror(ENOMEM)); 59562216Sdes 59663235Sdes /* timeouts */ 59762216Sdes if ((s = getenv("FTP_TIMEOUT")) != NULL) { 59862216Sdes if (parseint(s, &ftp_timeout) == -1) { 59962216Sdes warnx("FTP_TIMEOUT is not a positive integer"); 60062216Sdes ftp_timeout = 0; 60162216Sdes } 60262216Sdes } 60362216Sdes if ((s = getenv("HTTP_TIMEOUT")) != NULL) { 60462216Sdes if (parseint(s, &http_timeout) == -1) { 60562216Sdes warnx("HTTP_TIMEOUT is not a positive integer"); 60662216Sdes http_timeout = 0; 60762216Sdes } 60862216Sdes } 60962216Sdes 61063235Sdes /* signal handling */ 61163235Sdes sa.sa_flags = 0; 61263235Sdes sa.sa_handler = sig_handler; 61363235Sdes sigemptyset(&sa.sa_mask); 61463345Sdes sigaction(SIGALRM, &sa, NULL); 61563345Sdes sa.sa_flags = SA_RESETHAND; 61663345Sdes sigaction(SIGINT, &sa, NULL); 61763345Sdes fetchRestartCalls = 0; 61863015Sdes 61962216Sdes /* output file */ 62062216Sdes if (o_flag) { 62162216Sdes if (strcmp(o_filename, "-") == 0) { 62262216Sdes o_stdout = 1; 62362216Sdes } else if (stat(o_filename, &sb) == -1) { 62462216Sdes if (errno == ENOENT) { 62562216Sdes if (argc > 1) 62662216Sdes errx(EX_USAGE, "%s is not a directory", o_filename); 62762216Sdes } else { 62862216Sdes err(EX_IOERR, "%s", o_filename); 62962216Sdes } 63062216Sdes } else { 63162216Sdes if (sb.st_mode & S_IFDIR) 63262216Sdes o_directory = 1; 63362216Sdes } 63462216Sdes } 63562216Sdes 63662216Sdes /* check if output is to a tty (for progress report) */ 63762815Sdes v_tty = isatty(STDERR_FILENO); 63862216Sdes r = 0; 63962216Sdes 64062216Sdes while (argc) { 64162216Sdes if ((p = strrchr(*argv, '/')) == NULL) 64262216Sdes p = *argv; 64362216Sdes else 64462216Sdes p++; 64562216Sdes 64662216Sdes if (!*p) 64762216Sdes p = "fetch.out"; 64862216Sdes 64962216Sdes fetchLastErrCode = 0; 65062216Sdes 65162216Sdes if (o_flag) { 65262216Sdes if (o_stdout) { 65362216Sdes e = fetch(*argv, "-"); 65462216Sdes } else if (o_directory) { 65562216Sdes asprintf(&q, "%s/%s", o_filename, p); 65662216Sdes e = fetch(*argv, q); 65762216Sdes free(q); 65862216Sdes } else { 65962216Sdes e = fetch(*argv, o_filename); 66062216Sdes } 66162216Sdes } else { 66262216Sdes e = fetch(*argv, p); 66362216Sdes } 66462216Sdes 66563015Sdes if (sigint) 66663345Sdes kill(getpid(), SIGINT); 66763015Sdes 66862216Sdes if (e == 0 && once_flag) 66962216Sdes exit(0); 67062216Sdes 67162216Sdes if (e) { 67262216Sdes r = 1; 67362216Sdes if ((fetchLastErrCode 67462216Sdes && fetchLastErrCode != FETCH_UNAVAIL 67562216Sdes && fetchLastErrCode != FETCH_MOVED 67662216Sdes && fetchLastErrCode != FETCH_URL 67762216Sdes && fetchLastErrCode != FETCH_RESOLV 67862216Sdes && fetchLastErrCode != FETCH_UNKNOWN)) { 67962216Sdes if (w_secs) { 68062216Sdes if (v_level) 68163353Sdes fprintf(stderr, "Waiting %d seconds before retrying\n", 68263353Sdes w_secs); 68362216Sdes sleep(w_secs); 68462216Sdes } 68562216Sdes if (a_flag) 68662216Sdes continue; 68762216Sdes } 68862216Sdes } 68962216Sdes 69062216Sdes argc--, argv++; 69162216Sdes } 69262216Sdes 69362216Sdes exit(r); 69462216Sdes} 695