home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / less-321-src.tgz / tar.out / fsf / less / screen.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  40KB  |  2,044 lines

  1. /*
  2.  * Copyright (c) 1984,1985,1989,1994,1995,1996  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.  
  33. #include "less.h"
  34. #include "cmd.h"
  35.  
  36. #if MSDOS_COMPILER
  37. #if MSDOS_COMPILER==MSOFTC
  38. #include <graph.h>
  39. #else
  40. #if MSDOS_COMPILER==BORLANDC
  41. #include <conio.h>
  42. #else
  43. #if MSDOS_COMPILER==WIN32C
  44. #include <windows.h>
  45. #endif
  46. #endif
  47. #endif
  48. #include <time.h>
  49.  
  50. #else
  51.  
  52. #if HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS
  53. #include <termios.h>
  54. #if HAVE_SYS_IOCTL_H && !defined(TIOCGWINSZ)
  55. #include <sys/ioctl.h>
  56. #endif
  57. #else
  58. #if HAVE_TERMIO_H
  59. #include <termio.h>
  60. #else
  61. #if HAVE_SGSTAT_H
  62. #include <sgstat.h>
  63. #else
  64. #include <sgtty.h>
  65. #endif
  66. #if HAVE_SYS_IOCTL_H && (defined(TIOCGWINSZ) || defined(TCGETA) || defined(TIOCGETP) || defined(WIOCGETD))
  67. #include <sys/ioctl.h>
  68. #endif
  69. #endif
  70. #endif
  71.  
  72. #if HAVE_TERMCAP_H
  73. #include <termcap.h>
  74. #endif
  75. #ifdef _OSK
  76. #include <signal.h>
  77. #endif
  78. #if OS2
  79. #include <sys/signal.h>
  80. #endif
  81. #if HAVE_SYS_WINDOW_H
  82. #include <sys/window.h>
  83. #endif
  84. #if HAVE_SYS_STREAM_H
  85. #include <sys/stream.h>
  86. #endif
  87. #if HAVE_SYS_PTEM_H
  88. #include <sys/ptem.h>
  89. #endif
  90.  
  91. #endif /* MSDOS_COMPILER */
  92.  
  93. /*
  94.  * Check for broken termios package that forces you to manually
  95.  * set the line discipline.
  96.  */
  97. #ifdef __ultrix__
  98. #define MUST_SET_LINE_DISCIPLINE 1
  99. #else
  100. #define MUST_SET_LINE_DISCIPLINE 0
  101. #endif
  102.  
  103. #if OS2
  104. #define    DEFAULT_TERM        "ansi"
  105. #else
  106. #define    DEFAULT_TERM        "unknown"
  107. #endif
  108.  
  109. #if MSDOS_COMPILER==MSOFTC
  110. static int videopages;
  111. static long msec_loops;
  112. #define    SETCOLORS(fg,bg)    _settextcolor(fg); _setbkcolor(bg);
  113. #endif
  114.  
  115. #if MSDOS_COMPILER==BORLANDC
  116. static unsigned short *whitescreen;
  117. #define _settextposition(y,x)   gotoxy(x,y)
  118. #define _clearscreen(m)         clrscr()
  119. #define _outtext(s)             cputs(s)
  120. #define    SETCOLORS(fg,bg)    textcolor(fg); textbackground(bg);
  121. #endif
  122.  
  123. #if MSDOS_COMPILER==WIN32C
  124. struct keyRecord
  125. {
  126.     int ascii;
  127.     int scan;
  128. } currentKey;
  129.  
  130. static int keyCount = 0;
  131. static WORD curr_attr;
  132. static int pending_scancode = 0;
  133. static WORD *whitescreen;
  134. static constant COORD TOPLEFT = {0, 0};
  135. #define FG_COLORS       (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY)
  136. #define BG_COLORS       (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY)
  137. HANDLE con_out;
  138. #define    MAKEATTR(fg,bg)        ((WORD)((fg)|((bg)<<4)))
  139. #define    SETCOLORS(fg,bg)    curr_attr = MAKEATTR(fg,bg); \
  140.                 SetConsoleTextAttribute(con_out, curr_attr);
  141. #endif
  142.  
  143. #if MSDOS_COMPILER
  144. public int nm_fg_color;        /* Color of normal text */
  145. public int nm_bg_color;
  146. public int bo_fg_color;        /* Color of bold text */
  147. public int bo_bg_color;
  148. public int ul_fg_color;        /* Color of underlined text */
  149. public int ul_bg_color;
  150. public int so_fg_color;        /* Color of standout text */
  151. public int so_bg_color;
  152. public int bl_fg_color;        /* Color of blinking text */
  153. public int bl_bg_color;
  154. static int sy_fg_color;        /* Color of system text (before less) */
  155. static int sy_bg_color;
  156.  
  157. static int flash_created = 0;
  158.  
  159. #else
  160.  
  161. /*
  162.  * Strings passed to tputs() to do various terminal functions.
  163.  */
  164. static char
  165.     *sc_pad,        /* Pad string */
  166.     *sc_home,        /* Cursor home */
  167.     *sc_addline,        /* Add line, scroll down following lines */
  168.     *sc_lower_left,        /* Cursor to last line, first column */
  169.     *sc_move,        /* General cursor positioning */
  170.     *sc_clear,        /* Clear screen */
  171.     *sc_eol_clear,        /* Clear to end of line */
  172.     *sc_eos_clear,        /* Clear to end of screen */
  173.     *sc_s_in,        /* Enter standout (highlighted) mode */
  174.     *sc_s_out,        /* Exit standout mode */
  175.     *sc_u_in,        /* Enter underline mode */
  176.     *sc_u_out,        /* Exit underline mode */
  177.     *sc_b_in,        /* Enter bold mode */
  178.     *sc_b_out,        /* Exit bold mode */
  179.     *sc_bl_in,        /* Enter blink mode */
  180.     *sc_bl_out,        /* Exit blink mode */
  181.     *sc_visual_bell,    /* Visual bell (flash screen) sequence */
  182.     *sc_backspace,        /* Backspace cursor */
  183.     *sc_s_keypad,        /* Start keypad mode */
  184.     *sc_e_keypad,        /* End keypad mode */
  185.     *sc_init,        /* Startup terminal initialization */
  186.     *sc_deinit;        /* Exit terminal de-initialization */
  187. #endif
  188.  
  189. static int init_done = 0;
  190.  
  191. public int auto_wrap;        /* Terminal does \r\n when write past margin */
  192. public int ignaw;        /* Terminal ignores \n immediately after wrap */
  193. public int erase_char, kill_char; /* The user's erase and line-kill chars */
  194. public int werase_char;        /* The user's word-erase char */
  195. public int sc_width, sc_height;    /* Height & width of screen */
  196. public int bo_s_width, bo_e_width;    /* Printing width of boldface seq */
  197. public int ul_s_width, ul_e_width;    /* Printing width of underline seq */
  198. public int so_s_width, so_e_width;    /* Printing width of standout seq */
  199. public int bl_s_width, bl_e_width;    /* Printing width of blink seq */
  200. public int above_mem, below_mem;    /* Memory retained above/below screen */
  201. public int can_goto_line;        /* Can move cursor to any line */
  202. public int missing_cap = 0;    /* Some capability is missing */
  203.  
  204. static char *cheaper();
  205. static void tmodes();
  206.  
  207. /*
  208.  * These two variables are sometimes defined in,
  209.  * and needed by, the termcap library.
  210.  */
  211. #if MUST_DEFINE_OSPEED
  212. extern short ospeed;    /* Terminal output baud rate */
  213. extern char PC;        /* Pad character */
  214. #endif
  215. #ifdef _OSK
  216. short ospeed;
  217. char PC_, *UP, *BC;
  218. #endif
  219.  
  220. extern int quiet;        /* If VERY_QUIET, use visual bell for bell */
  221. extern int no_back_scroll;
  222. extern int swindow;
  223. extern int no_init;
  224. #if HILITE_SEARCH
  225. extern int hilite_search;
  226. #endif
  227.  
  228. extern char *tgetstr();
  229. extern char *tgoto();
  230.  
  231.  
  232. /*
  233.  * Change terminal to "raw mode", or restore to "normal" mode.
  234.  * "Raw mode" means 
  235.  *    1. An outstanding read will complete on receipt of a single keystroke.
  236.  *    2. Input is not echoed.  
  237.  *    3. On output, \n is mapped to \r\n.
  238.  *    4. \t is NOT expanded into spaces.
  239.  *    5. Signal-causing characters such as ctrl-C (interrupt),
  240.  *       etc. are NOT disabled.
  241.  * It doesn't matter whether an input \n is mapped to \r, or vice versa.
  242.  */
  243.     public void
  244. raw_mode(on)
  245.     int on;
  246. {
  247.     static int curr_on = 0;
  248.  
  249.     if (on == curr_on)
  250.         return;
  251. #if HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS
  252.     {
  253.     struct termios s;
  254.     static struct termios save_term;
  255.  
  256.     if (on) 
  257.     {
  258.         /*
  259.          * Get terminal modes.
  260.          */
  261.         tcgetattr(2, &s);
  262.  
  263.         /*
  264.          * Save modes and set certain variables dependent on modes.
  265.          */
  266.         save_term = s;
  267. #if HAVE_OSPEED
  268.         switch (cfgetospeed(&s))
  269.         {
  270. #ifdef B0
  271.         case B0: ospeed = 0; break;
  272. #endif
  273. #ifdef B50
  274.         case B50: ospeed = 1; break;
  275. #endif
  276. #ifdef B75
  277.         case B75: ospeed = 2; break;
  278. #endif
  279. #ifdef B110
  280.         case B110: ospeed = 3; break;
  281. #endif
  282. #ifdef B134
  283.         case B134: ospeed = 4; break;
  284. #endif
  285. #ifdef B150
  286.         case B150: ospeed = 5; break;
  287. #endif
  288. #ifdef B200
  289.         case B200: ospeed = 6; break;
  290. #endif
  291. #ifdef B300
  292.         case B300: ospeed = 7; break;
  293. #endif
  294. #ifdef B600
  295.         case B600: ospeed = 8; break;
  296. #endif
  297. #ifdef B1200
  298.         case B1200: ospeed = 9; break;
  299. #endif
  300. #ifdef B1800
  301.         case B1800: ospeed = 10; break;
  302. #endif
  303. #ifdef B2400
  304.         case B2400: ospeed = 11; break;
  305. #endif
  306. #ifdef B4800
  307.         case B4800: ospeed = 12; break;
  308. #endif
  309. #ifdef B9600
  310.         case B9600: ospeed = 13; break;
  311. #endif
  312. #ifdef EXTA
  313.         case EXTA: ospeed = 14; break;
  314. #endif
  315. #ifdef EXTB
  316.         case EXTB: ospeed = 15; break;
  317. #endif
  318. #ifdef B57600
  319.         case B57600: ospeed = 16; break;
  320. #endif
  321. #ifdef B115200
  322.         case B115200: ospeed = 17; break;
  323. #endif
  324.         default: ;
  325.         }
  326. #endif
  327.         erase_char = s.c_cc[VERASE];
  328.         kill_char = s.c_cc[VKILL];
  329. #ifdef VWERASE
  330.         werase_char = s.c_cc[VWERASE];
  331. #else
  332.         werase_char = CONTROL('W');
  333. #endif
  334.  
  335.         /*
  336.          * Set the modes to the way we want them.
  337.          */
  338.         s.c_lflag &= ~(0
  339. #ifdef ICANON
  340.             | ICANON
  341. #endif
  342. #ifdef ECHO
  343.             | ECHO
  344. #endif
  345. #ifdef ECHOE
  346.             | ECHOE
  347. #endif
  348. #ifdef ECHOK
  349.             | ECHOK
  350. #endif
  351. #if ECHONL
  352.             | ECHONL
  353. #endif
  354.         );
  355.  
  356.         s.c_oflag |= (0
  357. #ifdef XTABS
  358.             | XTABS
  359. #else
  360. #ifdef TAB3
  361.             | TAB3
  362. #else
  363. #ifdef OXTABS
  364.             | OXTABS
  365. #endif
  366. #endif
  367. #endif
  368. #ifdef OPOST
  369.             | OPOST
  370. #endif
  371. #ifdef ONLCR
  372.             | ONLCR
  373. #endif
  374.         );
  375.  
  376.         s.c_oflag &= ~(0
  377. #ifdef ONOEOT
  378.             | ONOEOT
  379. #endif
  380. #ifdef OCRNL
  381.             | OCRNL
  382. #endif
  383. #ifdef ONOCR
  384.             | ONOCR
  385. #endif
  386. #ifdef ONLRET
  387.             | ONLRET
  388. #endif
  389.         );
  390.         s.c_cc[VMIN] = 1;
  391.         s.c_cc[VTIME] = 0;
  392. #ifdef VLNEXT
  393.         s.c_cc[VLNEXT] = 0;
  394. #endif
  395. #ifdef VDSUSP
  396.         s.c_cc[VDSUSP] = 0;
  397. #endif
  398. #if MUST_SET_LINE_DISCIPLINE
  399.         /*
  400.          * System's termios is broken; need to explicitly 
  401.          * request TERMIODISC line discipline.
  402.          */
  403.         s.c_line = TERMIODISC;
  404. #endif
  405.     } else
  406.     {
  407.         /*
  408.          * Restore saved modes.
  409.          */
  410.         s = save_term;
  411.     }
  412.     tcsetattr(2, TCSADRAIN, &s);
  413. #if MUST_SET_LINE_DISCIPLINE
  414.     if (!on)
  415.     {
  416.         /*
  417.          * Broken termios *ignores* any line discipline
  418.          * except TERMIODISC.  A different old line discipline
  419.          * is therefore not restored, yet.  Restore the old
  420.          * line discipline by hand.
  421.          */
  422.         ioctl(2, TIOCSETD, &save_term.c_line);
  423.     }
  424. #endif
  425.     }
  426. #else
  427. #ifdef TCGETA
  428.     {
  429.     struct termio s;
  430.     static struct termio save_term;
  431.  
  432.     if (on)
  433.     {
  434.         /*
  435.          * Get terminal modes.
  436.          */
  437.         ioctl(2, TCGETA, &s);
  438.  
  439.         /*
  440.          * Save modes and set certain variables dependent on modes.
  441.          */
  442.         save_term = s;
  443. #if HAVE_OSPEED
  444.         ospeed = s.c_cflag & CBAUD;
  445. #endif
  446.         erase_char = s.c_cc[VERASE];
  447.         kill_char = s.c_cc[VKILL];
  448. #ifdef VWERASE
  449.         werase_char = s.c_cc[VWERASE];
  450. #else
  451.         werase_char = CONTROL('W');
  452. #endif
  453.  
  454.         /*
  455.          * Set the modes to the way we want them.
  456.          */
  457.         s.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL);
  458.         s.c_oflag |=  (OPOST|ONLCR|TAB3);
  459.         s.c_oflag &= ~(OCRNL|ONOCR|ONLRET);
  460.         s.c_cc[VMIN] = 1;
  461.         s.c_cc[VTIME] = 0;
  462.     } else
  463.     {
  464.         /*
  465.          * Restore saved modes.
  466.          */
  467.         s = save_term;
  468.     }
  469.     ioctl(2, TCSETAW, &s);
  470.     }
  471. #else
  472. #ifdef TIOCGETP
  473.     {
  474.     struct sgttyb s;
  475.     static struct sgttyb save_term;
  476.  
  477.     if (on)
  478.     {
  479.         /*
  480.          * Get terminal modes.
  481.          */
  482.         ioctl(2, TIOCGETP, &s);
  483.  
  484.         /*
  485.          * Save modes and set certain variables dependent on modes.
  486.          */
  487.         save_term = s;
  488. #if HAVE_OSPEED
  489.         ospeed = s.sg_ospeed;
  490. #endif
  491.         erase_char = s.sg_erase;
  492.         kill_char = s.sg_kill;
  493.         werase_char = CONTROL('W');
  494.  
  495.         /*
  496.          * Set the modes to the way we want them.
  497.          */
  498.         s.sg_flags |= CBREAK;
  499.         s.sg_flags &= ~(ECHO|XTABS);
  500.     } else
  501.     {
  502.         /*
  503.          * Restore saved modes.
  504.          */
  505.         s = save_term;
  506.     }
  507.     ioctl(2, TIOCSETN, &s);
  508.     }
  509. #else
  510. #ifdef _OSK
  511.     {
  512.     struct sgbuf s;
  513.     static struct sgbuf save_term;
  514.  
  515.     if (on)
  516.     {
  517.         /*
  518.          * Get terminal modes.
  519.          */
  520.         _gs_opt(2, &s);
  521.  
  522.         /*
  523.          * Save modes and set certain variables dependent on modes.
  524.          */
  525.         save_term = s;
  526.         erase_char = s.sg_bspch;
  527.         kill_char = s.sg_dlnch;
  528.         werase_char = CONTROL('W');
  529.  
  530.         /*
  531.          * Set the modes to the way we want them.
  532.          */
  533.         s.sg_echo = 0;
  534.         s.sg_eofch = 0;
  535.         s.sg_pause = 0;
  536.         s.sg_psch = 0;
  537.     } else
  538.     {
  539.         /*
  540.          * Restore saved modes.
  541.          */
  542.         s = save_term;
  543.     }
  544.     _ss_opt(2, &s);
  545.     }
  546. #else
  547.     /* MS-DOS, Windows, or OS2 */
  548. #if OS2
  549.     /* OS2 */
  550.     LSIGNAL(SIGINT, SIG_IGN);
  551. #endif
  552.     erase_char = '\b';
  553.     kill_char = '\033'; /* ESC */
  554.     werase_char = CONTROL('W');
  555. #endif
  556. #endif
  557. #endif
  558. #endif
  559.     curr_on = on;
  560. }
  561.  
  562. #if !MSDOS_COMPILER
  563. /*
  564.  * Some glue to prevent calling termcap functions if tgetent() failed.
  565.  */
  566. static int hardcopy;
  567.  
  568.     static int
  569. ltgetflag(capname)
  570.     char *capname;
  571. {
  572.     if (hardcopy)
  573.         return (0);
  574.     return (tgetflag(capname));
  575. }
  576.  
  577.     static int
  578. ltgetnum(capname)
  579.     char *capname;
  580. {
  581.     if (hardcopy)
  582.         return (-1);
  583.     return (tgetnum(capname));
  584. }
  585.  
  586.     static char *
  587. ltgetstr(capname, pp)
  588.     char *capname;
  589.     char **pp;
  590. {
  591.     if (hardcopy)
  592.         return (NULL);
  593.     return (tgetstr(capname, pp));
  594. }
  595. #endif /* MSDOS_COMPILER */
  596.  
  597. /*
  598.  * Get size of the output screen.
  599.  */
  600.     public void
  601. scrsize()
  602. {
  603.     register char *s;
  604.  
  605.     sc_width = sc_height = 0;
  606.  
  607. #if MSDOS_COMPILER==MSOFTC
  608.     {
  609.         struct videoconfig w;
  610.         _getvideoconfig(&w);
  611.         sc_height = w.numtextrows;
  612.         sc_width = w.numtextcols;
  613.     }
  614. #else
  615. #if MSDOS_COMPILER==BORLANDC
  616.     {
  617.         struct text_info w;
  618.         gettextinfo(&w);
  619.         sc_height = w.screenheight;
  620.         sc_width = w.screenwidth;
  621.     }
  622. #else
  623. #if MSDOS_COMPILER==WIN32C
  624.     {
  625.         CONSOLE_SCREEN_BUFFER_INFO scr;
  626.         GetConsoleScreenBufferInfo(con_out, &scr);
  627.         sc_height = scr.srWindow.Bottom - scr.srWindow.Top + 1;
  628.         sc_width = scr.srWindow.Right - scr.srWindow.Left + 1;
  629.     }
  630. #else
  631. #if OS2
  632.     {
  633.         int s[2];
  634.         _scrsize(s);
  635.         sc_width = s[0];
  636.         sc_height = s[1];
  637.     }
  638. #else
  639. #ifdef TIOCGWINSZ
  640.     {
  641.         struct winsize w;
  642.         if (ioctl(2, TIOCGWINSZ, &w) == 0)
  643.         {
  644.             if (w.ws_row > 0)
  645.                 sc_height = w.ws_row;
  646.             if (w.ws_col > 0)
  647.                 sc_width = w.ws_col;
  648.         }
  649.     }
  650. #else
  651. #ifdef WIOCGETD
  652.     {
  653.         struct uwdata w;
  654.         if (ioctl(2, WIOCGETD, &w) == 0)
  655.         {
  656.             if (w.uw_height > 0)
  657.                 sc_height = w.uw_height / w.uw_vs;
  658.             if (w.uw_width > 0)
  659.                 sc_width = w.uw_width / w.uw_hs;
  660.         }
  661.     }
  662. #endif
  663. #endif
  664. #endif
  665. #endif
  666. #endif
  667. #endif
  668.     if (sc_height > 0)
  669.         ;
  670.     else if ((s = lgetenv("LINES")) != NULL)
  671.         sc_height = atoi(s);
  672. #if !MSDOS_COMPILER
  673.     else
  674.          sc_height = ltgetnum("li");
  675. #endif
  676.     if (sc_height <= 0)
  677.         sc_height = 24;
  678.  
  679.     if (sc_width > 0)
  680.         ;
  681.     else if ((s = lgetenv("COLUMNS")) != NULL)
  682.         sc_width = atoi(s);
  683. #if !MSDOS_COMPILER
  684.     else
  685.          sc_width = ltgetnum("co");
  686. #endif
  687.      if (sc_width <= 0)
  688.           sc_width = 80;
  689. }
  690.  
  691. #if MSDOS_COMPILER==MSOFTC
  692. /*
  693.  * Figure out how many empty loops it takes to delay a millisecond.
  694.  */
  695.     static void
  696. get_clock()
  697. {
  698.     clock_t start;
  699.     
  700.     /*
  701.      * Get synchronized at the start of a tick.
  702.      */
  703.     start = clock();
  704.     while (clock() == start)
  705.         ;
  706.     /*
  707.      * Now count loops till the next tick.
  708.      */
  709.     start = clock();
  710.     msec_loops = 0;
  711.     while (clock() == start)
  712.         msec_loops++;
  713.     /*
  714.      * Convert from (loops per clock) to (loops per millisecond).
  715.      */
  716.     msec_loops *= CLOCKS_PER_SEC;
  717.     msec_loops /= 1000;
  718. }
  719.  
  720. /*
  721.  * Delay for a specified number of milliseconds.
  722.  */
  723.     static void
  724. dummy_func()
  725. {
  726.     static long delay_dummy = 0;
  727.     delay_dummy++;
  728. }
  729.  
  730.     static void
  731. delay(msec)
  732.     int msec;
  733. {
  734.     long i;
  735.     
  736.     while (msec-- > 0)
  737.     {
  738.         for (i = 0;  i < msec_loops;  i++)
  739.         {
  740.             /*
  741.              * Make it look like we're doing something here,
  742.              * so the optimizer doesn't remove the whole loop.
  743.              */
  744.             dummy_func();
  745.         }
  746.     }
  747. }
  748. #endif
  749.  
  750. /*
  751.  * Take care of the "variable" keys.
  752.  * Certain keys send escape sequences which differ on different terminals
  753.  * (such as the arrow keys, INSERT, DELETE, etc.)
  754.  * Construct the commands based on these keys.
  755.  */
  756.     public void
  757. get_editkeys()
  758. {
  759. #if MSDOS_COMPILER
  760. /*
  761.  * Table of line editting characters, for editchar() in decode.c.
  762.  */
  763. static char kecmdtable[] = {
  764.     '\340','\115',0,    EC_RIGHT,    /* RIGHTARROW */
  765.     '\340','\113',0,    EC_LEFT,    /* LEFTARROW */
  766.     '\340','\163',0,    EC_W_LEFT,    /* CTRL-LEFTARROW */
  767.     '\340','\164',0,    EC_W_RIGHT,    /* CTRL-RIGHTARROW */
  768.     '\340','\122',0,    EC_INSERT,    /* INSERT */
  769.     '\340','\123',0,    EC_DELETE,    /* DELETE */
  770.     '\340','\223',0,    EC_W_DELETE,    /* CTRL-DELETE */
  771.     '\177',0,        EC_W_BACKSPACE,    /* CTRL-BACKSPACE */
  772.     '\340','\107',0,    EC_HOME,    /* HOME */
  773.     '\340','\117',0,    EC_END,        /* END */
  774.     '\340','\110',0,    EC_UP,        /* UPARROW */
  775.     '\340','\120',0,    EC_DOWN,    /* DOWNARROW */
  776.     '\t',0,            EC_F_COMPLETE,    /* TAB */
  777.     '\17',0,        EC_B_COMPLETE,    /* BACKTAB (?) */
  778.     '\340','\17',0,        EC_B_COMPLETE,    /* BACKTAB */
  779.     '\14',0,        EC_EXPAND,    /* CTRL-L */
  780.     0  /* Extra byte to terminate; subtracted from size, below */
  781. };
  782. static int sz_kecmdtable = sizeof(kecmdtable) -1;
  783.  
  784. static char kfcmdtable[] =
  785. {
  786.     /*
  787.      * PC function keys.
  788.      * Note that '\0' is converted to '\340' on input.
  789.      */
  790.     '\340','\120',0,        A_F_LINE,        /* down arrow */
  791.     '\340','\121',0,        A_F_SCREEN,        /* page down */
  792.     '\340','\110',0,        A_B_LINE,        /* up arrow */
  793.     '\340','\111',0,        A_B_SCREEN,        /* page up */
  794.     '\340','\107',0,        A_GOLINE,        /* home */
  795.     '\340','\117',0,        A_GOEND,        /* end */
  796.     '\340','\073',0,        A_HELP,            /* F1 */
  797.     '\340','\022',0,        A_EXAMINE,        /* Alt-E */
  798.     0
  799. };
  800. static int sz_kfcmdtable = sizeof(kfcmdtable) - 1;
  801. #else
  802.     char *sp;
  803.     char *s;
  804.     char tbuf[40];
  805.  
  806.     static char kfcmdtable[400];
  807.     int sz_kfcmdtable = 0;
  808.     static char kecmdtable[400];
  809.     int sz_kecmdtable = 0;
  810.  
  811. #define    put_cmd(str,action,tbl,sz) { \
  812.     strcpy(tbl+sz, str);    \
  813.     sz += strlen(str) + 1;    \
  814.     tbl[sz++] = action; }
  815. #define    put_esc_cmd(str,action,tbl,sz) { \
  816.     tbl[sz++] = ESC; \
  817.     put_cmd(str,action,tbl,sz); }
  818.  
  819. #define    put_fcmd(str,action)    put_cmd(str,action,kfcmdtable,sz_kfcmdtable)
  820. #define    put_ecmd(str,action)    put_cmd(str,action,kecmdtable,sz_kecmdtable)
  821. #define    put_esc_fcmd(str,action) put_esc_cmd(str,action,kfcmdtable,sz_kfcmdtable)
  822. #define    put_esc_ecmd(str,action) put_esc_cmd(str,action,kecmdtable,sz_kecmdtable)
  823.  
  824.     /*
  825.      * Look at some interesting keys and see what strings they send.
  826.      * Create commands (both command keys and line-edit keys).
  827.      */
  828.  
  829.     /* RIGHT ARROW */
  830.     sp = tbuf;
  831.     if ((s = ltgetstr("kr", &sp)) != NULL)
  832.     {
  833.         put_ecmd(s, EC_RIGHT);
  834.         put_esc_ecmd(s, EC_W_RIGHT);
  835.     }
  836.     
  837.     /* LEFT ARROW */
  838.     sp = tbuf;
  839.     if ((s = ltgetstr("kl", &sp)) != NULL)
  840.     {
  841.         put_ecmd(s, EC_LEFT);
  842.         put_esc_ecmd(s, EC_W_LEFT);
  843.     }
  844.     
  845.     /* UP ARROW */
  846.     sp = tbuf;
  847.     if ((s = ltgetstr("ku", &sp)) != NULL) 
  848.     {
  849.         put_ecmd(s, EC_UP);
  850.         put_fcmd(s, A_B_LINE);
  851.     }
  852.         
  853.     /* DOWN ARROW */
  854.     sp = tbuf;
  855.     if ((s = ltgetstr("kd", &sp)) != NULL) 
  856.     {
  857.         put_ecmd(s, EC_DOWN);
  858.         put_fcmd(s, A_F_LINE);
  859.     }
  860.  
  861.     /* PAGE UP */
  862.     sp = tbuf;
  863.     if ((s = ltgetstr("kP", &sp)) != NULL) 
  864.     {
  865.         put_fcmd(s, A_B_SCREEN);
  866.     }
  867.  
  868.     /* PAGE DOWN */
  869.     sp = tbuf;
  870.     if ((s = ltgetstr("kN", &sp)) != NULL) 
  871.     {
  872.         put_fcmd(s, A_F_SCREEN);
  873.     }
  874.     
  875.     /* HOME */
  876.     sp = tbuf;
  877.     if ((s = ltgetstr("kh", &sp)) != NULL) 
  878.     {
  879.         put_ecmd(s, EC_HOME);
  880.     }
  881.  
  882.     /* END */
  883.     sp = tbuf;
  884.     if ((s = ltgetstr("@7", &sp)) != NULL) 
  885.     {
  886.         put_ecmd(s, EC_END);
  887.     }
  888.  
  889.     /* DELETE */
  890.     sp = tbuf;
  891.     if ((s = ltgetstr("kD", &sp)) == NULL) 
  892.     {
  893.         /* Use DEL (\177) if no "kD" termcap. */
  894.         tbuf[1] = '\177';
  895.         tbuf[2] = '\0';
  896.         s = tbuf+1;
  897.     }
  898.     put_ecmd(s, EC_DELETE);
  899.     put_esc_ecmd(s, EC_W_DELETE);
  900.         
  901.     /* BACKSPACE */
  902.     tbuf[0] = ESC;
  903.     tbuf[1] = erase_char;
  904.     tbuf[2] = '\0';
  905.     put_ecmd(tbuf, EC_W_BACKSPACE);
  906.  
  907.     if (werase_char != 0)
  908.     {
  909.         tbuf[0] = werase_char;
  910.         tbuf[1] = '\0';
  911.         put_ecmd(tbuf, EC_W_BACKSPACE);
  912.     }
  913. #endif /* MSDOS_COMPILER */
  914.  
  915.     /*
  916.      * Register the two tables.
  917.      */
  918.     add_fcmd_table(kfcmdtable, sz_kfcmdtable);
  919.     add_ecmd_table(kecmdtable, sz_kecmdtable);
  920. }
  921.  
  922. #if DEBUG
  923.     static void
  924. get_debug_term()
  925. {
  926.     auto_wrap = 1;
  927.     ignaw = 1;
  928.     so_s_width = so_e_width = 0;
  929.     bo_s_width = bo_e_width = 0;
  930.     ul_s_width = ul_e_width = 0;
  931.     bl_s_width = bl_e_width = 0;
  932.     sc_s_keypad =    "(InitKey)";
  933.     sc_e_keypad =    "(DeinitKey)";
  934.     sc_init =    "(InitTerm)";
  935.     sc_deinit =    "(DeinitTerm)";
  936.     sc_eol_clear =    "(ClearEOL)";
  937.     sc_eos_clear =    "(ClearEOS)";
  938.     sc_clear =    "(ClearScreen)";
  939.     sc_move =    "(Move<%d,%d>)";
  940.     sc_s_in =    "(SO+)";
  941.     sc_s_out =    "(SO-)";
  942.     sc_u_in =    "(UL+)";
  943.     sc_u_out =    "(UL-)";
  944.     sc_b_in =    "(BO+)";
  945.     sc_b_out =    "(BO-)";
  946.     sc_bl_in =    "(BL+)";
  947.     sc_bl_out =    "(BL-)";
  948.     sc_visual_bell ="(VBell)";
  949.     sc_backspace =    "(BS)";
  950.     sc_home =    "(Home)";
  951.     sc_lower_left =    "(LL)";
  952.     sc_addline =    "(AddLine)";
  953. }
  954. #endif
  955.  
  956. /*
  957.  * Get terminal capabilities via termcap.
  958.  */
  959.     public void
  960. get_term()
  961. {
  962. #if MSDOS_COMPILER
  963.     auto_wrap = 1;
  964.     ignaw = 0;
  965.     can_goto_line = 1;
  966.     /*
  967.      * Set up default colors.
  968.      * The xx_s_width and xx_e_width vars are already initialized to 0.
  969.      */
  970.     nm_fg_color = 7;
  971.     nm_bg_color = 0;
  972.     bo_fg_color = 15;
  973.     bo_bg_color = 0;
  974.     ul_fg_color = 9;
  975.     ul_bg_color = 0;
  976.     so_fg_color = 0;
  977.     so_bg_color = 7;
  978.     bl_fg_color = 12;
  979.     bl_bg_color = 0;
  980. #if MSDOS_COMPILER==MSOFTC
  981.     sy_bg_color = _getbkcolor();
  982.     sy_fg_color = _gettextcolor();
  983.     get_clock();
  984. #else
  985. #if MSDOS_COMPILER==BORLANDC
  986.     {
  987.     struct text_info w;
  988.     gettextinfo(&w);
  989.     sy_bg_color = (w.attribute >> 4) & 0x0F;
  990.     sy_fg_color = (w.attribute >> 0) & 0x0F;
  991.     }
  992. #else
  993. #if MSDOS_COMPILER==WIN32C
  994.     {
  995.     DWORD nread;
  996.     CONSOLE_SCREEN_BUFFER_INFO scr;
  997.  
  998.     con_out = GetStdHandle(STD_OUTPUT_HANDLE);
  999.  
  1000.     GetConsoleScreenBufferInfo(con_out, &scr);
  1001.     ReadConsoleOutputAttribute(con_out, &curr_attr, 
  1002.                     1, scr.dwCursorPosition, &nread);
  1003.     sy_bg_color = (curr_attr & BG_COLORS) >> 4; /* normalize */
  1004.     sy_fg_color = curr_attr & FG_COLORS;
  1005.  
  1006.     /*
  1007.      * If we use the default bg colors, for some as-yet-undetermined 
  1008.      * reason, when you scroll the text colors get messed up in what 
  1009.      * seems to be a random manner. Portions of text will be in 
  1010.      * nm_bg_color but the remainder of that line (which was fine 
  1011.      * before the scroll) will now appear in sy_bg_color. Too strange!
  1012.      */
  1013.     nm_bg_color = sy_bg_color;
  1014.     bo_bg_color = sy_bg_color;
  1015.     ul_bg_color = sy_bg_color;
  1016.     bl_bg_color = sy_bg_color;
  1017.  
  1018.     /* Make standout = inverse video. */
  1019.     so_fg_color = sy_bg_color;
  1020.     so_bg_color = sy_fg_color;
  1021.     }
  1022. #endif
  1023. #endif
  1024. #endif
  1025.     /*
  1026.      * Get size of the screen.
  1027.      */
  1028.     scrsize();
  1029.     pos_init();
  1030.  
  1031.  
  1032. #else /* !MSDOS_COMPILER */
  1033.  
  1034.     char *sp;
  1035.     register char *t1, *t2;
  1036.     char *term;
  1037.     char termbuf[TERMBUF_SIZE];
  1038.  
  1039.     static char sbuf[TERMSBUF_SIZE];
  1040.  
  1041. #if OS2
  1042.     /*
  1043.      * Make sure the termcap database is available.
  1044.      */
  1045.     sp = lgetenv("TERMCAP");
  1046.     if (sp == NULL || *sp == '\0')
  1047.     {
  1048.         char *termcap;
  1049.         if ((sp = homefile("termcap.dat")) != NULL)
  1050.         {
  1051.             termcap = (char *) ecalloc(strlen(sp)+9, sizeof(char));
  1052.             sprintf(termcap, "TERMCAP=%s", sp);
  1053.             free(sp);
  1054.             putenv(termcap);
  1055.         }
  1056.     }
  1057. #endif
  1058.     /*
  1059.      * Find out what kind of terminal this is.
  1060.      */
  1061.      if ((term = lgetenv("TERM")) == NULL)
  1062.          term = DEFAULT_TERM;
  1063.     hardcopy = 0;
  1064.      if (tgetent(termbuf, term) <= 0)
  1065.          hardcopy = 1;
  1066.      if (ltgetflag("hc"))
  1067.         hardcopy = 1;
  1068.  
  1069.     /*
  1070.      * Get size of the screen.
  1071.      */
  1072.     scrsize();
  1073.     pos_init();
  1074.  
  1075. #if DEBUG
  1076.     if (strncmp(term,"LESSDEBUG",9) == 0)
  1077.     {
  1078.         get_debug_term();
  1079.         return;
  1080.     }
  1081. #endif /* DEBUG */
  1082.  
  1083.     auto_wrap = ltgetflag("am");
  1084.     ignaw = ltgetflag("xn");
  1085.     above_mem = ltgetflag("da");
  1086.     below_mem = ltgetflag("db");
  1087.  
  1088.     /*
  1089.      * Assumes termcap variable "sg" is the printing width of:
  1090.      * the standout sequence, the end standout sequence,
  1091.      * the underline sequence, the end underline sequence,
  1092.      * the boldface sequence, and the end boldface sequence.
  1093.      */
  1094.     if ((so_s_width = ltgetnum("sg")) < 0)
  1095.         so_s_width = 0;
  1096.     so_e_width = so_s_width;
  1097.  
  1098.     bo_s_width = bo_e_width = so_s_width;
  1099.     ul_s_width = ul_e_width = so_s_width;
  1100.     bl_s_width = bl_e_width = so_s_width;
  1101.  
  1102. #if HILITE_SEARCH
  1103.     if (so_s_width > 0 || so_e_width > 0)
  1104.         /*
  1105.          * Disable highlighting by default on magic cookie terminals.
  1106.          * Turning on highlighting might change the displayed width
  1107.          * of a line, causing the display to get messed up.
  1108.          * The user can turn it back on with -g, 
  1109.          * but she won't like the results.
  1110.          */
  1111.         hilite_search = 0;
  1112. #endif
  1113.  
  1114.     /*
  1115.      * Get various string-valued capabilities.
  1116.      */
  1117.     sp = sbuf;
  1118.  
  1119. #if HAVE_OSPEED
  1120.     sc_pad = ltgetstr("pc", &sp);
  1121.     if (sc_pad != NULL)
  1122.         PC = *sc_pad;
  1123. #endif
  1124.  
  1125.     sc_s_keypad = ltgetstr("ks", &sp);
  1126.     if (sc_s_keypad == NULL)
  1127.         sc_s_keypad = "";
  1128.     sc_e_keypad = ltgetstr("ke", &sp);
  1129.     if (sc_e_keypad == NULL)
  1130.         sc_e_keypad = "";
  1131.         
  1132.     sc_init = ltgetstr("ti", &sp);
  1133.     if (sc_init == NULL)
  1134.         sc_init = "";
  1135.  
  1136.     sc_deinit= ltgetstr("te", &sp);
  1137.     if (sc_deinit == NULL)
  1138.         sc_deinit = "";
  1139.  
  1140.     sc_eol_clear = ltgetstr("ce", &sp);
  1141.     if (sc_eol_clear == NULL || *sc_eol_clear == '\0')
  1142.     {
  1143.         missing_cap = 1;
  1144.         sc_eol_clear = "";
  1145.     }
  1146.  
  1147.     sc_eos_clear = ltgetstr("cd", &sp);
  1148.     if (below_mem && (sc_eos_clear == NULL || *sc_eos_clear == '\0'))
  1149.     {
  1150.         missing_cap = 1;
  1151.         sc_eol_clear = "";
  1152.     }
  1153.  
  1154.     sc_clear = ltgetstr("cl", &sp);
  1155.     if (sc_clear == NULL || *sc_clear == '\0')
  1156.     {
  1157.         missing_cap = 1;
  1158.         sc_clear = "\n\n";
  1159.     }
  1160.  
  1161.     sc_move = ltgetstr("cm", &sp);
  1162.     if (sc_move == NULL || *sc_move == '\0')
  1163.     {
  1164.         /*
  1165.          * This is not an error here, because we don't 
  1166.          * always need sc_move.
  1167.          * We need it only if we don't have home or lower-left.
  1168.          */
  1169.         sc_move = "";
  1170.         can_goto_line = 0;
  1171.     } else
  1172.         can_goto_line = 1;
  1173.  
  1174.     tmodes("so", "se", &sc_s_in, &sc_s_out, "", "", &sp);
  1175.     tmodes("us", "ue", &sc_u_in, &sc_u_out, sc_s_in, sc_s_out, &sp);
  1176.     tmodes("md", "me", &sc_b_in, &sc_b_out, sc_s_in, sc_s_out, &sp);
  1177.     tmodes("mb", "me", &sc_bl_in, &sc_bl_out, sc_s_in, sc_s_out, &sp);
  1178.  
  1179.     sc_visual_bell = ltgetstr("vb", &sp);
  1180.     if (sc_visual_bell == NULL)
  1181.         sc_visual_bell = "";
  1182.  
  1183.     if (ltgetflag("bs"))
  1184.         sc_backspace = "\b";
  1185.     else
  1186.     {
  1187.         sc_backspace = ltgetstr("bc", &sp);
  1188.         if (sc_backspace == NULL || *sc_backspace == '\0')
  1189.             sc_backspace = "\b";
  1190.     }
  1191.  
  1192.     /*
  1193.      * Choose between using "ho" and "cm" ("home" and "cursor move")
  1194.      * to move the cursor to the upper left corner of the screen.
  1195.      */
  1196.     t1 = ltgetstr("ho", &sp);
  1197.     if (t1 == NULL)
  1198.         t1 = "";
  1199.     if (*sc_move == '\0')
  1200.         t2 = "";
  1201.     else
  1202.     {
  1203.         strcpy(sp, tgoto(sc_move, 0, 0));
  1204.         t2 = sp;
  1205.         sp += strlen(sp) + 1;
  1206.     }
  1207.     sc_home = cheaper(t1, t2, "|\b^");
  1208.  
  1209.     /*
  1210.      * Choose between using "ll" and "cm"  ("lower left" and "cursor move")
  1211.      * to move the cursor to the lower left corner of the screen.
  1212.      */
  1213.     t1 = ltgetstr("ll", &sp);
  1214.     if (t1 == NULL)
  1215.         t1 = "";
  1216.     if (*sc_move == '\0')
  1217.         t2 = "";
  1218.     else
  1219.     {
  1220.         strcpy(sp, tgoto(sc_move, 0, sc_height-1));
  1221.         t2 = sp;
  1222.         sp += strlen(sp) + 1;
  1223.     }
  1224.     sc_lower_left = cheaper(t1, t2, "\r");
  1225.  
  1226.     /*
  1227.      * Choose between using "al" or "sr" ("add line" or "scroll reverse")
  1228.      * to add a line at the top of the screen.
  1229.      */
  1230.     t1 = ltgetstr("al", &sp);
  1231.     if (t1 == NULL)
  1232.         t1 = "";
  1233.     t2 = ltgetstr("sr", &sp);
  1234.     if (t2 == NULL)
  1235.         t2 = "";
  1236. #if OS2
  1237.     if (*t1 == '\0' && *t2 == '\0')
  1238.         sc_addline = "";
  1239.     else
  1240. #endif
  1241.     if (above_mem)
  1242.         sc_addline = t1;
  1243.     else
  1244.         sc_addline = cheaper(t1, t2, "");
  1245.     if (*sc_addline == '\0')
  1246.     {
  1247.         /*
  1248.          * Force repaint on any backward movement.
  1249.          */
  1250.         no_back_scroll = 1;
  1251.     }
  1252. #endif /* MSDOS_COMPILER */
  1253. }
  1254.  
  1255. #if !MSDOS_COMPILER
  1256. /*
  1257.  * Return the cost of displaying a termcap string.
  1258.  * We use the trick of calling tputs, but as a char printing function
  1259.  * we give it inc_costcount, which just increments "costcount".
  1260.  * This tells us how many chars would be printed by using this string.
  1261.  * {{ Couldn't we just use strlen? }}
  1262.  */
  1263. static int costcount;
  1264.  
  1265. /*ARGSUSED*/
  1266.     static int
  1267. inc_costcount(c)
  1268.     int c;
  1269. {
  1270.     costcount++;
  1271.     return (c);
  1272. }
  1273.  
  1274.     static int
  1275. cost(t)
  1276.     char *t;
  1277. {
  1278.     costcount = 0;
  1279.     tputs(t, sc_height, inc_costcount);
  1280.     return (costcount);
  1281. }
  1282.  
  1283. /*
  1284.  * Return the "best" of the two given termcap strings.
  1285.  * The best, if both exist, is the one with the lower 
  1286.  * cost (see cost() function).
  1287.  */
  1288.     static char *
  1289. cheaper(t1, t2, def)
  1290.     char *t1, *t2;
  1291.     char *def;
  1292. {
  1293.     if (*t1 == '\0' && *t2 == '\0')
  1294.     {
  1295.         missing_cap = 1;
  1296.         return (def);
  1297.     }
  1298.     if (*t1 == '\0')
  1299.         return (t2);
  1300.     if (*t2 == '\0')
  1301.         return (t1);
  1302.     if (cost(t1) < cost(t2))
  1303.         return (t1);
  1304.     return (t2);
  1305. }
  1306.  
  1307.     static void
  1308. tmodes(incap, outcap, instr, outstr, def_instr, def_outstr, spp)
  1309.     char *incap;
  1310.     char *outcap;
  1311.     char **instr;
  1312.     char **outstr;
  1313.     char *def_instr;
  1314.     char *def_outstr;
  1315.     char **spp;
  1316. {
  1317.     *instr = ltgetstr(incap, spp);
  1318.     if (*instr == NULL)
  1319.     {
  1320.         /* Use defaults. */
  1321.         *instr = def_instr;
  1322.         *outstr = def_outstr;
  1323.         return;
  1324.     }
  1325.  
  1326.     *outstr = ltgetstr(outcap, spp);
  1327.     if (*outstr == NULL)
  1328.         /* No specific out capability; use "me". */
  1329.         *outstr = ltgetstr("me", spp);
  1330.     if (*outstr == NULL)
  1331.         /* Don't even have "me"; use a null string. */
  1332.         *outstr = "";
  1333. }
  1334.  
  1335. #endif /* MSDOS_COMPILER */
  1336.  
  1337.  
  1338. /*
  1339.  * Below are the functions which perform all the 
  1340.  * terminal-specific screen manipulation.
  1341.  */
  1342.  
  1343.  
  1344. #if MSDOS_COMPILER
  1345.  
  1346. #if MSDOS_COMPILER==WIN32C
  1347.         static void
  1348. _settextposition(int row, int col)
  1349. {
  1350.     COORD cpos;
  1351.     cpos.X = (short)(col-1);
  1352.     cpos.Y = (short)(row-1);
  1353.     SetConsoleCursorPosition(con_out, cpos);
  1354. }
  1355. #endif
  1356.  
  1357. /*
  1358.  * Initialize the screen to the correct color at startup.
  1359.  */
  1360.     static void
  1361. initcolor()
  1362. {
  1363.     SETCOLORS(nm_fg_color, nm_bg_color);
  1364. #if 0
  1365.     /*
  1366.      * This clears the screen at startup.  This is different from
  1367.      * the behavior of other versions of less.  Disable it for now.
  1368.      */
  1369.     char *blanks;
  1370.     int row;
  1371.     int col;
  1372.     
  1373.     /*
  1374.      * Create a complete, blank screen using "normal" colors.
  1375.      */
  1376.     SETCOLORS(nm_fg_color, nm_bg_color);
  1377.     blanks = (char *) ecalloc(width+1, sizeof(char));
  1378.     for (col = 0;  col < sc_width;  col++)
  1379.         blanks[col] = ' ';
  1380.     blanks[sc_width] = '\0';
  1381.     for (row = 0;  row < sc_height;  row++)
  1382.         _outtext(blanks);
  1383.     free(blanks);
  1384. #endif
  1385. }
  1386. #endif
  1387.  
  1388. /*
  1389.  * Initialize terminal
  1390.  */
  1391.     public void
  1392. init()
  1393. {
  1394.     if (no_init)
  1395.         return;
  1396. #if !MSDOS_COMPILER
  1397.     tputs(sc_init, sc_height, putchr);
  1398.     tputs(sc_s_keypad, sc_height, putchr);
  1399. #else
  1400.     initcolor();
  1401.     flush();
  1402. #endif
  1403.     init_done = 1;
  1404. }
  1405.  
  1406. /*
  1407.  * Deinitialize terminal
  1408.  */
  1409.     public void
  1410. deinit()
  1411. {
  1412.     if (no_init)
  1413.         return;
  1414.     if (!init_done)
  1415.         return;
  1416. #if !MSDOS_COMPILER
  1417.     tputs(sc_e_keypad, sc_height, putchr);
  1418.     tputs(sc_deinit, sc_height, putchr);
  1419. #else
  1420.     SETCOLORS(sy_fg_color, sy_bg_color);
  1421.     putstr("\n");
  1422. #endif
  1423.     init_done = 0;
  1424. }
  1425.  
  1426. /*
  1427.  * Home cursor (move to upper left corner of screen).
  1428.  */
  1429.     public void
  1430. home()
  1431. {
  1432. #if !MSDOS_COMPILER
  1433.     tputs(sc_home, 1, putchr);
  1434. #else
  1435.     flush();
  1436.     _settextposition(1,1);
  1437. #endif
  1438. }
  1439.  
  1440. /*
  1441.  * Add a blank line (called with cursor at home).
  1442.  * Should scroll the display down.
  1443.  */
  1444.     public void
  1445. add_line()
  1446. {
  1447. #if !MSDOS_COMPILER
  1448.     tputs(sc_addline, sc_height, putchr);
  1449. #else
  1450.     flush();
  1451. #if MSDOS_COMPILER==MSOFTC
  1452.     _scrolltextwindow(_GSCROLLDOWN);
  1453.     _settextposition(1,1);
  1454. #else
  1455. #if MSDOS_COMPILER==BORLANDC
  1456.     movetext(1,1, sc_width,sc_height-1, 1,2);
  1457.     gotoxy(1,1);
  1458.     clreol();
  1459. #else
  1460. #if MSDOS_COMPILER==WIN32C
  1461.     {
  1462.     CHAR_INFO fillchar;
  1463.     SMALL_RECT rcSrc, rcClip;
  1464.     COORD new_org = {0, 1};
  1465.  
  1466.     rcClip.Left = 0;
  1467.     rcClip.Top = 0;
  1468.     rcClip.Right = (short)(sc_width - 1);
  1469.     rcClip.Bottom = (short)(sc_height - 1);
  1470.     rcSrc.Left = 0;
  1471.     rcSrc.Top = 0;
  1472.     rcSrc.Right = (short)(sc_width - 1);
  1473.     rcSrc.Bottom = (short)(sc_height - 2);
  1474.     fillchar.Char.AsciiChar = ' ';
  1475.     curr_attr = MAKEATTR(nm_fg_color, nm_bg_color);
  1476.     fillchar.Attributes = curr_attr;
  1477.     ScrollConsoleScreenBuffer(con_out, &rcSrc, &rcClip, new_org, &fillchar);
  1478.     _settextposition(1,1);
  1479.     }
  1480. #endif
  1481. #endif
  1482. #endif
  1483. #endif
  1484. }
  1485.  
  1486. /*
  1487.  * Move cursor to lower left corner of screen.
  1488.  */
  1489.     public void
  1490. lower_left()
  1491. {
  1492. #if !MSDOS_COMPILER
  1493.     tputs(sc_lower_left, 1, putchr);
  1494. #else
  1495.     flush();
  1496.     _settextposition(sc_height, 1);
  1497. #endif
  1498. }
  1499.  
  1500. /*
  1501.  * Goto a specific line on the screen.
  1502.  */
  1503.     public void
  1504. goto_line(slinenum)
  1505.     int slinenum;
  1506. {
  1507. #if !MSDOS_COMPILER
  1508.     tputs(tgoto(sc_move, 0, slinenum), 1, putchr);
  1509. #else
  1510.     flush();
  1511.     _settextposition(slinenum+1, 1);
  1512. #endif
  1513. }
  1514.  
  1515. #if MSDOS_COMPILER
  1516. /*
  1517.  * Create an alternate screen which is all white.
  1518.  * This screen is used to create a "flash" effect, by displaying it
  1519.  * briefly and then switching back to the normal screen.
  1520.  * {{ Yuck!  There must be a better way to get a visual bell. }}
  1521.  */
  1522.     static void
  1523. create_flash()
  1524. {
  1525. #if MSDOS_COMPILER==MSOFTC
  1526.     struct videoconfig w;
  1527.     char *blanks;
  1528.     int row, col;
  1529.     
  1530.     _getvideoconfig(&w);
  1531.     videopages = w.numvideopages;
  1532.     if (videopages < 2)
  1533.     {
  1534.         so_enter();
  1535.         so_exit();
  1536.     } else
  1537.     {
  1538.         _setactivepage(1);
  1539.         so_enter();
  1540.         blanks = (char *) ecalloc(w.numtextcols, sizeof(char));
  1541.         for (col = 0;  col < w.numtextcols;  col++)
  1542.             blanks[col] = ' ';
  1543.         for (row = w.numtextrows;  row > 0;  row--)
  1544.             _outmem(blanks, w.numtextcols);
  1545.         _setactivepage(0);
  1546.         _setvisualpage(0);
  1547.         free(blanks);
  1548.         so_exit();
  1549.     }
  1550. #else
  1551. #if MSDOS_COMPILER==BORLANDC
  1552.     register int n;
  1553.  
  1554.     whitescreen = (unsigned short *) 
  1555.         malloc(sc_width * sc_height * sizeof(short));
  1556.     if (whitescreen == NULL)
  1557.         return;
  1558.     for (n = 0;  n < sc_width * sc_height;  n++)
  1559.         whitescreen[n] = 0x7020;
  1560. #else
  1561. #if MSDOS_COMPILER==WIN32C
  1562.     register int n;
  1563.  
  1564.     whitescreen = (WORD *)
  1565.         malloc(sc_height * sc_width * sizeof(WORD));
  1566.     if (whitescreen == NULL)
  1567.         return;
  1568.     /* Invert the standard colors. */
  1569.     for (n = 0;  n < sc_width * sc_height;  n++)
  1570.         whitescreen[n] = (WORD)((nm_fg_color << 4) | nm_bg_color);
  1571. #endif
  1572. #endif
  1573. #endif
  1574.     flash_created = 1;
  1575. }
  1576. #endif /* MSDOS_COMPILER */
  1577.  
  1578. /*
  1579.  * Output the "visual bell", if there is one.
  1580.  */
  1581.     public void
  1582. vbell()
  1583. {
  1584. #if !MSDOS_COMPILER
  1585.     if (*sc_visual_bell == '\0')
  1586.         return;
  1587.     tputs(sc_visual_bell, sc_height, putchr);
  1588. #else
  1589. #if MSDOS_COMPILER==MSOFTC
  1590.     /*
  1591.      * Create a flash screen on the second video page.
  1592.      * Switch to that page, then switch back.
  1593.      */
  1594.     if (!flash_created)
  1595.         create_flash();
  1596.     if (videopages < 2)
  1597.         return;
  1598.     _setvisualpage(1);
  1599.     delay(100);
  1600.     _setvisualpage(0);
  1601. #else
  1602. #if MSDOS_COMPILER==BORLANDC
  1603.     unsigned short *currscreen;
  1604.  
  1605.     /*
  1606.      * Get a copy of the current screen.
  1607.      * Display the flash screen.
  1608.      * Then restore the old screen.
  1609.      */
  1610.     if (!flash_created)
  1611.         create_flash();
  1612.     if (whitescreen == NULL)
  1613.         return;
  1614.     currscreen = (unsigned short *) 
  1615.         malloc(sc_width * sc_height * sizeof(short));
  1616.     if (currscreen == NULL)
  1617.         return;
  1618.     gettext(1, 1, sc_width, sc_height, currscreen);
  1619.     puttext(1, 1, sc_width, sc_height, whitescreen);
  1620.     delay(100);
  1621.     puttext(1, 1, sc_width, sc_height, currscreen);
  1622.     free(currscreen);
  1623. #else
  1624. #if MSDOS_COMPILER==WIN32C
  1625.     WORD *currscreen;
  1626.     DWORD nread;
  1627.  
  1628.     if (!flash_created)
  1629.         create_flash();
  1630.     if (whitescreen == NULL)
  1631.         return;
  1632.     currscreen = (WORD *) 
  1633.         malloc(sc_width * sc_height * sizeof(WORD));
  1634.     if (currscreen == NULL)
  1635.         return;
  1636.     ReadConsoleOutputAttribute(con_out, currscreen, 
  1637.                 sc_height * sc_width, TOPLEFT, &nread);
  1638.     WriteConsoleOutputAttribute(con_out, whitescreen,
  1639.                 sc_height * sc_width, TOPLEFT, &nread);
  1640.     Sleep(100);
  1641.     WriteConsoleOutputAttribute(con_out, currscreen,
  1642.                 sc_height * sc_width, TOPLEFT, &nread);
  1643.     free(currscreen);
  1644. #endif
  1645. #endif
  1646. #endif
  1647. #endif
  1648. }
  1649.  
  1650. /*
  1651.  * Make a noise.
  1652.  */
  1653.     static void
  1654. beep()
  1655. {
  1656. #if !MSDOS_COMPILER
  1657.     putchr('\7');
  1658. #else
  1659. #if MSDOS_COMPILER==WIN32C
  1660.     MessageBeep(0);
  1661. #else
  1662.     write(1, "\7", 1);
  1663. #endif
  1664. #endif
  1665. }
  1666.  
  1667. /*
  1668.  * Ring the terminal bell.
  1669.  */
  1670.     public void
  1671. bell()
  1672. {
  1673.     if (quiet == VERY_QUIET)
  1674.         vbell();
  1675.     else
  1676.         beep();
  1677. }
  1678.  
  1679. /*
  1680.  * Clear the screen.
  1681.  */
  1682.     public void
  1683. clear()
  1684. {
  1685. #if !MSDOS_COMPILER
  1686.     tputs(sc_clear, sc_height, putchr);
  1687. #else
  1688.     flush();
  1689. #if MSDOS_COMPILER==WIN32C
  1690.     {
  1691.     DWORD nchars;
  1692.     curr_attr = MAKEATTR(nm_fg_color, nm_bg_color);
  1693.     FillConsoleOutputCharacter(con_out, ' ', 
  1694.             sc_width * sc_height, TOPLEFT, &nchars);
  1695.     FillConsoleOutputAttribute(con_out, curr_attr, 
  1696.             sc_width * sc_height, TOPLEFT, &nchars);
  1697.     }
  1698. #else
  1699.     _clearscreen(_GCLEARSCREEN);
  1700. #endif
  1701. #endif
  1702. }
  1703.  
  1704. /*
  1705.  * Clear from the cursor to the end of the cursor's line.
  1706.  * {{ This must not move the cursor. }}
  1707.  */
  1708.     public void
  1709. clear_eol()
  1710. {
  1711. #if !MSDOS_COMPILER
  1712.     tputs(sc_eol_clear, 1, putchr);
  1713. #else
  1714. #if MSDOS_COMPILER==MSOFTC
  1715.     short top, left;
  1716.     short bot, right;
  1717.     struct rccoord tpos;
  1718.     
  1719.     flush();
  1720.     /*
  1721.      * Save current state.
  1722.      */
  1723.     tpos = _gettextposition();
  1724.     _gettextwindow(&top, &left, &bot, &right);
  1725.     /*
  1726.      * Set a temporary window to the current line,
  1727.      * from the cursor's position to the right edge of the screen.
  1728.      * Then clear that window.
  1729.      */
  1730.     _settextwindow(tpos.row, tpos.col, tpos.row, sc_width);
  1731.     _clearscreen(_GWINDOW);
  1732.     /*
  1733.      * Restore state.
  1734.      */
  1735.     _settextwindow(top, left, bot, right);
  1736.     _settextposition(tpos.row, tpos.col);
  1737. #else
  1738. #if MSDOS_COMPILER==BORLANDC
  1739.     flush();
  1740.     clreol();
  1741. #else
  1742. #if MSDOS_COMPILER==WIN32C
  1743.     DWORD           nchars;
  1744.     COORD           cpos;
  1745.     CONSOLE_SCREEN_BUFFER_INFO scr;
  1746.  
  1747.     flush();
  1748.     memset(&scr, 0, sizeof(scr));
  1749.     GetConsoleScreenBufferInfo(con_out, &scr);
  1750.     cpos.X = scr.dwCursorPosition.X;
  1751.     cpos.Y = scr.dwCursorPosition.Y;
  1752.     curr_attr = MAKEATTR(nm_fg_color, nm_bg_color);
  1753.     FillConsoleOutputAttribute(con_out, curr_attr,
  1754.         sc_width - cpos.X, cpos, &nchars);
  1755.     FillConsoleOutputCharacter(con_out, ' ',
  1756.         sc_width - cpos.X, cpos, &nchars);
  1757. #endif
  1758. #endif
  1759. #endif
  1760. #endif
  1761. }
  1762.  
  1763. /*
  1764.  * Clear the bottom line of the display.
  1765.  * Leave the cursor at the beginning of the bottom line.
  1766.  */
  1767.     public void
  1768. clear_bot()
  1769. {
  1770.     lower_left();
  1771. #if MSDOS_COMPILER
  1772.     clear_eol();
  1773. #else
  1774.     if (below_mem)
  1775.         tputs(sc_eos_clear, 1, putchr);
  1776.     else
  1777.         tputs(sc_eol_clear, 1, putchr);
  1778. #endif
  1779. }
  1780.  
  1781. /*
  1782.  * Begin "standout" (bold, underline, or whatever).
  1783.  */
  1784.     public void
  1785. so_enter()
  1786. {
  1787. #if !MSDOS_COMPILER
  1788.     tputs(sc_s_in, 1, putchr);
  1789. #else
  1790.     flush();
  1791.     SETCOLORS(so_fg_color, so_bg_color);
  1792. #endif
  1793. }
  1794.  
  1795. /*
  1796.  * End "standout".
  1797.  */
  1798.     public void
  1799. so_exit()
  1800. {
  1801. #if !MSDOS_COMPILER
  1802.     tputs(sc_s_out, 1, putchr);
  1803. #else
  1804.     flush();
  1805.     SETCOLORS(nm_fg_color, nm_bg_color);
  1806. #endif
  1807. }
  1808.  
  1809. /*
  1810.  * Begin "underline" (hopefully real underlining, 
  1811.  * otherwise whatever the terminal provides).
  1812.  */
  1813.     public void
  1814. ul_enter()
  1815. {
  1816. #if !MSDOS_COMPILER
  1817.     tputs(sc_u_in, 1, putchr);
  1818. #else
  1819.     flush();
  1820.     SETCOLORS(ul_fg_color, ul_bg_color);
  1821. #endif
  1822. }
  1823.  
  1824. /*
  1825.  * End "underline".
  1826.  */
  1827.     public void
  1828. ul_exit()
  1829. {
  1830. #if !MSDOS_COMPILER
  1831.     tputs(sc_u_out, 1, putchr);
  1832. #else
  1833.     flush();
  1834.     SETCOLORS(nm_fg_color, nm_bg_color);
  1835. #endif
  1836. }
  1837.  
  1838. /*
  1839.  * Begin "bold"
  1840.  */
  1841.     public void
  1842. bo_enter()
  1843. {
  1844. #if !MSDOS_COMPILER
  1845.     tputs(sc_b_in, 1, putchr);
  1846. #else
  1847.     flush();
  1848.     SETCOLORS(bo_fg_color, bo_bg_color);
  1849. #endif
  1850. }
  1851.  
  1852. /*
  1853.  * End "bold".
  1854.  */
  1855.     public void
  1856. bo_exit()
  1857. {
  1858. #if !MSDOS_COMPILER
  1859.     tputs(sc_b_out, 1, putchr);
  1860. #else
  1861.     flush();
  1862.     SETCOLORS(nm_fg_color, nm_bg_color);
  1863. #endif
  1864. }
  1865.  
  1866. /*
  1867.  * Begin "blink"
  1868.  */
  1869.     public void
  1870. bl_enter()
  1871. {
  1872. #if !MSDOS_COMPILER
  1873.     tputs(sc_bl_in, 1, putchr);
  1874. #else
  1875.     flush();
  1876.     SETCOLORS(bl_fg_color, bl_bg_color);
  1877. #endif
  1878. }
  1879.  
  1880. /*
  1881.  * End "blink".
  1882.  */
  1883.     public void
  1884. bl_exit()
  1885. {
  1886. #if !MSDOS_COMPILER
  1887.     tputs(sc_bl_out, 1, putchr);
  1888. #else
  1889.     flush();
  1890.     SETCOLORS(nm_fg_color, nm_bg_color);
  1891. #endif
  1892. }
  1893.  
  1894. /*
  1895.  * Erase the character to the left of the cursor 
  1896.  * and move the cursor left.
  1897.  */
  1898.     public void
  1899. backspace()
  1900. {
  1901. #if !MSDOS_COMPILER
  1902.     /* 
  1903.      * Erase the previous character by overstriking with a space.
  1904.      */
  1905.     tputs(sc_backspace, 1, putchr);
  1906.     putchr(' ');
  1907.     tputs(sc_backspace, 1, putchr);
  1908. #else
  1909. #if MSDOS_COMPILER==MSOFTC
  1910.     struct rccoord tpos;
  1911.     
  1912.     flush();
  1913.     tpos = _gettextposition();
  1914.     if (tpos.col <= 1)
  1915.         return;
  1916.     _settextposition(tpos.row, tpos.col-1);
  1917.     _outtext(" ");
  1918.     _settextposition(tpos.row, tpos.col-1);
  1919. #else
  1920. #if MSDOS_COMPILER==BORLANDC
  1921.     cputs("\b");
  1922. #else
  1923. #if MSDOS_COMPILER==WIN32C
  1924.         COORD cpos;
  1925.     CONSOLE_SCREEN_BUFFER_INFO scr;
  1926.  
  1927.         flush();
  1928.         GetConsoleScreenBufferInfo(con_out, &scr);
  1929.         cpos.X = scr.dwCursorPosition.X;
  1930.         cpos.Y = scr.dwCursorPosition.Y;
  1931.         if (cpos.X <= 0)
  1932.                 return;
  1933.         _settextposition(cpos.Y, cpos.X-1);
  1934.         putch(' ');
  1935.         _settextposition(cpos.Y, cpos.X-1);
  1936. #endif
  1937. #endif
  1938. #endif
  1939. #endif
  1940. }
  1941.  
  1942. /*
  1943.  * Output a plain backspace, without erasing the previous char.
  1944.  */
  1945.     public void
  1946. putbs()
  1947. {
  1948. #if !MSDOS_COMPILER
  1949.     tputs(sc_backspace, 1, putchr);
  1950. #else
  1951.     int row, col;
  1952.  
  1953.     flush();
  1954.     {
  1955. #if MSDOS_COMPILER==MSOFTC
  1956.         struct rccoord tpos;
  1957.         tpos = _gettextposition();
  1958.         row = tpos.row;
  1959.         col = tpos.col;
  1960. #else
  1961. #if MSDOS_COMPILER==BORLANDC
  1962.         row = wherey();
  1963.         col = wherex();
  1964. #else
  1965. #if MSDOS_COMPILER==WIN32C
  1966.         CONSOLE_SCREEN_BUFFER_INFO scr;
  1967.         GetConsoleScreenBufferInfo(con_out, &scr);
  1968.         row = scr.dwCursorPosition.Y + 1;
  1969.         col = scr.dwCursorPosition.X + 1;
  1970. #endif
  1971. #endif
  1972. #endif
  1973.     }
  1974.     if (col <= 1)
  1975.         return;
  1976.     _settextposition(row, col-1);
  1977. #endif /* MSDOS_COMPILER */
  1978. }
  1979.  
  1980. #if MSDOS_COMPILER==WIN32C
  1981.     static int
  1982. win32_kbhit(tty)
  1983.     HANDLE tty;
  1984. {
  1985.     INPUT_RECORD ip;
  1986.     DWORD read;
  1987.  
  1988.     if (keyCount > 0)
  1989.         return TRUE;
  1990.  
  1991.     currentKey.scan = 0;
  1992.     currentKey.ascii = 0;
  1993.  
  1994.     for (;;)
  1995.     {
  1996.         PeekConsoleInput(tty, &ip, 1, &read);
  1997.         if (read == 0)
  1998.             return FALSE;
  1999.         ReadConsoleInput(tty, &ip, 1, &read);
  2000.  
  2001.         if (ip.EventType == KEY_EVENT &&
  2002.             ip.Event.KeyEvent.bKeyDown == TRUE &&
  2003.             ip.Event.KeyEvent.wVirtualScanCode != 0)
  2004.         {
  2005.             /* Filter out SHIFT and CONTROL key events */
  2006.             if (ip.Event.KeyEvent.wVirtualKeyCode == VK_SHIFT ||
  2007.                 ip.Event.KeyEvent.wVirtualKeyCode == VK_CONTROL ||
  2008.                 ip.Event.KeyEvent.wVirtualKeyCode == VK_MENU)
  2009.                 continue;
  2010.             
  2011.             currentKey.ascii = ip.Event.KeyEvent.uChar.AsciiChar;
  2012.             currentKey.scan = ip.Event.KeyEvent.wVirtualScanCode;
  2013.             keyCount = ip.Event.KeyEvent.wRepeatCount;
  2014.             return TRUE;
  2015.         }
  2016.     }
  2017. }
  2018.  
  2019.     public char
  2020. WIN32getch(tty)
  2021.     int tty;
  2022. {
  2023.     int ascii;
  2024.  
  2025.     if (pending_scancode)
  2026.     {
  2027.         pending_scancode = 0;
  2028.         return ((char)(currentKey.scan & 0x00FF));
  2029.     }
  2030.  
  2031.     while (win32_kbhit((HANDLE)tty) == FALSE)
  2032.         continue;
  2033.     keyCount --;
  2034.     ascii = currentKey.ascii;
  2035.     /*
  2036.      * On PC's, the extended keys return a 2 byte sequence beginning 
  2037.      * with '00', so if the ascii code is 00, the next byte will be 
  2038.      * the lsb of the scan code.
  2039.      */
  2040.     pending_scancode = (ascii == 0x00);
  2041.     return ((char)ascii);
  2042. }
  2043. #endif
  2044.