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