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