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