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