1 /* $NetBSD: util.c,v 1.35 1998/11/06 16:53:29 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 1998 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, 9 * NASA Ames Research Center. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by the NetBSD 22 * Foundation, Inc. and its contributors. 23 * 4. Neither the name of The NetBSD Foundation nor the names of its 24 * contributors may be used to endorse or promote products derived 25 * from this software without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 * POSSIBILITY OF SUCH DAMAGE. 38 */ 39 40 /* 41 * Copyright (c) 1985, 1989, 1993, 1994 42 * The Regents of the University of California. All rights reserved. 43 * 44 * Redistribution and use in source and binary forms, with or without 45 * modification, are permitted provided that the following conditions 46 * are met: 47 * 1. Redistributions of source code must retain the above copyright 48 * notice, this list of conditions and the following disclaimer. 49 * 2. Redistributions in binary form must reproduce the above copyright 50 * notice, this list of conditions and the following disclaimer in the 51 * documentation and/or other materials provided with the distribution. 52 * 3. All advertising materials mentioning features or use of this software 53 * must display the following acknowledgement: 54 * This product includes software developed by the University of 55 * California, Berkeley and its contributors. 56 * 4. Neither the name of the University nor the names of its contributors 57 * may be used to endorse or promote products derived from this software 58 * without specific prior written permission. 59 * 60 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 61 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 62 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 63 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 64 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 65 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 66 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 67 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 68 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 69 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 70 * SUCH DAMAGE. 71 */ 72 73 #include <sys/cdefs.h> 74 #ifndef lint 75 __RCSID("$NetBSD: util.c,v 1.35 1998/11/06 16:53:29 christos Exp $"); 76 #endif /* not lint */ 77 78 /* 79 * FTP User Program -- Misc support routines 80 */ 81 #include <sys/types.h> 82 #include <sys/socket.h> 83 #include <sys/ioctl.h> 84 #include <sys/time.h> 85 #include <arpa/ftp.h> 86 87 #include <ctype.h> 88 #include <err.h> 89 #include <fcntl.h> 90 #include <glob.h> 91 #include <termios.h> 92 #include <signal.h> 93 #include <limits.h> 94 #include <pwd.h> 95 #include <stdio.h> 96 #include <stdlib.h> 97 #include <string.h> 98 #include <time.h> 99 #include <tzfile.h> 100 #include <unistd.h> 101 102 #include "ftp_var.h" 103 #include "pathnames.h" 104 105 /* 106 * Connect to peer server and 107 * auto-login, if possible. 108 */ 109 void 110 setpeer(argc, argv) 111 int argc; 112 char *argv[]; 113 { 114 char *host; 115 in_port_t port; 116 117 if (connected) { 118 fprintf(ttyout, "Already connected to %s, use close first.\n", 119 hostname); 120 code = -1; 121 return; 122 } 123 if (argc < 2) 124 (void)another(&argc, &argv, "to"); 125 if (argc < 2 || argc > 3) { 126 fprintf(ttyout, "usage: %s host-name [port]\n", argv[0]); 127 code = -1; 128 return; 129 } 130 if (gatemode) 131 port = gateport; 132 else 133 port = ftpport; 134 if (argc > 2) { 135 char *ep; 136 long nport; 137 138 nport = strtol(argv[2], &ep, 10); 139 if (nport < 1 || nport > MAX_IN_PORT_T || *ep != '\0') { 140 fprintf(ttyout, "%s: bad port number '%s'.\n", 141 argv[1], argv[2]); 142 fprintf(ttyout, "usage: %s host-name [port]\n", 143 argv[0]); 144 code = -1; 145 return; 146 } 147 port = htons((in_port_t)nport); 148 } 149 150 if (gatemode) { 151 if (gateserver == NULL || *gateserver == '\0') 152 errx(1, "gateserver not defined (shouldn't happen)"); 153 host = hookup(gateserver, port); 154 } else 155 host = hookup(argv[1], port); 156 157 if (host) { 158 int overbose; 159 160 if (gatemode) { 161 if (command("PASSERVE %s", argv[1]) != COMPLETE) 162 return; 163 if (verbose) 164 fprintf(ttyout, 165 "Connected via pass-through server %s\n", 166 gateserver); 167 } 168 169 connected = 1; 170 /* 171 * Set up defaults for FTP. 172 */ 173 (void)strcpy(typename, "ascii"), type = TYPE_A; 174 curtype = TYPE_A; 175 (void)strcpy(formname, "non-print"), form = FORM_N; 176 (void)strcpy(modename, "stream"), mode = MODE_S; 177 (void)strcpy(structname, "file"), stru = STRU_F; 178 (void)strcpy(bytename, "8"), bytesize = 8; 179 if (autologin) 180 (void)ftp_login(argv[1], NULL, NULL); 181 182 overbose = verbose; 183 if (debug == 0) 184 verbose = -1; 185 if (command("SYST") == COMPLETE && overbose) { 186 char *cp, c; 187 c = 0; 188 cp = strchr(reply_string + 4, ' '); 189 if (cp == NULL) 190 cp = strchr(reply_string + 4, '\r'); 191 if (cp) { 192 if (cp[-1] == '.') 193 cp--; 194 c = *cp; 195 *cp = '\0'; 196 } 197 198 fprintf(ttyout, "Remote system type is %s.\n", 199 reply_string + 4); 200 if (cp) 201 *cp = c; 202 } 203 if (!strncmp(reply_string, "215 UNIX Type: L8", 17)) { 204 if (proxy) 205 unix_proxy = 1; 206 else 207 unix_server = 1; 208 /* 209 * Set type to 0 (not specified by user), 210 * meaning binary by default, but don't bother 211 * telling server. We can use binary 212 * for text files unless changed by the user. 213 */ 214 type = 0; 215 (void)strcpy(typename, "binary"); 216 if (overbose) 217 fprintf(ttyout, 218 "Using %s mode to transfer files.\n", 219 typename); 220 } else { 221 if (proxy) 222 unix_proxy = 0; 223 else 224 unix_server = 0; 225 if (overbose && 226 !strncmp(reply_string, "215 TOPS20", 10)) 227 fputs( 228 "Remember to set tenex mode when transferring binary files from this machine.\n", 229 ttyout); 230 } 231 verbose = overbose; 232 } 233 } 234 235 /* 236 * login to remote host, using given username & password if supplied 237 */ 238 int 239 ftp_login(host, user, pass) 240 const char *host; 241 const char *user, *pass; 242 { 243 char tmp[80]; 244 const char *acct; 245 char anonpass[MAXLOGNAME + 2]; /* "user@" */ 246 struct passwd *pw; 247 int n, aflag = 0; 248 249 acct = NULL; 250 if (user == NULL) { 251 if (ruserpass(host, &user, &pass, &acct) < 0) { 252 code = -1; 253 return (0); 254 } 255 } 256 257 /* 258 * Set up arguments for an anonymous FTP session, if necessary. 259 */ 260 if ((user == NULL || pass == NULL) && anonftp) { 261 memset(anonpass, 0, sizeof(anonpass)); 262 263 /* 264 * Set up anonymous login password. 265 */ 266 if ((pass = getenv("FTPANONPASS")) == NULL) { 267 if ((pass = getlogin()) == NULL) { 268 if ((pw = getpwuid(getuid())) == NULL) 269 pass = "anonymous"; 270 else 271 pass = pw->pw_name; 272 } 273 /* 274 * Every anonymous FTP server I've encountered 275 * will accept the string "username@", and will 276 * append the hostname itself. We do this by default 277 * since many servers are picky about not having 278 * a FQDN in the anonymous password. 279 * - thorpej@netbsd.org 280 */ 281 snprintf(anonpass, sizeof(anonpass) - 1, "%s@", pass); 282 pass = anonpass; 283 } 284 user = "anonymous"; /* as per RFC 1635 */ 285 } 286 287 while (user == NULL) { 288 const char *myname = getlogin(); 289 290 if (myname == NULL && (pw = getpwuid(getuid())) != NULL) 291 myname = pw->pw_name; 292 if (myname) 293 fprintf(ttyout, "Name (%s:%s): ", host, myname); 294 else 295 fprintf(ttyout, "Name (%s): ", host); 296 *tmp = '\0'; 297 (void)fgets(tmp, sizeof(tmp) - 1, stdin); 298 tmp[strlen(tmp) - 1] = '\0'; 299 if (*tmp == '\0') 300 user = myname; 301 else 302 user = tmp; 303 } 304 n = command("USER %s", user); 305 if (n == CONTINUE) { 306 if (pass == NULL) 307 pass = getpass("Password:"); 308 n = command("PASS %s", pass); 309 } 310 if (n == CONTINUE) { 311 aflag++; 312 if (acct == NULL) 313 acct = getpass("Account:"); 314 n = command("ACCT %s", acct); 315 } 316 if ((n != COMPLETE) || 317 (!aflag && acct != NULL && command("ACCT %s", acct) != COMPLETE)) { 318 warnx("Login failed."); 319 return (0); 320 } 321 if (proxy) 322 return (1); 323 connected = -1; 324 for (n = 0; n < macnum; ++n) { 325 if (!strcmp("init", macros[n].mac_name)) { 326 (void)strcpy(line, "$init"); 327 makeargv(); 328 domacro(margc, margv); 329 break; 330 } 331 } 332 return (1); 333 } 334 335 /* 336 * `another' gets another argument, and stores the new argc and argv. 337 * It reverts to the top level (via main.c's intr()) on EOF/error. 338 * 339 * Returns false if no new arguments have been added. 340 */ 341 int 342 another(pargc, pargv, prompt) 343 int *pargc; 344 char ***pargv; 345 const char *prompt; 346 { 347 int len = strlen(line), ret; 348 349 if (len >= sizeof(line) - 3) { 350 fputs("sorry, arguments too long.\n", ttyout); 351 intr(); 352 } 353 fprintf(ttyout, "(%s) ", prompt); 354 line[len++] = ' '; 355 if (fgets(&line[len], sizeof(line) - len, stdin) == NULL) 356 intr(); 357 len += strlen(&line[len]); 358 if (len > 0 && line[len - 1] == '\n') 359 line[len - 1] = '\0'; 360 makeargv(); 361 ret = margc > *pargc; 362 *pargc = margc; 363 *pargv = margv; 364 return (ret); 365 } 366 367 /* 368 * glob files given in argv[] from the remote server. 369 * if errbuf isn't NULL, store error messages there instead 370 * of writing to the screen. 371 */ 372 char * 373 remglob(argv, doswitch, errbuf) 374 char *argv[]; 375 int doswitch; 376 char **errbuf; 377 { 378 char temp[MAXPATHLEN]; 379 static char buf[MAXPATHLEN]; 380 static FILE *ftemp = NULL; 381 static char **args; 382 int oldverbose, oldhash, fd; 383 char *cp, *mode; 384 385 if (!mflag) { 386 if (!doglob) 387 args = NULL; 388 else { 389 if (ftemp) { 390 (void)fclose(ftemp); 391 ftemp = NULL; 392 } 393 } 394 return (NULL); 395 } 396 if (!doglob) { 397 if (args == NULL) 398 args = argv; 399 if ((cp = *++args) == NULL) 400 args = NULL; 401 return (cp); 402 } 403 if (ftemp == NULL) { 404 (void)snprintf(temp, sizeof(temp), "%s/%s", tmpdir, TMPFILE); 405 if ((fd = mkstemp(temp)) < 0) { 406 warn("unable to create temporary file %s", temp); 407 return (NULL); 408 } 409 close(fd); 410 oldverbose = verbose; 411 verbose = (errbuf != NULL) ? -1 : 0; 412 oldhash = hash; 413 hash = 0; 414 if (doswitch) 415 pswitch(!proxy); 416 for (mode = "w"; *++argv != NULL; mode = "a") 417 recvrequest("NLST", temp, *argv, mode, 0, 0); 418 if ((code / 100) != COMPLETE) { 419 if (errbuf != NULL) 420 *errbuf = reply_string; 421 } 422 if (doswitch) 423 pswitch(!proxy); 424 verbose = oldverbose; 425 hash = oldhash; 426 ftemp = fopen(temp, "r"); 427 (void)unlink(temp); 428 if (ftemp == NULL) { 429 if (errbuf == NULL) 430 fputs( 431 "can't find list of remote files, oops.\n", 432 ttyout); 433 else 434 *errbuf = 435 "can't find list of remote files, oops."; 436 return (NULL); 437 } 438 } 439 if (fgets(buf, sizeof(buf), ftemp) == NULL) { 440 (void)fclose(ftemp); 441 ftemp = NULL; 442 return (NULL); 443 } 444 if ((cp = strchr(buf, '\n')) != NULL) 445 *cp = '\0'; 446 return (buf); 447 } 448 449 int 450 confirm(cmd, file) 451 const char *cmd, *file; 452 { 453 char line[BUFSIZ]; 454 455 if (!interactive || confirmrest) 456 return (1); 457 fprintf(ttyout, "%s %s? ", cmd, file); 458 (void)fflush(ttyout); 459 if (fgets(line, sizeof(line), stdin) == NULL) 460 return (0); 461 switch (tolower(*line)) { 462 case 'n': 463 return (0); 464 case 'p': 465 interactive = 0; 466 fputs("Interactive mode: off.\n", ttyout); 467 break; 468 case 'a': 469 confirmrest = 1; 470 fprintf(ttyout, "Prompting off for duration of %s.\n", 471 cmd); 472 break; 473 } 474 return (1); 475 } 476 477 /* 478 * Glob a local file name specification with 479 * the expectation of a single return value. 480 * Can't control multiple values being expanded 481 * from the expression, we return only the first. 482 */ 483 int 484 globulize(cpp) 485 char **cpp; 486 { 487 glob_t gl; 488 int flags; 489 490 if (!doglob) 491 return (1); 492 493 flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE; 494 memset(&gl, 0, sizeof(gl)); 495 if (glob(*cpp, flags, NULL, &gl) || 496 gl.gl_pathc == 0) { 497 warnx("%s: not found", *cpp); 498 globfree(&gl); 499 return (0); 500 } 501 /* XXX: caller should check if *cpp changed, and 502 * free(*cpp) if that is the case 503 */ 504 *cpp = xstrdup(gl.gl_pathv[0]); 505 globfree(&gl); 506 return (1); 507 } 508 509 /* 510 * determine size of remote file 511 */ 512 off_t 513 remotesize(file, noisy) 514 const char *file; 515 int noisy; 516 { 517 int overbose; 518 off_t size; 519 520 overbose = verbose; 521 size = -1; 522 if (debug == 0) 523 verbose = -1; 524 if (command("SIZE %s", file) == COMPLETE) { 525 char *cp, *ep; 526 527 cp = strchr(reply_string, ' '); 528 if (cp != NULL) { 529 cp++; 530 #ifndef NO_QUAD 531 size = strtoq(cp, &ep, 10); 532 #else 533 size = strtol(cp, &ep, 10); 534 #endif 535 if (*ep != '\0' && !isspace((unsigned char)*ep)) 536 size = -1; 537 } 538 } else if (noisy && debug == 0) { 539 fputs(reply_string, ttyout); 540 putc('\n', ttyout); 541 } 542 verbose = overbose; 543 return (size); 544 } 545 546 /* 547 * determine last modification time (in GMT) of remote file 548 */ 549 time_t 550 remotemodtime(file, noisy) 551 const char *file; 552 int noisy; 553 { 554 int overbose; 555 time_t rtime; 556 int ocode; 557 558 overbose = verbose; 559 ocode = code; 560 rtime = -1; 561 if (debug == 0) 562 verbose = -1; 563 if (command("MDTM %s", file) == COMPLETE) { 564 struct tm timebuf; 565 int yy, mo, day, hour, min, sec; 566 sscanf(reply_string, "%*s %04d%02d%02d%02d%02d%02d", &yy, &mo, 567 &day, &hour, &min, &sec); 568 memset(&timebuf, 0, sizeof(timebuf)); 569 timebuf.tm_sec = sec; 570 timebuf.tm_min = min; 571 timebuf.tm_hour = hour; 572 timebuf.tm_mday = day; 573 timebuf.tm_mon = mo - 1; 574 timebuf.tm_year = yy - TM_YEAR_BASE; 575 timebuf.tm_isdst = -1; 576 rtime = mktime(&timebuf); 577 if (rtime == -1 && (noisy || debug != 0)) 578 fprintf(ttyout, "Can't convert %s to a time.\n", 579 reply_string); 580 else 581 #ifndef __SVR4 582 rtime += timebuf.tm_gmtoff; /* conv. local -> GMT */ 583 #else 584 rtime -= timezone; 585 #endif 586 } else if (noisy && debug == 0) { 587 fputs(reply_string, ttyout); 588 putc('\n', ttyout); 589 } 590 verbose = overbose; 591 if (rtime == -1) 592 code = ocode; 593 return (rtime); 594 } 595 596 #ifndef SMALL 597 598 /* 599 * return non-zero if we're the current foreground process 600 */ 601 int 602 foregroundproc() 603 { 604 static pid_t pgrp = -1; 605 int ctty_pgrp; 606 607 if (pgrp == -1) 608 pgrp = getpgrp(); 609 610 return ((ioctl(fileno(ttyout), TIOCGPGRP, &ctty_pgrp) != -1 && 611 ctty_pgrp == (int)pgrp)); 612 } 613 614 615 static void updateprogressmeter __P((int)); 616 617 static void 618 updateprogressmeter(dummy) 619 int dummy; 620 { 621 622 /* 623 * print progress bar only if we are foreground process. 624 */ 625 if (foregroundproc()) 626 progressmeter(0); 627 } 628 #endif /* SMALL */ 629 630 /* 631 * Display a transfer progress bar if progress is non-zero. 632 * SIGALRM is hijacked for use by this function. 633 * - Before the transfer, set filesize to size of file (or -1 if unknown), 634 * and call with flag = -1. This starts the once per second timer, 635 * and a call to updateprogressmeter() upon SIGALRM. 636 * - During the transfer, updateprogressmeter will call progressmeter 637 * with flag = 0 638 * - After the transfer, call with flag = 1 639 */ 640 static struct timeval start; 641 static struct timeval lastupdate; 642 643 void 644 progressmeter(flag) 645 int flag; 646 { 647 #ifndef SMALL 648 /* 649 * List of order of magnitude prefixes. 650 * The last is `P', as 2^64 = 16384 Petabytes 651 */ 652 static const char prefixes[] = "BKMGTP"; 653 654 static off_t lastsize; 655 struct timeval now, td, wait; 656 off_t cursize, abbrevsize, bytespersec; 657 double elapsed; 658 int ratio, barlength, i, len, remaining; 659 char buf[256]; 660 661 len = 0; 662 663 if (flag == -1) { 664 (void)gettimeofday(&start, NULL); 665 lastupdate = start; 666 lastsize = restart_point; 667 } 668 (void)gettimeofday(&now, NULL); 669 if (!progress || filesize <= 0) 670 return; 671 cursize = bytes + restart_point; 672 673 ratio = (int)((double)cursize * 100.0 / (double)filesize); 674 ratio = MAX(ratio, 0); 675 ratio = MIN(ratio, 100); 676 len += snprintf(buf + len, sizeof(buf) - len, "\r%3d%% ", ratio); 677 678 barlength = ttywidth - 42; 679 if (barlength > 0) { 680 i = barlength * ratio / 100; 681 len += snprintf(buf + len, sizeof(buf) - len, 682 "|%.*s%*s|", i, 683 "*****************************************************************************" 684 "*****************************************************************************", 685 barlength - i, ""); 686 } 687 688 abbrevsize = cursize; 689 for (i = 0; abbrevsize >= 100000 && i < sizeof(prefixes); i++) 690 abbrevsize >>= 10; 691 len += snprintf(buf + len, sizeof(buf) - len, 692 #ifndef NO_QUAD 693 " %5qd %c%c ", (long long)abbrevsize, 694 #else 695 " %5ld %c%c ", (long)abbrevsize, 696 #endif 697 i == 0 ? ' ' : prefixes[i], 698 i == 0 ? ' ' : 'B'); 699 700 timersub(&now, &lastupdate, &wait); 701 if (cursize > lastsize) { 702 lastupdate = now; 703 lastsize = cursize; 704 if (wait.tv_sec >= STALLTIME) { /* fudge out stalled time */ 705 start.tv_sec += wait.tv_sec; 706 start.tv_usec += wait.tv_usec; 707 } 708 wait.tv_sec = 0; 709 } 710 711 timersub(&now, &start, &td); 712 elapsed = td.tv_sec + (td.tv_usec / 1000000.0); 713 714 bytespersec = 0; 715 if (bytes > 0) { 716 bytespersec = bytes; 717 if (elapsed > 0.0) 718 bytespersec /= elapsed; 719 } 720 for (i = 0; bytespersec >= 100000 && i < sizeof(prefixes); i++) 721 bytespersec >>= 10; 722 len += snprintf(buf + len, sizeof(buf) - len, 723 #ifndef NO_QUAD 724 " %5qd %c%s ", (long long)bytespersec, 725 #else 726 " %5ld %c%s ", (long)bytespersec, 727 #endif 728 prefixes[i], 729 i == 0 ? "/s " : "B/s"); 730 731 if (bytes <= 0 || elapsed <= 0.0 || cursize > filesize) { 732 len += snprintf(buf + len, sizeof(buf) - len, 733 " --:-- ETA"); 734 } else if (wait.tv_sec >= STALLTIME) { 735 len += snprintf(buf + len, sizeof(buf) - len, 736 " - stalled -"); 737 } else { 738 remaining = (int) 739 ((filesize - restart_point) / (bytes / elapsed) - elapsed); 740 if (remaining >= 100 * SECSPERHOUR) 741 len += snprintf(buf + len, sizeof(buf) - len, 742 " --:-- ETA"); 743 else { 744 i = remaining / SECSPERHOUR; 745 if (i) 746 len += snprintf(buf + len, sizeof(buf) - len, 747 "%2d:", i); 748 else 749 len += snprintf(buf + len, sizeof(buf) - len, 750 " "); 751 i = remaining % SECSPERHOUR; 752 len += snprintf(buf + len, sizeof(buf) - len, 753 "%02d:%02d ETA", i / 60, i % 60); 754 } 755 } 756 (void)write(fileno(ttyout), buf, len); 757 758 if (flag == -1) { 759 (void)xsignal(SIGALRM, updateprogressmeter); 760 alarmtimer(1); /* set alarm timer for 1 Hz */ 761 } else if (flag == 1) { 762 (void)xsignal(SIGALRM, SIG_DFL); 763 alarmtimer(0); 764 (void)putc('\n', ttyout); 765 } 766 fflush(ttyout); 767 #endif /* SMALL */ 768 } 769 770 /* 771 * Display transfer statistics. 772 * Requires start to be initialised by progressmeter(-1), 773 * direction to be defined by xfer routines, and filesize and bytes 774 * to be updated by xfer routines 775 * If siginfo is nonzero, an ETA is displayed, and the output goes to stderr 776 * instead of ttyout. 777 */ 778 void 779 ptransfer(siginfo) 780 int siginfo; 781 { 782 #ifndef SMALL 783 struct timeval now, td, wait; 784 double elapsed; 785 off_t bytespersec; 786 int meg, remaining, hh, len; 787 char buf[100]; 788 789 if (!verbose && !siginfo) 790 return; 791 792 (void)gettimeofday(&now, NULL); 793 timersub(&now, &start, &td); 794 elapsed = td.tv_sec + (td.tv_usec / 1000000.0); 795 bytespersec = 0; 796 meg = 0; 797 if (bytes > 0) { 798 bytespersec = bytes; 799 if (elapsed > 0.0) 800 bytespersec /= elapsed; 801 if (bytespersec > (1024 * 1024)) 802 meg = 1; 803 } 804 len = 0; 805 len += snprintf(buf + len, sizeof(buf) - len, 806 #ifndef NO_QUAD 807 "%qd byte%s %s in ", (long long)bytes, 808 #else 809 "%ld byte%s %s in ", (long)bytes, 810 #endif 811 bytes == 1 ? "" : "s", direction); 812 remaining = (int)elapsed; 813 if (remaining > SECSPERDAY) { 814 int days; 815 816 days = remaining / SECSPERDAY; 817 remaining %= SECSPERDAY; 818 len += snprintf(buf + len, sizeof(buf) - len, 819 "%d day%s ", days, days == 1 ? "" : "s"); 820 } 821 hh = remaining / SECSPERHOUR; 822 remaining %= SECSPERHOUR; 823 if (hh) 824 len += snprintf(buf + len, sizeof(buf) - len, "%2d:", hh); 825 len += snprintf(buf + len, sizeof(buf) - len, 826 "%02d:%02d (%.2f %sB/s)", remaining / 60, remaining % 60, 827 bytespersec / (1024.0 * (meg ? 1024.0 : 1.0)), 828 meg ? "M" : "K"); 829 830 if (siginfo && bytes > 0 && elapsed > 0.0 && filesize >= 0 831 && bytes + restart_point <= filesize) { 832 remaining = (int)((filesize - restart_point) / 833 (bytes / elapsed) - elapsed); 834 hh = remaining / SECSPERHOUR; 835 remaining %= SECSPERHOUR; 836 len += snprintf(buf + len, sizeof(buf) - len, " ETA: "); 837 if (hh) 838 len += snprintf(buf + len, sizeof(buf) - len, "%2d:", 839 hh); 840 len += snprintf(buf + len, sizeof(buf) - len, 841 "%02d:%02d", remaining / 60, remaining % 60); 842 timersub(&now, &lastupdate, &wait); 843 if (wait.tv_sec >= STALLTIME) 844 len += snprintf(buf + len, sizeof(buf) - len, 845 " (stalled)"); 846 } 847 len += snprintf(buf + len, sizeof(buf) - len, "\n"); 848 (void)write(siginfo ? STDERR_FILENO : fileno(ttyout), buf, len); 849 #endif /* SMALL */ 850 } 851 852 /* 853 * List words in stringlist, vertically arranged 854 */ 855 void 856 list_vertical(sl) 857 StringList *sl; 858 { 859 int i, j, w; 860 int columns, width, lines, items; 861 char *p; 862 863 width = items = 0; 864 865 for (i = 0 ; i < sl->sl_cur ; i++) { 866 w = strlen(sl->sl_str[i]); 867 if (w > width) 868 width = w; 869 } 870 width = (width + 8) &~ 7; 871 872 columns = ttywidth / width; 873 if (columns == 0) 874 columns = 1; 875 lines = (sl->sl_cur + columns - 1) / columns; 876 for (i = 0; i < lines; i++) { 877 for (j = 0; j < columns; j++) { 878 p = sl->sl_str[j * lines + i]; 879 if (p) 880 fputs(p, ttyout); 881 if (j * lines + i + lines >= sl->sl_cur) { 882 putc('\n', ttyout); 883 break; 884 } 885 w = strlen(p); 886 while (w < width) { 887 w = (w + 8) &~ 7; 888 (void)putc('\t', ttyout); 889 } 890 } 891 } 892 } 893 894 /* 895 * Update the global ttywidth value, using TIOCGWINSZ. 896 */ 897 void 898 setttywidth(a) 899 int a; 900 { 901 struct winsize winsize; 902 903 if (ioctl(fileno(ttyout), TIOCGWINSZ, &winsize) != -1 && 904 winsize.ws_col != 0) 905 ttywidth = winsize.ws_col; 906 else 907 ttywidth = 80; 908 } 909 910 /* 911 * Set the SIGALRM interval timer for wait seconds, 0 to disable. 912 */ 913 void 914 alarmtimer(wait) 915 int wait; 916 { 917 struct itimerval itv; 918 919 itv.it_value.tv_sec = wait; 920 itv.it_value.tv_usec = 0; 921 itv.it_interval = itv.it_value; 922 setitimer(ITIMER_REAL, &itv, NULL); 923 } 924 925 /* 926 * Setup or cleanup EditLine structures 927 */ 928 #ifndef SMALL 929 void 930 controlediting() 931 { 932 if (editing && el == NULL && hist == NULL) { 933 HistEvent ev; 934 int editmode; 935 936 el = el_init(__progname, stdin, ttyout, stderr); 937 /* init editline */ 938 hist = history_init(); /* init the builtin history */ 939 history(hist, &ev, H_SETSIZE, 100);/* remember 100 events */ 940 el_set(el, EL_HIST, history, hist); /* use history */ 941 942 el_set(el, EL_EDITOR, "emacs"); /* default editor is emacs */ 943 el_set(el, EL_PROMPT, prompt); /* set the prompt function */ 944 945 /* add local file completion, bind to TAB */ 946 el_set(el, EL_ADDFN, "ftp-complete", 947 "Context sensitive argument completion", 948 complete); 949 el_set(el, EL_BIND, "^I", "ftp-complete", NULL); 950 el_source(el, NULL); /* read ~/.editrc */ 951 if ((el_get(el, EL_EDITMODE, &editmode) != -1) && editmode == 0) 952 editing = 0; /* the user doesn't want editing, 953 * so disable, and let statement 954 * below cleanup */ 955 else 956 el_set(el, EL_SIGNAL, 1); 957 } 958 if (!editing) { 959 if (hist) { 960 history_end(hist); 961 hist = NULL; 962 } 963 if (el) { 964 el_end(el); 965 el = NULL; 966 } 967 } 968 } 969 #endif /* !SMALL */ 970 971 /* 972 * Parse the specified socket buffer size. 973 */ 974 int 975 getsockbufsize(arg) 976 const char *arg; 977 { 978 char *cp; 979 int val; 980 981 if (!isdigit((unsigned char)arg[0])) 982 return (-1); 983 984 val = strtol(arg, &cp, 10); 985 if (cp != NULL) { 986 if (cp[1] != '\0') 987 return (-1); 988 if (cp[0] == 'k') 989 val *= 1024; 990 if (cp[0] == 'm') 991 val *= 1024 * 1024; 992 } 993 994 if (val < 0) 995 return (-1); 996 997 return (val); 998 } 999 1000 /* 1001 * Set up socket buffer sizes before a connection is made. 1002 */ 1003 void 1004 setupsockbufsize(sock) 1005 int sock; 1006 { 1007 static int sndbuf_default, rcvbuf_default; 1008 int len, size; 1009 1010 /* 1011 * Get the default socket buffer sizes if we don't already 1012 * have them. It doesn't matter which socket we do this 1013 * to, because on the first call no socket buffer sizes 1014 * will have been modified, so we are guaranteed to get 1015 * the system defaults. 1016 */ 1017 if (sndbuf_default == 0) { 1018 len = sizeof(sndbuf_default); 1019 if (getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sndbuf_default, 1020 &len) < 0) 1021 err(1, "unable to get default sndbuf size"); 1022 len = sizeof(rcvbuf_default); 1023 if (getsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rcvbuf_default, 1024 &len) < 0) 1025 err(1, "unable to get default rcvbuf size"); 1026 1027 } 1028 1029 size = sndbuf_size ? sndbuf_size : sndbuf_default; 1030 if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)) < 0) 1031 warn("unable to set sndbuf size %d", size); 1032 1033 size = rcvbuf_size ? rcvbuf_size : rcvbuf_default; 1034 if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) < 0) 1035 warn("unable to set rcvbuf size %d", size); 1036 } 1037 1038 /* 1039 * If the socket buffer sizes were not set manually (i.e. came from a 1040 * configuration file), reset them so the right thing will happen on 1041 * subsequent connections. 1042 */ 1043 void 1044 resetsockbufsize() 1045 { 1046 1047 if (sndbuf_manual == 0) 1048 sndbuf_size = 0; 1049 if (rcvbuf_manual == 0) 1050 rcvbuf_size = 0; 1051 } 1052 1053 /* 1054 * Internal version of connect(2); sets socket buffer sizes first. 1055 */ 1056 int 1057 xconnect(sock, name, namelen) 1058 int sock; 1059 const struct sockaddr *name; 1060 int namelen; 1061 { 1062 1063 setupsockbufsize(sock); 1064 return (connect(sock, name, namelen)); 1065 } 1066 1067 /* 1068 * Internal version of listen(2); sets socket buffer sizes first. 1069 */ 1070 int 1071 xlisten(sock, backlog) 1072 int sock, backlog; 1073 { 1074 1075 setupsockbufsize(sock); 1076 return (listen(sock, backlog)); 1077 } 1078 1079 void * 1080 xmalloc(size) 1081 size_t size; 1082 { 1083 void *p; 1084 1085 p = malloc(size); 1086 if (p == NULL) 1087 err(1, "Unable to allocate %ld bytes of memory", (long)size); 1088 return (p); 1089 } 1090 1091 char * 1092 xstrdup(str) 1093 const char *str; 1094 { 1095 char *s; 1096 1097 if (str == NULL) 1098 errx(1, "xstrdup() called with NULL argument"); 1099 s = strdup(str); 1100 if (s == NULL) 1101 err(1, "Unable to allocate memory for string copy"); 1102 return (s); 1103 } 1104 1105 sig_t 1106 xsignal(sig, func) 1107 int sig; 1108 void (*func) __P((int)); 1109 { 1110 struct sigaction act, oact; 1111 1112 act.sa_handler = func; 1113 sigemptyset(&act.sa_mask); 1114 act.sa_flags = SA_RESTART; 1115 if (sigaction(sig, &act, &oact) < 0) 1116 return (SIG_ERR); 1117 return (oact.sa_handler); 1118 } 1119