fetch.c revision 77576
1/*-
2 * Copyright (c) 2000 Dag-Erling Co�dan Sm�rgrav
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer
10 *    in this position and unchanged.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 *    derived from this software without specific prior written permission
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 *	$FreeBSD: head/usr.bin/fetch/fetch.c 77576 2001-06-01 10:24:58Z des $
29 */
30
31#include <sys/param.h>
32#include <sys/stat.h>
33#include <sys/socket.h>
34
35#include <ctype.h>
36#include <err.h>
37#include <errno.h>
38#include <signal.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <string.h>
42#include <sysexits.h>
43#include <termios.h>
44#include <unistd.h>
45
46#include <fetch.h>
47
48#define MINBUFSIZE	4096
49
50/* Option flags */
51int	 A_flag;	/*    -A: do not follow 302 redirects */
52int	 a_flag;	/*    -a: auto retry */
53size_t	 B_size;	/*    -B: buffer size */
54int	 b_flag;	/*!   -b: workaround TCP bug */
55char    *c_dirname;	/*    -c: remote directory */
56int	 d_flag;	/*    -d: direct connection */
57int	 F_flag;	/*    -F: restart without checking mtime  */
58char	*f_filename;	/*    -f: file to fetch */
59char	*h_hostname;	/*    -h: host to fetch from */
60int	 l_flag;	/*    -l: link rather than copy file: URLs */
61int	 m_flag;	/* -[Mm]: mirror mode */
62int	 n_flag;	/*    -n: do not preserve modification time */
63int	 o_flag;	/*    -o: specify output file */
64int	 o_directory;	/*        output file is a directory */
65char	*o_filename;	/*        name of output file */
66int	 o_stdout;	/*        output file is stdout */
67int	 once_flag;	/*    -1: stop at first successful file */
68int	 p_flag;	/* -[Pp]: use passive FTP */
69int	 R_flag;	/*    -R: don't delete partially transferred files */
70int	 r_flag;	/*    -r: restart previously interrupted transfer */
71off_t	 S_size;        /*    -S: require size to match */
72int	 s_flag;        /*    -s: show size, don't fetch */
73u_int	 T_secs = 0;	/*    -T: transfer timeout in seconds */
74int	 t_flag;	/*!   -t: workaround TCP bug */
75int	 U_flag;	/*    -U: do not use high ports */
76int	 v_level = 1;	/*    -v: verbosity level */
77int	 v_tty;		/*        stdout is a tty */
78u_int	 w_secs;	/*    -w: retry delay */
79int	 family = PF_UNSPEC;	/* -[46]: address family to use */
80
81int	 sigalrm;	/* SIGALRM received */
82int	 siginfo;	/* SIGINFO received */
83int	 sigint;	/* SIGINT received */
84
85u_int	 ftp_timeout;	/* default timeout for FTP transfers */
86u_int	 http_timeout;	/* default timeout for HTTP transfers */
87u_char	*buf;		/* transfer buffer */
88
89
90void
91sig_handler(int sig)
92{
93    switch (sig) {
94    case SIGALRM:
95	sigalrm = 1;
96	break;
97    case SIGINFO:
98	siginfo = 1;
99	break;
100    case SIGINT:
101	sigint = 1;
102	break;
103    }
104}
105
106struct xferstat {
107    char		 name[40];
108    struct timeval	 start;
109    struct timeval	 end;
110    struct timeval	 last;
111    off_t		 size;
112    off_t		 offset;
113    off_t		 rcvd;
114};
115
116void
117stat_display(struct xferstat *xs, int force)
118{
119    struct timeval now;
120
121    if (!v_tty || !v_level)
122	return;
123
124    gettimeofday(&now, NULL);
125    if (!force && now.tv_sec <= xs->last.tv_sec)
126	return;
127    xs->last = now;
128
129    fprintf(stderr, "\rReceiving %s", xs->name);
130    if (xs->size <= 0)
131	fprintf(stderr, ": %lld bytes", xs->rcvd);
132    else
133	fprintf(stderr, " (%lld bytes): %d%%", xs->size,
134		(int)((100.0 * xs->rcvd) / xs->size));
135}
136
137void
138stat_start(struct xferstat *xs, char *name, off_t size, off_t offset)
139{
140    snprintf(xs->name, sizeof xs->name, "%s", name);
141    gettimeofday(&xs->start, NULL);
142    xs->last.tv_sec = xs->last.tv_usec = 0;
143    xs->end = xs->last;
144    xs->size = size;
145    xs->offset = offset;
146    xs->rcvd = offset;
147    stat_display(xs, 1);
148}
149
150void
151stat_update(struct xferstat *xs, off_t rcvd, int force)
152{
153    xs->rcvd = rcvd;
154    stat_display(xs, 0);
155}
156
157void
158stat_end(struct xferstat *xs)
159{
160    double delta;
161    double bps;
162
163    if (!v_level)
164	return;
165
166    gettimeofday(&xs->end, NULL);
167
168    stat_display(xs, 1);
169    fputc('\n', stderr);
170    delta = (xs->end.tv_sec + (xs->end.tv_usec / 1.e6))
171	- (xs->start.tv_sec + (xs->start.tv_usec / 1.e6));
172    fprintf(stderr, "%lld bytes transferred in %.1f seconds ",
173	    xs->rcvd - xs->offset, delta);
174    bps = (xs->rcvd - xs->offset) / delta;
175    if (bps > 1024*1024)
176	fprintf(stderr, "(%.2f MBps)\n", bps / (1024*1024));
177    else if (bps > 1024)
178	fprintf(stderr, "(%.2f kBps)\n", bps / 1024);
179    else
180	fprintf(stderr, "(%.2f Bps)\n", bps);
181}
182
183int
184query_auth(struct url *URL)
185{
186    struct termios tios;
187    tcflag_t saved_flags;
188    int i, nopwd;
189
190
191    fprintf(stderr, "Authentication required for <%s://%s:%d/>!\n",
192	    URL->scheme, URL->host, URL->port, URL->doc);
193
194    fprintf(stderr, "Login: ");
195    if (fgets(URL->user, sizeof URL->user, stdin) == NULL)
196	return -1;
197    for (i = 0; URL->user[i]; ++i)
198	if (isspace(URL->user[i]))
199	    URL->user[i] = '\0';
200
201    fprintf(stderr, "Password: ");
202    if (tcgetattr(STDIN_FILENO, &tios) == 0) {
203	saved_flags = tios.c_lflag;
204	tios.c_lflag &= ~ECHO;
205	tios.c_lflag |= ECHONL|ICANON;
206	tcsetattr(STDIN_FILENO, TCSAFLUSH|TCSASOFT, &tios);
207	nopwd = (fgets(URL->pwd, sizeof URL->pwd, stdin) == NULL);
208	tios.c_lflag = saved_flags;
209	tcsetattr(STDIN_FILENO, TCSANOW|TCSASOFT, &tios);
210    } else {
211	nopwd = (fgets(URL->pwd, sizeof URL->pwd, stdin) == NULL);
212    }
213    if (nopwd)
214	return -1;
215
216    for (i = 0; URL->pwd[i]; ++i)
217	if (isspace(URL->pwd[i]))
218	    URL->pwd[i] = '\0';
219    return 0;
220}
221
222int
223fetch(char *URL, char *path)
224{
225    struct url *url;
226    struct url_stat us;
227    struct stat sb;
228    struct xferstat xs;
229    FILE *f, *of;
230    size_t size, wr;
231    off_t count;
232    char flags[8];
233    int r;
234    u_int timeout;
235    u_char *ptr;
236
237    f = of = NULL;
238
239    /* parse URL */
240    if ((url = fetchParseURL(URL)) == NULL) {
241	warnx("%s: parse error", URL);
242	goto failure;
243    }
244
245    /* if no scheme was specified, take a guess */
246    if (!*url->scheme) {
247	if (!*url->host)
248	    strcpy(url->scheme, SCHEME_FILE);
249	else if (strncasecmp(url->host, "ftp.", 4))
250	    strcpy(url->scheme, SCHEME_FTP);
251	else if (strncasecmp(url->host, "www.", 4))
252	    strcpy(url->scheme, SCHEME_HTTP);
253    }
254
255    timeout = 0;
256    *flags = 0;
257    count = 0;
258
259    /* common flags */
260    if (v_level > 1)
261	strcat(flags, "v");
262    switch (family) {
263    case PF_INET:
264	strcat(flags, "4");
265	break;
266    case PF_INET6:
267	strcat(flags, "6");
268	break;
269    }
270
271    /* FTP specific flags */
272    if (strcmp(url->scheme, "ftp") == 0) {
273	if (p_flag)
274	    strcat(flags, "p");
275	if (d_flag)
276	    strcat(flags, "d");
277	if (U_flag)
278	    strcat(flags, "l");
279	timeout = T_secs ? T_secs : ftp_timeout;
280    }
281
282    /* HTTP specific flags */
283    if (strcmp(url->scheme, "http") == 0) {
284	if (d_flag)
285	    strcat(flags, "d");
286	if (A_flag)
287	    strcat(flags, "A");
288	timeout = T_secs ? T_secs : http_timeout;
289    }
290
291    /* set the protocol timeout. */
292    fetchTimeout = timeout;
293
294    /* just print size */
295    if (s_flag) {
296	if (fetchStat(url, &us, flags) == -1)
297	    goto failure;
298	if (us.size == -1)
299	    printf("Unknown\n");
300	else
301	    printf("%lld\n", us.size);
302	goto success;
303    }
304
305    /*
306     * If the -r flag was specified, we have to compare the local and
307     * remote files, so we should really do a fetchStat() first, but I
308     * know of at least one HTTP server that only sends the content
309     * size in response to GET requests, and leaves it out of replies
310     * to HEAD requests. Also, in the (frequent) case that the local
311     * and remote files match but the local file is truncated, we have
312     * sufficient information *before* the compare to issue a correct
313     * request. Therefore, we always issue a GET request as if we were
314     * sure the local file was a truncated copy of the remote file; we
315     * can drop the connection later if we change our minds.
316     */
317    if ((r_flag  || m_flag) && !o_stdout && stat(path, &sb) != -1) {
318	if (r_flag)
319	    url->offset = sb.st_size;
320    } else {
321	sb.st_size = -1;
322    }
323
324    /* start the transfer */
325    if ((f = fetchXGet(url, &us, flags)) == NULL) {
326	warnx("%s: %s", path, fetchLastErrString);
327	goto failure;
328    }
329    if (sigint)
330	goto signal;
331
332    /* check that size is as expected */
333    if (S_size) {
334	if (us.size == -1) {
335	    warnx("%s: size unknown", path);
336	    goto failure;
337	} else if (us.size != S_size) {
338	    warnx("%s: size mismatch: expected %lld, actual %lld",
339		  path, S_size, us.size);
340	    goto failure;
341	}
342    }
343
344    /* symlink instead of copy */
345    if (l_flag && strcmp(url->scheme, "file") == 0 && !o_stdout) {
346	if (symlink(url->doc, path) == -1) {
347	    warn("%s: symlink()", path);
348	    goto failure;
349	}
350	goto success;
351    }
352
353    if (us.size == -1)
354	warnx("%s: size of remote file is not known", path);
355    if (v_level > 1) {
356	if (sb.st_size != -1)
357	    fprintf(stderr, "local size / mtime: %lld / %ld\n",
358		    sb.st_size, (long)sb.st_mtime);
359	if (us.size != -1)
360	    fprintf(stderr, "remote size / mtime: %lld / %ld\n",
361		    us.size, (long)us.mtime);
362    }
363
364    /* open output file */
365    if (o_stdout) {
366	/* output to stdout */
367	of = stdout;
368    } else if (sb.st_size != -1) {
369	/* resume mode, local file exists */
370	if (!F_flag && us.mtime && sb.st_mtime != us.mtime) {
371	    /* no match! have to refetch */
372	    fclose(f);
373	    /* if precious, warn the user and give up */
374	    if (R_flag) {
375		warnx("%s: local modification time does not match remote",
376		      path);
377		goto failure_keep;
378	    }
379	    url->offset = 0;
380	    if ((f = fetchXGet(url, &us, flags)) == NULL) {
381		warnx("%s: %s", path, fetchLastErrString);
382		goto failure;
383	    }
384	    if (sigint)
385		goto signal;
386	} else {
387	    if (us.size == sb.st_size)
388		/* nothing to do */
389		goto success;
390	    if (sb.st_size > us.size) {
391		/* local file too long! */
392		warnx("%s: local file (%lld bytes) is longer "
393		      "than remote file (%lld bytes)",
394		      path, sb.st_size, us.size);
395		goto failure;
396	    }
397	    /* we got through, open local file and seek to offset */
398	    /*
399	     * XXX there's a race condition here - the file we open is not
400	     * necessarily the same as the one we stat()'ed earlier...
401	     */
402	    if ((of = fopen(path, "a")) == NULL) {
403		warn("%s: fopen()", path);
404		goto failure;
405	    }
406	    if (fseek(of, url->offset, SEEK_SET) == -1) {
407		warn("%s: fseek()", path);
408		goto failure;
409	    }
410	}
411    }
412    if (m_flag && sb.st_size != -1) {
413	/* mirror mode, local file exists */
414	if (sb.st_size == us.size && sb.st_mtime == us.mtime)
415	    goto success;
416    }
417    if (!of) {
418	/*
419	 * We don't yet have an output file; either this is a vanilla
420	 * run with no special flags, or the local and remote files
421	 * didn't match.
422	 */
423	if ((of = fopen(path, "w")) == NULL) {
424	    warn("%s: open()", path);
425	    goto failure;
426	}
427    }
428    count = url->offset;
429
430    /* start the counter */
431    stat_start(&xs, path, us.size, count);
432
433    sigalrm = siginfo = sigint = 0;
434
435    /* suck in the data */
436    signal(SIGINFO, sig_handler);
437    while (!sigint && !sigalrm) {
438	if (us.size != -1 && us.size - count < B_size)
439	    size = us.size - count;
440	else
441	    size = B_size;
442	if (timeout)
443	    alarm(timeout);
444	if (siginfo) {
445	    stat_end(&xs);
446	    siginfo = 0;
447	}
448	if ((size = fread(buf, 1, size, f)) == 0) {
449	    if (ferror(f) && errno == EINTR && !sigalrm && !sigint)
450		clearerr(f);
451	    else
452		break;
453	}
454	if (timeout)
455	    alarm(0);
456	stat_update(&xs, count += size, 0);
457	for (ptr = buf; size > 0; ptr += wr, size -= wr)
458	    if ((wr = fwrite(ptr, 1, size, of)) < size) {
459		if (ferror(of) && errno == EINTR && !sigalrm && !sigint)
460		    clearerr(of);
461		else
462		    break;
463	    }
464	if (size != 0)
465	    break;
466    }
467    signal(SIGINFO, SIG_DFL);
468
469    if (timeout)
470	alarm(0);
471
472    stat_end(&xs);
473
474    /* set mtime of local file */
475    if (!n_flag && us.mtime && !o_stdout
476	&& (stat(path, &sb) != -1) && sb.st_mode & S_IFREG) {
477	struct timeval tv[2];
478
479	fflush(of);
480	tv[0].tv_sec = (long)(us.atime ? us.atime : us.mtime);
481	tv[1].tv_sec = (long)us.mtime;
482	tv[0].tv_usec = tv[1].tv_usec = 0;
483	if (utimes(path, tv))
484	    warn("%s: utimes()", path);
485    }
486
487    /* timed out or interrupted? */
488 signal:
489    if (sigalrm)
490	warnx("transfer timed out");
491    if (sigint) {
492	warnx("transfer interrupted");
493	goto failure;
494    }
495
496    if (!sigalrm) {
497	/* check the status of our files */
498	if (ferror(f))
499	    warn("%s", URL);
500	if (ferror(of))
501	    warn("%s", path);
502	if (ferror(f) || ferror(of))
503	    goto failure;
504    }
505
506    /* did the transfer complete normally? */
507    if (us.size != -1 && count < us.size) {
508	warnx("%s appears to be truncated: %lld/%lld bytes",
509	      path, count, us.size);
510	goto failure_keep;
511    }
512
513    /*
514     * If the transfer timed out and we didn't know how much to
515     * expect, assume the worst (i.e. we didn't get all of it)
516     */
517    if (sigalrm && us.size == -1) {
518	warnx("%s may be truncated", path);
519	goto failure_keep;
520    }
521
522 success:
523    r = 0;
524    goto done;
525 failure:
526    if (of && of != stdout && !R_flag && !r_flag)
527	if (stat(path, &sb) != -1 && (sb.st_mode & S_IFREG))
528	    unlink(path);
529 failure_keep:
530    r = -1;
531    goto done;
532 done:
533    if (f)
534	fclose(f);
535    if (of && of != stdout)
536	fclose(of);
537    if (url)
538	fetchFreeURL(url);
539    return r;
540}
541
542void
543usage(void)
544{
545    fprintf(stderr,
546	    "Usage: fetch [-146AFMPRUadlmnpqrsv] [-o outputfile] [-S bytes]\n"
547	    "             [-B bytes] [-T seconds] [-w seconds]\n"
548	    "             [-h host -f file [-c dir] | URL ...]\n"
549	);
550}
551
552
553#define PARSENUM(NAME, TYPE)		\
554int					\
555NAME(char *s, TYPE *v)			\
556{					\
557    *v = 0;				\
558    for (*v = 0; *s; s++)		\
559	if (isdigit(*s))		\
560	    *v = *v * 10 + *s - '0';	\
561	else				\
562	    return -1;			\
563    return 0;				\
564}
565
566PARSENUM(parseint, u_int)
567PARSENUM(parsesize, size_t)
568PARSENUM(parseoff, off_t)
569
570int
571main(int argc, char *argv[])
572{
573    struct stat sb;
574    struct sigaction sa;
575    char *p, *q, *s;
576    int c, e, r;
577
578    while ((c = getopt(argc, argv,
579		       "146AaB:bc:dFf:Hh:lMmnPpo:qRrS:sT:tUvw:")) != EOF)
580	switch (c) {
581	case '1':
582	    once_flag = 1;
583	    break;
584	case '4':
585	    family = PF_INET;
586	    break;
587	case '6':
588	    family = PF_INET6;
589	    break;
590	case 'A':
591	    A_flag = 1;
592	    break;
593	case 'a':
594	    a_flag = 1;
595	    break;
596	case 'B':
597	    if (parsesize(optarg, &B_size) == -1)
598		errx(1, "invalid buffer size");
599	    break;
600	case 'b':
601	    warnx("warning: the -b option is deprecated");
602	    b_flag = 1;
603	    break;
604	case 'c':
605	    c_dirname = optarg;
606	    break;
607	case 'd':
608	    d_flag = 1;
609	    break;
610	case 'F':
611	    F_flag = 1;
612	    break;
613	case 'f':
614	    f_filename = optarg;
615	    break;
616	case 'H':
617	    warnx("The -H option is now implicit, use -U to disable\n");
618	    break;
619	case 'h':
620	    h_hostname = optarg;
621	    break;
622	case 'l':
623	    l_flag = 1;
624	    break;
625	case 'o':
626	    o_flag = 1;
627	    o_filename = optarg;
628	    break;
629	case 'M':
630	case 'm':
631	    if (r_flag)
632		errx(1, "the -m and -r flags are mutually exclusive");
633	    m_flag = 1;
634	    break;
635	case 'n':
636	    n_flag = 1;
637	    break;
638	case 'P':
639	case 'p':
640	    p_flag = 1;
641	    break;
642	case 'q':
643	    v_level = 0;
644	    break;
645	case 'R':
646	    R_flag = 1;
647	    break;
648	case 'r':
649	    if (m_flag)
650		errx(1, "the -m and -r flags are mutually exclusive");
651	    r_flag = 1;
652	    break;
653	case 'S':
654	    if (parseoff(optarg, &S_size) == -1)
655		errx(1, "invalid size");
656	    break;
657	case 's':
658	    s_flag = 1;
659	    break;
660	case 'T':
661	    if (parseint(optarg, &T_secs) == -1)
662		errx(1, "invalid timeout");
663	    break;
664	case 't':
665	    t_flag = 1;
666	    warnx("warning: the -t option is deprecated");
667	    break;
668	case 'U':
669	    U_flag = 1;
670	    break;
671	case 'v':
672	    v_level++;
673	    break;
674	case 'w':
675	    a_flag = 1;
676	    if (parseint(optarg, &w_secs) == -1)
677		errx(1, "invalid delay");
678	    break;
679	default:
680	    usage();
681	    exit(EX_USAGE);
682	}
683
684    argc -= optind;
685    argv += optind;
686
687    if (h_hostname || f_filename || c_dirname) {
688	if (!h_hostname || !f_filename || argc) {
689	    usage();
690	    exit(EX_USAGE);
691	}
692	/* XXX this is a hack. */
693	if (strcspn(h_hostname, "@:/") != strlen(h_hostname))
694	    errx(1, "invalid hostname");
695	if (asprintf(argv, "ftp://%s/%s/%s", h_hostname,
696		     c_dirname ? c_dirname : "", f_filename) == -1)
697	    errx(1, "%s", strerror(ENOMEM));
698	argc++;
699    }
700
701    if (!argc) {
702	usage();
703	exit(EX_USAGE);
704    }
705
706    /* allocate buffer */
707    if (B_size < MINBUFSIZE)
708	B_size = MINBUFSIZE;
709    if ((buf = malloc(B_size)) == NULL)
710	errx(1, "%s", strerror(ENOMEM));
711
712    /* timeouts */
713    if ((s = getenv("FTP_TIMEOUT")) != NULL) {
714	if (parseint(s, &ftp_timeout) == -1) {
715	    warnx("FTP_TIMEOUT is not a positive integer");
716	    ftp_timeout = 0;
717	}
718    }
719    if ((s = getenv("HTTP_TIMEOUT")) != NULL) {
720	if (parseint(s, &http_timeout) == -1) {
721	    warnx("HTTP_TIMEOUT is not a positive integer");
722	    http_timeout = 0;
723	}
724    }
725
726    /* signal handling */
727    sa.sa_flags = 0;
728    sa.sa_handler = sig_handler;
729    sigemptyset(&sa.sa_mask);
730    sigaction(SIGALRM, &sa, NULL);
731    sa.sa_flags = SA_RESETHAND;
732    sigaction(SIGINT, &sa, NULL);
733    fetchRestartCalls = 0;
734
735    /* output file */
736    if (o_flag) {
737	if (strcmp(o_filename, "-") == 0) {
738	    o_stdout = 1;
739	} else if (stat(o_filename, &sb) == -1) {
740	    if (errno == ENOENT) {
741		if (argc > 1)
742		    errx(EX_USAGE, "%s is not a directory", o_filename);
743	    } else {
744		err(EX_IOERR, "%s", o_filename);
745	    }
746	} else {
747	    if (sb.st_mode & S_IFDIR)
748		o_directory = 1;
749	}
750    }
751
752    /* check if output is to a tty (for progress report) */
753    v_tty = isatty(STDERR_FILENO);
754    r = 0;
755
756    /* authentication */
757    if (v_tty)
758	fetchAuthMethod = query_auth;
759
760    while (argc) {
761	if ((p = strrchr(*argv, '/')) == NULL)
762	    p = *argv;
763	else
764	    p++;
765
766	if (!*p)
767	    p = "fetch.out";
768
769	fetchLastErrCode = 0;
770
771	if (o_flag) {
772	    if (o_stdout) {
773		e = fetch(*argv, "-");
774	    } else if (o_directory) {
775		asprintf(&q, "%s/%s", o_filename, p);
776		e = fetch(*argv, q);
777		free(q);
778	    } else {
779		e = fetch(*argv, o_filename);
780	    }
781	} else {
782	    e = fetch(*argv, p);
783	}
784
785	if (sigint)
786	    kill(getpid(), SIGINT);
787
788	if (e == 0 && once_flag)
789	    exit(0);
790
791	if (e) {
792	    r = 1;
793	    if ((fetchLastErrCode
794		 && fetchLastErrCode != FETCH_UNAVAIL
795		 && fetchLastErrCode != FETCH_MOVED
796		 && fetchLastErrCode != FETCH_URL
797		 && fetchLastErrCode != FETCH_RESOLV
798		 && fetchLastErrCode != FETCH_UNKNOWN)) {
799		if (w_secs) {
800		    if (v_level)
801			fprintf(stderr, "Waiting %d seconds before retrying\n",
802				w_secs);
803		    sleep(w_secs);
804		}
805		if (a_flag)
806		    continue;
807	    }
808	}
809
810	argc--, argv++;
811    }
812
813    exit(r);
814}
815