fetch.c revision 62254
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 62254 2000-06-29 10:32:56Z 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> 3862216Sdes#include <stdio.h> 3962216Sdes#include <stdlib.h> 4062216Sdes#include <string.h> 4162216Sdes#include <sysexits.h> 4262216Sdes#include <unistd.h> 4362216Sdes 4462216Sdes#include <fetch.h> 4562216Sdes 4662216Sdes#define MINBUFSIZE 4096 4762216Sdes 4862216Sdes/* Option flags */ 4962216Sdesint A_flag; /* -A: do not follow 302 redirects */ 5062216Sdesint a_flag; /* -a: auto retry */ 5162216Sdessize_t B_size; /* -B: buffer size */ 5262216Sdesint b_flag; /*! -b: workaround TCP bug */ 5362254Sdeschar *c_dirname; /* -c: remote directory */ 5462216Sdesint d_flag; /* -d: direct connection */ 5562216Sdesint F_flag; /* -F: restart without checking mtime */ 5662216Sdeschar *f_filename; /* -f: file to fetch */ 5762216Sdesint H_flag; /* -H: use high port */ 5862216Sdeschar *h_hostname; /* -h: host to fetch from */ 5962216Sdesint l_flag; /* -l: link rather than copy file: URLs */ 6062216Sdesint m_flag; /* -[Mm]: set local timestamp to remote timestamp */ 6162216Sdesint o_flag; /* -o: specify output file */ 6262216Sdesint o_directory; /* output file is a directory */ 6362216Sdeschar *o_filename; /* name of output file */ 6462216Sdesint o_stdout; /* output file is stdout */ 6562216Sdesint once_flag; /* -1: stop at first successful file */ 6662216Sdesint p_flag = 1; /* -[Pp]: use passive FTP */ 6762216Sdesint R_flag; /* -R: don't delete partially transferred files */ 6862216Sdesint r_flag; /* -r: restart previously interrupted transfer */ 6962216Sdesu_int T_secs = 0; /* -T: transfer timeout in seconds */ 7062216Sdesint s_flag; /* -s: show size, don't fetch */ 7162216Sdesoff_t S_size; /* -S: require size to match */ 7262216Sdesint t_flag; /*! -t: workaround TCP bug */ 7362216Sdesint v_level = 1; /* -v: verbosity level */ 7462216Sdesint v_tty; /* stdout is a tty */ 7562216Sdesu_int w_secs; /* -w: retry delay */ 7662216Sdesint family = PF_UNSPEC; /* -[46]: address family to use */ 7762216Sdes 7862216Sdes 7962216Sdesu_int ftp_timeout; /* default timeout for FTP transfers */ 8062216Sdesu_int http_timeout; /* default timeout for HTTP transfers */ 8162216Sdesu_char *buf; /* transfer buffer */ 8262216Sdes 8362216Sdes 8462216Sdesvoid 8562216Sdessig_handler(int sig) 8662216Sdes{ 8762216Sdes errx(1, "Transfer timed out"); 8862216Sdes} 8962216Sdes 9062216Sdesstruct xferstat { 9162216Sdes char name[40]; 9262216Sdes struct timeval start; 9362216Sdes struct timeval end; 9462216Sdes struct timeval last; 9562216Sdes off_t size; 9662216Sdes off_t offset; 9762216Sdes off_t rcvd; 9862216Sdes}; 9962216Sdes 10062216Sdesvoid 10162216Sdesstat_start(struct xferstat *xs, char *name, off_t size, off_t offset) 10262216Sdes{ 10362216Sdes snprintf(xs->name, sizeof xs->name, "%s", name); 10462216Sdes xs->size = size; 10562216Sdes xs->offset = offset; 10662216Sdes if (v_level) { 10762216Sdes fprintf(stderr, "Receiving %s", xs->name); 10862216Sdes if (xs->size != -1) 10962216Sdes fprintf(stderr, " (%lld bytes)", xs->size - xs->offset); 11062216Sdes } 11162216Sdes gettimeofday(&xs->start, NULL); 11262216Sdes xs->last = xs->start; 11362216Sdes} 11462216Sdes 11562216Sdesvoid 11662216Sdesstat_update(struct xferstat *xs, off_t rcvd) 11762216Sdes{ 11862216Sdes struct timeval now; 11962216Sdes 12062216Sdes xs->rcvd = rcvd; 12162216Sdes 12262216Sdes if (v_level <= 1 || !v_tty) 12362216Sdes return; 12462216Sdes 12562216Sdes gettimeofday(&now, NULL); 12662216Sdes if (now.tv_sec <= xs->last.tv_sec) 12762216Sdes return; 12862216Sdes xs->last = now; 12962216Sdes 13062216Sdes fprintf(stderr, "\rReceiving %s", xs->name); 13162216Sdes if (xs->size == -1) 13262216Sdes fprintf(stderr, ": %lld bytes", xs->rcvd - xs->offset); 13362216Sdes else 13462216Sdes fprintf(stderr, " (%lld bytes): %d%%", xs->size - xs->offset, 13562216Sdes (int)((100.0 * xs->rcvd) / (xs->size - xs->offset))); 13662216Sdes} 13762216Sdes 13862216Sdesvoid 13962216Sdesstat_end(struct xferstat *xs) 14062216Sdes{ 14162216Sdes double delta; 14262216Sdes double bps; 14362216Sdes 14462216Sdes gettimeofday(&xs->end, NULL); 14562216Sdes 14662216Sdes if (!v_level) 14762216Sdes return; 14862216Sdes 14962216Sdes fputc('\n', stderr); 15062216Sdes delta = (xs->end.tv_sec + (xs->end.tv_usec / 1.e6)) 15162216Sdes - (xs->start.tv_sec + (xs->start.tv_usec / 1.e6)); 15262216Sdes fprintf(stderr, "%lld bytes transferred in %.1f seconds ", 15362216Sdes xs->size - xs->offset, delta); 15462216Sdes bps = (xs->size - xs->offset) / delta; 15562216Sdes if (bps > 1024*1024) 15662216Sdes fprintf(stderr, "(%.2f MBps)\n", bps / (1024*1024)); 15762216Sdes else if (bps > 1024) 15862216Sdes fprintf(stderr, "(%.2f kBps)\n", bps / 1024); 15962216Sdes else 16062216Sdes fprintf(stderr, "(%.2f Bps)\n", bps); 16162216Sdes} 16262216Sdes 16362216Sdesint 16462216Sdesfetch(char *URL, char *path) 16562216Sdes{ 16662216Sdes struct url *url; 16762216Sdes struct url_stat us; 16862216Sdes struct stat sb; 16962216Sdes struct xferstat xs; 17062216Sdes FILE *f, *of; 17162216Sdes size_t size; 17262216Sdes off_t count; 17362216Sdes char flags[8]; 17462216Sdes int ch, n, r; 17562216Sdes u_int timeout; 17662216Sdes 17762216Sdes f = of = NULL; 17862216Sdes 17962216Sdes /* parse URL */ 18062216Sdes if ((url = fetchParseURL(URL)) == NULL) { 18162216Sdes warnx("%s: parse error", URL); 18262216Sdes goto failure; 18362216Sdes } 18462216Sdes 18562216Sdes timeout = 0; 18662216Sdes *flags = 0; 18762216Sdes 18862216Sdes /* common flags */ 18962216Sdes if (v_level > 2) 19062216Sdes strcat(flags, "v"); 19162216Sdes switch (family) { 19262216Sdes case PF_INET: 19362216Sdes strcat(flags, "4"); 19462216Sdes break; 19562216Sdes case PF_INET6: 19662216Sdes strcat(flags, "6"); 19762216Sdes break; 19862216Sdes } 19962216Sdes 20062216Sdes /* FTP specific flags */ 20162216Sdes if (strcmp(url->scheme, "ftp") == 0) { 20262216Sdes if (p_flag) 20362216Sdes strcat(flags, "p"); 20462216Sdes if (d_flag) 20562216Sdes strcat(flags, "d"); 20662216Sdes if (H_flag) 20762216Sdes strcat(flags, "h"); 20862216Sdes timeout = T_secs ? T_secs : ftp_timeout; 20962216Sdes } 21062216Sdes 21162216Sdes /* HTTP specific flags */ 21262216Sdes if (strcmp(url->scheme, "http") == 0) { 21362216Sdes if (d_flag) 21462216Sdes strcat(flags, "d"); 21562216Sdes if (A_flag) 21662216Sdes strcat(flags, "A"); 21762216Sdes timeout = T_secs ? T_secs : http_timeout; 21862216Sdes } 21962216Sdes 22062216Sdes /* 22162216Sdes * Set the protocol timeout. 22262216Sdes * This currently only works for FTP, so we still use 22362216Sdes * alarm(timeout) further down. 22462216Sdes */ 22562216Sdes fetchTimeout = timeout; 22662216Sdes 22762216Sdes /* stat remote file */ 22862216Sdes alarm(timeout); 22962216Sdes if (fetchStat(url, &us, flags) == -1) 23062216Sdes warnx("%s: size not known", path); 23162216Sdes alarm(timeout); 23262216Sdes 23362216Sdes /* just print size */ 23462216Sdes if (s_flag) { 23562216Sdes if (us.size == -1) 23662216Sdes printf("Unknown\n"); 23762216Sdes else 23862216Sdes printf("%lld\n", us.size); 23962216Sdes goto success; 24062216Sdes } 24162216Sdes 24262216Sdes /* check that size is as expected */ 24362216Sdes if (S_size && us.size != -1 && us.size != S_size) { 24462216Sdes warnx("%s: size mismatch: expected %lld, actual %lld", 24562216Sdes path, S_size, us.size); 24662216Sdes goto failure; 24762216Sdes } 24862216Sdes 24962216Sdes /* symlink instead of copy */ 25062216Sdes if (l_flag && strcmp(url->scheme, "file") == 0 && !o_stdout) { 25162216Sdes if (symlink(url->doc, path) == -1) { 25262216Sdes warn("%s: symlink()", path); 25362216Sdes goto failure; 25462216Sdes } 25562216Sdes goto success; 25662216Sdes } 25762216Sdes 25862216Sdes if (o_stdout) { 25962216Sdes /* output to stdout */ 26062216Sdes of = stdout; 26162216Sdes } else if (r_flag && us.size != -1 && stat(path, &sb) != -1 26262216Sdes && (F_flag || (us.mtime && sb.st_mtime == us.mtime))) { 26362216Sdes /* output to file, restart aborted transfer */ 26462216Sdes if (us.size == sb.st_size) 26562216Sdes goto success; 26662216Sdes else if (sb.st_size > us.size && truncate(path, us.size) == -1) { 26762216Sdes warn("%s: truncate()", path); 26862216Sdes goto failure; 26962216Sdes } 27062216Sdes if ((of = fopen(path, "a")) == NULL) { 27162216Sdes warn("%s: open()", path); 27262216Sdes goto failure; 27362216Sdes } 27462216Sdes url->offset = sb.st_size; 27562216Sdes } else if (m_flag && us.size != -1 && stat(path, &sb) != -1) { 27662216Sdes /* output to file, mirror mode */ 27762216Sdes warnx(" local: %lld bytes, mtime %ld", sb.st_size, sb.st_mtime); 27862216Sdes warnx("remote: %lld bytes, mtime %ld", us.size, us.mtime); 27962216Sdes if (sb.st_size == us.size && sb.st_mtime == us.mtime) 28062216Sdes return 0; 28162216Sdes if ((of = fopen(path, "w")) == NULL) { 28262216Sdes warn("%s: open()", path); 28362216Sdes goto failure; 28462216Sdes } 28562216Sdes } else { 28662216Sdes /* output to file, all other cases */ 28762216Sdes if ((of = fopen(path, "w")) == NULL) { 28862216Sdes warn("%s: open()", path); 28962216Sdes goto failure; 29062216Sdes } 29162216Sdes } 29262216Sdes count = url->offset; 29362216Sdes 29462216Sdes /* start the transfer */ 29562216Sdes if ((f = fetchGet(url, flags)) == NULL) { 29662216Sdes warnx("%s", fetchLastErrString); 29762245Sdes if (!R_flag && !r_flag && !o_stdout) 29862245Sdes unlink(path); 29962216Sdes goto failure; 30062216Sdes } 30162216Sdes 30262216Sdes /* start the counter */ 30362216Sdes stat_start(&xs, path, us.size, count); 30462216Sdes 30562216Sdes n = 0; 30662216Sdes 30762216Sdes if (us.size == -1) { 30862216Sdes /* 30962216Sdes * We have no idea how much data to expect, so do it byte by 31062216Sdes * byte. This is incredibly inefficient, but there's not much 31162216Sdes * we can do about it... :( 31262216Sdes */ 31362216Sdes while (1) { 31462216Sdes if (timeout) 31562216Sdes alarm(timeout); 31662216Sdes#ifdef STDIO_HACK 31762216Sdes /* 31862216Sdes * This is a non-portable hack, but it makes things go 31962216Sdes * faster. Basically, if there is data in the input file's 32062216Sdes * buffer, write it out; then fall through to the fgetc() 32162216Sdes * which forces a refill. It saves a memcpy() and reduces 32262216Sdes * the number of iterations, i.e the number of calls to 32362216Sdes * alarm(). Empirical evidence shows this can cut user 32462216Sdes * time by up to 90%. There may be better (even portable) 32562216Sdes * ways to do this. 32662216Sdes */ 32762216Sdes if (f->_r && (f->_ub._base == NULL)) { 32862216Sdes if (fwrite(f->_p, f->_r, 1, of) < 1) 32962216Sdes break; 33062216Sdes count += f->_r; 33162216Sdes f->_p += f->_r; 33262216Sdes f->_r = 0; 33362216Sdes } 33462216Sdes#endif 33562216Sdes if ((ch = fgetc(f)) == EOF || fputc(ch, of) == EOF) 33662216Sdes break; 33762216Sdes stat_update(&xs, count++); 33862216Sdes n++; 33962216Sdes } 34062216Sdes } else { 34162216Sdes /* we know exactly how much to transfer, so do it efficiently */ 34262216Sdes for (size = B_size; count != us.size; n++) { 34362216Sdes if (us.size - count < B_size) 34462216Sdes size = us.size - count; 34562216Sdes if (timeout) 34662216Sdes alarm(timeout); 34762216Sdes if (fread(buf, size, 1, f) != 1 || fwrite(buf, size, 1, of) != 1) 34862216Sdes break; 34962216Sdes stat_update(&xs, count += size); 35062216Sdes } 35162216Sdes } 35262216Sdes 35362216Sdes if (timeout) 35462216Sdes alarm(0); 35562216Sdes 35662216Sdes stat_end(&xs); 35762216Sdes 35862216Sdes /* check the status of our files */ 35962216Sdes if (ferror(f)) 36062216Sdes warn("%s", URL); 36162216Sdes if (ferror(of)) 36262216Sdes warn("%s", path); 36362216Sdes if (ferror(f) || ferror(of)) { 36462245Sdes if (!R_flag && !r_flag && !o_stdout) 36562216Sdes unlink(path); 36662216Sdes goto failure; 36762216Sdes } 36862216Sdes 36962216Sdes /* need to close the file before setting mtime */ 37062216Sdes if (of != stdout) { 37162216Sdes fclose(of); 37262216Sdes of = NULL; 37362216Sdes } 37462216Sdes 37562216Sdes /* Set mtime of local file */ 37662216Sdes if (m_flag && us.size != -1 && !o_stdout) { 37762216Sdes struct timeval tv[2]; 37862216Sdes 37962216Sdes tv[0].tv_sec = (long)us.atime; 38062216Sdes tv[1].tv_sec = (long)us.mtime; 38162216Sdes tv[0].tv_usec = tv[1].tv_usec = 0; 38262216Sdes if (utimes(path, tv)) 38362216Sdes warn("%s: utimes()", path); 38462216Sdes } 38562216Sdes 38662216Sdes success: 38762216Sdes r = 0; 38862216Sdes goto done; 38962216Sdes failure: 39062216Sdes r = -1; 39162216Sdes goto done; 39262216Sdes done: 39362216Sdes if (f) 39462216Sdes fclose(f); 39562216Sdes if (of && of != stdout) 39662216Sdes fclose(of); 39762216Sdes fetchFreeURL(url); 39862216Sdes return r; 39962216Sdes} 40062216Sdes 40162216Sdesvoid 40262216Sdesusage(void) 40362216Sdes{ 40462216Sdes /* XXX badly out of synch */ 40562216Sdes fprintf(stderr, 40662216Sdes "Usage: fetch [-1AFHMPRabdlmnpqrstv] [-o outputfile] [-S bytes]\n" 40762216Sdes " [-B bytes] [-T seconds] [-w seconds]\n" 40862216Sdes " [-f file -h host [-c dir] | URL ...]\n" 40962216Sdes ); 41062216Sdes} 41162216Sdes 41262216Sdes 41362216Sdes#define PARSENUM(NAME, TYPE) \ 41462216Sdesint \ 41562216SdesNAME(char *s, TYPE *v) \ 41662216Sdes{ \ 41762216Sdes *v = 0; \ 41862216Sdes for (*v = 0; *s; s++) \ 41962216Sdes if (isdigit(*s)) \ 42062216Sdes *v = *v * 10 + *s - '0'; \ 42162216Sdes else \ 42262216Sdes return -1; \ 42362216Sdes return 0; \ 42462216Sdes} 42562216Sdes 42662216SdesPARSENUM(parseint, u_int) 42762216SdesPARSENUM(parsesize, size_t) 42862216SdesPARSENUM(parseoff, off_t) 42962216Sdes 43062216Sdesint 43162216Sdesmain(int argc, char *argv[]) 43262216Sdes{ 43362216Sdes struct stat sb; 43462216Sdes char *p, *q, *s; 43562216Sdes int c, e, r; 43662216Sdes 43762216Sdes while ((c = getopt(argc, argv, 43862254Sdes "146AaB:bc:dFf:h:lHMmnPpo:qRrS:sT:tvw:")) != EOF) 43962216Sdes switch (c) { 44062216Sdes case '1': 44162216Sdes once_flag = 1; 44262216Sdes break; 44362216Sdes case '4': 44462216Sdes family = PF_INET; 44562216Sdes break; 44662216Sdes case '6': 44762216Sdes family = PF_INET6; 44862216Sdes break; 44962216Sdes case 'A': 45062216Sdes A_flag = 1; 45162216Sdes break; 45262216Sdes case 'a': 45362216Sdes a_flag = 1; 45462216Sdes break; 45562216Sdes case 'B': 45662216Sdes if (parsesize(optarg, &B_size) == -1) 45762216Sdes errx(1, "invalid buffer size"); 45862216Sdes break; 45962216Sdes case 'b': 46062216Sdes warnx("warning: the -b option is deprecated"); 46162216Sdes b_flag = 1; 46262216Sdes break; 46362254Sdes case 'c': 46462254Sdes c_dirname = optarg; 46562254Sdes break; 46662216Sdes case 'd': 46762216Sdes d_flag = 1; 46862216Sdes break; 46962216Sdes case 'F': 47062216Sdes F_flag = 1; 47162216Sdes break; 47262216Sdes case 'f': 47362216Sdes f_filename = optarg; 47462216Sdes break; 47562216Sdes case 'H': 47662216Sdes H_flag = 1; 47762216Sdes break; 47862216Sdes case 'h': 47962216Sdes h_hostname = optarg; 48062216Sdes break; 48162216Sdes case 'l': 48262216Sdes l_flag = 1; 48362216Sdes break; 48462216Sdes case 'o': 48562216Sdes o_flag = 1; 48662216Sdes o_filename = optarg; 48762216Sdes break; 48862216Sdes case 'M': 48962216Sdes case 'm': 49062216Sdes m_flag = 1; 49162216Sdes break; 49262216Sdes case 'n': 49362216Sdes m_flag = 0; 49462216Sdes break; 49562216Sdes case 'P': 49662216Sdes case 'p': 49762216Sdes p_flag = 1; 49862216Sdes break; 49962216Sdes case 'q': 50062216Sdes v_level = 0; 50162216Sdes break; 50262216Sdes case 'R': 50362216Sdes R_flag = 1; 50462216Sdes break; 50562216Sdes case 'r': 50662216Sdes r_flag = 1; 50762216Sdes break; 50862216Sdes case 'S': 50962216Sdes if (parseoff(optarg, &S_size) == -1) 51062216Sdes errx(1, "invalid size"); 51162216Sdes break; 51262216Sdes case 's': 51362216Sdes s_flag = 1; 51462216Sdes break; 51562216Sdes case 'T': 51662216Sdes if (parseint(optarg, &T_secs) == -1) 51762216Sdes errx(1, "invalid timeout"); 51862216Sdes break; 51962216Sdes case 't': 52062216Sdes t_flag = 1; 52162216Sdes warnx("warning: the -t option is deprecated"); 52262216Sdes break; 52362216Sdes case 'v': 52462216Sdes v_level++; 52562216Sdes break; 52662216Sdes case 'w': 52762216Sdes a_flag = 1; 52862216Sdes if (parseint(optarg, &w_secs) == -1) 52962216Sdes errx(1, "invalid delay"); 53062216Sdes break; 53162216Sdes default: 53262216Sdes usage(); 53362216Sdes exit(EX_USAGE); 53462216Sdes } 53562216Sdes 53662216Sdes argc -= optind; 53762216Sdes argv += optind; 53862216Sdes 53962254Sdes if (h_hostname || f_filename || c_dirname) { 54062216Sdes if (!h_hostname || !f_filename || argc) { 54162216Sdes usage(); 54262216Sdes exit(EX_USAGE); 54362216Sdes } 54462216Sdes /* XXX this is a hack. */ 54562216Sdes if (strcspn(h_hostname, "@:/") != strlen(h_hostname)) 54662216Sdes errx(1, "invalid hostname"); 54762254Sdes if (asprintf(argv, "ftp://%s/%s/%s", h_hostname, 54862254Sdes c_dirname ? c_dirname : "", f_filename) == -1) 54962216Sdes errx(1, strerror(ENOMEM)); 55062216Sdes argc++; 55162216Sdes } 55262216Sdes 55362216Sdes if (!argc) { 55462216Sdes usage(); 55562216Sdes exit(EX_USAGE); 55662216Sdes } 55762216Sdes 55862216Sdes /* allocate buffer */ 55962216Sdes if (B_size < MINBUFSIZE) 56062216Sdes B_size = MINBUFSIZE; 56162216Sdes if ((buf = malloc(B_size)) == NULL) 56262216Sdes errx(1, strerror(ENOMEM)); 56362216Sdes 56462216Sdes /* timeout handling */ 56562216Sdes signal(SIGALRM, sig_handler); 56662216Sdes if ((s = getenv("FTP_TIMEOUT")) != NULL) { 56762216Sdes if (parseint(s, &ftp_timeout) == -1) { 56862216Sdes warnx("FTP_TIMEOUT is not a positive integer"); 56962216Sdes ftp_timeout = 0; 57062216Sdes } 57162216Sdes } 57262216Sdes if ((s = getenv("HTTP_TIMEOUT")) != NULL) { 57362216Sdes if (parseint(s, &http_timeout) == -1) { 57462216Sdes warnx("HTTP_TIMEOUT is not a positive integer"); 57562216Sdes http_timeout = 0; 57662216Sdes } 57762216Sdes } 57862216Sdes 57962216Sdes /* output file */ 58062216Sdes if (o_flag) { 58162216Sdes if (strcmp(o_filename, "-") == 0) { 58262216Sdes o_stdout = 1; 58362216Sdes } else if (stat(o_filename, &sb) == -1) { 58462216Sdes if (errno == ENOENT) { 58562216Sdes if (argc > 1) 58662216Sdes errx(EX_USAGE, "%s is not a directory", o_filename); 58762216Sdes } else { 58862216Sdes err(EX_IOERR, "%s", o_filename); 58962216Sdes } 59062216Sdes } else { 59162216Sdes if (sb.st_mode & S_IFDIR) 59262216Sdes o_directory = 1; 59362216Sdes } 59462216Sdes } 59562216Sdes 59662216Sdes /* check if output is to a tty (for progress report) */ 59762216Sdes v_tty = isatty(STDOUT_FILENO); 59862216Sdes r = 0; 59962216Sdes 60062216Sdes while (argc) { 60162216Sdes if ((p = strrchr(*argv, '/')) == NULL) 60262216Sdes p = *argv; 60362216Sdes else 60462216Sdes p++; 60562216Sdes 60662216Sdes if (!*p) 60762216Sdes p = "fetch.out"; 60862216Sdes 60962216Sdes fetchLastErrCode = 0; 61062216Sdes 61162216Sdes if (o_flag) { 61262216Sdes if (o_stdout) { 61362216Sdes e = fetch(*argv, "-"); 61462216Sdes } else if (o_directory) { 61562216Sdes asprintf(&q, "%s/%s", o_filename, p); 61662216Sdes e = fetch(*argv, q); 61762216Sdes free(q); 61862216Sdes } else { 61962216Sdes e = fetch(*argv, o_filename); 62062216Sdes } 62162216Sdes } else { 62262216Sdes e = fetch(*argv, p); 62362216Sdes } 62462216Sdes 62562216Sdes if (e == 0 && once_flag) 62662216Sdes exit(0); 62762216Sdes 62862216Sdes if (e) { 62962216Sdes r = 1; 63062216Sdes if ((fetchLastErrCode 63162216Sdes && fetchLastErrCode != FETCH_UNAVAIL 63262216Sdes && fetchLastErrCode != FETCH_MOVED 63362216Sdes && fetchLastErrCode != FETCH_URL 63462216Sdes && fetchLastErrCode != FETCH_RESOLV 63562216Sdes && fetchLastErrCode != FETCH_UNKNOWN)) { 63662216Sdes if (w_secs) { 63762216Sdes if (v_level) 63862216Sdes fprintf(stderr, "Waiting %d seconds before retrying\n", w_secs); 63962216Sdes sleep(w_secs); 64062216Sdes } 64162216Sdes if (a_flag) 64262216Sdes continue; 64362216Sdes fprintf(stderr, "Skipping %s\n", *argv); 64462216Sdes } 64562216Sdes } 64662216Sdes 64762216Sdes argc--, argv++; 64862216Sdes } 64962216Sdes 65062216Sdes exit(r); 65162216Sdes} 652