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