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