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