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