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