1 /* 2 * Copyright (c) 1988 Mark Nudleman 3 * Copyright (c) 1988 Regents of the University of California. 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms are permitted 7 * provided that the above copyright notice and this paragraph are 8 * duplicated in all such forms and that any documentation, 9 * advertising materials, and other materials related to such 10 * distribution and use acknowledge that the software was developed 11 * by Mark Nudleman and the University of California, Berkeley. The 12 * name of Mark Nudleman or the 13 * University may not be used to endorse or promote products derived 14 * from this software without specific prior written permission. 15 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 17 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 18 */ 19 20 #ifndef lint 21 static char sccsid[] = "@(#)screen.c 5.3 (Berkeley) 07/25/88"; 22 #endif /* not lint */ 23 24 /* 25 * Routines which deal with the characteristics of the terminal. 26 * Uses termcap to be as terminal-independent as possible. 27 * 28 * {{ Someday this should be rewritten to use curses. }} 29 */ 30 31 #include "less.h" 32 #if XENIX 33 #include <sys/types.h> 34 #include <sys/ioctl.h> 35 #endif 36 37 #if TERMIO 38 #include <termio.h> 39 #else 40 #include <sgtty.h> 41 #endif 42 43 #ifdef TIOCGWINSZ 44 #include <sys/ioctl.h> 45 #else 46 /* 47 * For the Unix PC (ATT 7300 & 3B1): 48 * Since WIOCGETD is defined in sys/window.h, we can't use that to decide 49 * whether to include sys/window.h. Use SIGPHONE from sys/signal.h instead. 50 */ 51 #include <sys/signal.h> 52 #ifdef SIGPHONE 53 #include <sys/window.h> 54 #endif 55 #endif 56 57 /* 58 * Strings passed to tputs() to do various terminal functions. 59 */ 60 static char 61 *sc_pad, /* Pad string */ 62 *sc_home, /* Cursor home */ 63 *sc_addline, /* Add line, scroll down following lines */ 64 *sc_lower_left, /* Cursor to last line, first column */ 65 *sc_move, /* General cursor positioning */ 66 *sc_clear, /* Clear screen */ 67 *sc_eol_clear, /* Clear to end of line */ 68 *sc_s_in, /* Enter standout (highlighted) mode */ 69 *sc_s_out, /* Exit standout mode */ 70 *sc_u_in, /* Enter underline mode */ 71 *sc_u_out, /* Exit underline mode */ 72 *sc_b_in, /* Enter bold mode */ 73 *sc_b_out, /* Exit bold mode */ 74 *sc_visual_bell, /* Visual bell (flash screen) sequence */ 75 *sc_backspace, /* Backspace cursor */ 76 *sc_init, /* Startup terminal initialization */ 77 *sc_deinit; /* Exit terminal de-intialization */ 78 79 public int auto_wrap; /* Terminal does \r\n when write past margin */ 80 public int ignaw; /* Terminal ignores \n immediately after wrap */ 81 public int erase_char, kill_char; /* The user's erase and line-kill chars */ 82 public int sc_width, sc_height; /* Height & width of screen */ 83 public int sc_window = -1; /* window size for forward and backward */ 84 public int bo_width, be_width; /* Printing width of boldface sequences */ 85 public int ul_width, ue_width; /* Printing width of underline sequences */ 86 public int so_width, se_width; /* Printing width of standout sequences */ 87 88 /* 89 * These two variables are sometimes defined in, 90 * and needed by, the termcap library. 91 * It may be necessary on some systems to declare them extern here. 92 */ 93 /*extern*/ short ospeed; /* Terminal output baud rate */ 94 /*extern*/ char PC; /* Pad character */ 95 96 extern int quiet; /* If VERY_QUIET, use visual bell for bell */ 97 extern int know_dumb; /* Don't complain about a dumb terminal */ 98 extern int back_scroll; 99 char *tgetstr(); 100 char *tgoto(); 101 102 /* 103 * Change terminal to "raw mode", or restore to "normal" mode. 104 * "Raw mode" means 105 * 1. An outstanding read will complete on receipt of a single keystroke. 106 * 2. Input is not echoed. 107 * 3. On output, \n is mapped to \r\n. 108 * 4. \t is NOT expanded into spaces. 109 * 5. Signal-causing characters such as ctrl-C (interrupt), 110 * etc. are NOT disabled. 111 * It doesn't matter whether an input \n is mapped to \r, or vice versa. 112 */ 113 public void 114 raw_mode(on) 115 int on; 116 { 117 #if TERMIO 118 struct termio s; 119 static struct termio save_term; 120 121 if (on) 122 { 123 /* 124 * Get terminal modes. 125 */ 126 ioctl(2, TCGETA, &s); 127 128 /* 129 * Save modes and set certain variables dependent on modes. 130 */ 131 save_term = s; 132 ospeed = s.c_cflag & CBAUD; 133 erase_char = s.c_cc[VERASE]; 134 kill_char = s.c_cc[VKILL]; 135 136 /* 137 * Set the modes to the way we want them. 138 */ 139 s.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL); 140 s.c_oflag |= (OPOST|ONLCR|TAB3); 141 s.c_oflag &= ~(OCRNL|ONOCR|ONLRET); 142 s.c_cc[VMIN] = 1; 143 s.c_cc[VTIME] = 0; 144 } else 145 { 146 /* 147 * Restore saved modes. 148 */ 149 s = save_term; 150 } 151 ioctl(2, TCSETAW, &s); 152 #else 153 struct sgttyb s; 154 static struct sgttyb save_term; 155 156 if (on) 157 { 158 /* 159 * Get terminal modes. 160 */ 161 ioctl(2, TIOCGETP, &s); 162 163 /* 164 * Save modes and set certain variables dependent on modes. 165 */ 166 save_term = s; 167 ospeed = s.sg_ospeed; 168 erase_char = s.sg_erase; 169 kill_char = s.sg_kill; 170 171 /* 172 * Set the modes to the way we want them. 173 */ 174 s.sg_flags |= CBREAK; 175 s.sg_flags &= ~(ECHO|XTABS); 176 } else 177 { 178 /* 179 * Restore saved modes. 180 */ 181 s = save_term; 182 } 183 ioctl(2, TIOCSETN, &s); 184 #endif 185 } 186 187 static void 188 cannot(s) 189 char *s; 190 { 191 char message[100]; 192 193 if (know_dumb) 194 /* 195 * He knows he has a dumb terminal, so don't tell him. 196 */ 197 return; 198 199 (void)sprintf(message, "WARNING: terminal cannot \"%s\"", s); 200 error(message); 201 } 202 203 /* 204 * Get terminal capabilities via termcap. 205 */ 206 public void 207 get_term() 208 { 209 char termbuf[2048]; 210 char *sp; 211 char *term; 212 int hard; 213 #ifdef TIOCGWINSZ 214 struct winsize w; 215 #else 216 #ifdef WIOCGETD 217 struct uwdata w; 218 #endif 219 #endif 220 static char sbuf[1024]; 221 222 char *getenv(), *strcpy(); 223 224 /* 225 * Find out what kind of terminal this is. 226 */ 227 if ((term = getenv("TERM")) == NULL) 228 term = "unknown"; 229 if (tgetent(termbuf, term) <= 0) 230 (void)strcpy(termbuf, "dumb:co#80:hc:"); 231 232 /* 233 * Get size of the screen. 234 */ 235 #ifdef TIOCGWINSZ 236 if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_row > 0) 237 sc_height = w.ws_row; 238 else 239 #else 240 #ifdef WIOCGETD 241 if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_height > 0) 242 sc_height = w.uw_height/w.uw_vs; 243 else 244 #endif 245 #endif 246 sc_height = tgetnum("li"); 247 hard = (sc_height < 0 || tgetflag("hc")); 248 if (hard) 249 { 250 /* Oh no, this is a hardcopy terminal. */ 251 sc_height = 24; 252 } 253 254 #ifdef TIOCGWINSZ 255 if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_col > 0) 256 sc_width = w.ws_col; 257 else 258 #ifdef WIOCGETD 259 if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_width > 0) 260 sc_width = w.uw_width/w.uw_hs; 261 else 262 #endif 263 #endif 264 sc_width = tgetnum("co"); 265 if (sc_width < 0) 266 sc_width = 80; 267 268 auto_wrap = tgetflag("am"); 269 ignaw = tgetflag("xn"); 270 271 /* 272 * Assumes termcap variable "sg" is the printing width of 273 * the standout sequence, the end standout sequence, 274 * the underline sequence, the end underline sequence, 275 * the boldface sequence, and the end boldface sequence. 276 */ 277 if ((so_width = tgetnum("sg")) < 0) 278 so_width = 0; 279 be_width = bo_width = ue_width = ul_width = se_width = so_width; 280 281 /* 282 * Get various string-valued capabilities. 283 */ 284 sp = sbuf; 285 286 sc_pad = tgetstr("pc", &sp); 287 if (sc_pad != NULL) 288 PC = *sc_pad; 289 290 sc_init = tgetstr("ti", &sp); 291 if (sc_init == NULL) 292 sc_init = ""; 293 294 sc_deinit= tgetstr("te", &sp); 295 if (sc_deinit == NULL) 296 sc_deinit = ""; 297 298 sc_eol_clear = tgetstr("ce", &sp); 299 if (hard || sc_eol_clear == NULL || *sc_eol_clear == '\0') 300 { 301 cannot("clear to end of line"); 302 sc_eol_clear = ""; 303 } 304 305 sc_clear = tgetstr("cl", &sp); 306 if (hard || sc_clear == NULL || *sc_clear == '\0') 307 { 308 cannot("clear screen"); 309 sc_clear = "\n\n"; 310 } 311 312 sc_move = tgetstr("cm", &sp); 313 if (hard || sc_move == NULL || *sc_move == '\0') 314 { 315 /* 316 * This is not an error here, because we don't 317 * always need sc_move. 318 * We need it only if we don't have home or lower-left. 319 */ 320 sc_move = ""; 321 } 322 323 sc_s_in = tgetstr("so", &sp); 324 if (hard || sc_s_in == NULL) 325 sc_s_in = ""; 326 327 sc_s_out = tgetstr("se", &sp); 328 if (hard || sc_s_out == NULL) 329 sc_s_out = ""; 330 331 sc_u_in = tgetstr("us", &sp); 332 if (hard || sc_u_in == NULL) 333 sc_u_in = sc_s_in; 334 335 sc_u_out = tgetstr("ue", &sp); 336 if (hard || sc_u_out == NULL) 337 sc_u_out = sc_s_out; 338 339 sc_b_in = tgetstr("md", &sp); 340 if (hard || sc_b_in == NULL) 341 { 342 sc_b_in = sc_s_in; 343 sc_b_out = sc_s_out; 344 } else 345 { 346 sc_b_out = tgetstr("me", &sp); 347 if (hard || sc_b_out == NULL) 348 sc_b_out = ""; 349 } 350 351 sc_visual_bell = tgetstr("vb", &sp); 352 if (hard || sc_visual_bell == NULL) 353 sc_visual_bell = ""; 354 355 sc_home = tgetstr("ho", &sp); 356 if (hard || sc_home == NULL || *sc_home == '\0') 357 { 358 if (*sc_move == '\0') 359 { 360 cannot("home cursor"); 361 /* 362 * This last resort for sc_home is supposed to 363 * be an up-arrow suggesting moving to the 364 * top of the "virtual screen". (The one in 365 * your imagination as you try to use this on 366 * a hard copy terminal.) 367 */ 368 sc_home = "|\b^"; 369 } else 370 { 371 /* 372 * No "home" string, 373 * but we can use "move(0,0)". 374 */ 375 (void)strcpy(sp, tgoto(sc_move, 0, 0)); 376 sc_home = sp; 377 sp += strlen(sp) + 1; 378 } 379 } 380 381 sc_lower_left = tgetstr("ll", &sp); 382 if (hard || sc_lower_left == NULL || *sc_lower_left == '\0') 383 { 384 if (*sc_move == '\0') 385 { 386 cannot("move cursor to lower left of screen"); 387 sc_lower_left = "\r"; 388 } else 389 { 390 /* 391 * No "lower-left" string, 392 * but we can use "move(0,last-line)". 393 */ 394 (void)strcpy(sp, tgoto(sc_move, 0, sc_height-1)); 395 sc_lower_left = sp; 396 sp += strlen(sp) + 1; 397 } 398 } 399 400 /* 401 * To add a line at top of screen and scroll the display down, 402 * we use "al" (add line) or "sr" (scroll reverse). 403 */ 404 if ((sc_addline = tgetstr("al", &sp)) == NULL || 405 *sc_addline == '\0') 406 sc_addline = tgetstr("sr", &sp); 407 408 if (hard || sc_addline == NULL || *sc_addline == '\0') 409 { 410 cannot("scroll backwards"); 411 sc_addline = ""; 412 /* Force repaint on any backward movement */ 413 back_scroll = 0; 414 } 415 416 if (tgetflag("bs")) 417 sc_backspace = "\b"; 418 else 419 { 420 sc_backspace = tgetstr("bc", &sp); 421 if (sc_backspace == NULL || *sc_backspace == '\0') 422 sc_backspace = "\b"; 423 } 424 } 425 426 427 /* 428 * Below are the functions which perform all the 429 * terminal-specific screen manipulation. 430 */ 431 432 433 /* 434 * Initialize terminal 435 */ 436 public void 437 init() 438 { 439 tputs(sc_init, sc_height, putchr); 440 } 441 442 /* 443 * Deinitialize terminal 444 */ 445 public void 446 deinit() 447 { 448 tputs(sc_deinit, sc_height, putchr); 449 } 450 451 /* 452 * Home cursor (move to upper left corner of screen). 453 */ 454 public void 455 home() 456 { 457 tputs(sc_home, 1, putchr); 458 } 459 460 /* 461 * Add a blank line (called with cursor at home). 462 * Should scroll the display down. 463 */ 464 public void 465 add_line() 466 { 467 tputs(sc_addline, sc_height, putchr); 468 } 469 470 /* 471 * Move cursor to lower left corner of screen. 472 */ 473 public void 474 lower_left() 475 { 476 tputs(sc_lower_left, 1, putchr); 477 } 478 479 /* 480 * Ring the terminal bell. 481 */ 482 public void 483 bell() 484 { 485 if (quiet == VERY_QUIET) 486 vbell(); 487 else 488 putchr('\7'); 489 } 490 491 /* 492 * Output the "visual bell", if there is one. 493 */ 494 public void 495 vbell() 496 { 497 if (*sc_visual_bell == '\0') 498 return; 499 tputs(sc_visual_bell, sc_height, putchr); 500 } 501 502 /* 503 * Clear the screen. 504 */ 505 public void 506 clear() 507 { 508 tputs(sc_clear, sc_height, putchr); 509 } 510 511 /* 512 * Clear from the cursor to the end of the cursor's line. 513 * {{ This must not move the cursor. }} 514 */ 515 public void 516 clear_eol() 517 { 518 tputs(sc_eol_clear, 1, putchr); 519 } 520 521 /* 522 * Begin "standout" (bold, underline, or whatever). 523 */ 524 public void 525 so_enter() 526 { 527 tputs(sc_s_in, 1, putchr); 528 } 529 530 /* 531 * End "standout". 532 */ 533 public void 534 so_exit() 535 { 536 tputs(sc_s_out, 1, putchr); 537 } 538 539 /* 540 * Begin "underline" (hopefully real underlining, 541 * otherwise whatever the terminal provides). 542 */ 543 public void 544 ul_enter() 545 { 546 tputs(sc_u_in, 1, putchr); 547 } 548 549 /* 550 * End "underline". 551 */ 552 public void 553 ul_exit() 554 { 555 tputs(sc_u_out, 1, putchr); 556 } 557 558 /* 559 * Begin "bold" 560 */ 561 public void 562 bo_enter() 563 { 564 tputs(sc_b_in, 1, putchr); 565 } 566 567 /* 568 * End "bold". 569 */ 570 public void 571 bo_exit() 572 { 573 tputs(sc_b_out, 1, putchr); 574 } 575 576 /* 577 * Erase the character to the left of the cursor 578 * and move the cursor left. 579 */ 580 public void 581 backspace() 582 { 583 /* 584 * Try to erase the previous character by overstriking with a space. 585 */ 586 tputs(sc_backspace, 1, putchr); 587 putchr(' '); 588 tputs(sc_backspace, 1, putchr); 589 } 590 591 /* 592 * Output a plain backspace, without erasing the previous char. 593 */ 594 public void 595 putbs() 596 { 597 tputs(sc_backspace, 1, putchr); 598 } 599