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