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