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