home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 22 gnu / 22-gnu.zip / less2912.zip / screen.c < prev    next >
C/C++ Source or Header  |  1995-11-09  |  24KB  |  1,210 lines

  1. /*
  2.  * Copyright (c) 1984,1985,1989,1994,1995  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. #if OS2
  77. #define    DEFAULT_TERM        "ansi"
  78. #else
  79. #define    DEFAULT_TERM        "unknown"
  80. #endif
  81.  
  82. /*
  83.  * Strings passed to tputs() to do various terminal functions.
  84.  */
  85. static char
  86.     *sc_pad,        /* Pad string */
  87.     *sc_home,        /* Cursor home */
  88.     *sc_addline,        /* Add line, scroll down following lines */
  89.     *sc_lower_left,        /* Cursor to last line, first column */
  90.     *sc_move,        /* General cursor positioning */
  91.     *sc_clear,        /* Clear screen */
  92.     *sc_eol_clear,        /* Clear to end of line */
  93.     *sc_eos_clear,        /* Clear to end of screen */
  94.     *sc_s_in,        /* Enter standout (highlighted) mode */
  95.     *sc_s_out,        /* Exit standout mode */
  96.     *sc_u_in,        /* Enter underline mode */
  97.     *sc_u_out,        /* Exit underline mode */
  98.     *sc_b_in,        /* Enter bold mode */
  99.     *sc_b_out,        /* Exit bold mode */
  100.     *sc_bl_in,        /* Enter blink mode */
  101.     *sc_bl_out,        /* Exit blink mode */
  102.     *sc_visual_bell,    /* Visual bell (flash screen) sequence */
  103.     *sc_backspace,        /* Backspace cursor */
  104.     *sc_s_keypad,        /* Start keypad mode */
  105.     *sc_e_keypad,        /* End keypad mode */
  106.     *sc_init,        /* Startup terminal initialization */
  107.     *sc_deinit;        /* Exit terminal de-initialization */
  108.  
  109. static int init_done = 0;
  110.  
  111. public int auto_wrap;        /* Terminal does \r\n when write past margin */
  112. public int ignaw;        /* Terminal ignores \n immediately after wrap */
  113. public int erase_char, kill_char; /* The user's erase and line-kill chars */
  114. public int werase_char;        /* The user's word-erase char */
  115. public int sc_width, sc_height;    /* Height & width of screen */
  116. public int bo_s_width, bo_e_width;    /* Printing width of boldface seq */
  117. public int ul_s_width, ul_e_width;    /* Printing width of underline seq */
  118. public int so_s_width, so_e_width;    /* Printing width of standout seq */
  119. public int bl_s_width, bl_e_width;    /* Printing width of blink seq */
  120. public int above_mem, below_mem;    /* Memory retained above/below screen */
  121. public int can_goto_line;        /* Can move cursor to any line */
  122.  
  123. static char *cheaper();
  124.  
  125. /*
  126.  * These two variables are sometimes defined in,
  127.  * and needed by, the termcap library.
  128.  */
  129. #if MUST_DEFINE_OSPEED
  130. extern short ospeed;    /* Terminal output baud rate */
  131. extern char PC;        /* Pad character */
  132. #endif
  133.  
  134. extern int quiet;        /* If VERY_QUIET, use visual bell for bell */
  135. extern int know_dumb;        /* Don't complain about a dumb terminal */
  136. extern int no_back_scroll;
  137. extern int swindow;
  138. extern int no_init;
  139. #if HILITE_SEARCH
  140. extern int hilite_search;
  141. #endif
  142.  
  143. extern char *tgetstr();
  144. extern char *tgoto();
  145.  
  146.  
  147. /*
  148.  * Change terminal to "raw mode", or restore to "normal" mode.
  149.  * "Raw mode" means 
  150.  *    1. An outstanding read will complete on receipt of a single keystroke.
  151.  *    2. Input is not echoed.  
  152.  *    3. On output, \n is mapped to \r\n.
  153.  *    4. \t is NOT expanded into spaces.
  154.  *    5. Signal-causing characters such as ctrl-C (interrupt),
  155.  *       etc. are NOT disabled.
  156.  * It doesn't matter whether an input \n is mapped to \r, or vice versa.
  157.  */
  158.     public void
  159. raw_mode(on)
  160.     int on;
  161. {
  162.     static int curr_on = 0;
  163.  
  164.     if (on == curr_on)
  165.         return;
  166. #if OS2
  167.     signal(SIGINT, SIG_IGN);
  168.     erase_char = '\b';
  169.     kill_char = '\033';
  170. #else
  171. #if HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS
  172.     {
  173.     struct termios s;
  174.     static struct termios save_term;
  175.  
  176.     if (on) 
  177.     {
  178.         /*
  179.          * Get terminal modes.
  180.          */
  181.         tcgetattr(2, &s);
  182.  
  183.         /*
  184.          * Save modes and set certain variables dependent on modes.
  185.          */
  186.         save_term = s;
  187. #if HAVE_OSPEED
  188.         switch (cfgetospeed(&s))
  189.         {
  190. #ifdef B0
  191.         case B0: ospeed = 0; break;
  192. #endif
  193. #ifdef B50
  194.         case B50: ospeed = 1; break;
  195. #endif
  196. #ifdef B75
  197.         case B75: ospeed = 2; break;
  198. #endif
  199. #ifdef B110
  200.         case B110: ospeed = 3; break;
  201. #endif
  202. #ifdef B134
  203.         case B134: ospeed = 4; break;
  204. #endif
  205. #ifdef B150
  206.         case B150: ospeed = 5; break;
  207. #endif
  208. #ifdef B200
  209.         case B200: ospeed = 6; break;
  210. #endif
  211. #ifdef B300
  212.         case B300: ospeed = 7; break;
  213. #endif
  214. #ifdef B600
  215.         case B600: ospeed = 8; break;
  216. #endif
  217. #ifdef B1200
  218.         case B1200: ospeed = 9; break;
  219. #endif
  220. #ifdef B1800
  221.         case B1800: ospeed = 10; break;
  222. #endif
  223. #ifdef B2400
  224.         case B2400: ospeed = 11; break;
  225. #endif
  226. #ifdef B4800
  227.         case B4800: ospeed = 12; break;
  228. #endif
  229. #ifdef B9600
  230.         case B9600: ospeed = 13; break;
  231. #endif
  232. #ifdef EXTA
  233.         case EXTA: ospeed = 14; break;
  234. #endif
  235. #ifdef EXTB
  236.         case EXTB: ospeed = 15; break;
  237. #endif
  238. #ifdef B57600
  239.         case B57600: ospeed = 16; break;
  240. #endif
  241. #ifdef B115200
  242.         case B115200: ospeed = 17; break;
  243. #endif
  244.         default: ;
  245.         }
  246. #endif
  247.         erase_char = s.c_cc[VERASE];
  248.         kill_char = s.c_cc[VKILL];
  249. #ifdef VWERASE
  250.         werase_char = s.c_cc[VWERASE];
  251. #else
  252.         werase_char = 0;
  253. #endif
  254.  
  255.         /*
  256.          * Set the modes to the way we want them.
  257.          */
  258.         s.c_lflag &= ~(0
  259. #ifdef ICANON
  260.             | ICANON
  261. #endif
  262. #ifdef ECHO
  263.             | ECHO
  264. #endif
  265. #ifdef ECHOE
  266.             | ECHOE
  267. #endif
  268. #ifdef ECHOK
  269.             | ECHOK
  270. #endif
  271. #if ECHONL
  272.             | ECHONL
  273. #endif
  274.         );
  275.  
  276.         s.c_oflag |= (0
  277. #ifdef XTABS
  278.             | XTABS
  279. #else
  280. #ifdef TAB3
  281.             | TAB3
  282. #else
  283. #ifdef OXTABS
  284.             | OXTABS
  285. #endif
  286. #endif
  287. #endif
  288. #ifdef OPOST
  289.             | OPOST
  290. #endif
  291. #ifdef ONLCR
  292.             | ONLCR
  293. #endif
  294.         );
  295.  
  296.         s.c_oflag &= ~(0
  297. #ifdef ONOEOT
  298.             | ONOEOT
  299. #endif
  300. #ifdef OCRNL
  301.             | OCRNL
  302. #endif
  303. #ifdef ONOCR
  304.             | ONOCR
  305. #endif
  306. #ifdef ONLRET
  307.             | ONLRET
  308. #endif
  309.         );
  310.         s.c_cc[VMIN] = 1;
  311.         s.c_cc[VTIME] = 0;
  312.     } else
  313.     {
  314.         /*
  315.          * Restore saved modes.
  316.          */
  317.         s = save_term;
  318.     }
  319.     tcsetattr(2, TCSADRAIN, &s);
  320.     }
  321. #else
  322. #ifdef TCGETA
  323.     {
  324.     struct termio s;
  325.     static struct termio save_term;
  326.  
  327.     if (on)
  328.     {
  329.         /*
  330.          * Get terminal modes.
  331.          */
  332.         ioctl(2, TCGETA, &s);
  333.  
  334.         /*
  335.          * Save modes and set certain variables dependent on modes.
  336.          */
  337.         save_term = s;
  338. #if HAVE_OSPEED
  339.         ospeed = s.c_cflag & CBAUD;
  340. #endif
  341.         erase_char = s.c_cc[VERASE];
  342.         kill_char = s.c_cc[VKILL];
  343. #ifdef VWERASE
  344.         werase_char = s.c_cc[VWERASE];
  345. #else
  346.         werase_char = 0;
  347. #endif
  348.  
  349.         /*
  350.          * Set the modes to the way we want them.
  351.          */
  352.         s.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL);
  353.         s.c_oflag |=  (OPOST|ONLCR|TAB3);
  354.         s.c_oflag &= ~(OCRNL|ONOCR|ONLRET);
  355.         s.c_cc[VMIN] = 1;
  356.         s.c_cc[VTIME] = 0;
  357.     } else
  358.     {
  359.         /*
  360.          * Restore saved modes.
  361.          */
  362.         s = save_term;
  363.     }
  364.     ioctl(2, TCSETAW, &s);
  365.     }
  366. #else
  367.     {
  368.     struct sgttyb s;
  369.     static struct sgttyb save_term;
  370.  
  371.     if (on)
  372.     {
  373.         /*
  374.          * Get terminal modes.
  375.          */
  376.         ioctl(2, TIOCGETP, &s);
  377.  
  378.         /*
  379.          * Save modes and set certain variables dependent on modes.
  380.          */
  381.         save_term = s;
  382. #if HAVE_OSPEED
  383.         ospeed = s.sg_ospeed;
  384. #endif
  385.         erase_char = s.sg_erase;
  386.         kill_char = s.sg_kill;
  387.         werase_char = 0;
  388.  
  389.         /*
  390.          * Set the modes to the way we want them.
  391.          */
  392.         s.sg_flags |= CBREAK;
  393.         s.sg_flags &= ~(ECHO|XTABS);
  394.     } else
  395.     {
  396.         /*
  397.          * Restore saved modes.
  398.          */
  399.         s = save_term;
  400.     }
  401.     ioctl(2, TIOCSETN, &s);
  402.     }
  403. #endif
  404. #endif
  405. #endif
  406.     curr_on = on;
  407. }
  408.  
  409.     static void
  410. cannot(s)
  411.     char *s;
  412. {
  413.     PARG parg;
  414.  
  415.     if (know_dumb)
  416.         /* 
  417.          * User knows this is a dumb terminal, so don't tell him.
  418.          */
  419.         return;
  420.  
  421.     parg.p_string = s;
  422.     error("WARNING: terminal cannot %s", &parg);
  423. }
  424.  
  425. /*
  426.  * Get size of the output screen.
  427.  */
  428. #if OS2
  429.     public void
  430. scrsize()
  431. {
  432.     int s[2];
  433.  
  434.     _scrsize(s);
  435.     sc_width = s[0];
  436.     sc_height = s[1];
  437. }
  438.  
  439. #else
  440.  
  441.     public void
  442. scrsize()
  443. {
  444.     register char *s;
  445. #ifdef TIOCGWINSZ
  446.     struct winsize w;
  447. #else
  448. #ifdef WIOCGETD
  449.     struct uwdata w;
  450. #endif
  451. #endif
  452.  
  453. #ifdef TIOCGWINSZ
  454.     if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_row > 0)
  455.         sc_height = w.ws_row;
  456.     else
  457. #else
  458. #ifdef WIOCGETD
  459.     if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_height > 0)
  460.         sc_height = w.uw_height/w.uw_vs;
  461.     else
  462. #endif
  463. #endif
  464.     if ((s = lgetenv("LINES")) != NULL)
  465.         sc_height = atoi(s);
  466.     else
  467.          sc_height = tgetnum("li");
  468.  
  469.     if (sc_height <= 0)
  470.         sc_height = 24;
  471.  
  472. #ifdef TIOCGWINSZ
  473.      if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_col > 0)
  474.         sc_width = w.ws_col;
  475.     else
  476. #ifdef WIOCGETD
  477.     if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_width > 0)
  478.         sc_width = w.uw_width/w.uw_hs;
  479.     else
  480. #endif
  481. #endif
  482.     if ((s = lgetenv("COLUMNS")) != NULL)
  483.         sc_width = atoi(s);
  484.     else
  485.          sc_width = tgetnum("co");
  486.  
  487.      if (sc_width <= 0)
  488.           sc_width = 80;
  489. }
  490. #endif /* OS2 */
  491.  
  492. /*
  493.  * Take care of the "variable" keys.
  494.  * Certain keys send escape sequences which differ on different terminals
  495.  * (such as the arrow keys, INSERT, DELETE, etc.)
  496.  * Construct the commands based on these keys.
  497.  */
  498.     public void
  499. get_editkeys()
  500. {
  501.     char *sp;
  502.     char *s;
  503.     char tbuf[40];
  504.  
  505.     static char kfcmdtable[400];
  506.     int sz_kfcmdtable = 0;
  507.     static char kecmdtable[400];
  508.     int sz_kecmdtable = 0;
  509.  
  510. #define    put_cmd(str,action,tbl,sz) { \
  511.     strcpy(tbl+sz, str);    \
  512.     sz += strlen(str) + 1;    \
  513.     tbl[sz++] = action; }
  514. #define    put_esc_cmd(str,action,tbl,sz) { \
  515.     tbl[sz++] = ESC; \
  516.     put_cmd(str,action,tbl,sz); }
  517.  
  518. #define    put_fcmd(str,action)    put_cmd(str,action,kfcmdtable,sz_kfcmdtable)
  519. #define    put_ecmd(str,action)    put_cmd(str,action,kecmdtable,sz_kecmdtable)
  520. #define    put_esc_fcmd(str,action) put_esc_cmd(str,action,kfcmdtable,sz_kfcmdtable)
  521. #define    put_esc_ecmd(str,action) put_esc_cmd(str,action,kecmdtable,sz_kecmdtable)
  522.  
  523.     /*
  524.      * Look at some interesting keys and see what strings they send.
  525.      * Create commands (both command keys and line-edit keys).
  526.      */
  527.  
  528.     /* RIGHT ARROW */
  529.     sp = tbuf;
  530.     if ((s = tgetstr("kr", &sp)) != NULL)
  531.     {
  532.         put_ecmd(s, EC_RIGHT);
  533.         put_esc_ecmd(s, EC_W_RIGHT);
  534.     }
  535.     
  536.     /* LEFT ARROW */
  537.     sp = tbuf;
  538.     if ((s = tgetstr("kl", &sp)) != NULL)
  539.     {
  540.         put_ecmd(s, EC_LEFT);
  541.         put_esc_ecmd(s, EC_W_LEFT);
  542.     }
  543.     
  544.     /* UP ARROW */
  545.     sp = tbuf;
  546.     if ((s = tgetstr("ku", &sp)) != NULL) 
  547.     {
  548.         put_ecmd(s, EC_UP);
  549.         put_fcmd(s, A_B_LINE);
  550.     }
  551.         
  552.     /* DOWN ARROW */
  553.     sp = tbuf;
  554.     if ((s = tgetstr("kd", &sp)) != NULL) 
  555.     {
  556.         put_ecmd(s, EC_DOWN);
  557.         put_fcmd(s, A_F_LINE);
  558.     }
  559.  
  560.     /* PAGE UP */
  561.     sp = tbuf;
  562.     if ((s = tgetstr("kP", &sp)) != NULL) 
  563.     {
  564.         put_fcmd(s, A_B_SCREEN);
  565.     }
  566.  
  567.     /* PAGE DOWN */
  568.     sp = tbuf;
  569.     if ((s = tgetstr("kN", &sp)) != NULL) 
  570.     {
  571.         put_fcmd(s, A_F_SCREEN);
  572.     }
  573.     
  574.     /* HOME */
  575.     sp = tbuf;
  576.     if ((s = tgetstr("kh", &sp)) != NULL) 
  577.     {
  578.         put_fcmd(s, A_GOLINE);
  579.         put_ecmd(s, EC_HOME);
  580.     }
  581.  
  582.     /* END */
  583.     sp = tbuf;
  584.     if ((s = tgetstr("@7", &sp)) != NULL) 
  585.     {
  586.         put_fcmd(s, A_GOEND);
  587.         put_ecmd(s, EC_END);
  588.     }
  589.  
  590.     /* DELETE */
  591.     sp = tbuf;
  592.     if ((s = tgetstr("kD", &sp)) == NULL) 
  593.     {
  594.         /* Use DEL (\177) if no "kD" termcap. */
  595.         tbuf[1] = '\177';
  596.         tbuf[2] = '\0';
  597.         s = tbuf+1;
  598.     }
  599.     put_ecmd(s, EC_DELETE);
  600.     put_esc_ecmd(s, EC_W_DELETE);
  601.         
  602.     /* BACKSPACE */
  603.     tbuf[0] = ESC;
  604.     tbuf[1] = erase_char;
  605.     tbuf[2] = '\0';
  606.     put_ecmd(tbuf, EC_W_BACKSPACE);
  607.  
  608.     if (werase_char != 0)
  609.     {
  610.         tbuf[0] = werase_char;
  611.         tbuf[1] = '\0';
  612.         put_ecmd(tbuf, EC_W_BACKSPACE);
  613.     }
  614.  
  615.     /*
  616.      * Register the two tables.
  617.      */
  618.     insert_fcmd_table(kfcmdtable, sz_kfcmdtable);
  619.     insert_ecmd_table(kecmdtable, sz_kecmdtable);
  620. }
  621.  
  622. #if DEBUG
  623.     static void
  624. get_debug_term()
  625. {
  626.     auto_wrap = 1;
  627.     ignaw = 1;
  628.     so_s_width = so_e_width = 0;
  629.     bo_s_width = bo_e_width = 0;
  630.     ul_s_width = ul_e_width = 0;
  631.     bl_s_width = bl_e_width = 0;
  632.     sc_s_keypad =    "(InitKey)";
  633.     sc_e_keypad =    "(DeinitKey)";
  634.     sc_init =    "(InitTerm)";
  635.     sc_deinit =    "(DeinitTerm)";
  636.     sc_eol_clear =    "(ClearEOL)";
  637.     sc_eos_clear =    "(ClearEOS)";
  638.     sc_clear =    "(ClearScreen)";
  639.     sc_move =    "(Move<%d,%d>)";
  640.     sc_s_in =    "(SO+)";
  641.     sc_s_out =    "(SO-)";
  642.     sc_u_in =    "(UL+)";
  643.     sc_u_out =    "(UL-)";
  644.     sc_b_in =    "(BO+)";
  645.     sc_b_out =    "(BO-)";
  646.     sc_bl_in =    "(BL+)";
  647.     sc_bl_out =    "(BL-)";
  648.     sc_visual_bell ="(VBell)";
  649.     sc_backspace =    "(BS)";
  650.     sc_home =    "(Home)";
  651.     sc_lower_left =    "(LL)";
  652.     sc_addline =    "(AddLine)";
  653. }
  654. #endif
  655.  
  656. /*
  657.  * Get terminal capabilities via termcap.
  658.  */
  659.     public void
  660. get_term()
  661. {
  662.     char *sp;
  663.     register char *t1, *t2;
  664.     register int hard;
  665.     char *term;
  666.     char termbuf[2048];
  667.  
  668.     static char sbuf[1024];
  669.     PARG parg;
  670.  
  671. #ifdef OS2
  672.     /*
  673.      * Make sure the termcap database is available.
  674.      */
  675.     sp = lgetenv("TERMCAP");
  676.     if (sp == NULL || *sp == '\0')
  677.     {
  678.         char *termcap;
  679.         if ((sp = homefile("termcap.dat")) != NULL)
  680.         {
  681.             termcap = (char *) ecalloc(strlen(sp)+9, sizeof(char));
  682.             sprintf(termcap, "TERMCAP=%s", sp);
  683.             free(sp);
  684.             putenv(termcap);
  685.         }
  686.     }
  687. #endif
  688.     /*
  689.      * Find out what kind of terminal this is.
  690.      */
  691.      if ((term = lgetenv("TERM")) == NULL)
  692.          term = DEFAULT_TERM;
  693.     /*Borrow "hard" here to output error messages */
  694.      if ((hard = tgetent(termbuf, term)) <= 0)
  695.          strcpy(termbuf, "dumb:hc:");
  696.  
  697.     if (hard == 0 && !know_dumb)
  698.     {
  699.       parg.p_string = term;      
  700.       error("WARNING: terminal \"%s\" not found in termcap database.",&parg);
  701.     }
  702.     else if (hard == -1 && !know_dumb)
  703.       error("WARNING: The termcap database was not found.",NULL_PARG);
  704.          
  705.     /* hard now goes back to its original purpose.... */
  706.      hard = tgetflag("hc");
  707.  
  708.     /*
  709.      * Get size of the screen.
  710.      */
  711.     scrsize();
  712.     pos_init();
  713.  
  714. #if DEBUG
  715.     if (strncmp(term,"LESSDEBUG",9) == 0)
  716.     {
  717.         get_debug_term();
  718.         return;
  719.     }
  720. #endif /* DEBUG */
  721.  
  722.     auto_wrap = tgetflag("am");
  723.     ignaw = tgetflag("xn");
  724.     above_mem = tgetflag("da");
  725.     below_mem = tgetflag("db");
  726.  
  727.     /*
  728.      * Assumes termcap variable "sg" is the printing width of:
  729.      * the standout sequence, the end standout sequence,
  730.      * the underline sequence, the end underline sequence,
  731.      * the boldface sequence, and the end boldface sequence.
  732.      */
  733.     if ((so_s_width = tgetnum("sg")) < 0)
  734.         so_s_width = 0;
  735.     so_e_width = so_s_width;
  736.  
  737.     bo_s_width = bo_e_width = so_s_width;
  738.     ul_s_width = ul_e_width = so_s_width;
  739.     bl_s_width = bl_e_width = so_s_width;
  740.  
  741. #if HILITE_SEARCH
  742.     if (so_s_width > 0 || so_e_width > 0)
  743.         /*
  744.          * Disable highlighting by default on magic cookie terminals.
  745.          * Turning on highlighting might change the displayed width
  746.          * of a line, causing the display to get messed up.
  747.          * The user can turn it back on with -g, 
  748.          * but she won't like the results.
  749.          */
  750.         hilite_search = 0;
  751. #endif
  752.  
  753.     /*
  754.      * Get various string-valued capabilities.
  755.      */
  756.     sp = sbuf;
  757.  
  758. #if HAVE_OSPEED
  759.     sc_pad = tgetstr("pc", &sp);
  760.     if (sc_pad != NULL)
  761.         PC = *sc_pad;
  762. #endif
  763.  
  764.     sc_s_keypad = tgetstr("ks", &sp);
  765.     if (sc_s_keypad == NULL)
  766.         sc_s_keypad = "";
  767.     sc_e_keypad = tgetstr("ke", &sp);
  768.     if (sc_e_keypad == NULL)
  769.         sc_e_keypad = "";
  770.         
  771.     sc_init = tgetstr("ti", &sp);
  772.     if (sc_init == NULL)
  773.         sc_init = "";
  774.  
  775.     sc_deinit= tgetstr("te", &sp);
  776.     if (sc_deinit == NULL)
  777.         sc_deinit = "";
  778.  
  779.     sc_eol_clear = tgetstr("ce", &sp);
  780.     if (hard || sc_eol_clear == NULL || *sc_eol_clear == '\0')
  781.     {
  782.         cannot("clear to end of line");
  783.         sc_eol_clear = "";
  784.     }
  785.  
  786.     sc_eos_clear = tgetstr("cd", &sp);
  787.     if (below_mem && 
  788.         (hard || sc_eos_clear == NULL || *sc_eos_clear == '\0'))
  789.     {
  790.         cannot("clear to end of screen");
  791.         sc_eol_clear = "";
  792.     }
  793.  
  794.     sc_clear = tgetstr("cl", &sp);
  795.     if (hard || sc_clear == NULL || *sc_clear == '\0')
  796.     {
  797.         cannot("clear screen");
  798.         sc_clear = "\n\n";
  799.     }
  800.  
  801.     sc_move = tgetstr("cm", &sp);
  802.     if (hard || sc_move == NULL || *sc_move == '\0')
  803.     {
  804.         /*
  805.          * This is not an error here, because we don't 
  806.          * always need sc_move.
  807.          * We need it only if we don't have home or lower-left.
  808.          */
  809.         sc_move = "";
  810.         can_goto_line = 0;
  811.     } else
  812.         can_goto_line = 1;
  813.  
  814.     sc_s_in = tgetstr("so", &sp);
  815.     if (hard || sc_s_in == NULL)
  816.         sc_s_in = "";
  817.  
  818.     sc_s_out = tgetstr("se", &sp);
  819.     if (hard || sc_s_out == NULL)
  820.         sc_s_out = "";
  821.  
  822.     sc_u_in = tgetstr("us", &sp);
  823.     if (hard || sc_u_in == NULL)
  824.         sc_u_in = sc_s_in;
  825.  
  826.     sc_u_out = tgetstr("ue", &sp);
  827.     if (hard || sc_u_out == NULL)
  828.         sc_u_out = sc_s_out;
  829.  
  830.     sc_b_in = tgetstr("md", &sp);
  831.     if (hard || sc_b_in == NULL)
  832.     {
  833.         sc_b_in = sc_s_in;
  834.         sc_b_out = sc_s_out;
  835.     } else
  836.     {
  837.         sc_b_out = tgetstr("me", &sp);
  838.         if (hard || sc_b_out == NULL)
  839.             sc_b_out = "";
  840.     }
  841.  
  842.     sc_bl_in = tgetstr("mb", &sp);
  843.     if (hard || sc_bl_in == NULL)
  844.     {
  845.         sc_bl_in = sc_s_in;
  846.         sc_bl_out = sc_s_out;
  847.     } else
  848.     {
  849.         sc_bl_out = tgetstr("me", &sp);
  850.         if (hard || sc_bl_out == NULL)
  851.             sc_bl_out = "";
  852.     }
  853.  
  854.     sc_visual_bell = tgetstr("vb", &sp);
  855.     if (hard || sc_visual_bell == NULL)
  856.         sc_visual_bell = "";
  857.  
  858.     if (tgetflag("bs"))
  859.         sc_backspace = "\b";
  860.     else
  861.     {
  862.         sc_backspace = tgetstr("bc", &sp);
  863.         if (sc_backspace == NULL || *sc_backspace == '\0')
  864.             sc_backspace = "\b";
  865.     }
  866.  
  867.     /*
  868.      * Choose between using "ho" and "cm" ("home" and "cursor move")
  869.      * to move the cursor to the upper left corner of the screen.
  870.      */
  871.     t1 = tgetstr("ho", &sp);
  872.     if (hard || t1 == NULL)
  873.         t1 = "";
  874.     if (*sc_move == '\0')
  875.         t2 = "";
  876.     else
  877.     {
  878.         strcpy(sp, tgoto(sc_move, 0, 0));
  879.         t2 = sp;
  880.         sp += strlen(sp) + 1;
  881.     }
  882.     sc_home = cheaper(t1, t2, "home cursor", "|\b^");
  883.  
  884.     /*
  885.      * Choose between using "ll" and "cm"  ("lower left" and "cursor move")
  886.      * to move the cursor to the lower left corner of the screen.
  887.      */
  888.     t1 = tgetstr("ll", &sp);
  889.     if (hard || t1 == NULL)
  890.         t1 = "";
  891.     if (*sc_move == '\0')
  892.         t2 = "";
  893.     else
  894.     {
  895.         strcpy(sp, tgoto(sc_move, 0, sc_height-1));
  896.         t2 = sp;
  897.         sp += strlen(sp) + 1;
  898.     }
  899.     sc_lower_left = cheaper(t1, t2,
  900.         "move cursor to lower left of screen", "\r");
  901.  
  902.     /*
  903.      * Choose between using "al" or "sr" ("add line" or "scroll reverse")
  904.      * to add a line at the top of the screen.
  905.      */
  906.     t1 = tgetstr("al", &sp);
  907.     if (hard || t1 == NULL)
  908.         t1 = "";
  909.     t2 = tgetstr("sr", &sp);
  910.     if (hard || t2 == NULL)
  911.         t2 = "";
  912. #if OS2
  913.     if (*t1 == '\0' && *t2 == '\0')
  914.         sc_addline = "";
  915.     else
  916. #endif
  917.     if (above_mem)
  918.         sc_addline = t1;
  919.     else
  920.         sc_addline = cheaper(t1, t2, "scroll backwards", "");
  921.     if (*sc_addline == '\0')
  922.     {
  923.         /*
  924.          * Force repaint on any backward movement.
  925.          */
  926.               no_back_scroll = 1;
  927.     }
  928. }
  929.  
  930. /*
  931.  * Return the cost of displaying a termcap string.
  932.  * We use the trick of calling tputs, but as a char printing function
  933.  * we give it inc_costcount, which just increments "costcount".
  934.  * This tells us how many chars would be printed by using this string.
  935.  * {{ Couldn't we just use strlen? }}
  936.  */
  937. static int costcount;
  938.  
  939. /*ARGSUSED*/
  940.     static int
  941. inc_costcount(c)
  942.     int c;
  943. {
  944.     costcount++;
  945.     return (c);
  946. }
  947.  
  948.     static int
  949. cost(t)
  950.     char *t;
  951. {
  952.     costcount = 0;
  953.     tputs(t, sc_height, inc_costcount);
  954.     return (costcount);
  955. }
  956.  
  957. /*
  958.  * Return the "best" of the two given termcap strings.
  959.  * The best, if both exist, is the one with the lower 
  960.  * cost (see cost() function).
  961.  */
  962.     static char *
  963. cheaper(t1, t2, doit, def)
  964.     char *t1, *t2;
  965.     char *doit;
  966.     char *def;
  967. {
  968.     if (*t1 == '\0' && *t2 == '\0')
  969.     {
  970.         cannot(doit);
  971.         return (def);
  972.     }
  973.     if (*t1 == '\0')
  974.         return (t2);
  975.     if (*t2 == '\0')
  976.         return (t1);
  977.     if (cost(t1) < cost(t2))
  978.         return (t1);
  979.     return (t2);
  980. }
  981.  
  982.  
  983. /*
  984.  * Below are the functions which perform all the 
  985.  * terminal-specific screen manipulation.
  986.  */
  987.  
  988.  
  989. /*
  990.  * Initialize terminal
  991.  */
  992.     public void
  993. init()
  994. {
  995.     if (no_init)
  996.         return;
  997.     tputs(sc_init, sc_height, putchr);
  998.     tputs(sc_s_keypad, sc_height, putchr);
  999.     init_done = 1;
  1000. }
  1001.  
  1002. /*
  1003.  * Deinitialize terminal
  1004.  */
  1005.     public void
  1006. deinit()
  1007. {
  1008.     if (no_init)
  1009.         return;
  1010.     if (!init_done)
  1011.         return;
  1012.     tputs(sc_e_keypad, sc_height, putchr);
  1013.     tputs(sc_deinit, sc_height, putchr);
  1014.     init_done = 0;
  1015. }
  1016.  
  1017. /*
  1018.  * Home cursor (move to upper left corner of screen).
  1019.  */
  1020.     public void
  1021. home()
  1022. {
  1023.     tputs(sc_home, 1, putchr);
  1024. }
  1025.  
  1026. /*
  1027.  * Add a blank line (called with cursor at home).
  1028.  * Should scroll the display down.
  1029.  */
  1030.     public void
  1031. add_line()
  1032. {
  1033.     tputs(sc_addline, sc_height, putchr);
  1034. }
  1035.  
  1036. /*
  1037.  * Move cursor to lower left corner of screen.
  1038.  */
  1039.     public void
  1040. lower_left()
  1041. {
  1042.     tputs(sc_lower_left, 1, putchr);
  1043. }
  1044.  
  1045. /*
  1046.  * Goto a specific line on the screen.
  1047.  */
  1048.     public void
  1049. goto_line(slinenum)
  1050.     int slinenum;
  1051. {
  1052.     char *sc_goto;
  1053.  
  1054.     sc_goto = tgoto(sc_move, 0, slinenum);
  1055.     tputs(sc_goto, 1, putchr);
  1056. }
  1057.  
  1058. /*
  1059.  * Ring the terminal bell.
  1060.  */
  1061.     public void
  1062. bell()
  1063. {
  1064.     if (quiet == VERY_QUIET)
  1065.         vbell();
  1066.     else
  1067.         putchr('\7');
  1068. }
  1069.  
  1070. /*
  1071.  * Output the "visual bell", if there is one.
  1072.  */
  1073.     public void
  1074. vbell()
  1075. {
  1076.     if (*sc_visual_bell == '\0')
  1077.         return;
  1078.     tputs(sc_visual_bell, sc_height, putchr);
  1079. }
  1080.  
  1081. /*
  1082.  * Clear the screen.
  1083.  */
  1084.     public void
  1085. clear()
  1086. {
  1087.     tputs(sc_clear, sc_height, putchr);
  1088. }
  1089.  
  1090. /*
  1091.  * Clear from the cursor to the end of the cursor's line.
  1092.  * {{ This must not move the cursor. }}
  1093.  */
  1094.     public void
  1095. clear_eol()
  1096. {
  1097.     tputs(sc_eol_clear, 1, putchr);
  1098. }
  1099.  
  1100. /*
  1101.  * Clear the bottom line of the display.
  1102.  * Leave the cursor at the beginning of the bottom line.
  1103.  */
  1104.     public void
  1105. clear_bot()
  1106. {
  1107.     lower_left();
  1108.     if (below_mem)
  1109.         tputs(sc_eos_clear, 1, putchr);
  1110.     else
  1111.         tputs(sc_eol_clear, 1, putchr);
  1112. }
  1113.  
  1114. /*
  1115.  * Begin "standout" (bold, underline, or whatever).
  1116.  */
  1117.     public void
  1118. so_enter()
  1119. {
  1120.     tputs(sc_s_in, 1, putchr);
  1121. }
  1122.  
  1123. /*
  1124.  * End "standout".
  1125.  */
  1126.     public void
  1127. so_exit()
  1128. {
  1129.     tputs(sc_s_out, 1, putchr);
  1130. }
  1131.  
  1132. /*
  1133.  * Begin "underline" (hopefully real underlining, 
  1134.  * otherwise whatever the terminal provides).
  1135.  */
  1136.     public void
  1137. ul_enter()
  1138. {
  1139.     tputs(sc_u_in, 1, putchr);
  1140. }
  1141.  
  1142. /*
  1143.  * End "underline".
  1144.  */
  1145.     public void
  1146. ul_exit()
  1147. {
  1148.     tputs(sc_u_out, 1, putchr);
  1149. }
  1150.  
  1151. /*
  1152.  * Begin "bold"
  1153.  */
  1154.     public void
  1155. bo_enter()
  1156. {
  1157.     tputs(sc_b_in, 1, putchr);
  1158. }
  1159.  
  1160. /*
  1161.  * End "bold".
  1162.  */
  1163.     public void
  1164. bo_exit()
  1165. {
  1166.     tputs(sc_b_out, 1, putchr);
  1167. }
  1168.  
  1169. /*
  1170.  * Begin "blink"
  1171.  */
  1172.     public void
  1173. bl_enter()
  1174. {
  1175.     tputs(sc_bl_in, 1, putchr);
  1176. }
  1177.  
  1178. /*
  1179.  * End "blink".
  1180.  */
  1181.     public void
  1182. bl_exit()
  1183. {
  1184.     tputs(sc_bl_out, 1, putchr);
  1185. }
  1186.  
  1187. /*
  1188.  * Erase the character to the left of the cursor 
  1189.  * and move the cursor left.
  1190.  */
  1191.     public void
  1192. backspace()
  1193. {
  1194.     /* 
  1195.      * Try to erase the previous character by overstriking with a space.
  1196.      */
  1197.     tputs(sc_backspace, 1, putchr);
  1198.     putchr(' ');
  1199.     tputs(sc_backspace, 1, putchr);
  1200. }
  1201.  
  1202. /*
  1203.  * Output a plain backspace, without erasing the previous char.
  1204.  */
  1205.     public void
  1206. putbs()
  1207. {
  1208.     tputs(sc_backspace, 1, putchr);
  1209. }
  1210.