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