1 /*- 2 * Copyright (c) 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * Copyright (c) 1993, 1994, 1995, 1996 5 * Keith Bostic. All rights reserved. 6 * 7 * See the LICENSE file for redistribution information. 8 */ 9 10 #include "config.h" 11 12 #ifndef lint 13 static const char sccsid[] = "@(#)cl_funcs.c 10.50 (Berkeley) 9/24/96"; 14 #endif /* not lint */ 15 16 #include <sys/types.h> 17 #include <sys/queue.h> 18 #include <sys/time.h> 19 20 #include <bitstring.h> 21 #include <ctype.h> 22 #include <curses.h> 23 #include <signal.h> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <string.h> 27 #include <termios.h> 28 #include <unistd.h> 29 30 #include "../common/common.h" 31 #include "../vi/vi.h" 32 #include "cl.h" 33 34 /* 35 * cl_addstr -- 36 * Add len bytes from the string at the cursor, advancing the cursor. 37 * 38 * PUBLIC: int cl_addstr __P((SCR *, const char *, size_t)); 39 */ 40 int 41 cl_addstr(sp, str, len) 42 SCR *sp; 43 const char *str; 44 size_t len; 45 { 46 CL_PRIVATE *clp; 47 size_t oldy, oldx; 48 int iv; 49 50 clp = CLP(sp); 51 52 /* 53 * If ex isn't in control, it's the last line of the screen and 54 * it's a split screen, use inverse video. 55 */ 56 iv = 0; 57 getyx(stdscr, oldy, oldx); 58 if (!F_ISSET(sp, SC_SCR_EXWROTE) && 59 oldy == RLNO(sp, LASTLINE(sp)) && IS_SPLIT(sp)) { 60 iv = 1; 61 (void)standout(); 62 } 63 64 if (addnstr(str, len) == ERR) 65 return (1); 66 67 if (iv) 68 (void)standend(); 69 return (0); 70 } 71 72 /* 73 * cl_attr -- 74 * Toggle a screen attribute on/off. 75 * 76 * PUBLIC: int cl_attr __P((SCR *, scr_attr_t, int)); 77 */ 78 int 79 cl_attr(sp, attribute, on) 80 SCR *sp; 81 scr_attr_t attribute; 82 int on; 83 { 84 CL_PRIVATE *clp; 85 86 clp = CLP(sp); 87 88 switch (attribute) { 89 case SA_ALTERNATE: 90 /* 91 * !!! 92 * There's a major layering violation here. The problem is that the 93 * X11 xterm screen has what's known as an "alternate" screen. Some 94 * xterm termcap/terminfo entries include sequences to switch to/from 95 * that alternate screen as part of the ti/te (smcup/rmcup) strings. 96 * Vi runs in the alternate screen, so that you are returned to the 97 * same screen contents on exit from vi that you had when you entered 98 * vi. Further, when you run :shell, or :!date or similar ex commands, 99 * you also see the original screen contents. This wasn't deliberate 100 * on vi's part, it's just that it historically sent terminal init/end 101 * sequences at those times, and the addition of the alternate screen 102 * sequences to the strings changed the behavior of vi. The problem 103 * caused by this is that we don't want to switch back to the alternate 104 * screen while getting a new command from the user, when the user is 105 * continuing to enter ex commands, e.g.: 106 * 107 * :!date <<< switch to original screen 108 * [Hit return to continue] <<< prompt user to continue 109 * :command <<< get command from user 110 * 111 * Note that the :command input is a true vi input mode, e.g., input 112 * maps and abbreviations are being done. So, we need to be able to 113 * switch back into the vi screen mode, without flashing the screen. 114 * 115 * To make matters worse, the curses initscr() and endwin() calls will 116 * do this automatically -- so, this attribute isn't as controlled by 117 * the higher level screen as closely as one might like. 118 */ 119 if (on) { 120 if (clp->ti_te != TI_SENT) { 121 clp->ti_te = TI_SENT; 122 if (clp->smcup == NULL) 123 (void)cl_getcap(sp, "smcup", &clp->smcup); 124 if (clp->smcup != NULL) 125 (void)tputs(clp->smcup, 1, cl_putchar); 126 } 127 } else 128 if (clp->ti_te != TE_SENT) { 129 clp->ti_te = TE_SENT; 130 if (clp->rmcup == NULL) 131 (void)cl_getcap(sp, "rmcup", &clp->rmcup); 132 if (clp->rmcup != NULL) 133 (void)tputs(clp->rmcup, 1, cl_putchar); 134 (void)fflush(stdout); 135 } 136 (void)fflush(stdout); 137 break; 138 case SA_INVERSE: 139 if (F_ISSET(sp, SC_EX | SC_SCR_EXWROTE)) { 140 if (clp->smso == NULL) 141 return (1); 142 if (on) 143 (void)tputs(clp->smso, 1, cl_putchar); 144 else 145 (void)tputs(clp->rmso, 1, cl_putchar); 146 (void)fflush(stdout); 147 } else { 148 if (on) 149 (void)standout(); 150 else 151 (void)standend(); 152 } 153 break; 154 default: 155 abort(); 156 } 157 return (0); 158 } 159 160 /* 161 * cl_baud -- 162 * Return the baud rate. 163 * 164 * PUBLIC: int cl_baud __P((SCR *, u_long *)); 165 */ 166 int 167 cl_baud(sp, ratep) 168 SCR *sp; 169 u_long *ratep; 170 { 171 CL_PRIVATE *clp; 172 173 /* 174 * XXX 175 * There's no portable way to get a "baud rate" -- cfgetospeed(3) 176 * returns the value associated with some #define, which we may 177 * never have heard of, or which may be a purely local speed. Vi 178 * only cares if it's SLOW (w300), slow (w1200) or fast (w9600). 179 * Try and detect the slow ones, and default to fast. 180 */ 181 clp = CLP(sp); 182 switch (cfgetospeed(&clp->orig)) { 183 case B50: 184 case B75: 185 case B110: 186 case B134: 187 case B150: 188 case B200: 189 case B300: 190 case B600: 191 *ratep = 600; 192 break; 193 case B1200: 194 *ratep = 1200; 195 break; 196 default: 197 *ratep = 9600; 198 break; 199 } 200 return (0); 201 } 202 203 /* 204 * cl_bell -- 205 * Ring the bell/flash the screen. 206 * 207 * PUBLIC: int cl_bell __P((SCR *)); 208 */ 209 int 210 cl_bell(sp) 211 SCR *sp; 212 { 213 if (F_ISSET(sp, SC_EX | SC_SCR_EXWROTE)) 214 (void)write(STDOUT_FILENO, "\07", 1); /* \a */ 215 else { 216 /* 217 * Vi has an edit option which determines if the terminal 218 * should be beeped or the screen flashed. 219 */ 220 if (O_ISSET(sp, O_FLASH)) 221 (void)flash(); 222 else 223 (void)beep(); 224 } 225 return (0); 226 } 227 228 /* 229 * cl_clrtoeol -- 230 * Clear from the current cursor to the end of the line. 231 * 232 * PUBLIC: int cl_clrtoeol __P((SCR *)); 233 */ 234 int 235 cl_clrtoeol(sp) 236 SCR *sp; 237 { 238 return (clrtoeol() == ERR); 239 } 240 241 /* 242 * cl_cursor -- 243 * Return the current cursor position. 244 * 245 * PUBLIC: int cl_cursor __P((SCR *, size_t *, size_t *)); 246 */ 247 int 248 cl_cursor(sp, yp, xp) 249 SCR *sp; 250 size_t *yp, *xp; 251 { 252 /* 253 * The curses screen support splits a single underlying curses screen 254 * into multiple screens to support split screen semantics. For this 255 * reason the returned value must be adjusted to be relative to the 256 * current screen, and not absolute. Screens that implement the split 257 * using physically distinct screens won't need this hack. 258 */ 259 getyx(stdscr, *yp, *xp); 260 *yp -= sp->woff; 261 return (0); 262 } 263 264 /* 265 * cl_deleteln -- 266 * Delete the current line, scrolling all lines below it. 267 * 268 * PUBLIC: int cl_deleteln __P((SCR *)); 269 */ 270 int 271 cl_deleteln(sp) 272 SCR *sp; 273 { 274 CHAR_T ch; 275 CL_PRIVATE *clp; 276 size_t col, lno, spcnt, oldy, oldx; 277 278 clp = CLP(sp); 279 280 /* 281 * This clause is required because the curses screen uses reverse 282 * video to delimit split screens. If the screen does not do this, 283 * this code won't be necessary. 284 * 285 * If the bottom line was in reverse video, rewrite it in normal 286 * video before it's scrolled. 287 * 288 * Check for the existence of a chgat function; XSI requires it, but 289 * historic implementations of System V curses don't. If it's not 290 * a #define, we'll fall back to doing it by hand, which is slow but 291 * acceptable. 292 * 293 * By hand means walking through the line, retrieving and rewriting 294 * each character. Curses has no EOL marker, so track strings of 295 * spaces, and copy the trailing spaces only if there's a non-space 296 * character following. 297 */ 298 if (!F_ISSET(sp, SC_SCR_EXWROTE) && IS_SPLIT(sp)) { 299 getyx(stdscr, oldy, oldx); 300 #ifdef mvchgat 301 mvchgat(RLNO(sp, LASTLINE(sp)), 0, -1, A_NORMAL, 0, NULL); 302 #else 303 for (lno = RLNO(sp, LASTLINE(sp)), col = spcnt = 0;;) { 304 (void)move(lno, col); 305 ch = winch(stdscr); 306 if (isblank(ch)) 307 ++spcnt; 308 else { 309 (void)move(lno, col - spcnt); 310 for (; spcnt > 0; --spcnt) 311 (void)addch(' '); 312 (void)addch(ch); 313 } 314 if (++col >= sp->cols) 315 break; 316 } 317 #endif 318 (void)move(oldy, oldx); 319 } 320 321 /* 322 * The bottom line is expected to be blank after this operation, 323 * and other screens must support that semantic. 324 */ 325 return (deleteln() == ERR); 326 } 327 328 /* 329 * cl_ex_adjust -- 330 * Adjust the screen for ex. This routine is purely for standalone 331 * ex programs. All special purpose, all special case. 332 * 333 * PUBLIC: int cl_ex_adjust __P((SCR *, exadj_t)); 334 */ 335 int 336 cl_ex_adjust(sp, action) 337 SCR *sp; 338 exadj_t action; 339 { 340 CL_PRIVATE *clp; 341 int cnt; 342 343 clp = CLP(sp); 344 switch (action) { 345 case EX_TERM_SCROLL: 346 /* Move the cursor up one line if that's possible. */ 347 if (clp->cuu1 != NULL) 348 (void)tputs(clp->cuu1, 1, cl_putchar); 349 else if (clp->cup != NULL) 350 (void)tputs(tgoto(clp->cup, 351 0, LINES - 2), 1, cl_putchar); 352 else 353 return (0); 354 /* FALLTHROUGH */ 355 case EX_TERM_CE: 356 /* Clear the line. */ 357 if (clp->el != NULL) { 358 (void)putchar('\r'); 359 (void)tputs(clp->el, 1, cl_putchar); 360 } else { 361 /* 362 * Historically, ex didn't erase the line, so, if the 363 * displayed line was only a single glyph, and <eof> 364 * was more than one glyph, the output would not fully 365 * overwrite the user's input. To fix this, output 366 * the maxiumum character number of spaces. Note, 367 * this won't help if the user entered extra prompt 368 * or <blank> characters before the command character. 369 * We'd have to do a lot of work to make that work, and 370 * it's almost certainly not worth the effort. 371 */ 372 for (cnt = 0; cnt < MAX_CHARACTER_COLUMNS; ++cnt) 373 (void)putchar('\b'); 374 for (cnt = 0; cnt < MAX_CHARACTER_COLUMNS; ++cnt) 375 (void)putchar(' '); 376 (void)putchar('\r'); 377 (void)fflush(stdout); 378 } 379 break; 380 default: 381 abort(); 382 } 383 return (0); 384 } 385 386 /* 387 * cl_insertln -- 388 * Push down the current line, discarding the bottom line. 389 * 390 * PUBLIC: int cl_insertln __P((SCR *)); 391 */ 392 int 393 cl_insertln(sp) 394 SCR *sp; 395 { 396 /* 397 * The current line is expected to be blank after this operation, 398 * and the screen must support that semantic. 399 */ 400 return (insertln() == ERR); 401 } 402 403 /* 404 * cl_keyval -- 405 * Return the value for a special key. 406 * 407 * PUBLIC: int cl_keyval __P((SCR *, scr_keyval_t, CHAR_T *, int *)); 408 */ 409 int 410 cl_keyval(sp, val, chp, dnep) 411 SCR *sp; 412 scr_keyval_t val; 413 CHAR_T *chp; 414 int *dnep; 415 { 416 CL_PRIVATE *clp; 417 418 /* 419 * VEOF, VERASE and VKILL are required by POSIX 1003.1-1990, 420 * VWERASE is a 4BSD extension. 421 */ 422 clp = CLP(sp); 423 switch (val) { 424 case KEY_VEOF: 425 *dnep = (*chp = clp->orig.c_cc[VEOF]) == _POSIX_VDISABLE; 426 break; 427 case KEY_VERASE: 428 *dnep = (*chp = clp->orig.c_cc[VERASE]) == _POSIX_VDISABLE; 429 break; 430 case KEY_VKILL: 431 *dnep = (*chp = clp->orig.c_cc[VKILL]) == _POSIX_VDISABLE; 432 break; 433 #ifdef VWERASE 434 case KEY_VWERASE: 435 *dnep = (*chp = clp->orig.c_cc[VWERASE]) == _POSIX_VDISABLE; 436 break; 437 #endif 438 default: 439 *dnep = 1; 440 break; 441 } 442 return (0); 443 } 444 445 /* 446 * cl_move -- 447 * Move the cursor. 448 * 449 * PUBLIC: int cl_move __P((SCR *, size_t, size_t)); 450 */ 451 int 452 cl_move(sp, lno, cno) 453 SCR *sp; 454 size_t lno, cno; 455 { 456 /* See the comment in cl_cursor. */ 457 if (move(RLNO(sp, lno), cno) == ERR) { 458 msgq(sp, M_ERR, 459 "Error: move: l(%u) c(%u) o(%u)", lno, cno, sp->woff); 460 return (1); 461 } 462 return (0); 463 } 464 465 /* 466 * cl_refresh -- 467 * Refresh the screen. 468 * 469 * PUBLIC: int cl_refresh __P((SCR *, int)); 470 */ 471 int 472 cl_refresh(sp, repaint) 473 SCR *sp; 474 int repaint; 475 { 476 CL_PRIVATE *clp; 477 478 clp = CLP(sp); 479 480 /* 481 * If we received a killer signal, we're done, there's no point 482 * in refreshing the screen. 483 */ 484 if (clp->killersig) 485 return (0); 486 487 /* 488 * If repaint is set, the editor is telling us that we don't know 489 * what's on the screen, so we have to repaint from scratch. 490 * 491 * In the curses library, doing wrefresh(curscr) is okay, but the 492 * screen flashes when we then apply the refresh() to bring it up 493 * to date. So, use clearok(). 494 */ 495 if (repaint) 496 clearok(curscr, 1); 497 return (refresh() == ERR); 498 } 499 500 /* 501 * cl_rename -- 502 * Rename the file. 503 * 504 * PUBLIC: int cl_rename __P((SCR *, char *, int)); 505 */ 506 int 507 cl_rename(sp, name, on) 508 SCR *sp; 509 char *name; 510 int on; 511 { 512 GS *gp; 513 CL_PRIVATE *clp; 514 char *ttype; 515 516 gp = sp->gp; 517 clp = CLP(sp); 518 519 ttype = OG_STR(gp, GO_TERM); 520 521 /* 522 * XXX 523 * We can only rename windows for xterm. 524 */ 525 if (on) { 526 if (F_ISSET(clp, CL_RENAME_OK) && 527 !strncmp(ttype, "xterm", sizeof("xterm") - 1)) { 528 F_SET(clp, CL_RENAME); 529 (void)printf(XTERM_RENAME, name); 530 (void)fflush(stdout); 531 } 532 } else 533 if (F_ISSET(clp, CL_RENAME)) { 534 F_CLR(clp, CL_RENAME); 535 (void)printf(XTERM_RENAME, ttype); 536 (void)fflush(stdout); 537 } 538 return (0); 539 } 540 541 /* 542 * cl_suspend -- 543 * Suspend a screen. 544 * 545 * PUBLIC: int cl_suspend __P((SCR *, int *)); 546 */ 547 int 548 cl_suspend(sp, allowedp) 549 SCR *sp; 550 int *allowedp; 551 { 552 struct termios t; 553 CL_PRIVATE *clp; 554 GS *gp; 555 size_t oldy, oldx; 556 int changed; 557 558 gp = sp->gp; 559 clp = CLP(sp); 560 *allowedp = 1; 561 562 /* 563 * The ex implementation of this function isn't needed by screens not 564 * supporting ex commands that require full terminal canonical mode 565 * (e.g. :suspend). 566 * 567 * The vi implementation of this function isn't needed by screens not 568 * supporting vi process suspension, i.e. any screen that isn't backed 569 * by a UNIX shell. 570 * 571 * Setting allowedp to 0 will cause the editor to reject the command. 572 */ 573 if (F_ISSET(sp, SC_EX)) { 574 /* Save the terminal settings, and restore the original ones. */ 575 if (F_ISSET(clp, CL_STDIN_TTY)) { 576 (void)tcgetattr(STDIN_FILENO, &t); 577 (void)tcsetattr(STDIN_FILENO, 578 TCSASOFT | TCSADRAIN, &clp->orig); 579 } 580 581 /* Stop the process group. */ 582 (void)kill(0, SIGTSTP); 583 584 /* Time passes ... */ 585 586 /* Restore terminal settings. */ 587 if (F_ISSET(clp, CL_STDIN_TTY)) 588 (void)tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &t); 589 return (0); 590 } 591 592 /* 593 * Move to the lower left-hand corner of the screen. 594 * 595 * XXX 596 * Not sure this is necessary in System V implementations, but it 597 * shouldn't hurt. 598 */ 599 getyx(stdscr, oldy, oldx); 600 (void)move(LINES - 1, 0); 601 (void)refresh(); 602 603 /* 604 * Temporarily end the screen. System V introduced a semantic where 605 * endwin() could be restarted. We use it because restarting curses 606 * from scratch often fails in System V. 4BSD curses didn't support 607 * restarting after endwin(), so we have to do what clean up we can 608 * without calling it. 609 */ 610 #ifdef HAVE_BSD_CURSES 611 /* Save the terminal settings. */ 612 (void)tcgetattr(STDIN_FILENO, &t); 613 #endif 614 615 /* Restore the cursor keys to normal mode. */ 616 (void)keypad(stdscr, FALSE); 617 618 /* Restore the window name. */ 619 (void)cl_rename(sp, NULL, 0); 620 621 #ifdef HAVE_BSD_CURSES 622 (void)cl_attr(sp, SA_ALTERNATE, 0); 623 #else 624 (void)endwin(); 625 #endif 626 /* 627 * XXX 628 * Restore the original terminal settings. This is bad -- the 629 * reset can cause character loss from the tty queue. However, 630 * we can't call endwin() in BSD curses implementations, and too 631 * many System V curses implementations don't get it right. 632 */ 633 (void)tcsetattr(STDIN_FILENO, TCSADRAIN | TCSASOFT, &clp->orig); 634 635 /* Stop the process group. */ 636 (void)kill(0, SIGTSTP); 637 638 /* Time passes ... */ 639 640 /* 641 * If we received a killer signal, we're done. Leave everything 642 * unchanged. In addition, the terminal has already been reset 643 * correctly, so leave it alone. 644 */ 645 if (clp->killersig) { 646 F_CLR(clp, CL_SCR_EX_INIT | CL_SCR_VI_INIT); 647 return (0); 648 } 649 650 #ifdef HAVE_BSD_CURSES 651 /* Restore terminal settings. */ 652 if (F_ISSET(clp, CL_STDIN_TTY)) 653 (void)tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &t); 654 655 (void)cl_attr(sp, SA_ALTERNATE, 1); 656 #endif 657 658 /* Set the window name. */ 659 (void)cl_rename(sp, sp->frp->name, 1); 660 661 /* Put the cursor keys into application mode. */ 662 (void)keypad(stdscr, TRUE); 663 664 /* Refresh and repaint the screen. */ 665 (void)move(oldy, oldx); 666 (void)cl_refresh(sp, 1); 667 668 /* If the screen changed size, set the SIGWINCH bit. */ 669 if (cl_ssize(sp, 1, NULL, NULL, &changed)) 670 return (1); 671 if (changed) 672 F_SET(CLP(sp), CL_SIGWINCH); 673 674 return (0); 675 } 676 677 /* 678 * cl_usage -- 679 * Print out the curses usage messages. 680 * 681 * PUBLIC: void cl_usage __P((void)); 682 */ 683 void 684 cl_usage() 685 { 686 #define USAGE "\ 687 usage: ex [-eFRrSsv] [-c command] [-t tag] [-w size] [file ...]\n\ 688 usage: vi [-eFlRrSv] [-c command] [-t tag] [-w size] [file ...]\n" 689 (void)fprintf(stderr, "%s", USAGE); 690 #undef USAGE 691 } 692 693 #ifdef DEBUG 694 /* 695 * gdbrefresh -- 696 * Stub routine so can flush out curses screen changes using gdb. 697 */ 698 int 699 gdbrefresh() 700 { 701 refresh(); 702 return (0); /* XXX Convince gdb to run it. */ 703 } 704 #endif 705