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