home *** CD-ROM | disk | FTP | other *** search
/ The Pier Shareware 6 / The_Pier_Shareware_Number_6_(The_Pier_Exchange)_(1995).iso / 036 / less232.zip / SCREEN.C < prev    next >
C/C++ Source or Header  |  1994-09-24  |  21KB  |  1,035 lines

  1. /*
  2.  * Copyright (c) 1984,1985,1989,1994  Mark Nudelman
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer.
  10.  * 2. Redistributions in binary form must reproduce the above copyright
  11.  *    notice in the documentation and/or other materials provided with 
  12.  *    the distribution.
  13.  *
  14.  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
  15.  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  16.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
  17.  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
  18.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
  19.  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
  20.  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
  21.  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
  22.  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
  23.  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 
  24.  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25.  */
  26.  
  27.  
  28. /*
  29.  * Routines which deal with the characteristics of the terminal.
  30.  * Uses termcap to be as terminal-independent as possible.
  31.  *
  32.  * {{ Maybe someday this should be rewritten to use curses or terminfo. }}
  33.  */
  34.  
  35. #include "less.h"
  36. #include "cmd.h"
  37.  
  38. #if HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS
  39. #include <termios.h>
  40. #if HAVE_SYS_IOCTL_H && !defined(TIOCGWINSZ)
  41. #include <sys/ioctl.h>
  42. #endif
  43. #else
  44. #if HAVE_TERMIO_H
  45. #include <termio.h>
  46. #else
  47. #include <sgtty.h>
  48. #if HAVE_SYS_IOCTL_H && (defined(TIOCGWINSZ) || defined(TCGETA) || defined(TIOCGETP) || defined(WIOCGETD))
  49. #include <sys/ioctl.h>
  50. #endif
  51. #endif
  52. #endif
  53. #if HAVE_TERMCAP_H
  54. #include <termcap.h>
  55. #endif
  56.  
  57. #ifndef TIOCGWINSZ
  58. /*
  59.  * For the Unix PC (ATT 7300 & 3B1):
  60.  * Since WIOCGETD is defined in sys/window.h, we can't use that to decide
  61.  * whether to include sys/window.h.  Use SIGPHONE from sys/signal.h instead.
  62.  */
  63. #include <sys/signal.h>
  64. #ifdef SIGPHONE
  65. #include <sys/window.h>
  66. #endif
  67. #endif
  68.  
  69. #if HAVE_SYS_STREAM_H
  70. #include <sys/stream.h>
  71. #endif
  72. #if HAVE_SYS_PTEM_H
  73. #include <sys/ptem.h>
  74. #endif
  75.  
  76.  
  77. /*
  78.  * Strings passed to tputs() to do various terminal functions.
  79.  */
  80. static char
  81.     *sc_pad,        /* Pad string */
  82.     *sc_home,        /* Cursor home */
  83.     *sc_addline,        /* Add line, scroll down following lines */
  84.     *sc_lower_left,        /* Cursor to last line, first column */
  85.     *sc_move,        /* General cursor positioning */
  86.     *sc_clear,        /* Clear screen */
  87.     *sc_eol_clear,        /* Clear to end of line */
  88.     *sc_eos_clear,        /* Clear to end of screen */
  89.     *sc_s_in,        /* Enter standout (highlighted) mode */
  90.     *sc_s_out,        /* Exit standout mode */
  91.     *sc_u_in,        /* Enter underline mode */
  92.     *sc_u_out,        /* Exit underline mode */
  93.     *sc_b_in,        /* Enter bold mode */
  94.     *sc_b_out,        /* Exit bold mode */
  95.     *sc_bl_in,        /* Enter blink mode */
  96.     *sc_bl_out,        /* Exit blink mode */
  97.     *sc_visual_bell,    /* Visual bell (flash screen) sequence */
  98.     *sc_backspace,        /* Backspace cursor */
  99.     *sc_s_keypad,        /* Start keypad mode */
  100.     *sc_e_keypad,        /* End keypad mode */
  101.     *sc_init,        /* Startup terminal initialization */
  102.     *sc_deinit;        /* Exit terminal de-initialization */
  103.  
  104. static int init_done = 0;
  105.  
  106. public int auto_wrap;        /* Terminal does \r\n when write past margin */
  107. public int ignaw;        /* Terminal ignores \n immediately after wrap */
  108. public int erase_char, kill_char; /* The user's erase and line-kill chars */
  109. public int sc_width, sc_height;    /* Height & width of screen */
  110. public int bo_s_width, bo_e_width;    /* Printing width of boldface seq */
  111. public int ul_s_width, ul_e_width;    /* Printing width of underline seq */
  112. public int so_s_width, so_e_width;    /* Printing width of standout seq */
  113. public int bl_s_width, bl_e_width;    /* Printing width of blink seq */
  114. public int above_mem, below_mem;    /* Memory retained above/below screen */
  115.  
  116. static char *cheaper();
  117.  
  118. /*
  119.  * These two variables are sometimes defined in,
  120.  * and needed by, the termcap library.
  121.  */
  122. #if MUST_DEFINE_OSPEED
  123. extern short ospeed;    /* Terminal output baud rate */
  124. extern char PC;        /* Pad character */
  125. #endif
  126.  
  127. extern int quiet;        /* If VERY_QUIET, use visual bell for bell */
  128. extern int know_dumb;        /* Don't complain about a dumb terminal */
  129. extern int back_scroll;
  130. extern int swindow;
  131. extern int no_init;
  132. extern char *tgetstr();
  133. extern char *tgoto();
  134.  
  135.  
  136. /*
  137.  * Change terminal to "raw mode", or restore to "normal" mode.
  138.  * "Raw mode" means 
  139.  *    1. An outstanding read will complete on receipt of a single keystroke.
  140.  *    2. Input is not echoed.  
  141.  *    3. On output, \n is mapped to \r\n.
  142.  *    4. \t is NOT expanded into spaces.
  143.  *    5. Signal-causing characters such as ctrl-C (interrupt),
  144.  *       etc. are NOT disabled.
  145.  * It doesn't matter whether an input \n is mapped to \r, or vice versa.
  146.  */
  147.     public void
  148. raw_mode(on)
  149.     int on;
  150. {
  151.     static int curr_on = 0;
  152.  
  153.     if (on == curr_on)
  154.         return;
  155. #if OS2
  156.         signal(SIGINT, SIG_IGN);
  157.     erase_char = '\b';
  158.     kill_char = '\033';
  159. #else
  160. #if HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS
  161.     {
  162.     struct termios s;
  163.     static struct termios save_term;
  164.  
  165.     if (on) 
  166.     {
  167.         /*
  168.          * Get terminal modes.
  169.          */
  170.         tcgetattr(2, &s);
  171.  
  172.         /*
  173.          * Save modes and set certain variables dependent on modes.
  174.          */
  175.         save_term = s;
  176. #if HAVE_OSPEED
  177.         ospeed = cfgetospeed(&s);
  178. #endif
  179.         erase_char = s.c_cc[VERASE];
  180.         kill_char = s.c_cc[VKILL];
  181.  
  182.         /*
  183.          * Set the modes to the way we want them.
  184.          */
  185.         s.c_lflag &= ~(0
  186. #ifdef ICANON
  187.             | ICANON
  188. #endif
  189. #ifdef ECHO
  190.             | ECHO
  191. #endif
  192. #ifdef ECHOE
  193.             | ECHOE
  194. #endif
  195. #ifdef ECHOK
  196.             | ECHOK
  197. #endif
  198. #if ECHONL
  199.             | ECHONL
  200. #endif
  201.         );
  202.  
  203.         s.c_oflag |= (0
  204. #ifdef XTABS
  205.             | XTABS
  206. #else
  207. #ifdef TAB3
  208.             | TAB3
  209. #else
  210. #ifdef OXTABS
  211.             | OXTABS
  212. #endif
  213. #endif
  214. #endif
  215. #ifdef OPOST
  216.             | OPOST
  217. #endif
  218. #ifdef ONLCR
  219.             | ONLCR
  220. #endif
  221.         );
  222.  
  223.         s.c_oflag &= ~(0
  224. #ifdef ONOEOT
  225.             | ONOEOT
  226. #endif
  227. #ifdef OCRNL
  228.             | OCRNL
  229. #endif
  230. #ifdef ONOCR
  231.             | ONOCR
  232. #endif
  233. #ifdef ONLRET
  234.             | ONLRET
  235. #endif
  236.         );
  237.         s.c_cc[VMIN] = 1;
  238.         s.c_cc[VTIME] = 0;
  239.     } else
  240.     {
  241.         /*
  242.          * Restore saved modes.
  243.          */
  244.         s = save_term;
  245.     }
  246.     tcsetattr(2, TCSADRAIN, &s);
  247.     }
  248. #else
  249. #ifdef TCGETA
  250.     {
  251.     struct termio s;
  252.     static struct termio save_term;
  253.  
  254.     if (on)
  255.     {
  256.         /*
  257.          * Get terminal modes.
  258.          */
  259.         ioctl(2, TCGETA, &s);
  260.  
  261.         /*
  262.          * Save modes and set certain variables dependent on modes.
  263.          */
  264.         save_term = s;
  265. #if HAVE_OSPEED
  266.         ospeed = s.c_cflag & CBAUD;
  267. #endif
  268.         erase_char = s.c_cc[VERASE];
  269.         kill_char = s.c_cc[VKILL];
  270.  
  271.         /*
  272.          * Set the modes to the way we want them.
  273.          */
  274.         s.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL);
  275.         s.c_oflag |=  (OPOST|ONLCR|TAB3);
  276.         s.c_oflag &= ~(OCRNL|ONOCR|ONLRET);
  277.         s.c_cc[VMIN] = 1;
  278.         s.c_cc[VTIME] = 0;
  279.     } else
  280.     {
  281.         /*
  282.          * Restore saved modes.
  283.          */
  284.         s = save_term;
  285.     }
  286.     ioctl(2, TCSETAW, &s);
  287.     }
  288. #else
  289.     {
  290.     struct sgttyb s;
  291.     static struct sgttyb save_term;
  292.  
  293.     if (on)
  294.     {
  295.         /*
  296.          * Get terminal modes.
  297.          */
  298.         ioctl(2, TIOCGETP, &s);
  299.  
  300.         /*
  301.          * Save modes and set certain variables dependent on modes.
  302.          */
  303.         save_term = s;
  304. #if HAVE_OSPEED
  305.         ospeed = s.sg_ospeed;
  306. #endif
  307.         erase_char = s.sg_erase;
  308.         kill_char = s.sg_kill;
  309.  
  310.         /*
  311.          * Set the modes to the way we want them.
  312.          */
  313.         s.sg_flags |= CBREAK;
  314.         s.sg_flags &= ~(ECHO|XTABS);
  315.     } else
  316.     {
  317.         /*
  318.          * Restore saved modes.
  319.          */
  320.         s = save_term;
  321.     }
  322.     ioctl(2, TIOCSETN, &s);
  323.     }
  324. #endif
  325. #endif
  326. #endif
  327.     curr_on = on;
  328. }
  329.  
  330.     static void
  331. cannot(s)
  332.     char *s;
  333. {
  334.     PARG parg;
  335.  
  336.     if (know_dumb)
  337.         /* 
  338.          * User knows this is a dumb terminal, so don't tell him.
  339.          */
  340.         return;
  341.  
  342.     parg.p_string = s;
  343.     error("WARNING: terminal cannot %s", &parg);
  344. }
  345.  
  346. /*
  347.  * Get size of the output screen.
  348.  */
  349.     public void
  350. scrsize()
  351. {
  352. #if OS2
  353.         {
  354.           int s[2];
  355.       _scrsize(s);
  356.           sc_width = s[0];
  357.           sc_height = s[1];
  358.         }
  359. #else
  360.     register char *s;
  361. #ifdef TIOCGWINSZ
  362.     struct winsize w;
  363. #else
  364. #ifdef WIOCGETD
  365.     struct uwdata w;
  366. #endif
  367. #endif
  368.  
  369. #ifdef TIOCGWINSZ
  370.     if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_row > 0)
  371.         sc_height = w.ws_row;
  372.     else
  373. #else
  374. #ifdef WIOCGETD
  375.     if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_height > 0)
  376.         sc_height = w.uw_height/w.uw_vs;
  377.     else
  378. #endif
  379. #endif
  380.     if ((s = getenv("LINES")) != NULL)
  381.         sc_height = atoi(s);
  382.     else
  383.          sc_height = tgetnum("li");
  384.  
  385.     if (sc_height <= 0)
  386.         sc_height = 24;
  387.  
  388. #ifdef TIOCGWINSZ
  389.      if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_col > 0)
  390.         sc_width = w.ws_col;
  391.     else
  392. #ifdef WIOCGETD
  393.     if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_width > 0)
  394.         sc_width = w.uw_width/w.uw_hs;
  395.     else
  396. #endif
  397. #endif
  398.     if ((s = getenv("COLUMNS")) != NULL)
  399.         sc_width = atoi(s);
  400.     else
  401.          sc_width = tgetnum("co");
  402. #endif
  403.  
  404.      if (sc_width <= 0)
  405.           sc_width = 80;
  406. }
  407.  
  408. /*
  409.  * Static table of command editting characters.
  410.  * These don't depend on the termcap.
  411.  */
  412. char st_edittable[] =
  413. {
  414.     '\t',0,                EC_F_COMPLETE,    /* TAB */
  415.     '\17',0,        EC_B_COMPLETE,    /* BACKTAB */
  416.     '\14',0,        EC_EXPAND,    /* CTRL-L */
  417.        '\33','l',0,        EC_RIGHT,    /* ESC l */
  418.     '\33','h',0,        EC_LEFT,    /* ESC h */
  419.     '\33','b',0,        EC_W_LEFT,    /* ESC b */
  420.     '\33','w',0,        EC_W_RIGHT,    /* ESC w */
  421.     '\33','i',0,        EC_INSERT,    /* ESC i */
  422.     '\33','x',0,        EC_DELETE,    /* ESC x */
  423.     '\33','X',0,        EC_W_DELETE,    /* ESC X */
  424.     '\33','\b',0,        EC_W_BACKSPACE,    /* ESC BACKSPACE */
  425.     '\33','0',0,        EC_HOME,    /* ESC 0 */
  426.     '\33','$',0,        EC_END,        /* ESC $ */
  427.     '\33','k',0,        EC_UP,        /* ESC k */
  428.     '\33','j',0,        EC_DOWN,    /* ESC j */
  429.     '\33','\t',0,        EC_B_COMPLETE,    /* ESC TAB */
  430.     0
  431. };
  432.  
  433. char edittable[1024];
  434. int sz_edittable = 0;
  435.  
  436. char kcmdtable[400];
  437. int sz_kcmdtable = 0;
  438.  
  439.     public void
  440. get_editkeys()
  441. {
  442.     char *sp;
  443.     char *s;
  444.     char tbuf[40];
  445.  
  446. #define    add_table(str,action,tbl,sz) { \
  447.     strcpy(tbl+sz, str);    \
  448.     sz += strlen(str) + 1;    \
  449.     tbl[sz++] = action; }
  450.     
  451.     /* RIGHT ARROW */
  452.     sp = tbuf+1;
  453.     if ((s = tgetstr("kr", &sp)) != NULL)
  454.     {
  455.         add_table(s, EC_RIGHT, edittable, sz_edittable);
  456.         *--s = ESC;
  457.         add_table(s, EC_W_RIGHT, edittable, sz_edittable);
  458.     }
  459.     
  460.     /* LEFT ARROW */
  461.     sp = tbuf+1;
  462.     if ((s = tgetstr("kl", &sp)) != NULL)
  463.     {
  464.         add_table(s, EC_LEFT, edittable, sz_edittable);
  465.         *--s = ESC;
  466.         add_table(s, EC_W_LEFT, edittable, sz_edittable);
  467.     }
  468.     
  469.     /* UP ARROW */
  470.     s = tbuf;
  471.     if ((s = tgetstr("ku", &sp)) != NULL) 
  472.     {
  473.         add_table(s, EC_UP, edittable, sz_edittable);
  474.         add_table(s, A_B_LINE, kcmdtable, sz_kcmdtable);
  475.     }
  476.         
  477.     /* DOWN ARROW */
  478.     s = tbuf;
  479.     if ((s = tgetstr("kd", &sp)) != NULL) 
  480.     {
  481.         add_table(s, EC_DOWN, edittable, sz_edittable);
  482.         add_table(s, A_F_LINE, kcmdtable, sz_kcmdtable);
  483.     }
  484.  
  485.     /* PAGE UP */
  486.     s = tbuf;
  487.     if ((s = tgetstr("kP", &sp)) != NULL) 
  488.     {
  489.         add_table(s, A_B_SCREEN, kcmdtable, sz_kcmdtable);
  490.     }
  491.  
  492.     /* PAGE DOWN */
  493.     s = tbuf;
  494.     if ((s = tgetstr("kN", &sp)) != NULL) 
  495.     {
  496.         add_table(s, A_F_SCREEN, kcmdtable, sz_kcmdtable);
  497.     }
  498.     
  499.     /* HOME */
  500.     sp = tbuf;
  501.     if ((s = tgetstr("kh", &sp)) != NULL) 
  502.     {
  503.         add_table(s, EC_HOME, edittable, sz_edittable);
  504.     }
  505.  
  506.     /* END */
  507.     sp = tbuf;
  508.     if ((s = tgetstr("@7", &sp)) != NULL) 
  509.     {
  510.         add_table(s, EC_END, edittable, sz_edittable);
  511.     }
  512.  
  513.     /* DELETE */
  514.     sp = tbuf+1;
  515.     if ((s = tgetstr("kD", &sp)) == NULL) 
  516.     {
  517.         /* Use DEL (\177) if no "kD" termcap. */
  518.         tbuf[1] = '\177';
  519.         tbuf[2] = '\0';
  520.         s = tbuf+1;
  521.     }
  522.     add_table(s, EC_DELETE, edittable, sz_edittable);
  523.     *--s = ESC;
  524.     add_table(s, EC_W_DELETE, edittable, sz_edittable);
  525.         
  526.     /* BACKSPACE */
  527.     tbuf[0] = ESC;
  528.     tbuf[1] = erase_char;
  529.     tbuf[2] = '\0';
  530.     add_table(tbuf, EC_W_BACKSPACE, edittable, sz_edittable);
  531.  
  532.     /*
  533.      * Append the static edittable.
  534.      */
  535.     memcpy(edittable + sz_edittable, st_edittable, sizeof(st_edittable)-1);
  536.     sz_edittable += sizeof(st_edittable)-1;
  537. }
  538.  
  539. /*
  540.  * Get terminal capabilities via termcap.
  541.  */
  542.     public void
  543. get_term()
  544. {
  545.     char *sp;
  546.     register char *t1, *t2;
  547.     register int hard;
  548.     char *term;
  549.     char termbuf[2048];
  550.  
  551.     static char sbuf[1024];
  552.  
  553.     /*
  554.      * Find out what kind of terminal this is.
  555.      */
  556.      if ((term = getenv("TERM")) == NULL)
  557. #if OS2
  558.                 term = "ansi";
  559. #else
  560.          term = "unknown";
  561. #endif
  562.      if (tgetent(termbuf, term) <= 0)
  563.          strcpy(termbuf, "dumb:hc:");
  564.  
  565.      hard = tgetflag("hc");
  566.  
  567.     /*
  568.      * Get size of the screen.
  569.      */
  570.     scrsize();
  571.     pos_init();
  572.  
  573.     auto_wrap = tgetflag("am");
  574.     ignaw = tgetflag("xn");
  575.     above_mem = tgetflag("da");
  576.     below_mem = tgetflag("db");
  577.  
  578.     /*
  579.      * Assumes termcap variable "sg" is the printing width of:
  580.      * the standout sequence, the end standout sequence,
  581.      * the underline sequence, the end underline sequence,
  582.      * the boldface sequence, and the end boldface sequence.
  583.      */
  584.     if ((so_s_width = tgetnum("sg")) < 0)
  585.         so_s_width = 0;
  586.     so_e_width = so_s_width;
  587.  
  588.     bo_s_width = bo_e_width = so_s_width;
  589.     ul_s_width = ul_e_width = so_s_width;
  590.     bl_s_width = bl_e_width = so_s_width;
  591.  
  592.     /*
  593.      * Get various string-valued capabilities.
  594.      */
  595.     sp = sbuf;
  596.  
  597. #if HAVE_OSPEED
  598.     sc_pad = tgetstr("pc", &sp);
  599.     if (sc_pad != NULL)
  600.         PC = *sc_pad;
  601. #endif
  602.  
  603.     sc_s_keypad = tgetstr("ks", &sp);
  604.     if (sc_s_keypad == NULL)
  605.         sc_s_keypad = "";
  606.     sc_e_keypad = tgetstr("ke", &sp);
  607.     if (sc_e_keypad == NULL)
  608.         sc_e_keypad = "";
  609.         
  610.     sc_init = tgetstr("ti", &sp);
  611.     if (sc_init == NULL)
  612.         sc_init = "";
  613.  
  614.     sc_deinit= tgetstr("te", &sp);
  615.     if (sc_deinit == NULL)
  616.         sc_deinit = "";
  617.  
  618.     sc_eol_clear = tgetstr("ce", &sp);
  619.     if (hard || sc_eol_clear == NULL || *sc_eol_clear == '\0')
  620.     {
  621.         cannot("clear to end of line");
  622.         sc_eol_clear = "";
  623.     }
  624.  
  625.     sc_eos_clear = tgetstr("cd", &sp);
  626.     if (below_mem && 
  627.         (hard || sc_eos_clear == NULL || *sc_eos_clear == '\0'))
  628.     {
  629.         cannot("clear to end of screen");
  630.         sc_eol_clear = "";
  631.     }
  632.  
  633.     sc_clear = tgetstr("cl", &sp);
  634.     if (hard || sc_clear == NULL || *sc_clear == '\0')
  635.     {
  636.         cannot("clear screen");
  637.         sc_clear = "\n\n";
  638.     }
  639.  
  640.     sc_move = tgetstr("cm", &sp);
  641.     if (hard || sc_move == NULL || *sc_move == '\0')
  642.     {
  643.         /*
  644.          * This is not an error here, because we don't 
  645.          * always need sc_move.
  646.          * We need it only if we don't have home or lower-left.
  647.          */
  648.         sc_move = "";
  649.     }
  650.  
  651.     sc_s_in = tgetstr("so", &sp);
  652.     if (hard || sc_s_in == NULL)
  653.         sc_s_in = "";
  654.  
  655.     sc_s_out = tgetstr("se", &sp);
  656.     if (hard || sc_s_out == NULL)
  657.         sc_s_out = "";
  658.  
  659.     sc_u_in = tgetstr("us", &sp);
  660.     if (hard || sc_u_in == NULL)
  661.         sc_u_in = sc_s_in;
  662.  
  663.     sc_u_out = tgetstr("ue", &sp);
  664.     if (hard || sc_u_out == NULL)
  665.         sc_u_out = sc_s_out;
  666.  
  667.     sc_b_in = tgetstr("md", &sp);
  668.     if (hard || sc_b_in == NULL)
  669.     {
  670.         sc_b_in = sc_s_in;
  671.         sc_b_out = sc_s_out;
  672.     } else
  673.     {
  674.         sc_b_out = tgetstr("me", &sp);
  675.         if (hard || sc_b_out == NULL)
  676.             sc_b_out = "";
  677.     }
  678.  
  679.     sc_bl_in = tgetstr("mb", &sp);
  680.     if (hard || sc_bl_in == NULL)
  681.     {
  682.         sc_bl_in = sc_s_in;
  683.         sc_bl_out = sc_s_out;
  684.     } else
  685.     {
  686.         sc_bl_out = tgetstr("me", &sp);
  687.         if (hard || sc_bl_out == NULL)
  688.             sc_bl_out = "";
  689.     }
  690.  
  691.     sc_visual_bell = tgetstr("vb", &sp);
  692.     if (hard || sc_visual_bell == NULL)
  693.         sc_visual_bell = "";
  694.  
  695.     if (tgetflag("bs"))
  696.         sc_backspace = "\b";
  697.     else
  698.     {
  699.         sc_backspace = tgetstr("bc", &sp);
  700.         if (sc_backspace == NULL || *sc_backspace == '\0')
  701.             sc_backspace = "\b";
  702.     }
  703.  
  704.     /*
  705.      * Choose between using "ho" and "cm" ("home" and "cursor move")
  706.      * to move the cursor to the upper left corner of the screen.
  707.      */
  708.     t1 = tgetstr("ho", &sp);
  709.     if (hard || t1 == NULL)
  710.         t1 = "";
  711.     if (*sc_move == '\0')
  712.         t2 = "";
  713.     else
  714.     {
  715.         strcpy(sp, tgoto(sc_move, 0, 0));
  716.         t2 = sp;
  717.         sp += strlen(sp) + 1;
  718.     }
  719.     sc_home = cheaper(t1, t2, "home cursor", "|\b^");
  720.  
  721.     /*
  722.      * Choose between using "ll" and "cm"  ("lower left" and "cursor move")
  723.      * to move the cursor to the lower left corner of the screen.
  724.      */
  725.     t1 = tgetstr("ll", &sp);
  726.     if (hard || t1 == NULL)
  727.         t1 = "";
  728.     if (*sc_move == '\0')
  729.         t2 = "";
  730.     else
  731.     {
  732.         strcpy(sp, tgoto(sc_move, 0, sc_height-1));
  733.         t2 = sp;
  734.         sp += strlen(sp) + 1;
  735.     }
  736.     sc_lower_left = cheaper(t1, t2,
  737.         "move cursor to lower left of screen", "\r");
  738.  
  739.     /*
  740.      * Choose between using "al" or "sr" ("add line" or "scroll reverse")
  741.      * to add a line at the top of the screen.
  742.      */
  743.     t1 = tgetstr("al", &sp);
  744.     if (hard || t1 == NULL)
  745.         t1 = "";
  746.     t2 = tgetstr("sr", &sp);
  747.     if (hard || t2 == NULL)
  748.         t2 = "";
  749. #if OS2
  750.         if (*t1 == '\0' && *t2 == '\0')
  751.            sc_addline = "";
  752.         else
  753. #endif
  754.     if (above_mem)
  755.         sc_addline = t1;
  756.     else
  757.         sc_addline = cheaper(t1, t2, "scroll backwards", "");
  758.  
  759.     if (*sc_addline == '\0')
  760.     {
  761.         /*
  762.          * Force repaint on any backward movement.
  763.          */
  764.         back_scroll = 0;
  765.     }
  766. }
  767.  
  768. /*
  769.  * Return the cost of displaying a termcap string.
  770.  * We use the trick of calling tputs, but as a char printing function
  771.  * we give it inc_costcount, which just increments "costcount".
  772.  * This tells us how many chars would be printed by using this string.
  773.  * {{ Couldn't we just use strlen? }}
  774.  */
  775. static int costcount;
  776.  
  777. /*ARGSUSED*/
  778.     static int
  779. inc_costcount(c)
  780.     int c;
  781. {
  782.     costcount++;
  783.     return (c);
  784. }
  785.  
  786.     static int
  787. cost(t)
  788.     char *t;
  789. {
  790.     costcount = 0;
  791.     tputs(t, sc_height, inc_costcount);
  792.     return (costcount);
  793. }
  794.  
  795. /*
  796.  * Return the "best" of the two given termcap strings.
  797.  * The best, if both exist, is the one with the lower 
  798.  * cost (see cost() function).
  799.  */
  800.     static char *
  801. cheaper(t1, t2, doit, def)
  802.     char *t1, *t2;
  803.     char *doit;
  804.     char *def;
  805. {
  806.     if (*t1 == '\0' && *t2 == '\0')
  807.     {
  808.         cannot(doit);
  809.         return (def);
  810.     }
  811.     if (*t1 == '\0')
  812.         return (t2);
  813.     if (*t2 == '\0')
  814.         return (t1);
  815.     if (cost(t1) < cost(t2))
  816.         return (t1);
  817.     return (t2);
  818. }
  819.  
  820.  
  821. /*
  822.  * Below are the functions which perform all the 
  823.  * terminal-specific screen manipulation.
  824.  */
  825.  
  826.  
  827. /*
  828.  * Initialize terminal
  829.  */
  830.     public void
  831. init()
  832. {
  833.     if (no_init)
  834.         return;
  835.     tputs(sc_init, sc_height, putchr);
  836.     tputs(sc_s_keypad, sc_height, putchr);
  837.     init_done = 1;
  838. }
  839.  
  840. /*
  841.  * Deinitialize terminal
  842.  */
  843.     public void
  844. deinit()
  845. {
  846.     if (no_init)
  847.         return;
  848.     if (!init_done)
  849.         return;
  850.     tputs(sc_e_keypad, sc_height, putchr);
  851.     tputs(sc_deinit, sc_height, putchr);
  852.     init_done = 0;
  853. }
  854.  
  855. /*
  856.  * Home cursor (move to upper left corner of screen).
  857.  */
  858.     public void
  859. home()
  860. {
  861.     tputs(sc_home, 1, putchr);
  862. }
  863.  
  864. /*
  865.  * Add a blank line (called with cursor at home).
  866.  * Should scroll the display down.
  867.  */
  868.     public void
  869. add_line()
  870. {
  871.     tputs(sc_addline, sc_height, putchr);
  872. }
  873.  
  874. /*
  875.  * Move cursor to lower left corner of screen.
  876.  */
  877.     public void
  878. lower_left()
  879. {
  880.     tputs(sc_lower_left, 1, putchr);
  881. }
  882.  
  883. /*
  884.  * Ring the terminal bell.
  885.  */
  886.     public void
  887. bell()
  888. {
  889.     if (quiet == VERY_QUIET)
  890.         vbell();
  891.     else
  892.         putchr('\7');
  893. }
  894.  
  895. /*
  896.  * Output the "visual bell", if there is one.
  897.  */
  898.     public void
  899. vbell()
  900. {
  901.     if (*sc_visual_bell == '\0')
  902.         return;
  903.     tputs(sc_visual_bell, sc_height, putchr);
  904. }
  905.  
  906. /*
  907.  * Clear the screen.
  908.  */
  909.     public void
  910. clear()
  911. {
  912.     tputs(sc_clear, sc_height, putchr);
  913. }
  914.  
  915. /*
  916.  * Clear from the cursor to the end of the cursor's line.
  917.  * {{ This must not move the cursor. }}
  918.  */
  919.     public void
  920. clear_eol()
  921. {
  922.     tputs(sc_eol_clear, 1, putchr);
  923. }
  924.  
  925. /*
  926.  * Clear the bottom line of the display.
  927.  * Leave the cursor at the beginning of the bottom line.
  928.  */
  929.     public void
  930. clear_bot()
  931. {
  932.     lower_left();
  933.     if (below_mem)
  934.         tputs(sc_eos_clear, 1, putchr);
  935.     else
  936.         tputs(sc_eol_clear, 1, putchr);
  937. }
  938.  
  939. /*
  940.  * Begin "standout" (bold, underline, or whatever).
  941.  */
  942.     public void
  943. so_enter()
  944. {
  945.     tputs(sc_s_in, 1, putchr);
  946. }
  947.  
  948. /*
  949.  * End "standout".
  950.  */
  951.     public void
  952. so_exit()
  953. {
  954.     tputs(sc_s_out, 1, putchr);
  955. }
  956.  
  957. /*
  958.  * Begin "underline" (hopefully real underlining, 
  959.  * otherwise whatever the terminal provides).
  960.  */
  961.     public void
  962. ul_enter()
  963. {
  964.     tputs(sc_u_in, 1, putchr);
  965. }
  966.  
  967. /*
  968.  * End "underline".
  969.  */
  970.     public void
  971. ul_exit()
  972. {
  973.     tputs(sc_u_out, 1, putchr);
  974. }
  975.  
  976. /*
  977.  * Begin "bold"
  978.  */
  979.     public void
  980. bo_enter()
  981. {
  982.     tputs(sc_b_in, 1, putchr);
  983. }
  984.  
  985. /*
  986.  * End "bold".
  987.  */
  988.     public void
  989. bo_exit()
  990. {
  991.     tputs(sc_b_out, 1, putchr);
  992. }
  993.  
  994. /*
  995.  * Begin "blink"
  996.  */
  997.     public void
  998. bl_enter()
  999. {
  1000.     tputs(sc_bl_in, 1, putchr);
  1001. }
  1002.  
  1003. /*
  1004.  * End "blink".
  1005.  */
  1006.     public void
  1007. bl_exit()
  1008. {
  1009.     tputs(sc_bl_out, 1, putchr);
  1010. }
  1011.  
  1012. /*
  1013.  * Erase the character to the left of the cursor 
  1014.  * and move the cursor left.
  1015.  */
  1016.     public void
  1017. backspace()
  1018. {
  1019.     /* 
  1020.      * Try to erase the previous character by overstriking with a space.
  1021.      */
  1022.     tputs(sc_backspace, 1, putchr);
  1023.     putchr(' ');
  1024.     tputs(sc_backspace, 1, putchr);
  1025. }
  1026.  
  1027. /*
  1028.  * Output a plain backspace, without erasing the previous char.
  1029.  */
  1030.     public void
  1031. putbs()
  1032. {
  1033.     tputs(sc_backspace, 1, putchr);
  1034. }
  1035.