home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume6 / less2 / part2 / screen.c < prev    next >
Encoding:
C/C++ Source or Header  |  1986-11-30  |  10.6 KB  |  543 lines

  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 be 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 int couldnt = 0;
  153.  
  154.     static void
  155. cannot(s)
  156.     char *s;
  157. {
  158.     if (know_dumb)
  159.         /* 
  160.          * He knows he has a dumb terminal, so don't tell him. 
  161.          */
  162.         return;
  163.  
  164.     printf("WARNING: terminal cannot \"%s\"\n", s);
  165.     couldnt = 1;
  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.     if (couldnt)
  366.         /* Give him time to read all the "cannot" messages. */
  367.         error("");
  368. }
  369.  
  370.  
  371. /*
  372.  * Below are the functions which perform all the 
  373.  * terminal-specific screen manipulation.
  374.  */
  375.  
  376.  
  377. /*
  378.  * Initialize terminal
  379.  */
  380.     public void
  381. init()
  382. {
  383.     tputs(sc_init, sc_height, putc);
  384. }
  385.  
  386. /*
  387.  * Deinitialize terminal
  388.  */
  389.     public void
  390. deinit()
  391. {
  392.     tputs(sc_deinit, sc_height, putc);
  393. }
  394.  
  395. /*
  396.  * Home cursor (move to upper left corner of screen).
  397.  */
  398.     public void
  399. home()
  400. {
  401.     tputs(sc_home, 1, putc);
  402. }
  403.  
  404. /*
  405.  * Add a blank line (called with cursor at home).
  406.  * Should scroll the display down.
  407.  */
  408.     public void
  409. add_line()
  410. {
  411.     tputs(sc_addline, sc_height, putc);
  412. }
  413.  
  414. /*
  415.  * Move cursor to lower left corner of screen.
  416.  */
  417.     public void
  418. lower_left()
  419. {
  420.     tputs(sc_lower_left, 1, putc);
  421. }
  422.  
  423. /*
  424.  * Ring the terminal bell.
  425.  */
  426.     public void
  427. bell()
  428. {
  429.     if (quiet == VERY_QUIET)
  430.         vbell();
  431.     else
  432.         putc('\7');
  433. }
  434.  
  435. /*
  436.  * Output the "visual bell", if there is one.
  437.  */
  438.     public void
  439. vbell()
  440. {
  441.     if (*sc_visual_bell == '\0')
  442.         return;
  443.     tputs(sc_visual_bell, sc_height, putc);
  444. }
  445.  
  446. /*
  447.  * Clear the screen.
  448.  */
  449.     public void
  450. clear()
  451. {
  452.     tputs(sc_clear, sc_height, putc);
  453. }
  454.  
  455. /*
  456.  * Clear from the cursor to the end of the cursor's line.
  457.  * {{ This must not move the cursor. }}
  458.  */
  459.     public void
  460. clear_eol()
  461. {
  462.     tputs(sc_eol_clear, 1, putc);
  463. }
  464.  
  465. /*
  466.  * Begin "standout"
  467.  */
  468.     public void
  469. so_enter()
  470. {
  471.     tputs(sc_s_in, 1, putc);
  472. }
  473.  
  474. /*
  475.  * End "standout".
  476.  */
  477.     public void
  478. so_exit()
  479. {
  480.     tputs(sc_s_out, 1, putc);
  481. }
  482.  
  483. /*
  484.  * Begin "underline" (hopefully real underlining, 
  485.  * otherwise whatever the terminal provides).
  486.  */
  487.     public void
  488. ul_enter()
  489. {
  490.     tputs(sc_u_in, 1, putc);
  491. }
  492.  
  493. /*
  494.  * End "underline".
  495.  */
  496.     public void
  497. ul_exit()
  498. {
  499.     tputs(sc_u_out, 1, putc);
  500. }
  501.  
  502. /*
  503.  * Begin "bold"
  504.  */
  505.     public void
  506. bo_enter()
  507. {
  508.     tputs(sc_b_in, 1, putc);
  509. }
  510.  
  511. /*
  512.  * End "bold".
  513.  */
  514.     public void
  515. bo_exit()
  516. {
  517.     tputs(sc_b_out, 1, putc);
  518. }
  519.  
  520. /*
  521.  * Erase the character to the left of the cursor 
  522.  * and move the cursor left.
  523.  */
  524.     public void
  525. backspace()
  526. {
  527.     /* 
  528.      * Try to erase the previous character by overstriking with a space.
  529.      */
  530.     tputs(sc_backspace, 1, putc);
  531.     putc(' ');
  532.     tputs(sc_backspace, 1, putc);
  533. }
  534.  
  535. /*
  536.  * Output a plain backspace, without erasing the previous char.
  537.  */
  538.     public void
  539. putbs()
  540. {
  541.     tputs(sc_backspace, 1, putc);
  542. }
  543.