home *** CD-ROM | disk | FTP | other *** search
/ BCI NET 2 / BCI NET 2.iso / archives / utilities / text / less-278.lha / less-278 / src.lha / source / screen.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-02-01  |  22.1 KB  |  1,197 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 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 = getenv("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 = getenv("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_ecmd(s, EC_HOME);
  579.     }
  580.  
  581.     /* END */
  582.     sp = tbuf;
  583.     if ((s = tgetstr("@7", &sp)) != NULL) 
  584.     {
  585.         put_ecmd(s, EC_END);
  586.     }
  587.  
  588.     /* DELETE */
  589.     sp = tbuf;
  590.     if ((s = tgetstr("kD", &sp)) == NULL) 
  591.     {
  592.         /* Use DEL (\177) if no "kD" termcap. */
  593.         tbuf[1] = '\177';
  594.         tbuf[2] = '\0';
  595.         s = tbuf+1;
  596.     }
  597.     put_ecmd(s, EC_DELETE);
  598.     put_esc_ecmd(s, EC_W_DELETE);
  599.         
  600.     /* BACKSPACE */
  601.     tbuf[0] = ESC;
  602.     tbuf[1] = erase_char;
  603.     tbuf[2] = '\0';
  604.     put_ecmd(tbuf, EC_W_BACKSPACE);
  605.  
  606.     if (werase_char != 0)
  607.     {
  608.         tbuf[0] = werase_char;
  609.         tbuf[1] = '\0';
  610.         put_ecmd(tbuf, EC_W_BACKSPACE);
  611.     }
  612.  
  613.     /*
  614.      * Register the two tables.
  615.      */
  616.     add_fcmd_table(kfcmdtable, sz_kfcmdtable);
  617.     add_ecmd_table(kecmdtable, sz_kecmdtable);
  618. }
  619.  
  620. #if DEBUG
  621.     static void
  622. get_debug_term()
  623. {
  624.     auto_wrap = 1;
  625.     ignaw = 1;
  626.     so_s_width = so_e_width = 0;
  627.     bo_s_width = bo_e_width = 0;
  628.     ul_s_width = ul_e_width = 0;
  629.     bl_s_width = bl_e_width = 0;
  630.     sc_s_keypad =    "(InitKey)";
  631.     sc_e_keypad =    "(DeinitKey)";
  632.     sc_init =    "(InitTerm)";
  633.     sc_deinit =    "(DeinitTerm)";
  634.     sc_eol_clear =    "(ClearEOL)";
  635.     sc_eos_clear =    "(ClearEOS)";
  636.     sc_clear =    "(ClearScreen)";
  637.     sc_move =    "(Move<%d,%d>)";
  638.     sc_s_in =    "(SO+)";
  639.     sc_s_out =    "(SO-)";
  640.     sc_u_in =    "(UL+)";
  641.     sc_u_out =    "(UL-)";
  642.     sc_b_in =    "(BO+)";
  643.     sc_b_out =    "(BO-)";
  644.     sc_bl_in =    "(BL+)";
  645.     sc_bl_out =    "(BL-)";
  646.     sc_visual_bell ="(VBell)";
  647.     sc_backspace =    "(BS)";
  648.     sc_home =    "(Home)";
  649.     sc_lower_left =    "(LL)";
  650.     sc_addline =    "(AddLine)";
  651. }
  652. #endif
  653.  
  654. /*
  655.  * Get terminal capabilities via termcap.
  656.  */
  657.     public void
  658. get_term()
  659. {
  660.     char *sp;
  661.     register char *t1, *t2;
  662.     register int hard;
  663.     char *term;
  664.     char termbuf[2048];
  665.  
  666.     static char sbuf[1024];
  667.  
  668. #ifdef OS2
  669.     /*
  670.      * Make sure the termcap database is available.
  671.      */
  672.     sp = getenv("TERMCAP");
  673.     if (sp == NULL || *sp == '\0')
  674.     {
  675.         char *termcap;
  676.         if ((sp = homefile("termcap.dat")) != NULL)
  677.         {
  678.             termcap = (char *) ecalloc(strlen(sp)+9, sizeof(char));
  679.             sprintf(termcap, "TERMCAP=%s", sp);
  680.             free(sp);
  681.             putenv(termcap);
  682.         }
  683.     }
  684. #endif
  685.     /*
  686.      * Find out what kind of terminal this is.
  687.      */
  688.      if ((term = getenv("TERM")) == NULL)
  689.          term = DEFAULT_TERM;
  690.      if (tgetent(termbuf, term) <= 0)
  691.          strcpy(termbuf, "dumb:hc:");
  692.  
  693.      hard = tgetflag("hc");
  694.  
  695.     /*
  696.      * Get size of the screen.
  697.      */
  698.     scrsize();
  699.     pos_init();
  700.  
  701. #if DEBUG
  702.     if (strncmp(term,"LESSDEBUG",9) == 0)
  703.     {
  704.         get_debug_term();
  705.         return;
  706.     }
  707. #endif /* DEBUG */
  708.  
  709.     auto_wrap = tgetflag("am");
  710.     ignaw = tgetflag("xn");
  711.     above_mem = tgetflag("da");
  712.     below_mem = tgetflag("db");
  713.  
  714.     /*
  715.      * Assumes termcap variable "sg" is the printing width of:
  716.      * the standout sequence, the end standout sequence,
  717.      * the underline sequence, the end underline sequence,
  718.      * the boldface sequence, and the end boldface sequence.
  719.      */
  720.     if ((so_s_width = tgetnum("sg")) < 0)
  721.         so_s_width = 0;
  722.     so_e_width = so_s_width;
  723.  
  724.     bo_s_width = bo_e_width = so_s_width;
  725.     ul_s_width = ul_e_width = so_s_width;
  726.     bl_s_width = bl_e_width = so_s_width;
  727.  
  728. #if HILITE_SEARCH
  729.     if (so_s_width > 0 || so_e_width > 0)
  730.         /*
  731.          * Disable highlighting by default on magic cookie terminals.
  732.          * Turning on highlighting might change the displayed width
  733.          * of a line, causing the display to get messed up.
  734.          * The user can turn it back on with -g, 
  735.          * but she won't like the results.
  736.          */
  737.         hilite_search = 0;
  738. #endif
  739.  
  740.     /*
  741.      * Get various string-valued capabilities.
  742.      */
  743.     sp = sbuf;
  744.  
  745. #if HAVE_OSPEED
  746.     sc_pad = tgetstr("pc", &sp);
  747.     if (sc_pad != NULL)
  748.         PC = *sc_pad;
  749. #endif
  750.  
  751.     sc_s_keypad = tgetstr("ks", &sp);
  752.     if (sc_s_keypad == NULL)
  753.         sc_s_keypad = "";
  754.     sc_e_keypad = tgetstr("ke", &sp);
  755.     if (sc_e_keypad == NULL)
  756.         sc_e_keypad = "";
  757.         
  758.     sc_init = tgetstr("ti", &sp);
  759.     if (sc_init == NULL)
  760.         sc_init = "";
  761.  
  762.     sc_deinit= tgetstr("te", &sp);
  763.     if (sc_deinit == NULL)
  764.         sc_deinit = "";
  765.  
  766.     sc_eol_clear = tgetstr("ce", &sp);
  767.     if (hard || sc_eol_clear == NULL || *sc_eol_clear == '\0')
  768.     {
  769.         cannot("clear to end of line");
  770.         sc_eol_clear = "";
  771.     }
  772.  
  773.     sc_eos_clear = tgetstr("cd", &sp);
  774.     if (below_mem && 
  775.         (hard || sc_eos_clear == NULL || *sc_eos_clear == '\0'))
  776.     {
  777.         cannot("clear to end of screen");
  778.         sc_eol_clear = "";
  779.     }
  780.  
  781.     sc_clear = tgetstr("cl", &sp);
  782.     if (hard || sc_clear == NULL || *sc_clear == '\0')
  783.     {
  784.         cannot("clear screen");
  785.         sc_clear = "\n\n";
  786.     }
  787.  
  788.     sc_move = tgetstr("cm", &sp);
  789.     if (hard || sc_move == NULL || *sc_move == '\0')
  790.     {
  791.         /*
  792.          * This is not an error here, because we don't 
  793.          * always need sc_move.
  794.          * We need it only if we don't have home or lower-left.
  795.          */
  796.         sc_move = "";
  797.         can_goto_line = 0;
  798.     } else
  799.         can_goto_line = 1;
  800.  
  801.     sc_s_in = tgetstr("so", &sp);
  802.     if (hard || sc_s_in == NULL)
  803.         sc_s_in = "";
  804.  
  805.     sc_s_out = tgetstr("se", &sp);
  806.     if (hard || sc_s_out == NULL)
  807.         sc_s_out = "";
  808.  
  809.     sc_u_in = tgetstr("us", &sp);
  810.     if (hard || sc_u_in == NULL)
  811.         sc_u_in = sc_s_in;
  812.  
  813.     sc_u_out = tgetstr("ue", &sp);
  814.     if (hard || sc_u_out == NULL)
  815.         sc_u_out = sc_s_out;
  816.  
  817.     sc_b_in = tgetstr("md", &sp);
  818.     if (hard || sc_b_in == NULL)
  819.     {
  820.         sc_b_in = sc_s_in;
  821.         sc_b_out = sc_s_out;
  822.     } else
  823.     {
  824.         sc_b_out = tgetstr("me", &sp);
  825.         if (hard || sc_b_out == NULL)
  826.             sc_b_out = "";
  827.     }
  828.  
  829.     sc_bl_in = tgetstr("mb", &sp);
  830.     if (hard || sc_bl_in == NULL)
  831.     {
  832.         sc_bl_in = sc_s_in;
  833.         sc_bl_out = sc_s_out;
  834.     } else
  835.     {
  836.         sc_bl_out = tgetstr("me", &sp);
  837.         if (hard || sc_bl_out == NULL)
  838.             sc_bl_out = "";
  839.     }
  840.  
  841.     sc_visual_bell = tgetstr("vb", &sp);
  842.     if (hard || sc_visual_bell == NULL)
  843.         sc_visual_bell = "";
  844.  
  845.     if (tgetflag("bs"))
  846.         sc_backspace = "\b";
  847.     else
  848.     {
  849.         sc_backspace = tgetstr("bc", &sp);
  850.         if (sc_backspace == NULL || *sc_backspace == '\0')
  851.             sc_backspace = "\b";
  852.     }
  853.  
  854.     /*
  855.      * Choose between using "ho" and "cm" ("home" and "cursor move")
  856.      * to move the cursor to the upper left corner of the screen.
  857.      */
  858.     t1 = tgetstr("ho", &sp);
  859.     if (hard || t1 == NULL)
  860.         t1 = "";
  861.     if (*sc_move == '\0')
  862.         t2 = "";
  863.     else
  864.     {
  865.         strcpy(sp, tgoto(sc_move, 0, 0));
  866.         t2 = sp;
  867.         sp += strlen(sp) + 1;
  868.     }
  869.     sc_home = cheaper(t1, t2, "home cursor", "|\b^");
  870.  
  871.     /*
  872.      * Choose between using "ll" and "cm"  ("lower left" and "cursor move")
  873.      * to move the cursor to the lower left corner of the screen.
  874.      */
  875.     t1 = tgetstr("ll", &sp);
  876.     if (hard || t1 == NULL)
  877.         t1 = "";
  878.     if (*sc_move == '\0')
  879.         t2 = "";
  880.     else
  881.     {
  882.         strcpy(sp, tgoto(sc_move, 0, sc_height-1));
  883.         t2 = sp;
  884.         sp += strlen(sp) + 1;
  885.     }
  886.     sc_lower_left = cheaper(t1, t2,
  887.         "move cursor to lower left of screen", "\r");
  888.  
  889.     /*
  890.      * Choose between using "al" or "sr" ("add line" or "scroll reverse")
  891.      * to add a line at the top of the screen.
  892.      */
  893.     t1 = tgetstr("al", &sp);
  894.     if (hard || t1 == NULL)
  895.         t1 = "";
  896.     t2 = tgetstr("sr", &sp);
  897.     if (hard || t2 == NULL)
  898.         t2 = "";
  899. #if OS2
  900.     if (*t1 == '\0' && *t2 == '\0')
  901.         sc_addline = "";
  902.     else
  903. #endif
  904.     if (above_mem)
  905.         sc_addline = t1;
  906.     else
  907.         sc_addline = cheaper(t1, t2, "scroll backwards", "");
  908.     if (*sc_addline == '\0')
  909.     {
  910.         /*
  911.          * Force repaint on any backward movement.
  912.          */
  913.         back_scroll = 0;
  914.     }
  915. }
  916.  
  917. /*
  918.  * Return the cost of displaying a termcap string.
  919.  * We use the trick of calling tputs, but as a char printing function
  920.  * we give it inc_costcount, which just increments "costcount".
  921.  * This tells us how many chars would be printed by using this string.
  922.  * {{ Couldn't we just use strlen? }}
  923.  */
  924. static int costcount;
  925.  
  926. /*ARGSUSED*/
  927.     static int
  928. inc_costcount(c)
  929.     int c;
  930. {
  931.     costcount++;
  932.     return (c);
  933. }
  934.  
  935.     static int
  936. cost(t)
  937.     char *t;
  938. {
  939.     costcount = 0;
  940.     tputs(t, sc_height, inc_costcount);
  941.     return (costcount);
  942. }
  943.  
  944. /*
  945.  * Return the "best" of the two given termcap strings.
  946.  * The best, if both exist, is the one with the lower 
  947.  * cost (see cost() function).
  948.  */
  949.     static char *
  950. cheaper(t1, t2, doit, def)
  951.     char *t1, *t2;
  952.     char *doit;
  953.     char *def;
  954. {
  955.     if (*t1 == '\0' && *t2 == '\0')
  956.     {
  957.         cannot(doit);
  958.         return (def);
  959.     }
  960.     if (*t1 == '\0')
  961.         return (t2);
  962.     if (*t2 == '\0')
  963.         return (t1);
  964.     if (cost(t1) < cost(t2))
  965.         return (t1);
  966.     return (t2);
  967. }
  968.  
  969.  
  970. /*
  971.  * Below are the functions which perform all the 
  972.  * terminal-specific screen manipulation.
  973.  */
  974.  
  975.  
  976. /*
  977.  * Initialize terminal
  978.  */
  979.     public void
  980. init()
  981. {
  982.     if (no_init)
  983.         return;
  984.     tputs(sc_init, sc_height, putchr);
  985.     tputs(sc_s_keypad, sc_height, putchr);
  986.     init_done = 1;
  987. }
  988.  
  989. /*
  990.  * Deinitialize terminal
  991.  */
  992.     public void
  993. deinit()
  994. {
  995.     if (no_init)
  996.         return;
  997.     if (!init_done)
  998.         return;
  999.     tputs(sc_e_keypad, sc_height, putchr);
  1000.     tputs(sc_deinit, sc_height, putchr);
  1001.     init_done = 0;
  1002. }
  1003.  
  1004. /*
  1005.  * Home cursor (move to upper left corner of screen).
  1006.  */
  1007.     public void
  1008. home()
  1009. {
  1010.     tputs(sc_home, 1, putchr);
  1011. }
  1012.  
  1013. /*
  1014.  * Add a blank line (called with cursor at home).
  1015.  * Should scroll the display down.
  1016.  */
  1017.     public void
  1018. add_line()
  1019. {
  1020.     tputs(sc_addline, sc_height, putchr);
  1021. }
  1022.  
  1023. /*
  1024.  * Move cursor to lower left corner of screen.
  1025.  */
  1026.     public void
  1027. lower_left()
  1028. {
  1029.     tputs(sc_lower_left, 1, putchr);
  1030. }
  1031.  
  1032. /*
  1033.  * Goto a specific line on the screen.
  1034.  */
  1035.     public void
  1036. goto_line(slinenum)
  1037.     int slinenum;
  1038. {
  1039.     char *sc_goto;
  1040.  
  1041.     sc_goto = tgoto(sc_move, 0, slinenum);
  1042.     tputs(sc_goto, 1, putchr);
  1043. }
  1044.  
  1045. /*
  1046.  * Ring the terminal bell.
  1047.  */
  1048.     public void
  1049. bell()
  1050. {
  1051.     if (quiet == VERY_QUIET)
  1052.         vbell();
  1053.     else
  1054.         putchr('\7');
  1055. }
  1056.  
  1057. /*
  1058.  * Output the "visual bell", if there is one.
  1059.  */
  1060.     public void
  1061. vbell()
  1062. {
  1063.     if (*sc_visual_bell == '\0')
  1064.         return;
  1065.     tputs(sc_visual_bell, sc_height, putchr);
  1066. }
  1067.  
  1068. /*
  1069.  * Clear the screen.
  1070.  */
  1071.     public void
  1072. clear()
  1073. {
  1074.     tputs(sc_clear, sc_height, putchr);
  1075. }
  1076.  
  1077. /*
  1078.  * Clear from the cursor to the end of the cursor's line.
  1079.  * {{ This must not move the cursor. }}
  1080.  */
  1081.     public void
  1082. clear_eol()
  1083. {
  1084.     tputs(sc_eol_clear, 1, putchr);
  1085. }
  1086.  
  1087. /*
  1088.  * Clear the bottom line of the display.
  1089.  * Leave the cursor at the beginning of the bottom line.
  1090.  */
  1091.     public void
  1092. clear_bot()
  1093. {
  1094.     lower_left();
  1095.     if (below_mem)
  1096.         tputs(sc_eos_clear, 1, putchr);
  1097.     else
  1098.         tputs(sc_eol_clear, 1, putchr);
  1099. }
  1100.  
  1101. /*
  1102.  * Begin "standout" (bold, underline, or whatever).
  1103.  */
  1104.     public void
  1105. so_enter()
  1106. {
  1107.     tputs(sc_s_in, 1, putchr);
  1108. }
  1109.  
  1110. /*
  1111.  * End "standout".
  1112.  */
  1113.     public void
  1114. so_exit()
  1115. {
  1116.     tputs(sc_s_out, 1, putchr);
  1117. }
  1118.  
  1119. /*
  1120.  * Begin "underline" (hopefully real underlining, 
  1121.  * otherwise whatever the terminal provides).
  1122.  */
  1123.     public void
  1124. ul_enter()
  1125. {
  1126.     tputs(sc_u_in, 1, putchr);
  1127. }
  1128.  
  1129. /*
  1130.  * End "underline".
  1131.  */
  1132.     public void
  1133. ul_exit()
  1134. {
  1135.     tputs(sc_u_out, 1, putchr);
  1136. }
  1137.  
  1138. /*
  1139.  * Begin "bold"
  1140.  */
  1141.     public void
  1142. bo_enter()
  1143. {
  1144.     tputs(sc_b_in, 1, putchr);
  1145. }
  1146.  
  1147. /*
  1148.  * End "bold".
  1149.  */
  1150.     public void
  1151. bo_exit()
  1152. {
  1153.     tputs(sc_b_out, 1, putchr);
  1154. }
  1155.  
  1156. /*
  1157.  * Begin "blink"
  1158.  */
  1159.     public void
  1160. bl_enter()
  1161. {
  1162.     tputs(sc_bl_in, 1, putchr);
  1163. }
  1164.  
  1165. /*
  1166.  * End "blink".
  1167.  */
  1168.     public void
  1169. bl_exit()
  1170. {
  1171.     tputs(sc_bl_out, 1, putchr);
  1172. }
  1173.  
  1174. /*
  1175.  * Erase the character to the left of the cursor 
  1176.  * and move the cursor left.
  1177.  */
  1178.     public void
  1179. backspace()
  1180. {
  1181.     /* 
  1182.      * Try to erase the previous character by overstriking with a space.
  1183.      */
  1184.     tputs(sc_backspace, 1, putchr);
  1185.     putchr(' ');
  1186.     tputs(sc_backspace, 1, putchr);
  1187. }
  1188.  
  1189. /*
  1190.  * Output a plain backspace, without erasing the previous char.
  1191.  */
  1192.     public void
  1193. putbs()
  1194. {
  1195.     tputs(sc_backspace, 1, putchr);
  1196. }
  1197.