1 /* $OpenBSD: screen.c,v 1.8 2001/11/19 19:02:14 mpech Exp $ */ 2 3 /* 4 * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman 5 * 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 in the documentation and/or other materials provided with 14 * the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY 17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 22 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 23 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 25 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 26 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 30 /* 31 * Routines which deal with the characteristics of the terminal. 32 * Uses termcap to be as terminal-independent as possible. 33 * 34 * {{ Maybe someday this should be rewritten to use curses or terminfo. }} 35 */ 36 37 #include "less.h" 38 #include "cmd.h" 39 40 #if HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS 41 #include <termios.h> 42 #if HAVE_SYS_IOCTL_H && !defined(TIOCGWINSZ) 43 #include <sys/ioctl.h> 44 #endif 45 #else 46 #if HAVE_TERMIO_H 47 #include <termio.h> 48 #else 49 #include <sgtty.h> 50 #if HAVE_SYS_IOCTL_H && (defined(TIOCGWINSZ) || defined(TCGETA) || defined(TIOCGETP) || defined(WIOCGETD)) 51 #include <sys/ioctl.h> 52 #endif 53 #endif 54 #endif 55 #if HAVE_TERMCAP_H 56 #include <termcap.h> 57 #endif 58 59 #ifndef TIOCGWINSZ 60 /* 61 * For the Unix PC (ATT 7300 & 3B1): 62 * Since WIOCGETD is defined in sys/window.h, we can't use that to decide 63 * whether to include sys/window.h. Use SIGPHONE from sys/signal.h instead. 64 */ 65 #include <sys/signal.h> 66 #ifdef SIGPHONE 67 #include <sys/window.h> 68 #endif 69 #endif 70 71 #if HAVE_SYS_STREAM_H 72 #include <sys/stream.h> 73 #endif 74 #if HAVE_SYS_PTEM_H 75 #include <sys/ptem.h> 76 #endif 77 78 #if OS2 79 #define DEFAULT_TERM "ansi" 80 #else 81 #define DEFAULT_TERM "unknown" 82 #endif 83 84 /* 85 * Strings passed to tputs() to do various terminal functions. 86 */ 87 static char 88 *sc_pad, /* Pad string */ 89 *sc_home, /* Cursor home */ 90 *sc_addline, /* Add line, scroll down following lines */ 91 *sc_lower_left, /* Cursor to last line, first column */ 92 *sc_move, /* General cursor positioning */ 93 *sc_clear, /* Clear screen */ 94 *sc_eol_clear, /* Clear to end of line */ 95 *sc_eos_clear, /* Clear to end of screen */ 96 *sc_s_in, /* Enter standout (highlighted) mode */ 97 *sc_s_out, /* Exit standout mode */ 98 *sc_u_in, /* Enter underline mode */ 99 *sc_u_out, /* Exit underline mode */ 100 *sc_b_in, /* Enter bold mode */ 101 *sc_b_out, /* Exit bold mode */ 102 *sc_bl_in, /* Enter blink mode */ 103 *sc_bl_out, /* Exit blink mode */ 104 *sc_visual_bell, /* Visual bell (flash screen) sequence */ 105 *sc_backspace, /* Backspace cursor */ 106 *sc_s_keypad, /* Start keypad mode */ 107 *sc_e_keypad, /* End keypad mode */ 108 *sc_init, /* Startup terminal initialization */ 109 *sc_deinit; /* Exit terminal de-initialization */ 110 111 static int init_done = 0; 112 static int tty_fd = -1; 113 114 public int auto_wrap; /* Terminal does \r\n when write past margin */ 115 public int ignaw; /* Terminal ignores \n immediately after wrap */ 116 public int erase_char, kill_char; /* The user's erase and line-kill chars */ 117 public int werase_char; /* The user's word-erase char */ 118 public int sc_width, sc_height; /* Height & width of screen */ 119 public int bo_s_width, bo_e_width; /* Printing width of boldface seq */ 120 public int ul_s_width, ul_e_width; /* Printing width of underline seq */ 121 public int so_s_width, so_e_width; /* Printing width of standout seq */ 122 public int bl_s_width, bl_e_width; /* Printing width of blink seq */ 123 public int above_mem, below_mem; /* Memory retained above/below screen */ 124 public int can_goto_line; /* Can move cursor to any line */ 125 126 static char *cheaper(); 127 128 /* 129 * These two variables are sometimes defined in, 130 * and needed by, the termcap library. 131 */ 132 #if MUST_DEFINE_OSPEED 133 extern short ospeed; /* Terminal output baud rate */ 134 extern char PC; /* Pad character */ 135 #endif 136 137 extern int quiet; /* If VERY_QUIET, use visual bell for bell */ 138 extern int know_dumb; /* Don't complain about a dumb terminal */ 139 extern int back_scroll; 140 extern int swindow; 141 extern int no_init; 142 extern int quit_at_eof; 143 extern int more_mode; 144 #if HILITE_SEARCH 145 extern int hilite_search; 146 #endif 147 148 extern char *tgetstr(); 149 extern char *tgoto(); 150 151 152 /* 153 * Change terminal to "raw mode", or restore to "normal" mode. 154 * "Raw mode" means 155 * 1. An outstanding read will complete on receipt of a single keystroke. 156 * 2. Input is not echoed. 157 * 3. On output, \n is mapped to \r\n. 158 * 4. \t is NOT expanded into spaces. 159 * 5. Signal-causing characters such as ctrl-C (interrupt), 160 * etc. are NOT disabled. 161 * It doesn't matter whether an input \n is mapped to \r, or vice versa. 162 */ 163 public void 164 raw_mode(on) 165 int on; 166 { 167 static int curr_on = 0; 168 169 if (on == curr_on) 170 return; 171 172 if (tty_fd == -1 && (tty_fd = open("/dev/tty", O_RDWR)) < 0) 173 tty_fd = 2; 174 175 #if OS2 176 signal(SIGINT, SIG_IGN); 177 erase_char = '\b'; 178 kill_char = '\033'; 179 #else 180 #if HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS 181 { 182 struct termios s; 183 static struct termios save_term; 184 185 if (on) 186 { 187 /* 188 * Get terminal modes. 189 */ 190 if (tcgetattr(tty_fd, &s) == -1) 191 return; 192 193 /* 194 * Save modes and set certain variables dependent on modes. 195 */ 196 save_term = s; 197 #if HAVE_OSPEED 198 switch (cfgetospeed(&s)) 199 { 200 #ifdef B0 201 case B0: ospeed = 0; break; 202 #endif 203 #ifdef B50 204 case B50: ospeed = 1; break; 205 #endif 206 #ifdef B75 207 case B75: ospeed = 2; break; 208 #endif 209 #ifdef B110 210 case B110: ospeed = 3; break; 211 #endif 212 #ifdef B134 213 case B134: ospeed = 4; break; 214 #endif 215 #ifdef B150 216 case B150: ospeed = 5; break; 217 #endif 218 #ifdef B200 219 case B200: ospeed = 6; break; 220 #endif 221 #ifdef B300 222 case B300: ospeed = 7; break; 223 #endif 224 #ifdef B600 225 case B600: ospeed = 8; break; 226 #endif 227 #ifdef B1200 228 case B1200: ospeed = 9; break; 229 #endif 230 #ifdef B1800 231 case B1800: ospeed = 10; break; 232 #endif 233 #ifdef B2400 234 case B2400: ospeed = 11; break; 235 #endif 236 #ifdef B4800 237 case B4800: ospeed = 12; break; 238 #endif 239 #ifdef B9600 240 case B9600: ospeed = 13; break; 241 #endif 242 #ifdef EXTA 243 case EXTA: ospeed = 14; break; 244 #endif 245 #ifdef EXTB 246 case EXTB: ospeed = 15; break; 247 #endif 248 #ifdef B57600 249 case B57600: ospeed = 16; break; 250 #endif 251 #ifdef B115200 252 case B115200: ospeed = 17; break; 253 #endif 254 default: ; 255 } 256 #endif 257 erase_char = s.c_cc[VERASE]; 258 kill_char = s.c_cc[VKILL]; 259 #ifdef VWERASE 260 werase_char = s.c_cc[VWERASE]; 261 #else 262 werase_char = 0; 263 #endif 264 265 /* 266 * Set the modes to the way we want them. 267 */ 268 s.c_lflag &= ~(0 269 #ifdef ICANON 270 | ICANON 271 #endif 272 #ifdef ECHO 273 | ECHO 274 #endif 275 #ifdef ECHOE 276 | ECHOE 277 #endif 278 #ifdef ECHOK 279 | ECHOK 280 #endif 281 #if ECHONL 282 | ECHONL 283 #endif 284 ); 285 286 s.c_oflag |= (0 287 #ifdef XTABS 288 | XTABS 289 #else 290 #ifdef TAB3 291 | TAB3 292 #else 293 #ifdef OXTABS 294 | OXTABS 295 #endif 296 #endif 297 #endif 298 #ifdef OPOST 299 | OPOST 300 #endif 301 #ifdef ONLCR 302 | ONLCR 303 #endif 304 ); 305 306 s.c_oflag &= ~(0 307 #ifdef ONOEOT 308 | ONOEOT 309 #endif 310 #ifdef OCRNL 311 | OCRNL 312 #endif 313 #ifdef ONOCR 314 | ONOCR 315 #endif 316 #ifdef ONLRET 317 | ONLRET 318 #endif 319 ); 320 s.c_cc[VMIN] = 1; 321 s.c_cc[VTIME] = 0; 322 } else 323 { 324 /* 325 * Restore saved modes. 326 */ 327 s = save_term; 328 } 329 if (tcsetattr(tty_fd, TCSANOW, &s) == -1) 330 return; 331 } 332 #else 333 #ifdef TCGETA 334 { 335 struct termio s; 336 static struct termio save_term; 337 338 if (on) 339 { 340 /* 341 * Get terminal modes. 342 */ 343 ioctl(tty_fd, TCGETA, &s); 344 345 /* 346 * Save modes and set certain variables dependent on modes. 347 */ 348 save_term = s; 349 #if HAVE_OSPEED 350 ospeed = s.c_cflag & CBAUD; 351 #endif 352 erase_char = s.c_cc[VERASE]; 353 kill_char = s.c_cc[VKILL]; 354 #ifdef VWERASE 355 werase_char = s.c_cc[VWERASE]; 356 #else 357 werase_char = 0; 358 #endif 359 360 /* 361 * Set the modes to the way we want them. 362 */ 363 s.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL); 364 s.c_oflag |= (OPOST|ONLCR|TAB3); 365 s.c_oflag &= ~(OCRNL|ONOCR|ONLRET); 366 s.c_cc[VMIN] = 1; 367 s.c_cc[VTIME] = 0; 368 } else 369 { 370 /* 371 * Restore saved modes. 372 */ 373 s = save_term; 374 } 375 ioctl(tty_fd, TCSETAW, &s); 376 } 377 #else 378 { 379 struct sgttyb s; 380 static struct sgttyb save_term; 381 382 if (on) 383 { 384 /* 385 * Get terminal modes. 386 */ 387 ioctl(tty_fd, TIOCGETP, &s); 388 389 /* 390 * Save modes and set certain variables dependent on modes. 391 */ 392 save_term = s; 393 #if HAVE_OSPEED 394 ospeed = s.sg_ospeed; 395 #endif 396 erase_char = s.sg_erase; 397 kill_char = s.sg_kill; 398 werase_char = 0; 399 400 /* 401 * Set the modes to the way we want them. 402 */ 403 s.sg_flags |= CBREAK; 404 s.sg_flags &= ~(ECHO|XTABS); 405 } else 406 { 407 /* 408 * Restore saved modes. 409 */ 410 s = save_term; 411 } 412 ioctl(tty_fd, TIOCSETN, &s); 413 } 414 #endif 415 #endif 416 #endif 417 curr_on = on; 418 } 419 420 static void 421 cannot(s) 422 char *s; 423 { 424 PARG parg; 425 426 if (know_dumb || more_mode) 427 /* 428 * User knows this is a dumb terminal, so don't tell him. 429 * more doesn't complain about these, either. 430 */ 431 return; 432 433 parg.p_string = s; 434 error("WARNING: terminal cannot %s", &parg); 435 } 436 437 /* 438 * Get size of the output screen. 439 */ 440 #if OS2 441 public void 442 scrsize() 443 { 444 int s[2]; 445 446 _scrsize(s); 447 sc_width = s[0]; 448 sc_height = s[1]; 449 } 450 451 #else 452 453 public void 454 scrsize() 455 { 456 char *s; 457 #ifdef TIOCGWINSZ 458 struct winsize w; 459 #else 460 #ifdef WIOCGETD 461 struct uwdata w; 462 #endif 463 #endif 464 465 if (tty_fd == -1 && (tty_fd = open("/dev/tty", O_RDWR)) < 0) 466 tty_fd = 2; 467 468 #ifdef TIOCGWINSZ 469 if (ioctl(tty_fd, TIOCGWINSZ, &w) == 0 && w.ws_row > 0) 470 sc_height = w.ws_row; 471 else 472 #else 473 #ifdef WIOCGETD 474 if (ioctl(tty_fd, WIOCGETD, &w) == 0 && w.uw_height > 0) 475 sc_height = w.uw_height/w.uw_vs; 476 else 477 #endif 478 #endif 479 if ((s = getenv("LINES")) != NULL) 480 sc_height = atoi(s); 481 else 482 sc_height = tgetnum("li"); 483 484 if (sc_height <= 0) 485 sc_height = 24; 486 487 #ifdef TIOCGWINSZ 488 if (ioctl(tty_fd, TIOCGWINSZ, &w) == 0 && w.ws_col > 0) 489 sc_width = w.ws_col; 490 else 491 #ifdef WIOCGETD 492 if (ioctl(tty_fd, WIOCGETD, &w) == 0 && w.uw_width > 0) 493 sc_width = w.uw_width/w.uw_hs; 494 else 495 #endif 496 #endif 497 if ((s = getenv("COLUMNS")) != NULL) 498 sc_width = atoi(s); 499 else 500 sc_width = tgetnum("co"); 501 502 if (sc_width <= 0) 503 sc_width = 80; 504 } 505 #endif /* OS2 */ 506 507 /* 508 * Take care of the "variable" keys. 509 * Certain keys send escape sequences which differ on different terminals 510 * (such as the arrow keys, INSERT, DELETE, etc.) 511 * Construct the commands based on these keys. 512 */ 513 public void 514 get_editkeys() 515 { 516 char *sp; 517 char *s; 518 char tbuf[40]; 519 520 static char kfcmdtable[400]; 521 int sz_kfcmdtable = 0; 522 static char kecmdtable[400]; 523 int sz_kecmdtable = 0; 524 525 #define put_cmd(str,action,tbl,sz) { \ 526 strcpy(tbl+sz, str); \ 527 sz += strlen(str) + 1; \ 528 tbl[sz++] = action; } 529 #define put_esc_cmd(str,action,tbl,sz) { \ 530 tbl[sz++] = ESC; \ 531 put_cmd(str,action,tbl,sz); } 532 533 #define put_fcmd(str,action) put_cmd(str,action,kfcmdtable,sz_kfcmdtable) 534 #define put_ecmd(str,action) put_cmd(str,action,kecmdtable,sz_kecmdtable) 535 #define put_esc_fcmd(str,action) put_esc_cmd(str,action,kfcmdtable,sz_kfcmdtable) 536 #define put_esc_ecmd(str,action) put_esc_cmd(str,action,kecmdtable,sz_kecmdtable) 537 538 /* 539 * Look at some interesting keys and see what strings they send. 540 * Create commands (both command keys and line-edit keys). 541 */ 542 543 /* RIGHT ARROW */ 544 sp = tbuf; 545 if ((s = tgetstr("kr", &sp)) != NULL) 546 { 547 put_ecmd(s, EC_RIGHT); 548 put_esc_ecmd(s, EC_W_RIGHT); 549 } 550 551 /* LEFT ARROW */ 552 sp = tbuf; 553 if ((s = tgetstr("kl", &sp)) != NULL) 554 { 555 put_ecmd(s, EC_LEFT); 556 put_esc_ecmd(s, EC_W_LEFT); 557 } 558 559 /* UP ARROW */ 560 sp = tbuf; 561 if ((s = tgetstr("ku", &sp)) != NULL) 562 { 563 put_ecmd(s, EC_UP); 564 put_fcmd(s, A_B_LINE); 565 } 566 567 /* DOWN ARROW */ 568 sp = tbuf; 569 if ((s = tgetstr("kd", &sp)) != NULL) 570 { 571 put_ecmd(s, EC_DOWN); 572 put_fcmd(s, A_F_LINE); 573 } 574 575 /* PAGE UP */ 576 sp = tbuf; 577 if ((s = tgetstr("kP", &sp)) != NULL) 578 { 579 put_fcmd(s, A_B_SCREEN); 580 } 581 582 /* PAGE DOWN */ 583 sp = tbuf; 584 if ((s = tgetstr("kN", &sp)) != NULL) 585 { 586 put_fcmd(s, A_F_SCREEN); 587 } 588 589 /* HOME */ 590 sp = tbuf; 591 if ((s = tgetstr("kh", &sp)) != NULL) 592 { 593 put_ecmd(s, EC_HOME); 594 } 595 596 /* END */ 597 sp = tbuf; 598 if ((s = tgetstr("@7", &sp)) != NULL) 599 { 600 put_ecmd(s, EC_END); 601 } 602 603 /* DELETE */ 604 sp = tbuf; 605 if ((s = tgetstr("kD", &sp)) == NULL) 606 { 607 /* Use DEL (\177) if no "kD" termcap. */ 608 tbuf[1] = '\177'; 609 tbuf[2] = '\0'; 610 s = tbuf+1; 611 } 612 put_ecmd(s, EC_DELETE); 613 put_esc_ecmd(s, EC_W_DELETE); 614 615 /* BACKSPACE */ 616 tbuf[0] = ESC; 617 tbuf[1] = erase_char; 618 tbuf[2] = '\0'; 619 put_ecmd(tbuf, EC_W_BACKSPACE); 620 621 if (werase_char != 0) 622 { 623 tbuf[0] = werase_char; 624 tbuf[1] = '\0'; 625 put_ecmd(tbuf, EC_W_BACKSPACE); 626 } 627 628 /* 629 * Register the two tables. 630 */ 631 add_fcmd_table(kfcmdtable, sz_kfcmdtable); 632 add_ecmd_table(kecmdtable, sz_kecmdtable); 633 } 634 635 #if DEBUG 636 static void 637 get_debug_term() 638 { 639 auto_wrap = 1; 640 ignaw = 1; 641 so_s_width = so_e_width = 0; 642 bo_s_width = bo_e_width = 0; 643 ul_s_width = ul_e_width = 0; 644 bl_s_width = bl_e_width = 0; 645 sc_s_keypad = "(InitKey)"; 646 sc_e_keypad = "(DeinitKey)"; 647 sc_init = "(InitTerm)"; 648 sc_deinit = "(DeinitTerm)"; 649 sc_eol_clear = "(ClearEOL)"; 650 sc_eos_clear = "(ClearEOS)"; 651 sc_clear = "(ClearScreen)"; 652 sc_move = "(Move<%d,%d>)"; 653 sc_s_in = "(SO+)"; 654 sc_s_out = "(SO-)"; 655 sc_u_in = "(UL+)"; 656 sc_u_out = "(UL-)"; 657 sc_b_in = "(BO+)"; 658 sc_b_out = "(BO-)"; 659 sc_bl_in = "(BL+)"; 660 sc_bl_out = "(BL-)"; 661 sc_visual_bell ="(VBell)"; 662 sc_backspace = "(BS)"; 663 sc_home = "(Home)"; 664 sc_lower_left = "(LL)"; 665 sc_addline = "(AddLine)"; 666 } 667 #endif 668 669 /* 670 * Get terminal capabilities via termcap. 671 */ 672 public void 673 get_term() 674 { 675 char *sp; 676 char *t1, *t2; 677 int hard; 678 char *term; 679 char termbuf[2048]; 680 681 static char sbuf[1024]; 682 683 #ifdef OS2 684 /* 685 * Make sure the termcap database is available. 686 */ 687 sp = getenv("TERMCAP"); 688 if (sp == NULL || *sp == '\0') 689 { 690 char *termcap; 691 if ((sp = homefile("termcap.dat")) != NULL) 692 { 693 termcap = (char *) ecalloc(strlen(sp)+9, sizeof(char)); 694 sprintf(termcap, "TERMCAP=%s", sp); 695 free(sp); 696 putenv(termcap); 697 } 698 } 699 #endif 700 /* 701 * Find out what kind of terminal this is. 702 */ 703 if ((term = getenv("TERM")) == NULL) 704 term = DEFAULT_TERM; 705 if (tgetent(termbuf, term) <= 0) 706 strcpy(termbuf, "dumb:hc:"); 707 708 hard = tgetflag("hc"); 709 710 /* 711 * Get size of the screen. 712 */ 713 scrsize(); 714 pos_init(); 715 716 #if DEBUG 717 if (strncmp(term,"LESSDEBUG",9) == 0) 718 { 719 get_debug_term(); 720 return; 721 } 722 #endif /* DEBUG */ 723 724 auto_wrap = tgetflag("am"); 725 ignaw = tgetflag("xn"); 726 above_mem = tgetflag("da"); 727 below_mem = tgetflag("db"); 728 729 /* 730 * Assumes termcap variable "sg" is the printing width of: 731 * the standout sequence, the end standout sequence, 732 * the underline sequence, the end underline sequence, 733 * the boldface sequence, and the end boldface sequence. 734 */ 735 if ((so_s_width = tgetnum("sg")) < 0) 736 so_s_width = 0; 737 so_e_width = so_s_width; 738 739 bo_s_width = bo_e_width = so_s_width; 740 ul_s_width = ul_e_width = so_s_width; 741 bl_s_width = bl_e_width = so_s_width; 742 743 #if HILITE_SEARCH 744 if (so_s_width > 0 || so_e_width > 0) 745 /* 746 * Disable highlighting by default on magic cookie terminals. 747 * Turning on highlighting might change the displayed width 748 * of a line, causing the display to get messed up. 749 * The user can turn it back on with -g, 750 * but she won't like the results. 751 */ 752 hilite_search = 0; 753 #endif 754 755 /* 756 * Get various string-valued capabilities. 757 */ 758 sp = sbuf; 759 760 #if HAVE_OSPEED 761 sc_pad = tgetstr("pc", &sp); 762 if (sc_pad != NULL) 763 PC = *sc_pad; 764 #endif 765 766 sc_s_keypad = tgetstr("ks", &sp); 767 if (sc_s_keypad == NULL) 768 sc_s_keypad = ""; 769 sc_e_keypad = tgetstr("ke", &sp); 770 if (sc_e_keypad == NULL) 771 sc_e_keypad = ""; 772 773 /* 774 * This loses for terminals with termcap entries with ti/te strings 775 * that switch to/from an alternate screen, and we're in quit_at_eof 776 * (eg, more(1)). 777 */ 778 if (!quit_at_eof && !more_mode) { 779 sc_init = tgetstr("ti", &sp); 780 sc_deinit = tgetstr("te", &sp); 781 } 782 if (sc_init == NULL) 783 sc_init = ""; 784 if (sc_deinit == NULL) 785 sc_deinit = ""; 786 787 sc_eol_clear = tgetstr("ce", &sp); 788 if (hard || sc_eol_clear == NULL || *sc_eol_clear == '\0') 789 { 790 cannot("clear to end of line"); 791 sc_eol_clear = ""; 792 } 793 794 sc_eos_clear = tgetstr("cd", &sp); 795 if (below_mem && 796 (hard || sc_eos_clear == NULL || *sc_eos_clear == '\0')) 797 { 798 cannot("clear to end of screen"); 799 sc_eol_clear = ""; 800 } 801 802 sc_clear = tgetstr("cl", &sp); 803 if (hard || sc_clear == NULL || *sc_clear == '\0') 804 { 805 cannot("clear screen"); 806 sc_clear = "\n\n"; 807 } 808 809 sc_move = tgetstr("cm", &sp); 810 if (hard || sc_move == NULL || *sc_move == '\0') 811 { 812 /* 813 * This is not an error here, because we don't 814 * always need sc_move. 815 * We need it only if we don't have home or lower-left. 816 */ 817 sc_move = ""; 818 can_goto_line = 0; 819 } else 820 can_goto_line = 1; 821 822 sc_s_in = tgetstr("so", &sp); 823 if (hard || sc_s_in == NULL) 824 sc_s_in = ""; 825 826 sc_s_out = tgetstr("se", &sp); 827 if (hard || sc_s_out == NULL) 828 sc_s_out = ""; 829 830 sc_u_in = tgetstr("us", &sp); 831 if (hard || sc_u_in == NULL) 832 sc_u_in = sc_s_in; 833 834 sc_u_out = tgetstr("ue", &sp); 835 if (hard || sc_u_out == NULL) 836 sc_u_out = sc_s_out; 837 838 sc_b_in = tgetstr("md", &sp); 839 if (hard || sc_b_in == NULL) 840 { 841 sc_b_in = sc_s_in; 842 sc_b_out = sc_s_out; 843 } else 844 { 845 sc_b_out = tgetstr("me", &sp); 846 if (hard || sc_b_out == NULL) 847 sc_b_out = ""; 848 } 849 850 sc_bl_in = tgetstr("mb", &sp); 851 if (hard || sc_bl_in == NULL) 852 { 853 sc_bl_in = sc_s_in; 854 sc_bl_out = sc_s_out; 855 } else 856 { 857 sc_bl_out = tgetstr("me", &sp); 858 if (hard || sc_bl_out == NULL) 859 sc_bl_out = ""; 860 } 861 862 sc_visual_bell = tgetstr("vb", &sp); 863 if (hard || sc_visual_bell == NULL) 864 sc_visual_bell = ""; 865 866 if (tgetflag("bs")) 867 sc_backspace = "\b"; 868 else 869 { 870 sc_backspace = tgetstr("bc", &sp); 871 if (sc_backspace == NULL || *sc_backspace == '\0') 872 sc_backspace = "\b"; 873 } 874 875 /* 876 * Choose between using "ho" and "cm" ("home" and "cursor move") 877 * to move the cursor to the upper left corner of the screen. 878 */ 879 t1 = tgetstr("ho", &sp); 880 if (hard || t1 == NULL) 881 t1 = ""; 882 if (*sc_move == '\0') 883 t2 = ""; 884 else 885 { 886 strcpy(sp, tgoto(sc_move, 0, 0)); 887 t2 = sp; 888 sp += strlen(sp) + 1; 889 } 890 sc_home = cheaper(t1, t2, "home cursor", "|\b^"); 891 892 /* 893 * Choose between using "ll" and "cm" ("lower left" and "cursor move") 894 * to move the cursor to the lower left corner of the screen. 895 */ 896 t1 = tgetstr("ll", &sp); 897 if (hard || t1 == NULL) 898 t1 = ""; 899 if (*sc_move == '\0') 900 t2 = ""; 901 else 902 { 903 strcpy(sp, tgoto(sc_move, 0, sc_height-1)); 904 t2 = sp; 905 sp += strlen(sp) + 1; 906 } 907 sc_lower_left = cheaper(t1, t2, 908 "move cursor to lower left of screen", "\r"); 909 910 /* 911 * Choose between using "al" or "sr" ("add line" or "scroll reverse") 912 * to add a line at the top of the screen. 913 */ 914 t1 = tgetstr("al", &sp); 915 if (hard || t1 == NULL) 916 t1 = ""; 917 t2 = tgetstr("sr", &sp); 918 if (hard || t2 == NULL) 919 t2 = ""; 920 #if OS2 921 if (*t1 == '\0' && *t2 == '\0') 922 sc_addline = ""; 923 else 924 #endif 925 if (above_mem) 926 sc_addline = t1; 927 else 928 sc_addline = cheaper(t1, t2, "scroll backwards", ""); 929 if (*sc_addline == '\0') 930 { 931 /* 932 * Force repaint on any backward movement. 933 */ 934 back_scroll = 0; 935 } 936 } 937 938 /* 939 * Return the cost of displaying a termcap string. 940 * We use the trick of calling tputs, but as a char printing function 941 * we give it inc_costcount, which just increments "costcount". 942 * This tells us how many chars would be printed by using this string. 943 * {{ Couldn't we just use strlen? }} 944 */ 945 static int costcount; 946 947 /*ARGSUSED*/ 948 static int 949 inc_costcount(c) 950 int c; 951 { 952 costcount++; 953 return (c); 954 } 955 956 static int 957 cost(t) 958 char *t; 959 { 960 costcount = 0; 961 tputs(t, sc_height, inc_costcount); 962 return (costcount); 963 } 964 965 /* 966 * Return the "best" of the two given termcap strings. 967 * The best, if both exist, is the one with the lower 968 * cost (see cost() function). 969 */ 970 static char * 971 cheaper(t1, t2, doit, def) 972 char *t1, *t2; 973 char *doit; 974 char *def; 975 { 976 if (*t1 == '\0' && *t2 == '\0') 977 { 978 cannot(doit); 979 return (def); 980 } 981 if (*t1 == '\0') 982 return (t2); 983 if (*t2 == '\0') 984 return (t1); 985 if (cost(t1) < cost(t2)) 986 return (t1); 987 return (t2); 988 } 989 990 991 /* 992 * Below are the functions which perform all the 993 * terminal-specific screen manipulation. 994 */ 995 996 997 /* 998 * Initialize terminal 999 */ 1000 public void 1001 init() 1002 { 1003 if (no_init) 1004 return; 1005 tputs(sc_init, sc_height, putchr); 1006 tputs(sc_s_keypad, sc_height, putchr); 1007 init_done = 1; 1008 } 1009 1010 /* 1011 * Deinitialize terminal 1012 */ 1013 public void 1014 deinit() 1015 { 1016 if (no_init) 1017 return; 1018 if (!init_done) 1019 return; 1020 tputs(sc_e_keypad, sc_height, putchr); 1021 tputs(sc_deinit, sc_height, putchr); 1022 init_done = 0; 1023 } 1024 1025 /* 1026 * Home cursor (move to upper left corner of screen). 1027 */ 1028 public void 1029 home() 1030 { 1031 tputs(sc_home, 1, putchr); 1032 } 1033 1034 /* 1035 * Add a blank line (called with cursor at home). 1036 * Should scroll the display down. 1037 */ 1038 public void 1039 add_line() 1040 { 1041 tputs(sc_addline, sc_height, putchr); 1042 } 1043 1044 /* 1045 * Move cursor to lower left corner of screen. 1046 */ 1047 public void 1048 lower_left() 1049 { 1050 tputs(sc_lower_left, 1, putchr); 1051 } 1052 1053 /* 1054 * Goto a specific line on the screen. 1055 */ 1056 public void 1057 goto_line(slinenum) 1058 int slinenum; 1059 { 1060 char *sc_goto; 1061 1062 sc_goto = tgoto(sc_move, 0, slinenum); 1063 tputs(sc_goto, 1, putchr); 1064 } 1065 1066 /* 1067 * Ring the terminal bell. 1068 */ 1069 public void 1070 bell() 1071 { 1072 if (quiet == VERY_QUIET) 1073 vbell(); 1074 else 1075 putchr('\7'); 1076 } 1077 1078 /* 1079 * Output the "visual bell", if there is one. 1080 */ 1081 public void 1082 vbell() 1083 { 1084 if (*sc_visual_bell == '\0') 1085 return; 1086 tputs(sc_visual_bell, sc_height, putchr); 1087 } 1088 1089 /* 1090 * Clear the screen. 1091 */ 1092 public void 1093 clear() 1094 { 1095 tputs(sc_clear, sc_height, putchr); 1096 } 1097 1098 /* 1099 * Clear from the cursor to the end of the cursor's line. 1100 * {{ This must not move the cursor. }} 1101 */ 1102 public void 1103 clear_eol() 1104 { 1105 tputs(sc_eol_clear, 1, putchr); 1106 } 1107 1108 /* 1109 * Clear the bottom line of the display. 1110 * Leave the cursor at the beginning of the bottom line. 1111 */ 1112 public void 1113 clear_bot() 1114 { 1115 lower_left(); 1116 if (below_mem) 1117 tputs(sc_eos_clear, 1, putchr); 1118 else 1119 tputs(sc_eol_clear, 1, putchr); 1120 } 1121 1122 /* 1123 * Begin "standout" (bold, underline, or whatever). 1124 */ 1125 public void 1126 so_enter() 1127 { 1128 tputs(sc_s_in, 1, putchr); 1129 } 1130 1131 /* 1132 * End "standout". 1133 */ 1134 public void 1135 so_exit() 1136 { 1137 tputs(sc_s_out, 1, putchr); 1138 } 1139 1140 /* 1141 * Begin "underline" (hopefully real underlining, 1142 * otherwise whatever the terminal provides). 1143 */ 1144 public void 1145 ul_enter() 1146 { 1147 tputs(sc_u_in, 1, putchr); 1148 } 1149 1150 /* 1151 * End "underline". 1152 */ 1153 public void 1154 ul_exit() 1155 { 1156 tputs(sc_u_out, 1, putchr); 1157 } 1158 1159 /* 1160 * Begin "bold" 1161 */ 1162 public void 1163 bo_enter() 1164 { 1165 tputs(sc_b_in, 1, putchr); 1166 } 1167 1168 /* 1169 * End "bold". 1170 */ 1171 public void 1172 bo_exit() 1173 { 1174 tputs(sc_b_out, 1, putchr); 1175 } 1176 1177 /* 1178 * Begin "blink" 1179 */ 1180 public void 1181 bl_enter() 1182 { 1183 tputs(sc_bl_in, 1, putchr); 1184 } 1185 1186 /* 1187 * End "blink". 1188 */ 1189 public void 1190 bl_exit() 1191 { 1192 tputs(sc_bl_out, 1, putchr); 1193 } 1194 1195 /* 1196 * Erase the character to the left of the cursor 1197 * and move the cursor left. 1198 */ 1199 public void 1200 backspace() 1201 { 1202 /* 1203 * Try to erase the previous character by overstriking with a space. 1204 */ 1205 tputs(sc_backspace, 1, putchr); 1206 putchr(' '); 1207 tputs(sc_backspace, 1, putchr); 1208 } 1209 1210 /* 1211 * Output a plain backspace, without erasing the previous char. 1212 */ 1213 public void 1214 putbs() 1215 { 1216 tputs(sc_backspace, 1, putchr); 1217 } 1218