1 /* $OpenBSD: cl_funcs.c,v 1.13 2009/01/28 13:02:22 sobrado 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 <term.h> 30 #include <termios.h> 31 #include <unistd.h> 32 33 #include "../common/common.h" 34 #include "../vi/vi.h" 35 #include "cl.h" 36 37 /* 38 * cl_addstr -- 39 * Add len bytes from the string at the cursor, advancing the cursor. 40 * 41 * PUBLIC: int cl_addstr(SCR *, const char *, size_t); 42 */ 43 int 44 cl_addstr(sp, str, len) 45 SCR *sp; 46 const char *str; 47 size_t len; 48 { 49 size_t oldy, oldx; 50 int iv; 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(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(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(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 * If the screen has not been setup we cannot call 218 * curses routines yet. 219 */ 220 if (F_ISSET(sp, SC_SCR_VI)) { 221 /* 222 * Vi has an edit option which determines if the 223 * terminal should be beeped or the screen flashed. 224 */ 225 if (O_ISSET(sp, O_FLASH)) 226 (void)flash(); 227 else 228 (void)beep(); 229 } else if (!O_ISSET(sp, O_FLASH)) 230 (void)write(STDOUT_FILENO, "\07", 1); 231 } 232 return (0); 233 } 234 235 /* 236 * cl_clrtoeol -- 237 * Clear from the current cursor to the end of the line. 238 * 239 * PUBLIC: int cl_clrtoeol(SCR *); 240 */ 241 int 242 cl_clrtoeol(sp) 243 SCR *sp; 244 { 245 return (clrtoeol() == ERR); 246 } 247 248 /* 249 * cl_cursor -- 250 * Return the current cursor position. 251 * 252 * PUBLIC: int cl_cursor(SCR *, size_t *, size_t *); 253 */ 254 int 255 cl_cursor(sp, yp, xp) 256 SCR *sp; 257 size_t *yp, *xp; 258 { 259 /* 260 * The curses screen support splits a single underlying curses screen 261 * into multiple screens to support split screen semantics. For this 262 * reason the returned value must be adjusted to be relative to the 263 * current screen, and not absolute. Screens that implement the split 264 * using physically distinct screens won't need this hack. 265 */ 266 getyx(stdscr, *yp, *xp); 267 *yp -= sp->woff; 268 return (0); 269 } 270 271 /* 272 * cl_deleteln -- 273 * Delete the current line, scrolling all lines below it. 274 * 275 * PUBLIC: int cl_deleteln(SCR *); 276 */ 277 int 278 cl_deleteln(sp) 279 SCR *sp; 280 { 281 #ifndef mvchgat 282 CHAR_T ch; 283 size_t col, lno, spcnt; 284 #endif 285 size_t oldy, oldx; 286 287 /* 288 * This clause is required because the curses screen uses reverse 289 * video to delimit split screens. If the screen does not do this, 290 * this code won't be necessary. 291 * 292 * If the bottom line was in reverse video, rewrite it in normal 293 * video before it's scrolled. 294 * 295 * Check for the existence of a chgat function; XSI requires it, but 296 * historic implementations of System V curses don't. If it's not 297 * a #define, we'll fall back to doing it by hand, which is slow but 298 * acceptable. 299 * 300 * By hand means walking through the line, retrieving and rewriting 301 * each character. Curses has no EOL marker, so track strings of 302 * spaces, and copy the trailing spaces only if there's a non-space 303 * character following. 304 */ 305 if (!F_ISSET(sp, SC_SCR_EXWROTE) && IS_SPLIT(sp)) { 306 getyx(stdscr, oldy, oldx); 307 #ifdef mvchgat 308 mvchgat(RLNO(sp, LASTLINE(sp)), 0, -1, A_NORMAL, 0, NULL); 309 #else 310 for (lno = RLNO(sp, LASTLINE(sp)), col = spcnt = 0;;) { 311 (void)move(lno, col); 312 ch = winch(stdscr); 313 if (isblank(ch)) 314 ++spcnt; 315 else { 316 (void)move(lno, col - spcnt); 317 for (; spcnt > 0; --spcnt) 318 (void)addch(' '); 319 (void)addch(ch); 320 } 321 if (++col >= sp->cols) 322 break; 323 } 324 #endif 325 (void)move(oldy, oldx); 326 } 327 328 /* 329 * The bottom line is expected to be blank after this operation, 330 * and other screens must support that semantic. 331 */ 332 return (deleteln() == ERR); 333 } 334 335 /* 336 * cl_ex_adjust -- 337 * Adjust the screen for ex. This routine is purely for standalone 338 * ex programs. All special purpose, all special case. 339 * 340 * PUBLIC: int cl_ex_adjust(SCR *, exadj_t); 341 */ 342 int 343 cl_ex_adjust(sp, action) 344 SCR *sp; 345 exadj_t action; 346 { 347 CL_PRIVATE *clp; 348 int cnt; 349 350 clp = CLP(sp); 351 switch (action) { 352 case EX_TERM_SCROLL: 353 /* Move the cursor up one line if that's possible. */ 354 if (clp->cuu1 != NULL) 355 (void)tputs(clp->cuu1, 1, cl_putchar); 356 else if (clp->cup != NULL) 357 (void)tputs(tgoto(clp->cup, 358 0, LINES - 2), 1, cl_putchar); 359 else 360 return (0); 361 /* FALLTHROUGH */ 362 case EX_TERM_CE: 363 /* Clear the line. */ 364 if (clp->el != NULL) { 365 (void)putchar('\r'); 366 (void)tputs(clp->el, 1, cl_putchar); 367 } else { 368 /* 369 * Historically, ex didn't erase the line, so, if the 370 * displayed line was only a single glyph, and <eof> 371 * was more than one glyph, the output would not fully 372 * overwrite the user's input. To fix this, output 373 * the maxiumum character number of spaces. Note, 374 * this won't help if the user entered extra prompt 375 * or <blank> characters before the command character. 376 * We'd have to do a lot of work to make that work, and 377 * it's almost certainly not worth the effort. 378 */ 379 for (cnt = 0; cnt < MAX_CHARACTER_COLUMNS; ++cnt) 380 (void)putchar('\b'); 381 for (cnt = 0; cnt < MAX_CHARACTER_COLUMNS; ++cnt) 382 (void)putchar(' '); 383 (void)putchar('\r'); 384 (void)fflush(stdout); 385 } 386 break; 387 default: 388 abort(); 389 } 390 return (0); 391 } 392 393 /* 394 * cl_insertln -- 395 * Push down the current line, discarding the bottom line. 396 * 397 * PUBLIC: int cl_insertln(SCR *); 398 */ 399 int 400 cl_insertln(sp) 401 SCR *sp; 402 { 403 /* 404 * The current line is expected to be blank after this operation, 405 * and the screen must support that semantic. 406 */ 407 return (insertln() == ERR); 408 } 409 410 /* 411 * cl_keyval -- 412 * Return the value for a special key. 413 * 414 * PUBLIC: int cl_keyval(SCR *, scr_keyval_t, CHAR_T *, int *); 415 */ 416 int 417 cl_keyval(sp, val, chp, dnep) 418 SCR *sp; 419 scr_keyval_t val; 420 CHAR_T *chp; 421 int *dnep; 422 { 423 CL_PRIVATE *clp; 424 425 /* 426 * VEOF, VERASE and VKILL are required by POSIX 1003.1-1990, 427 * VWERASE is a 4BSD extension. 428 */ 429 clp = CLP(sp); 430 switch (val) { 431 case KEY_VEOF: 432 *dnep = (*chp = clp->orig.c_cc[VEOF]) == _POSIX_VDISABLE; 433 break; 434 case KEY_VERASE: 435 *dnep = (*chp = clp->orig.c_cc[VERASE]) == _POSIX_VDISABLE; 436 break; 437 case KEY_VKILL: 438 *dnep = (*chp = clp->orig.c_cc[VKILL]) == _POSIX_VDISABLE; 439 break; 440 #ifdef VWERASE 441 case KEY_VWERASE: 442 *dnep = (*chp = clp->orig.c_cc[VWERASE]) == _POSIX_VDISABLE; 443 break; 444 #endif 445 default: 446 *dnep = 1; 447 break; 448 } 449 return (0); 450 } 451 452 /* 453 * cl_move -- 454 * Move the cursor. 455 * 456 * PUBLIC: int cl_move(SCR *, size_t, size_t); 457 */ 458 int 459 cl_move(sp, lno, cno) 460 SCR *sp; 461 size_t lno, cno; 462 { 463 /* See the comment in cl_cursor. */ 464 if (move(RLNO(sp, lno), cno) == ERR) { 465 msgq(sp, M_ERR, 466 "Error: move: l(%u) c(%u) o(%u)", lno, cno, sp->woff); 467 return (1); 468 } 469 return (0); 470 } 471 472 /* 473 * cl_refresh -- 474 * Refresh the screen. 475 * 476 * PUBLIC: int cl_refresh(SCR *, int); 477 */ 478 int 479 cl_refresh(sp, repaint) 480 SCR *sp; 481 int repaint; 482 { 483 CL_PRIVATE *clp; 484 485 clp = CLP(sp); 486 487 /* 488 * If we received a killer signal, we're done, there's no point 489 * in refreshing the screen. 490 */ 491 if (clp->killersig) 492 return (0); 493 494 /* 495 * If repaint is set, the editor is telling us that we don't know 496 * what's on the screen, so we have to repaint from scratch. 497 * 498 * In the curses library, doing wrefresh(curscr) is okay, but the 499 * screen flashes when we then apply the refresh() to bring it up 500 * to date. So, use clearok(). 501 */ 502 if (repaint) 503 clearok(curscr, 1); 504 return (refresh() == ERR); 505 } 506 507 /* 508 * cl_rename -- 509 * Rename the file. 510 * 511 * PUBLIC: int cl_rename(SCR *, char *, int); 512 */ 513 int 514 cl_rename(sp, name, on) 515 SCR *sp; 516 char *name; 517 int on; 518 { 519 GS *gp; 520 CL_PRIVATE *clp; 521 char *ttype; 522 523 gp = sp->gp; 524 clp = CLP(sp); 525 526 ttype = OG_STR(gp, GO_TERM); 527 528 /* 529 * XXX 530 * We can only rename windows for xterm. 531 */ 532 if (on) { 533 if (F_ISSET(clp, CL_RENAME_OK) && 534 !strncmp(ttype, "xterm", sizeof("xterm") - 1)) { 535 F_SET(clp, CL_RENAME); 536 (void)printf(XTERM_RENAME, name); 537 (void)fflush(stdout); 538 } 539 } else 540 if (F_ISSET(clp, CL_RENAME)) { 541 F_CLR(clp, CL_RENAME); 542 (void)printf(XTERM_RENAME, ttype); 543 (void)fflush(stdout); 544 } 545 return (0); 546 } 547 548 /* 549 * cl_suspend -- 550 * Suspend a screen. 551 * 552 * PUBLIC: int cl_suspend(SCR *, int *); 553 */ 554 int 555 cl_suspend(sp, allowedp) 556 SCR *sp; 557 int *allowedp; 558 { 559 struct termios t; 560 CL_PRIVATE *clp; 561 size_t oldy, oldx; 562 int changed; 563 564 clp = CLP(sp); 565 *allowedp = 1; 566 567 /* 568 * The ex implementation of this function isn't needed by screens not 569 * supporting ex commands that require full terminal canonical mode 570 * (e.g. :suspend). 571 * 572 * The vi implementation of this function isn't needed by screens not 573 * supporting vi process suspension, i.e. any screen that isn't backed 574 * by a UNIX shell. 575 * 576 * Setting allowedp to 0 will cause the editor to reject the command. 577 */ 578 if (F_ISSET(sp, SC_EX)) { 579 /* Save the terminal settings, and restore the original ones. */ 580 if (F_ISSET(clp, CL_STDIN_TTY)) { 581 (void)tcgetattr(STDIN_FILENO, &t); 582 (void)tcsetattr(STDIN_FILENO, 583 TCSASOFT | TCSADRAIN, &clp->orig); 584 } 585 586 /* Stop the process group. */ 587 (void)kill(0, SIGTSTP); 588 589 /* Time passes ... */ 590 591 /* Restore terminal settings. */ 592 if (F_ISSET(clp, CL_STDIN_TTY)) 593 (void)tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &t); 594 return (0); 595 } 596 597 /* 598 * Move to the lower left-hand corner of the screen. 599 * 600 * XXX 601 * Not sure this is necessary in System V implementations, but it 602 * shouldn't hurt. 603 */ 604 getyx(stdscr, oldy, oldx); 605 (void)move(LINES - 1, 0); 606 (void)refresh(); 607 608 /* 609 * Temporarily end the screen. System V introduced a semantic where 610 * endwin() could be restarted. We use it because restarting curses 611 * from scratch often fails in System V. 4BSD curses didn't support 612 * restarting after endwin(), so we have to do what clean up we can 613 * without calling it. 614 */ 615 #ifdef HAVE_BSD_CURSES 616 /* Save the terminal settings. */ 617 (void)tcgetattr(STDIN_FILENO, &t); 618 #endif 619 620 /* Restore the cursor keys to normal mode. */ 621 (void)keypad(stdscr, FALSE); 622 623 /* Restore the window name. */ 624 (void)cl_rename(sp, NULL, 0); 625 626 #ifdef HAVE_BSD_CURSES 627 (void)cl_attr(sp, SA_ALTERNATE, 0); 628 #else 629 (void)endwin(); 630 #endif 631 /* 632 * XXX 633 * Restore the original terminal settings. This is bad -- the 634 * reset can cause character loss from the tty queue. However, 635 * we can't call endwin() in BSD curses implementations, and too 636 * many System V curses implementations don't get it right. 637 */ 638 (void)tcsetattr(STDIN_FILENO, TCSADRAIN | TCSASOFT, &clp->orig); 639 640 /* Stop the process group. */ 641 (void)kill(0, SIGTSTP); 642 643 /* Time passes ... */ 644 645 /* 646 * If we received a killer signal, we're done. Leave everything 647 * unchanged. In addition, the terminal has already been reset 648 * correctly, so leave it alone. 649 */ 650 if (clp->killersig) { 651 F_CLR(clp, CL_SCR_EX_INIT | CL_SCR_VI_INIT); 652 return (0); 653 } 654 655 #ifdef HAVE_BSD_CURSES 656 /* Restore terminal settings. */ 657 if (F_ISSET(clp, CL_STDIN_TTY)) 658 (void)tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &t); 659 660 (void)cl_attr(sp, SA_ALTERNATE, 1); 661 #endif 662 663 /* Set the window name. */ 664 (void)cl_rename(sp, sp->frp->name, 1); 665 666 /* Put the cursor keys into application mode. */ 667 (void)keypad(stdscr, TRUE); 668 669 /* Refresh and repaint the screen. */ 670 (void)move(oldy, oldx); 671 (void)cl_refresh(sp, 1); 672 673 /* If the screen changed size, set the SIGWINCH bit. */ 674 if (cl_ssize(sp, 1, NULL, NULL, &changed)) 675 return (1); 676 if (changed) 677 F_SET(CLP(sp), CL_SIGWINCH); 678 679 return (0); 680 } 681 682 /* 683 * cl_usage -- 684 * Print out the curses usage messages. 685 * 686 * PUBLIC: void cl_usage(void); 687 */ 688 void 689 cl_usage() 690 { 691 switch (pmode) { 692 case MODE_EX: 693 (void)fprintf(stderr, "usage: " 694 "ex [-FRrSsv] [-c cmd] [-t tag] [-w size] [file ...]\n"); 695 break; 696 case MODE_VI: 697 (void)fprintf(stderr, "usage: " 698 "vi [-eFRrS] [-c cmd] [-t tag] [-w size] [file ...]\n"); 699 break; 700 case MODE_VIEW: 701 (void)fprintf(stderr, "usage: " 702 "view [-eFrS] [-c cmd] [-t tag] [-w size] [file ...]\n"); 703 break; 704 } 705 } 706 707 #ifdef DEBUG 708 /* 709 * gdbrefresh -- 710 * Stub routine so can flush out curses screen changes using gdb. 711 */ 712 int 713 gdbrefresh() 714 { 715 refresh(); 716 return (0); /* XXX Convince gdb to run it. */ 717 } 718 #endif 719