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