xref: /minix/minix/commands/fetch/fetch.c (revision ebfedea0)
1 /*-
2  * Copyright (c) 2000-2004 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 
29 #if HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32 #if !defined(NETBSD) && !defined(__minix)
33 #include <nbcompat.h>
34 #endif
35 
36 #if HAVE_SYS_PARAM_H
37 #include <sys/param.h>
38 #endif
39 #if HAVE_SYS_IOCTL_H
40 #include <sys/ioctl.h>
41 #endif
42 #if HAVE_SYS_SOCKET_H
43 #include <sys/socket.h>
44 #endif
45 #if HAVE_SYS_STAT_H
46 #include <sys/stat.h>
47 #endif
48 #if HAVE_SYS_TIME_H
49 #include <sys/time.h>
50 #endif
51 #if HAVE_UTIME_H
52 #include <utime.h>
53 #endif
54 #include <ctype.h>
55 #if HAVE_ERR_H
56 #include <err.h>
57 #endif
58 #include <errno.h>
59 #include <signal.h>
60 #if HAVE_STDINT_H
61 #include <stdint.h>
62 #endif
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <string.h>
66 #if HAVE_TERMIOS_H
67 #include <termios.h>
68 #endif
69 #include <unistd.h>
70 
71 #include <fetch.h>
72 
73 #if HAVE_SYSEXITS_H
74 #include <sysexits.h>
75 #endif
76 
77 #ifndef EX_USAGE
78 #define	EX_USAGE	64
79 #endif
80 
81 #ifndef EX_IOERR
82 #define	EX_IOERR	74
83 #endif
84 
85 #define MINBUFSIZE	4096
86 
87 /* Option flags */
88 int	 A_flag;	/*    -A: do not follow 302 redirects */
89 int	 a_flag;	/*    -a: auto retry */
90 off_t	 B_size;	/*    -B: buffer size */
91 int	 d_flag;	/*    -d: direct connection */
92 int	 F_flag;	/*    -F: restart without checking mtime  */
93 int	 i_flag;	/*    -i: fetch file if modified */
94 int	 l_flag;	/*    -l: link rather than copy file: URLs */
95 int	 m_flag;	/* -[Mm]: mirror mode */
96 char	*N_filename;	/*    -N: netrc file name */
97 int	 n_flag;	/*    -n: do not preserve modification time */
98 int	 o_flag;	/*    -o: specify output file */
99 int	 o_directory;	/*        output file is a directory */
100 char	*o_filename;	/*        name of output file */
101 int	 o_stdout;	/*        output file is stdout */
102 int	 once_flag;	/*    -1: stop at first successful file */
103 int	 R_flag;	/*    -R: don't delete partially transferred files */
104 int	 r_flag;	/*    -r: restart previously interrupted transfer */
105 off_t	 S_size;        /*    -S: require size to match */
106 int	 s_flag;        /*    -s: show size, don't fetch */
107 long	 T_secs = 120;	/*    -T: transfer timeout in seconds */
108 int	 U_flag;	/*    -U: do not use high ports */
109 int	 v_level = 1;	/*    -v: verbosity level */
110 int	 v_tty;		/*        stdout is a tty */
111 pid_t	 pgrp;		/*        our process group */
112 long	 w_secs;	/*    -w: retry delay */
113 int	 family = PF_UNSPEC;	/* -[46]: address family to use */
114 
115 volatile int	 sigalrm;	/* SIGALRM received */
116 #ifdef SIGINFO
117 volatile int	 siginfo;	/* SIGINFO received */
118 #endif
119 volatile int	 sigint;	/* SIGINT received */
120 
121 long	 ftp_timeout;	/* default timeout for FTP transfers */
122 long	 http_timeout;	/* default timeout for HTTP transfers */
123 char	*buf;		/* transfer buffer */
124 
125 
126 /*
127  * Signal handler
128  */
129 static void
130 sig_handler(int sig)
131 {
132 	switch (sig) {
133 	case SIGALRM:
134 		fetchRestartCalls = 0;
135 		sigalrm = 1;
136 		break;
137 #ifdef SIGINFO
138 	case SIGINFO:
139 		siginfo = 1;
140 		break;
141 #endif
142 	case SIGINT:
143 		fetchRestartCalls = 0;
144 		sigint = 1;
145 		break;
146 	}
147 }
148 
149 struct xferstat {
150 	char		 name[64];
151 	struct timeval	 start;
152 	struct timeval	 last;
153 	off_t		 size;
154 	off_t		 offset;
155 	off_t		 rcvd;
156 };
157 
158 /*
159  * Compute and display ETA
160  */
161 static const char *
162 stat_eta(struct xferstat *xs)
163 {
164 	static char str[16];
165 	long elapsed, eta;
166 	off_t received, expected;
167 
168 	elapsed = xs->last.tv_sec - xs->start.tv_sec;
169 	received = xs->rcvd - xs->offset;
170 	expected = xs->size - xs->rcvd;
171 	eta = (long)((double)elapsed * expected / received);
172 	if (eta > 3600)
173 		snprintf(str, sizeof str, "%02ldh%02ldm",
174 		    eta / 3600, (eta % 3600) / 60);
175 	else
176 		snprintf(str, sizeof str, "%02ldm%02lds",
177 		    eta / 60, eta % 60);
178 	return (str);
179 }
180 
181 /*
182  * Format a number as "xxxx YB" where Y is ' ', 'k', 'M'...
183  */
184 static const char *prefixes = " kMGTP";
185 static const char *
186 stat_bytes(off_t bytes)
187 {
188 	static char str[16];
189 	const char *prefix = prefixes;
190 
191 	while (bytes > 9999 && prefix[1] != '\0') {
192 		bytes /= 1024;
193 		prefix++;
194 	}
195 	snprintf(str, sizeof str, "%4jd %cB", (intmax_t)bytes, *prefix);
196 	return (str);
197 }
198 
199 /*
200  * Compute and display transfer rate
201  */
202 static const char *
203 stat_bps(struct xferstat *xs)
204 {
205 	static char str[16];
206 	double delta, bps;
207 
208 	delta = (xs->last.tv_sec + (xs->last.tv_usec / 1.e6))
209 	    - (xs->start.tv_sec + (xs->start.tv_usec / 1.e6));
210 	if (delta == 0.0) {
211 		snprintf(str, sizeof str, "?? Bps");
212 	} else {
213 		bps = (xs->rcvd - xs->offset) / delta;
214 		snprintf(str, sizeof str, "%sps", stat_bytes((off_t)bps));
215 	}
216 	return (str);
217 }
218 
219 /*
220  * Update the stats display
221  */
222 static void
223 stat_display(struct xferstat *xs, int force)
224 {
225 	struct timeval now;
226 #if !defined(__minix)
227 	int ctty_pgrp;
228 #endif /* !defined(__minix) */
229 
230 	/* Minix returns "Not a typewriter error" */
231 #if defined(TIOCGPGRP) && !defined(__minix)
232 	/* check if we're the foreground process */
233 	if (ioctl(STDERR_FILENO, TIOCGPGRP, &ctty_pgrp) == -1 ||
234 	    (pid_t)ctty_pgrp != pgrp)
235 		return;
236 #endif
237 
238 	gettimeofday(&now, NULL);
239 	if (!force && now.tv_sec <= xs->last.tv_sec)
240 		return;
241 	xs->last = now;
242 
243 	fprintf(stderr, "\r%-46.46s", xs->name);
244 	if (xs->size <= 0) {
245 #if HAVE_SETPROCTITLE
246 		setproctitle("%s [%s]", xs->name, stat_bytes(xs->rcvd));
247 #endif
248 		fprintf(stderr, "        %s", stat_bytes(xs->rcvd));
249 	} else {
250 #if HAVE_SETPROCTITLE
251 		setproctitle("%s [%d%% of %s]", xs->name,
252 		    (int)((100.0 * xs->rcvd) / xs->size),
253 		    stat_bytes(xs->size));
254 #endif
255 		fprintf(stderr, "%3d%% of %s",
256 		    (int)((100.0 * xs->rcvd) / xs->size),
257 		    stat_bytes(xs->size));
258 	}
259 	fprintf(stderr, " %s", stat_bps(xs));
260 	if (xs->size > 0 && xs->rcvd > 0 &&
261 	    xs->last.tv_sec >= xs->start.tv_sec + 10)
262 		fprintf(stderr, " %s", stat_eta(xs));
263 	fflush(stderr);
264 }
265 
266 /*
267  * Initialize the transfer statistics
268  */
269 static void
270 stat_start(struct xferstat *xs, const char *name, off_t size, off_t offset)
271 {
272 	snprintf(xs->name, sizeof xs->name, "%s", name);
273 	gettimeofday(&xs->start, NULL);
274 	xs->last.tv_sec = xs->last.tv_usec = 0;
275 	xs->size = size;
276 	xs->offset = offset;
277 	xs->rcvd = offset;
278 	if (v_tty && v_level > 0)
279 		stat_display(xs, 1);
280 	else if (v_level > 0)
281 		fprintf(stderr, "%-46s", xs->name);
282 }
283 
284 /*
285  * Update the transfer statistics
286  */
287 static void
288 stat_update(struct xferstat *xs, off_t rcvd)
289 {
290 	xs->rcvd = rcvd;
291 	if (v_tty && v_level > 0)
292 		stat_display(xs, 0);
293 }
294 
295 /*
296  * Finalize the transfer statistics
297  */
298 static void
299 stat_end(struct xferstat *xs)
300 {
301 	gettimeofday(&xs->last, NULL);
302 	if (v_tty && v_level > 0) {
303 		stat_display(xs, 1);
304 		putc('\n', stderr);
305 	} else if (v_level > 0) {
306 		fprintf(stderr, "        %s %s\n",
307 		    stat_bytes(xs->size), stat_bps(xs));
308 	}
309 }
310 
311 #if HAVE_TERMIOS_H && !defined(PREFER_GETPASS)
312 static int
313 read_password(const char *prompt, char *pwbuf, size_t pwbuf_len)
314 {
315 	struct termios tios;
316 	tcflag_t saved_flags;
317 	int nopwd;
318 
319 	fprintf(stderr, "%s", prompt);
320 	if (tcgetattr(STDIN_FILENO, &tios) != 0)
321 		return (fgets(pwbuf, pwbuf_len, stdin) == NULL);
322 
323 	saved_flags = tios.c_lflag;
324 	tios.c_lflag &= ~ECHO;
325 	tios.c_lflag |= ECHONL|ICANON;
326 #ifndef __minix
327 	tcsetattr(STDIN_FILENO, TCSAFLUSH|TCSASOFT, &tios);
328 #else
329 	tcsetattr(STDIN_FILENO, TCSAFLUSH, &tios);
330 #endif
331 	nopwd = (fgets(pwbuf, pwbuf_len, stdin) == NULL);
332 	tios.c_lflag = saved_flags;
333 #ifndef __minix
334 	tcsetattr(STDIN_FILENO, TCSANOW|TCSASOFT, &tios);
335 #else
336 	tcsetattr(STDIN_FILENO, TCSANOW, &tios);
337 #endif
338 
339 	return nopwd;
340 }
341 #elif HAVE_GETPASSPHRASE || HAVE_GETPASS
342 static int
343 read_password(const char *prompt, char *pwbuf, size_t pwbuf_len)
344 {
345 	char *pass;
346 
347 #if HAVE_GETPASSPHRASE && !defined(PREFER_GETPASS)
348 	pass = getpassphrase(prompt);
349 #else
350 	pass = getpass(prompt);
351 #endif
352 	if (pass == NULL || strlen(pass) >= pwbuf_len)
353 		return 1;
354 	strcpy(pwbuf, pass);
355 	return 0;
356 }
357 #else
358 static int
359 read_password(const char *prompt, char *pwbuf, size_t pwbuf_len)
360 {
361 
362 	fprintf(stderr, prompt);
363 	return (fgets(pwbuf, pwbuf_len, stdin) == NULL);
364 }
365 #endif
366 
367 /*
368  * Ask the user for authentication details
369  */
370 static int
371 query_auth(struct url *URL)
372 {
373 	int i, nopwd;
374 
375 	fprintf(stderr, "Authentication required for <%s://%s:%d/>!\n",
376 	    URL->scheme, URL->host, URL->port);
377 
378 	fprintf(stderr, "Login: ");
379 	if (fgets(URL->user, sizeof URL->user, stdin) == NULL)
380 		return (-1);
381 	for (i = strlen(URL->user); i >= 0; --i)
382 		if (URL->user[i] == '\r' || URL->user[i] == '\n')
383 			URL->user[i] = '\0';
384 
385 	nopwd = read_password("Password: ",  URL->pwd, sizeof(URL->pwd));
386 
387 	if (nopwd)
388 		return (-1);
389 	for (i = strlen(URL->pwd); i >= 0; --i)
390 		if (URL->pwd[i] == '\r' || URL->pwd[i] == '\n')
391 			URL->pwd[i] = '\0';
392 
393 	return (0);
394 }
395 
396 /*
397  * Fetch a file
398  */
399 static int
400 fetch(char *URL, const char *path)
401 {
402 	struct url *url;
403 	struct url_stat us;
404 	struct stat sb, nsb;
405 	struct xferstat xs;
406 	FILE *of;
407 	fetchIO *f;
408 	size_t size, wr;
409 	ssize_t ssize;
410 	off_t count;
411 	char flags[8];
412 	char *tmppath;
413 	int r;
414 	unsigned timeout;
415 	char *ptr;
416 
417 	f = NULL;
418 	of = NULL;
419 	tmppath = NULL;
420 
421 	timeout = 0;
422 	*flags = 0;
423 	count = 0;
424 
425 	/* set verbosity level */
426 	if (v_level > 1)
427 		strcat(flags, "v");
428 	if (v_level > 2)
429 		fetchDebug = 1;
430 
431 	/* parse URL */
432 	if ((url = fetchParseURL(URL)) == NULL) {
433 		warnx("%s: parse error", URL);
434 		goto failure;
435 	}
436 
437 	/* if no scheme was specified, take a guess */
438 	if (!*url->scheme) {
439 		if (!*url->host)
440 			strcpy(url->scheme, SCHEME_FILE);
441 		else if (strncasecmp(url->host, "ftp.", 4) == 0)
442 			strcpy(url->scheme, SCHEME_FTP);
443 		else if (strncasecmp(url->host, "www.", 4) == 0)
444 			strcpy(url->scheme, SCHEME_HTTP);
445 	}
446 
447 	/* common flags */
448 	switch (family) {
449 	case PF_INET:
450 		strcat(flags, "4");
451 		break;
452 #ifndef __minix
453 	case PF_INET6:
454 		strcat(flags, "6");
455 		break;
456 #endif
457 	}
458 
459 	/* Protocol independent flags */
460 	if (i_flag) {
461 		if (stat(path, &sb) == 0) {
462 			url->last_modified = sb.st_mtime;
463 			strcat(flags, "i");
464 		} else if (errno != ENOENT) {
465 			warn("%s: stat()", path);
466 			goto failure;
467 		}
468 	}
469 
470 	/* FTP specific flags */
471 	if (strcmp(url->scheme, SCHEME_FTP) == 0) {
472 		if (d_flag)
473 			strcat(flags, "d");
474 		if (U_flag)
475 			strcat(flags, "l");
476 		timeout = T_secs ? T_secs : ftp_timeout;
477 	}
478 
479 	/* HTTP specific flags */
480 	if (strcmp(url->scheme, SCHEME_HTTP) == 0) {
481 		if (d_flag)
482 			strcat(flags, "d");
483 		if (A_flag)
484 			strcat(flags, "A");
485 		timeout = T_secs ? T_secs : http_timeout;
486 	}
487 
488 	/* set the protocol timeout. */
489 	fetchTimeout = timeout;
490 
491 	/* just print size */
492 	if (s_flag) {
493 		if (timeout)
494 			alarm(timeout);
495 		r = fetchStat(url, &us, flags);
496 		if (timeout)
497 			alarm(0);
498 		if (sigalrm || sigint)
499 			goto signal;
500 		if (r == -1) {
501 			warnx("%s", fetchLastErrString);
502 			goto failure;
503 		}
504 		if (us.size == -1)
505 			printf("Unknown\n");
506 		else
507 			printf("%jd\n", (intmax_t)us.size);
508 		goto success;
509 	}
510 
511 	/*
512 	 * If the -r flag was specified, we have to compare the local
513 	 * and remote files, so we should really do a fetchStat()
514 	 * first, but I know of at least one HTTP server that only
515 	 * sends the content size in response to GET requests, and
516 	 * leaves it out of replies to HEAD requests.  Also, in the
517 	 * (frequent) case that the local and remote files match but
518 	 * the local file is truncated, we have sufficient information
519 	 * before the compare to issue a correct request.  Therefore,
520 	 * we always issue a GET request as if we were sure the local
521 	 * file was a truncated copy of the remote file; we can drop
522 	 * the connection later if we change our minds.
523 	 */
524 	sb.st_size = -1;
525 	if (!o_stdout) {
526 		r = stat(path, &sb);
527 		if (r == 0 && r_flag && S_ISREG(sb.st_mode)) {
528 			url->offset = sb.st_size;
529 		} else if (r == -1 || !S_ISREG(sb.st_mode)) {
530 			/*
531 			 * Whatever value sb.st_size has now is either
532 			 * wrong (if stat(2) failed) or irrelevant (if the
533 			 * path does not refer to a regular file)
534 			 */
535 			sb.st_size = -1;
536 		}
537 		if (r == -1 && errno != ENOENT) {
538 			warnx("%s: stat()", path);
539 			goto failure;
540 		}
541 	}
542 
543 	/* start the transfer */
544 	if (timeout)
545 		alarm(timeout);
546 	f = fetchXGet(url, &us, flags);
547 	if (timeout)
548 		alarm(0);
549 	if (sigalrm || sigint)
550 		goto signal;
551 	if (f == NULL && i_flag && fetchLastErrCode == FETCH_UNCHANGED) {
552 		/* URL was not modified, return OK. */
553 		printf("%s: not modified\n", URL);
554 		r = 0;
555 		goto done;
556 	}
557 	if (f == NULL) {
558 		warnx("%s: %s", URL, fetchLastErrString);
559 		goto failure;
560 	}
561 	if (sigint)
562 		goto signal;
563 
564 	/* check that size is as expected */
565 	if (S_size) {
566 		if (us.size == -1) {
567 			warnx("%s: size unknown", URL);
568 		} else if (us.size != S_size) {
569 			warnx("%s: size mismatch: expected %jd, actual %jd",
570 			    URL, (intmax_t)S_size, (intmax_t)us.size);
571 			goto failure;
572 		}
573 	}
574 
575 	/* symlink instead of copy */
576 	if (l_flag && strcmp(url->scheme, "file") == 0 && !o_stdout) {
577 		char *name = fetchUnquotePath(url);
578 		if (name == NULL) {
579 			warnx("Can't unquote URL");
580 			goto failure;
581 		}
582 		if (symlink(name, path) == -1) {
583 			warn("%s: symlink()", path);
584 			free(name);
585 			goto failure;
586 		}
587 		free(name);
588 		goto success;
589 	}
590 
591 	if (us.size == -1 && !o_stdout && v_level > 0)
592 		warnx("%s: size of remote file is not known", URL);
593 	if (v_level > 1) {
594 		if (sb.st_size != -1)
595 			fprintf(stderr, "local size / mtime: %jd / %ld\n",
596 			    (intmax_t)sb.st_size, (long)sb.st_mtime);
597 		if (us.size != -1)
598 			fprintf(stderr, "remote size / mtime: %jd / %ld\n",
599 			    (intmax_t)us.size, (long)us.mtime);
600 	}
601 
602 	/* open output file */
603 	if (o_stdout) {
604 		/* output to stdout */
605 		of = stdout;
606 	} else if (r_flag && sb.st_size != -1) {
607 		/* resume mode, local file exists */
608 		if (!F_flag && us.mtime && sb.st_mtime != us.mtime) {
609 			/* no match! have to refetch */
610 			fetchIO_close(f);
611 			/* if precious, warn the user and give up */
612 			if (R_flag) {
613 				warnx("%s: local modification time "
614 				    "does not match remote", path);
615 				goto failure_keep;
616 			}
617 		} else if (us.size != -1) {
618 			if (us.size == sb.st_size)
619 				/* nothing to do */
620 				goto success;
621 			if (sb.st_size > us.size) {
622 				/* local file too long! */
623 				warnx("%s: local file (%jd bytes) is longer "
624 				    "than remote file (%jd bytes)", path,
625 				    (intmax_t)sb.st_size, (intmax_t)us.size);
626 				goto failure;
627 			}
628 			/* we got it, open local file */
629 			if ((of = fopen(path, "a")) == NULL) {
630 				warn("%s: fopen()", path);
631 				goto failure;
632 			}
633 			/* check that it didn't move under our feet */
634 			if (fstat(fileno(of), &nsb) == -1) {
635 				/* can't happen! */
636 				warn("%s: fstat()", path);
637 				goto failure;
638 			}
639 			if (nsb.st_dev != sb.st_dev ||
640 			    nsb.st_ino != nsb.st_ino ||
641 			    nsb.st_size != sb.st_size) {
642 				warnx("%s: file has changed", URL);
643 				fclose(of);
644 				of = NULL;
645 				sb = nsb;
646 			}
647 		}
648 	} else if (m_flag && sb.st_size != -1) {
649 		/* mirror mode, local file exists */
650 		if (sb.st_size == us.size && sb.st_mtime == us.mtime)
651 			goto success;
652 	}
653 
654 	if (of == NULL) {
655 		/*
656 		 * We don't yet have an output file; either this is a
657 		 * vanilla run with no special flags, or the local and
658 		 * remote files didn't match.
659 		 */
660 
661 		if (url->offset > 0) {
662 			/*
663 			 * We tried to restart a transfer, but for
664 			 * some reason gave up - so we have to restart
665 			 * from scratch if we want the whole file
666 			 */
667 			url->offset = 0;
668 			if ((f = fetchXGet(url, &us, flags)) == NULL) {
669 				warnx("%s: %s", URL, fetchLastErrString);
670 				goto failure;
671 			}
672 			if (sigint)
673 				goto signal;
674 		}
675 
676 		/* construct a temp file name */
677 		if (sb.st_size != -1 && S_ISREG(sb.st_mode)) {
678 #ifndef __minix
679 			asprintf(&tmppath, "%s.fetch.XXXXXX", path);
680 #else
681 			{
682 				int len;
683 				if((tmppath = malloc(sizeof(char)*MINBUFSIZE)) != NULL) {
684 					len = snprintf(tmppath, MINBUFSIZE, "%s.fetch.XXXXXX", path);
685 					if(len >= MINBUFSIZE) {
686 						free(tmppath);
687 						tmppath = NULL;
688 					}
689 				}
690 			}
691 #endif
692 
693 			if (tmppath != NULL) {
694 				int fd;
695 
696 				fd = mkstemp(tmppath);
697 				if (fd == -1) {
698 					warn("%s: mkstemp failed", tmppath);
699 					goto failure;
700 				}
701 				fchown(fd, sb.st_uid, sb.st_gid);
702 				fchmod(fd, sb.st_mode & ALLPERMS);
703 				of = fdopen(fd, "w");
704 				if (of == NULL) {
705 					close(fd);
706 					unlink(tmppath);
707 					free(tmppath);
708 					tmppath = NULL;
709 				}
710 			}
711 		}
712 		if (of == NULL)
713 			of = fopen(path, "w");
714 		if (of == NULL) {
715 			warn("%s: open()", path);
716 			goto failure;
717 		}
718 	}
719 	count = url->offset;
720 
721 	/* start the counter */
722 	stat_start(&xs, path, us.size, count);
723 
724 	sigalrm = sigint = 0;
725 
726 	/* suck in the data */
727 #ifdef SIGINFO
728 	siginfo = 0;
729 	signal(SIGINFO, sig_handler);
730 #endif
731 	while (!sigint) {
732 		if (us.size != -1 && us.size - count < B_size &&
733 		    us.size - count >= 0)
734 			size = us.size - count;
735 		else
736 			size = B_size;
737 #ifdef SIGINFO
738 		if (siginfo) {
739 			stat_display(&xs, 1);
740 			siginfo = 0;
741 		}
742 #else
743 		/* Constant info is better than none. */
744 		if (v_level) {
745 			stat_display(&xs, 1);
746 		}
747 #endif
748 		if ((ssize = fetchIO_read(f, buf, B_size)) == 0)
749 			break;
750 		if (ssize == -1 && errno == EINTR)
751 			continue;
752 		if (ssize == -1)
753 			break;
754 		size = ssize;
755 		stat_update(&xs, count += size);
756 		for (ptr = buf; size > 0; ptr += wr, size -= wr) {
757 			if ((wr = fwrite(ptr, 1, size, of)) < size) {
758 				if (ferror(of) && errno == EINTR && !sigint)
759 					clearerr(of);
760 				else
761 					break;
762 			}
763 		}
764 		if (size != 0)
765 			break;
766 	}
767 	if (!sigalrm)
768 		sigalrm = 0;
769 #ifdef SIGINFO
770 	signal(SIGINFO, SIG_DFL);
771 #endif
772 
773 	stat_end(&xs);
774 
775 	/*
776 	 * If the transfer timed out or was interrupted, we still want to
777 	 * set the mtime in case the file is not removed (-r or -R) and
778 	 * the user later restarts the transfer.
779 	 */
780  signal:
781 	/* set mtime of local file */
782 	if (!n_flag && us.mtime && !o_stdout && of != NULL &&
783 	    (stat(path, &sb) != -1) && sb.st_mode & S_IFREG) {
784 		struct timeval tv[2];
785 
786 		fflush(of);
787 		tv[0].tv_sec = (long)(us.atime ? us.atime : us.mtime);
788 		tv[1].tv_sec = (long)us.mtime;
789 		tv[0].tv_usec = tv[1].tv_usec = 0;
790 		if (utimes(tmppath ? tmppath : path, tv))
791 			warn("%s: utimes()", tmppath ? tmppath : path);
792 	}
793 
794 	/* timed out or interrupted? */
795 	if (fetchLastErrCode == FETCH_TIMEOUT)
796 		sigalrm = 1;
797 	if (sigalrm)
798 		warnx("transfer timed out");
799 	if (sigint) {
800 		warnx("transfer interrupted");
801 		goto failure;
802 	}
803 
804 	/* timeout / interrupt before connection completley established? */
805 	if (f == NULL)
806 		goto failure;
807 
808 	if (!sigalrm && ferror(of)) {
809 		/* check the status of our files */
810 		warn("writing to %s failed", path);
811 		goto failure;
812 	}
813 
814 	/* did the transfer complete normally? */
815 	if (us.size != -1 && count < us.size) {
816 		warnx("%s appears to be truncated: %jd/%jd bytes",
817 		    path, (intmax_t)count, (intmax_t)us.size);
818 		goto failure_keep;
819 	}
820 
821 	/*
822 	 * If the transfer timed out and we didn't know how much to
823 	 * expect, assume the worst (i.e. we didn't get all of it)
824 	 */
825 	if (sigalrm && us.size == -1) {
826 		warnx("%s may be truncated", path);
827 		goto failure_keep;
828 	}
829 
830  success:
831 	r = 0;
832 	if (tmppath != NULL && rename(tmppath, path) == -1) {
833 		warn("%s: rename()", path);
834 		goto failure_keep;
835 	}
836 	goto done;
837  failure:
838 	if (of && of != stdout && !R_flag && !r_flag)
839 		if (stat(path, &sb) != -1 && (sb.st_mode & S_IFREG))
840 			unlink(tmppath ? tmppath : path);
841 	if (R_flag && tmppath != NULL && sb.st_size == -1)
842 		rename(tmppath, path); /* ignore errors here */
843  failure_keep:
844 	r = -1;
845 	goto done;
846  done:
847 	if (f)
848 		fetchIO_close(f);
849 	if (of && of != stdout)
850 		fclose(of);
851 	if (url)
852 		fetchFreeURL(url);
853 	if (tmppath != NULL)
854 		free(tmppath);
855 	return (r);
856 }
857 
858 static void
859 usage(void)
860 {
861 #ifndef __minix
862 	fprintf(stderr, "%s\n%s\n%s\n",
863 	    "usage: fetch [-146AFMPRUadilmnpqrsv] [-N netrc] [-o outputfile]",
864 	    "             [-S bytes] [-B bytes] [-T seconds] [-w seconds]",
865 	    "             [-h host -f file [-c dir] | URL ...]");
866 #else
867 	fprintf(stderr, "%s\n%s\n%s\n",
868 	    "usage: fetch [-146AFMPRUadilmnpqrsv] [-N netrc] [-o outputfile]",
869 	    "             [-S bytes] [-B bytes] [-T seconds] [-w seconds]",
870 	    "             [-h host -f file [-c dir] | URL ...]");
871 #endif
872 }
873 
874 
875 /*
876  * Entry point
877  */
878 int
879 main(int argc, char *argv[])
880 {
881 	struct stat sb;
882 	struct sigaction sa;
883 	const char *p, *s;
884 	char *end, *q;
885 	int c, e, r;
886 #ifndef __minix
887 	while ((c = getopt(argc, argv,
888 	    "14AaB:dFilMmN:no:qRrS:sT:Uvw:")) != -1)
889 #else
890 	while ((c = getopt(argc, argv,
891 	    "146AaB:dFilMmN:no:qRrS:sT:Uvw:")) != -1)
892 #endif
893 		switch (c) {
894 		case '1':
895 			once_flag = 1;
896 			break;
897 		case '4':
898 			family = PF_INET;
899 			break;
900 #ifndef __minix
901 		case '6':
902 			family = PF_INET6;
903 			break;
904 #endif
905 		case 'A':
906 			A_flag = 1;
907 			break;
908 		case 'a':
909 			a_flag = 1;
910 			break;
911 		case 'B':
912 			B_size = (off_t)strtol(optarg, &end, 10);
913 			if (*optarg == '\0' || *end != '\0')
914 				errx(1, "invalid buffer size (%s)", optarg);
915 			break;
916 		case 'd':
917 			d_flag = 1;
918 			break;
919 		case 'F':
920 			F_flag = 1;
921 			break;
922 		case 'i':
923 			i_flag = 1;
924 			break;
925 		case 'l':
926 			l_flag = 1;
927 			break;
928 		case 'o':
929 			o_flag = 1;
930 			o_filename = optarg;
931 			break;
932 		case 'M':
933 		case 'm':
934 			if (r_flag)
935 				errx(1, "the -m and -r flags "
936 				    "are mutually exclusive");
937 			m_flag = 1;
938 			break;
939 		case 'N':
940 			N_filename = optarg;
941 			break;
942 		case 'n':
943 			n_flag = 1;
944 			break;
945 		case 'q':
946 			v_level = 0;
947 			break;
948 		case 'R':
949 			R_flag = 1;
950 			break;
951 		case 'r':
952 			if (m_flag)
953 				errx(1, "the -m and -r flags "
954 				    "are mutually exclusive");
955 			r_flag = 1;
956 			break;
957 		case 'S':
958 			S_size = (off_t)strtol(optarg, &end, 10);
959 			if (*optarg == '\0' || *end != '\0')
960 				errx(1, "invalid size (%s)", optarg);
961 			break;
962 		case 's':
963 			s_flag = 1;
964 			break;
965 		case 'T':
966 			T_secs = strtol(optarg, &end, 10);
967 			if (*optarg == '\0' || *end != '\0')
968 				errx(1, "invalid timeout (%s)", optarg);
969 			break;
970 		case 'U':
971 			U_flag = 1;
972 			break;
973 		case 'v':
974 			v_level++;
975 			break;
976 		case 'w':
977 			a_flag = 1;
978 			w_secs = strtol(optarg, &end, 10);
979 			if (*optarg == '\0' || *end != '\0')
980 				errx(1, "invalid delay (%s)", optarg);
981 			break;
982 		default:
983 			usage();
984 			exit(EX_USAGE);
985 		}
986 
987 	argc -= optind;
988 	argv += optind;
989 
990 	if (!argc) {
991 		usage();
992 		exit(EX_USAGE);
993 	}
994 
995 	fetchConnectionCacheInit(10, 1);
996 
997 	/* allocate buffer */
998 	if (B_size < MINBUFSIZE)
999 		B_size = MINBUFSIZE;
1000 	if ((buf = malloc(B_size)) == NULL)
1001 		errx(1, "%s", strerror(ENOMEM));
1002 
1003 	/* timeouts */
1004 	if ((s = getenv("FTP_TIMEOUT")) != NULL) {
1005 		ftp_timeout = strtol(s, &end, 10);
1006 		if (*s == '\0' || *end != '\0' || ftp_timeout < 0) {
1007 			warnx("FTP_TIMEOUT (%s) is not a positive integer", s);
1008 			ftp_timeout = 0;
1009 		}
1010 	}
1011 	if ((s = getenv("HTTP_TIMEOUT")) != NULL) {
1012 		http_timeout = strtol(s, &end, 10);
1013 		if (*s == '\0' || *end != '\0' || http_timeout < 0) {
1014 			warnx("HTTP_TIMEOUT (%s) is not a positive integer", s);
1015 			http_timeout = 0;
1016 		}
1017 	}
1018 
1019 	/* signal handling */
1020 	sa.sa_flags = 0;
1021 	sa.sa_handler = sig_handler;
1022 	sigemptyset(&sa.sa_mask);
1023 	sigaction(SIGALRM, &sa, NULL);
1024 	sa.sa_flags = SA_RESETHAND;
1025 	sigaction(SIGINT, &sa, NULL);
1026 
1027 	/* output file */
1028 	if (o_flag) {
1029 		if (strcmp(o_filename, "-") == 0) {
1030 			o_stdout = 1;
1031 			if (i_flag) {
1032 				warnx("-i and -o - are incompatible, dropping -i");
1033 				i_flag = 0;
1034 			}
1035 		} else if (stat(o_filename, &sb) == -1) {
1036 			if (errno == ENOENT) {
1037 				if (argc > 1)
1038 					errx(EX_USAGE, "%s is not a directory",
1039 					    o_filename);
1040 			} else {
1041 				err(EX_IOERR, "%s", o_filename);
1042 			}
1043 		} else {
1044 			if (sb.st_mode & S_IFDIR)
1045 				o_directory = 1;
1046 		}
1047 	}
1048 
1049 	/* check if output is to a tty (for progress report) */
1050 	v_tty = isatty(STDERR_FILENO);
1051 	if (v_tty)
1052 		pgrp = getpgrp();
1053 
1054 	r = 0;
1055 
1056 	/* authentication */
1057 	if (v_tty)
1058 		fetchAuthMethod = query_auth;
1059 	if (N_filename != NULL)
1060 		setenv("NETRC", N_filename, 1);
1061 
1062 	while (argc) {
1063 		if ((p = strrchr(*argv, '/')) == NULL)
1064 			p = *argv;
1065 		else
1066 			p++;
1067 
1068 		if (!*p)
1069 			p = "fetch.out";
1070 
1071 		fetchLastErrCode = 0;
1072 
1073 		if (o_flag) {
1074 			if (o_stdout) {
1075 				e = fetch(*argv, "-");
1076 			} else if (o_directory) {
1077 #ifndef __minix
1078 				asprintf(&q, "%s/%s", o_filename, p);
1079 #else
1080 				{
1081 					int len;
1082 
1083 					if ((q = malloc(sizeof(char)*MINBUFSIZE)) != NULL) {
1084 						len = snprintf(q, MINBUFSIZE, "%s/%s", o_filename, p);
1085 						if (len >= MINBUFSIZE) {
1086 							free(q);
1087 							q = NULL;
1088 						}
1089 					}else{
1090 						err(1, "Unable to allocate memory");
1091 					}
1092 				}
1093 #endif
1094 				e = fetch(*argv, q);
1095 				free(q);
1096 			} else {
1097 				e = fetch(*argv, o_filename);
1098 			}
1099 		} else {
1100 			e = fetch(*argv, p);
1101 		}
1102 
1103 		if (sigint)
1104 			kill(getpid(), SIGINT);
1105 
1106 		if (e == 0 && once_flag)
1107 			exit(0);
1108 
1109 		if (e) {
1110 			r = 1;
1111 			if ((fetchLastErrCode
1112 			    && fetchLastErrCode != FETCH_UNAVAIL
1113 			    && fetchLastErrCode != FETCH_MOVED
1114 			    && fetchLastErrCode != FETCH_URL
1115 			    && fetchLastErrCode != FETCH_RESOLV
1116 			    && fetchLastErrCode != FETCH_UNKNOWN)) {
1117 				if (w_secs && v_level)
1118 					fprintf(stderr, "Waiting %ld seconds "
1119 					    "before retrying\n", w_secs);
1120 				if (w_secs)
1121 					sleep(w_secs);
1122 				if (a_flag)
1123 					continue;
1124 			}
1125 		}
1126 
1127 		argc--, argv++;
1128 	}
1129 
1130 	exit(r);
1131 }
1132