home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / gnu / less-177 / screen.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-04-03  |  12.8 KB  |  705 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. #if !TERMIO && defined(TIOCGWINSZ)
  21. #include <sys/ioctl.h>
  22. #else
  23. /*
  24.  * For the Unix PC (ATT 7300 & 3B1):
  25.  * Since WIOCGETD is defined in sys/window.h, we can't use that to decide
  26.  * whether to include sys/window.h.  Use SIGPHONE from signal.h instead.
  27.  */
  28. #include <signal.h>
  29. #ifdef SIGPHONE
  30. #include <sys/window.h>
  31. #endif
  32. #endif
  33.  
  34. #if NEED_PTEM_H && defined(TIOCGWINSZ)
  35. /*
  36.  * All this just to get struct winsize.  Sigh.
  37.  */
  38. #include <sys/types.h>
  39. #include <sys/stream.h>
  40. #include <sys/ptem.h>
  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_bl_in,        /* Enter blink mode */
  61.     *sc_bl_out,        /* Exit blink mode */
  62.     *sc_visual_bell,    /* Visual bell (flash screen) sequence */
  63.     *sc_backspace,        /* Backspace cursor */
  64.     *sc_init,        /* Startup terminal initialization */
  65.     *sc_deinit;        /* Exit terminal de-initialization */
  66.  
  67. static int init_done = 0;
  68.  
  69. public int auto_wrap;        /* Terminal does \r\n when write past margin */
  70. public int ignaw;        /* Terminal ignores \n immediately after wrap */
  71. public int erase_char, kill_char; /* The user's erase and line-kill chars */
  72. public int sc_width, sc_height;    /* Height & width of screen */
  73. public int bo_s_width, bo_e_width;    /* Printing width of boldface seq */
  74. public int ul_s_width, ul_e_width;    /* Printing width of underline seq */
  75. public int so_s_width, so_e_width;    /* Printing width of standout seq */
  76. public int bl_s_width, bl_e_width;    /* Printing width of blink seq */
  77.  
  78. static char *cheaper();
  79.  
  80. /*
  81.  * These two variables are sometimes defined in,
  82.  * and needed by, the termcap library.
  83.  * It may be necessary on some systems to declare them extern here.
  84.  */
  85. /*extern*/ short ospeed;    /* Terminal output baud rate */
  86. /*extern*/ char PC;        /* Pad character */
  87.  
  88. extern int quiet;        /* If VERY_QUIET, use visual bell for bell */
  89. extern int know_dumb;        /* Don't complain about a dumb terminal */
  90. extern int back_scroll;
  91. extern int swindow;
  92. extern char *tgetstr();
  93. extern char *tgoto();
  94. extern char *getenv();
  95.  
  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.     public void
  109. raw_mode(on)
  110.     int on;
  111. {
  112.     static int curr_on = 0;
  113.  
  114.     if (on == curr_on)
  115.         return;
  116. #if TERMIO
  117.     {
  118.     struct termio s;
  119.     static struct termio save_term;
  120.  
  121.     if (on)
  122.     {
  123.         /*
  124.          * Get terminal modes.
  125.          */
  126.         ioctl(2, TCGETA, &s);
  127.  
  128.         /*
  129.          * Save modes and set certain variables dependent on modes.
  130.          */
  131.         save_term = s;
  132.         ospeed = s.c_cflag & CBAUD;
  133.         erase_char = s.c_cc[VERASE];
  134.         kill_char = s.c_cc[VKILL];
  135.  
  136.         /*
  137.          * Set the modes to the way we want them.
  138.          */
  139.         s.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL);
  140.         s.c_oflag |=  (OPOST|ONLCR|TAB3);
  141.         s.c_oflag &= ~(OCRNL|ONOCR|ONLRET);
  142.         s.c_cc[VMIN] = 1;
  143.         s.c_cc[VTIME] = 0;
  144.     } else
  145.     {
  146.         /*
  147.          * Restore saved modes.
  148.          */
  149.         s = save_term;
  150.     }
  151.     ioctl(2, TCSETAW, &s);
  152.     }
  153. #else
  154.     {
  155.     struct sgttyb s;
  156.     static struct sgttyb save_term;
  157.  
  158.     if (on)
  159.     {
  160.         /*
  161.          * Get terminal modes.
  162.          */
  163.         ioctl(2, TIOCGETP, &s);
  164.  
  165.         /*
  166.          * Save modes and set certain variables dependent on modes.
  167.          */
  168.         save_term = s;
  169.         ospeed = s.sg_ospeed;
  170.         erase_char = s.sg_erase;
  171.         kill_char = s.sg_kill;
  172.  
  173.         /*
  174.          * Set the modes to the way we want them.
  175.          */
  176.         s.sg_flags |= CBREAK;
  177.         s.sg_flags &= ~(ECHO|XTABS);
  178.     } else
  179.     {
  180.         /*
  181.          * Restore saved modes.
  182.          */
  183.         s = save_term;
  184.     }
  185.     ioctl(2, TIOCSETN, &s);
  186.     }
  187. #endif
  188.     curr_on = on;
  189. }
  190.  
  191.     static void
  192. cannot(s)
  193.     char *s;
  194. {
  195.     PARG parg;
  196.  
  197.     if (know_dumb)
  198.         /* 
  199.          * User knows this is a dumb terminal, so don't tell him.
  200.          */
  201.         return;
  202.  
  203.     parg.p_string = s;
  204.     error("WARNING: terminal cannot %s", &parg);
  205. }
  206.  
  207. /*
  208.  * Get size of the output screen.
  209.  */
  210.     public void
  211. scrsize(p_height, p_width)
  212.     int *p_height;
  213.     int *p_width;
  214. {
  215.     register char *s;
  216. #ifdef TIOCGWINSZ
  217.     struct winsize w;
  218. #else
  219. #ifdef WIOCGETD
  220.     struct uwdata w;
  221. #endif
  222. #endif
  223.  
  224. #ifdef TIOCGWINSZ
  225.     if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_row > 0)
  226.         *p_height = w.ws_row;
  227.     else
  228. #else
  229. #ifdef WIOCGETD
  230.     if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_height > 0)
  231.         *p_height = w.uw_height/w.uw_vs;
  232.     else
  233. #endif
  234. #endif
  235.     if ((s = getenv("LINES")) != NULL)
  236.         *p_height = atoi(s);
  237.     else
  238.          *p_height = tgetnum("li");
  239.  
  240.     if (*p_height <= 0)
  241.         *p_height = 24;
  242.  
  243. #ifdef TIOCGWINSZ
  244.      if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_col > 0)
  245.         *p_width = w.ws_col;
  246.     else
  247. #ifdef WIOCGETD
  248.     if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_width > 0)
  249.         *p_width = w.uw_width/w.uw_hs;
  250.     else
  251. #endif
  252. #endif
  253.     if ((s = getenv("COLUMNS")) != NULL)
  254.         *p_width = atoi(s);
  255.     else
  256.          *p_width = tgetnum("co");
  257.  
  258.      if (*p_width <= 0)
  259.           *p_width = 80;
  260. }
  261.  
  262. /*
  263.  * Get terminal capabilities via termcap.
  264.  */
  265.     public void
  266. get_term()
  267. {
  268.     char *sp;
  269.     register char *t1, *t2;
  270.     register int hard;
  271.     char *term;
  272.     char termbuf[2048];
  273.  
  274.     static char sbuf[1024];
  275.  
  276.     /*
  277.      * Find out what kind of terminal this is.
  278.      */
  279.      if ((term = getenv("TERM")) == NULL)
  280.          term = "unknown";
  281.      if (tgetent(termbuf, term) <= 0)
  282.          strcpy(termbuf, "dumb:hc:");
  283.  
  284.      hard = tgetflag("hc");
  285.  
  286.     /*
  287.      * Get size of the screen.
  288.      */
  289.     scrsize(&sc_height, &sc_width);
  290.     pos_init();
  291.     if (swindow < 0)
  292.         swindow = sc_height - 1;
  293.  
  294.     auto_wrap = tgetflag("am");
  295.     ignaw = tgetflag("xn");
  296.  
  297.     /*
  298.      * Assumes termcap variable "sg" is the printing width of:
  299.      * the standout sequence, the end standout sequence,
  300.      * the underline sequence, the end underline sequence,
  301.      * the boldface sequence, and the end boldface sequence.
  302.      */
  303.     if ((so_s_width = tgetnum("sg")) < 0)
  304.         so_s_width = 0;
  305.     so_e_width = so_s_width;
  306.  
  307.     bo_s_width = bo_e_width = so_s_width;
  308.     ul_s_width = ul_e_width = so_s_width;
  309.     bl_s_width = bl_e_width = so_s_width;
  310.  
  311.     /*
  312.      * Get various string-valued capabilities.
  313.      */
  314.     sp = sbuf;
  315.  
  316.     sc_pad = tgetstr("pc", &sp);
  317.     if (sc_pad != NULL)
  318.         PC = *sc_pad;
  319.  
  320.     sc_init = tgetstr("ti", &sp);
  321.     if (sc_init == NULL)
  322.         sc_init = "";
  323.  
  324.     sc_deinit= tgetstr("te", &sp);
  325.     if (sc_deinit == NULL)
  326.         sc_deinit = "";
  327.  
  328.     sc_eol_clear = tgetstr("ce", &sp);
  329.     if (hard || sc_eol_clear == NULL || *sc_eol_clear == '\0')
  330.     {
  331.         cannot("clear to end of line");
  332.         sc_eol_clear = "";
  333.     }
  334.  
  335.     sc_clear = tgetstr("cl", &sp);
  336.     if (hard || sc_clear == NULL || *sc_clear == '\0')
  337.     {
  338.         cannot("clear screen");
  339.         sc_clear = "\n\n";
  340.     }
  341.  
  342.     sc_move = tgetstr("cm", &sp);
  343.     if (hard || sc_move == NULL || *sc_move == '\0')
  344.     {
  345.         /*
  346.          * This is not an error here, because we don't 
  347.          * always need sc_move.
  348.          * We need it only if we don't have home or lower-left.
  349.          */
  350.         sc_move = "";
  351.     }
  352.  
  353.     sc_s_in = tgetstr("so", &sp);
  354.     if (hard || sc_s_in == NULL)
  355.         sc_s_in = "";
  356.  
  357.     sc_s_out = tgetstr("se", &sp);
  358.     if (hard || sc_s_out == NULL)
  359.         sc_s_out = "";
  360.  
  361.     sc_u_in = tgetstr("us", &sp);
  362.     if (hard || sc_u_in == NULL)
  363.         sc_u_in = sc_s_in;
  364.  
  365.     sc_u_out = tgetstr("ue", &sp);
  366.     if (hard || sc_u_out == NULL)
  367.         sc_u_out = sc_s_out;
  368.  
  369.     sc_b_in = tgetstr("md", &sp);
  370.     if (hard || sc_b_in == NULL)
  371.     {
  372.         sc_b_in = sc_s_in;
  373.         sc_b_out = sc_s_out;
  374.     } else
  375.     {
  376.         sc_b_out = tgetstr("me", &sp);
  377.         if (hard || sc_b_out == NULL)
  378.             sc_b_out = "";
  379.     }
  380.  
  381.     sc_bl_in = tgetstr("mb", &sp);
  382.     if (hard || sc_bl_in == NULL)
  383.     {
  384.         sc_bl_in = sc_s_in;
  385.         sc_bl_out = sc_s_out;
  386.     } else
  387.     {
  388.         sc_bl_out = sc_b_out;
  389.     }
  390.  
  391.     sc_visual_bell = tgetstr("vb", &sp);
  392.     if (hard || sc_visual_bell == NULL)
  393.         sc_visual_bell = "";
  394.  
  395.     if (tgetflag("bs"))
  396.         sc_backspace = "\b";
  397.     else
  398.     {
  399.         sc_backspace = tgetstr("bc", &sp);
  400.         if (sc_backspace == NULL || *sc_backspace == '\0')
  401.             sc_backspace = "\b";
  402.     }
  403.  
  404.     /*
  405.      * Choose between using "ho" and "cm" ("home" and "cursor move")
  406.      * to move the cursor to the upper left corner of the screen.
  407.      */
  408.     t1 = tgetstr("ho", &sp);
  409.     if (hard || t1 == NULL)
  410.         t1 = "";
  411.     if (*sc_move == '\0')
  412.         t2 = "";
  413.     else
  414.     {
  415.         strcpy(sp, tgoto(sc_move, 0, 0));
  416.         t2 = sp;
  417.         sp += strlen(sp) + 1;
  418.     }
  419.     sc_home = cheaper(t1, t2, "home cursor", "|\b^");
  420.  
  421.     /*
  422.      * Choose between using "ll" and "cm"  ("lower left" and "cursor move")
  423.      * to move the cursor to the lower left corner of the screen.
  424.      */
  425.     t1 = tgetstr("ll", &sp);
  426.     if (hard || t1 == NULL)
  427.         t1 = "";
  428.     if (*sc_move == '\0')
  429.         t2 = "";
  430.     else
  431.     {
  432.         strcpy(sp, tgoto(sc_move, 0, sc_height-1));
  433.         t2 = sp;
  434.         sp += strlen(sp) + 1;
  435.     }
  436.     sc_lower_left = cheaper(t1, t2,
  437.         "move cursor to lower left of screen", "\r");
  438.  
  439.     /*
  440.      * Choose between using "al" or "sr" ("add line" or "scroll reverse")
  441.      * to add a line at the top of the screen.
  442.      */
  443.     t1 = tgetstr("al", &sp);
  444.     if (hard || t1 == NULL)
  445.         t1 = "";
  446.     t2 = tgetstr("sr", &sp);
  447.     if (hard || t2 == NULL)
  448.         t2 = "";
  449.     sc_addline = cheaper(t1, t2, "scroll backwards", "");
  450.     if (*sc_addline == '\0')
  451.     {
  452.         /*
  453.          * Force repaint on any backward movement.
  454.          */
  455.         back_scroll = 0;
  456.     }
  457. }
  458.  
  459. /*
  460.  * Return the cost of displaying a termcap string.
  461.  * We use the trick of calling tputs, but as a char printing function
  462.  * we give it inc_costcount, which just increments "costcount".
  463.  * This tells us how many chars would be printed by using this string.
  464.  * {{ Couldn't we just use strlen? }}
  465.  */
  466. static int costcount;
  467.  
  468. /*ARGSUSED*/
  469.     static void
  470. inc_costcount(c)
  471.     int c;
  472. {
  473.     costcount++;
  474. }
  475.  
  476.     static int
  477. cost(t)
  478.     char *t;
  479. {
  480.     costcount = 0;
  481.     tputs(t, sc_height, inc_costcount);
  482.     return (costcount);
  483. }
  484.  
  485. /*
  486.  * Return the "best" of the two given termcap strings.
  487.  * The best, if both exist, is the one with the lower 
  488.  * cost (see cost() function).
  489.  */
  490.     static char *
  491. cheaper(t1, t2, doit, def)
  492.     char *t1, *t2;
  493.     char *doit;
  494.     char *def;
  495. {
  496.     if (*t1 == '\0' && *t2 == '\0')
  497.     {
  498.         cannot(doit);
  499.         return (def);
  500.     }
  501.     if (*t1 == '\0')
  502.         return (t2);
  503.     if (*t2 == '\0')
  504.         return (t1);
  505.     if (cost(t1) < cost(t2))
  506.         return (t1);
  507.     return (t2);
  508. }
  509.  
  510.  
  511. /*
  512.  * Below are the functions which perform all the 
  513.  * terminal-specific screen manipulation.
  514.  */
  515.  
  516.  
  517. /*
  518.  * Initialize terminal
  519.  */
  520.     public void
  521. init()
  522. {
  523.     tputs(sc_init, sc_height, putchr);
  524.     init_done = 1;
  525. }
  526.  
  527. /*
  528.  * Deinitialize terminal
  529.  */
  530.     public void
  531. deinit()
  532. {
  533.     if (!init_done)
  534.         return;
  535.     tputs(sc_deinit, sc_height, putchr);
  536.     init_done = 0;
  537. }
  538.  
  539. /*
  540.  * Home cursor (move to upper left corner of screen).
  541.  */
  542.     public void
  543. home()
  544. {
  545.     tputs(sc_home, 1, putchr);
  546. }
  547.  
  548. /*
  549.  * Add a blank line (called with cursor at home).
  550.  * Should scroll the display down.
  551.  */
  552.     public void
  553. add_line()
  554. {
  555.     tputs(sc_addline, sc_height, putchr);
  556. }
  557.  
  558. /*
  559.  * Move cursor to lower left corner of screen.
  560.  */
  561.     public void
  562. lower_left()
  563. {
  564.     tputs(sc_lower_left, 1, putchr);
  565. }
  566.  
  567. /*
  568.  * Ring the terminal bell.
  569.  */
  570.     public void
  571. bell()
  572. {
  573.     if (quiet == VERY_QUIET)
  574.         vbell();
  575.     else
  576.         putchr('\7');
  577. }
  578.  
  579. /*
  580.  * Output the "visual bell", if there is one.
  581.  */
  582.     public void
  583. vbell()
  584. {
  585.     if (*sc_visual_bell == '\0')
  586.         return;
  587.     tputs(sc_visual_bell, sc_height, putchr);
  588. }
  589.  
  590. /*
  591.  * Clear the screen.
  592.  */
  593.     public void
  594. clear()
  595. {
  596.     tputs(sc_clear, sc_height, putchr);
  597. }
  598.  
  599. /*
  600.  * Clear from the cursor to the end of the cursor's line.
  601.  * {{ This must not move the cursor. }}
  602.  */
  603.     public void
  604. clear_eol()
  605. {
  606.     tputs(sc_eol_clear, 1, putchr);
  607. }
  608.  
  609. /*
  610.  * Begin "standout" (bold, underline, or whatever).
  611.  */
  612.     public void
  613. so_enter()
  614. {
  615.     tputs(sc_s_in, 1, putchr);
  616. }
  617.  
  618. /*
  619.  * End "standout".
  620.  */
  621.     public void
  622. so_exit()
  623. {
  624.     tputs(sc_s_out, 1, putchr);
  625. }
  626.  
  627. /*
  628.  * Begin "underline" (hopefully real underlining, 
  629.  * otherwise whatever the terminal provides).
  630.  */
  631.     public void
  632. ul_enter()
  633. {
  634.     tputs(sc_u_in, 1, putchr);
  635. }
  636.  
  637. /*
  638.  * End "underline".
  639.  */
  640.     public void
  641. ul_exit()
  642. {
  643.     tputs(sc_u_out, 1, putchr);
  644. }
  645.  
  646. /*
  647.  * Begin "bold"
  648.  */
  649.     public void
  650. bo_enter()
  651. {
  652.     tputs(sc_b_in, 1, putchr);
  653. }
  654.  
  655. /*
  656.  * End "bold".
  657.  */
  658.     public void
  659. bo_exit()
  660. {
  661.     tputs(sc_b_out, 1, putchr);
  662. }
  663.  
  664. /*
  665.  * Begin "blink"
  666.  */
  667.     public void
  668. bl_enter()
  669. {
  670.     tputs(sc_bl_in, 1, putchr);
  671. }
  672.  
  673. /*
  674.  * End "blink".
  675.  */
  676.     public void
  677. bl_exit()
  678. {
  679.     tputs(sc_bl_out, 1, putchr);
  680. }
  681.  
  682. /*
  683.  * Erase the character to the left of the cursor 
  684.  * and move the cursor left.
  685.  */
  686.     public void
  687. backspace()
  688. {
  689.     /* 
  690.      * Try to erase the previous character by overstriking with a space.
  691.      */
  692.     tputs(sc_backspace, 1, putchr);
  693.     putchr(' ');
  694.     tputs(sc_backspace, 1, putchr);
  695. }
  696.  
  697. /*
  698.  * Output a plain backspace, without erasing the previous char.
  699.  */
  700.     public void
  701. putbs()
  702. {
  703.     tputs(sc_backspace, 1, putchr);
  704. }
  705.