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