home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume8 / vtrm / part01 / p1trm.c < prev    next >
Encoding:
C/C++ Source or Header  |  1987-02-11  |  20.8 KB  |  1,060 lines

  1. /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
  2.  
  3. /*
  4.  * ibm Pc virtual TeRMinal package.
  5.  *
  6.  * An implementation of the VTRM interface for MS-DOS machines.
  7.  *
  8.  * This code supports two MODE's of accessing the screen.
  9.  * The first one (BIOS) will be used, unless the user overwrites this
  10.  * by setting the SCREEN environment variable.
  11.  * This variable can also be used to convey a screen size that differs
  12.  * from the default 25 lines and 80 columns. See below.
  13.  *
  14.  * The two modes are:
  15.  *
  16.  * 1) IBM BIOS interrupt 10 hex, video io.
  17.  *    (See IBM PC XT Technical Reference 6936833, May 1983,
  18.  *     Appendix A, pages A46-A47).
  19.  *    This is what you really want to use, since it's the only one that
  20.  *    can decently scroll. It cannot insert or delete characters, so
  21.  *    most optimisations from vtrm.c are useless and taken out.
  22.  *    Unfortunately, not every PC-compatible machine supports this BIOS
  23.  *    interrupt, so for these unlucky souls there is the following escape:
  24.  *
  25.  * 2) The ANSI.SYS driver.
  26.  *    (See IBM MS-DOS 6936839, Jan 1983, Version 2.00, Chapter 13.)
  27.  *    (Some compatibles don't have a separate ANSI.SYS driver but do the
  28.  *    same escape interpretation by default.)
  29.  *    This works reasonably, apart from scrolling downward, or part of
  30.  *    the screen, which is clumsy.
  31.  *    (The ANSI standard provides an escape sequence for scrolling
  32.  *    but ANSI.SYS does not support it, nor any other way of scrolling.)
  33.  *
  34.  * The rest of the interface is the same as described in vtrm.c,
  35.  * with the following exceptions:
  36.  *    - to ease coding for ansi scrolls, the terminal is supposed to
  37.  *    contain blanks at positions that were not written yet;
  38.  *    the unknown rubbish that is initially on the screen can
  39.  *    only be cleared by the caller by scrolling the whole screen up
  40.  *    by one or more lines;
  41.  *    - the number of lines on the terminal is assumed to be 25;
  42.  *    the number of columns is (1) determined by a BIOS function, or
  43.  *    (2) assumed to be 80 for ANSI;
  44.  *    the user can overwrite this by setting the environment variable:
  45.  *
  46.  *        SET SCREEN=BIOS x y
  47.  *    or
  48.  *        SET SCREEN=ANSI x y
  49.  *
  50.  *    where x and y are the number of lines and columns respectively.
  51.  *
  52.  * The lines and columns of our virtual terminal are numbered
  53.  *    y = {0...lines-1} from top to bottom, and
  54.  *    x = {0...cols-1} from left to right,
  55.  * respectively.
  56.  *
  57.  * The Visible Procedures in this package are as described in vtrm.c.
  58.  *
  59.  */
  60.  
  61. /*
  62.  * Includes and data definitions.
  63.  */
  64.  
  65. #include <stdio.h>
  66. #include <signal.h>
  67. #include <ctype.h>
  68. #include <dos.h>
  69.  
  70. char *malloc();
  71.  
  72. #include "trm.h"
  73.  
  74. #ifdef lint
  75. #define VOID (void)
  76. #else
  77. #define VOID
  78. #endif
  79.  
  80. #define Forward
  81. #define Visible
  82. #define Hidden static
  83. #define Procedure
  84.  
  85. typedef short intlet;
  86. typedef char *string;
  87. typedef char bool;
  88. #define Yes '\1'
  89. #define No  '\0'
  90. #define Undefined (-1)
  91.  
  92. #define Min(a,b) ((a) <= (b) ? (a) : (b))
  93.  
  94. #define MESS(number, text) text
  95.  
  96. #ifdef GFX
  97. #include "gfx.h"
  98. #endif
  99.  
  100. /* terminal status */
  101.  
  102. Hidden int started = No;
  103.  
  104. Hidden int scr_mode = 0;
  105. #define ANSI 'A'
  106. #define BIOS 'B'
  107.  
  108. #define Nlines    25
  109. #define Ncols    80
  110. Hidden int lines = Nlines;
  111. Hidden int cols = Ncols;
  112. Hidden int flags = 0;
  113.  
  114. /* current standout mode */
  115. #define Off    0
  116. #define On    0200
  117. Hidden int so_mode = Off;
  118.  
  119. /* masks for char's and intlet's */
  120. #define NULCHAR '\000'
  121. #define CHAR    0177
  122. #define SOBIT    On
  123. #define SOCHAR    0377
  124.  
  125. /* current cursor position */
  126. Hidden intlet cur_y = Undefined, cur_x = Undefined;
  127.  
  128. /* "line[y][x]" holds the char on the terminal, with the SOBIT.
  129.  * the SOBIT tells whether the character is standing out.
  130.  * "lenline[y]" holds the length of the line.
  131.  * (Partially) empty lines are distinghuished by "lenline[y] < cols".
  132.  * Unknown chars will be ' ', so the scrolling routines for ANSI
  133.  * can use "unwritten" chars (with indent > 0 in trmputdata).
  134.  * To make the optimising compare in putline fail, lenline[y] is initially 0.
  135.  * The latter implies that if a line is first addressed with trmputdata,
  136.  * any rubbish that is on the screen beyond the data that gets put, will
  137.  * remain there.
  138.  */
  139. Hidden char **line = 0;
  140. Hidden intlet *lenline = 0;
  141.  
  142. /* Make the cursor invisible when trmsync() tries to move outside the screen */
  143. Hidden bool no_cursor = No;
  144.  
  145. /*
  146.  * Starting, Ending and (fatal) Error.
  147.  */
  148.  
  149. /*
  150.  * Initialization call.
  151.  * Determine terminal mode.
  152.  * Start up terminal and internal administration.
  153.  * Return Yes if succeeded, No if trouble (which doesn't apply here).
  154.  */
  155. Visible int
  156. trmstart(plines, pcols, pflags)
  157. int *plines;
  158. int *pcols;
  159. int *pflags;
  160. {
  161.     static char setup = No;
  162.     int err;
  163.  
  164. #ifdef TRACE
  165. if (!setup) freopen("TRACE.DAT", "a", stderr);
  166. fprintf(stderr, "\ttrmstart(&li, &co, &fl);\n");
  167. #endif
  168.  
  169.     if (started)
  170.         return TE_TWICE;
  171.  
  172. #ifdef GFX
  173.     if (gfx_mode != TEXT_MODE)
  174.         gfx_mode= SPLIT_MODE;
  175. #endif
  176.  
  177.     if (!setup) {
  178.         err= set_screen_up();
  179.         if (err != TE_OK)
  180.             return err;
  181.         setup = Yes;
  182.     }
  183.  
  184.     err= start_trm();        /* internal administration */
  185.     if (err != TE_OK)
  186.         return err;
  187.  
  188.     *plines = lines;
  189.     *pcols = cols;
  190.     *pflags = flags;
  191.  
  192.     set_handler();
  193.     started = Yes;
  194.     return TE_OK;
  195. }
  196.  
  197. Hidden int
  198. set_screen_up()
  199. {
  200.     int height;
  201.     int width;
  202.     int get_screen_env();
  203.     int get_cols();
  204.  
  205.     height = width = 0;
  206.     scr_mode = get_screen_env(&height, &width);
  207.  
  208.     switch (scr_mode) {
  209.     case BIOS:
  210.     case TE_OK:
  211.         cols = get_cols();
  212.         flags = HAS_STANDOUT|CAN_SCROLL;
  213.         break;
  214.     case ANSI:
  215.         flags = HAS_STANDOUT;
  216.         break;
  217.     default:
  218.         return scr_mode; /* Error flag */
  219.     }
  220.  
  221.     /* allow x and y in environment variable SCREEN to override */
  222.     if (height > 0)
  223.         lines = height;
  224.     if (width > 0)
  225.         cols = width;
  226.     return TE_OK;
  227. }
  228.  
  229. Hidden int
  230. get_screen_env(pheight, pwidth)
  231.     int *pheight, *pwidth;
  232. {
  233.     string s;
  234.     int mode;
  235.     char screrr;
  236.     string getenv();
  237.     string strip();
  238.     string skip();
  239.  
  240.     screrr = No;
  241.     s = getenv("SCREEN");
  242.     if (s == NULL)
  243.         return BIOS;
  244.  
  245.     s = strip(s);
  246.     switch (*s) {
  247.     case '\0':
  248.         return BIOS;
  249.     case 'a':
  250.         mode = ANSI;
  251.         s = skip(s, "ansi");
  252.         break;
  253.     case 'A':
  254.         mode = ANSI;
  255.         s = skip(s, "ANSI");
  256.         break;
  257.     case 'b':
  258.         mode = BIOS;
  259.         s = skip(s, "bios");
  260.         break;
  261.     case 'B':
  262.         mode = BIOS;
  263.         s = skip(s, "BIOS");
  264.         break;
  265.     default:
  266.         mode = BIOS;
  267.         screrr = Yes;
  268.     }
  269.  
  270.     /* *pheight and *pwidth were set to 0 above */
  271.     s = strip(s);
  272.     while (isdigit(*s)) {
  273.         *pheight = *pheight * 10 + (*s++ - '0');
  274.     }
  275.     s = strip(s);
  276.     while (isdigit(*s)) {
  277.         *pwidth = *pwidth * 10 + (*s++ -'0');
  278.     }
  279.     s = strip(s);
  280.     if (screrr || *s != '\0')
  281.         return TE_BADTERM;
  282.  
  283.     return mode;
  284. }
  285.  
  286. Hidden string strip(s)
  287. string s;
  288. {
  289.     while (*s == ' ' || *s == '\t')
  290.         ++s;
  291.     return s;
  292. }
  293.  
  294. Hidden string skip(s, pat)
  295. string s, pat;
  296. {
  297.     while (*s == *pat)
  298.         ++s, ++pat;
  299.     return s;
  300. }
  301.  
  302. Hidden int        /* initialise internal administration */
  303. start_trm()
  304. {
  305.     register int y;
  306.  
  307.     if (line == 0) {
  308.         if ((line = (char**) malloc(lines * sizeof(char*))) == NULL)
  309.             return TE_NOMEM;
  310.         for (y = 0; y < lines; y++) {
  311.             if ((line[y] = malloc(cols * sizeof(char))) == NULL)
  312.                 return TE_NOMEM;
  313.         }
  314.     }
  315.     if (lenline == 0) {
  316.         if ((lenline = (intlet *)
  317.                 malloc(lines * sizeof(intlet))) == NULL)
  318.             return TE_NOMEM;
  319.     }
  320.  
  321.     trmundefined();
  322.     return TE_OK;
  323. }
  324.  
  325. /*
  326.  * Termination call.
  327.  * Beware that it might be called by a catched interrupt even in the middle
  328.  * of trmstart()!
  329.  */
  330.  
  331. Visible Procedure
  332. trmend()
  333. {
  334. #ifdef TRACE
  335. fprintf(stderr, "\ttrmend();\n");
  336. #endif
  337.     if (started && so_mode != Off)
  338.         standend();
  339.     if (scr_mode == ANSI) {
  340.         VOID fflush(stdout);
  341.     }
  342.  
  343.     started = No;
  344. }
  345.  
  346. /*
  347.  * Set all internal statuses to undefined, especially the contents of
  348.  * the screen, so a hard redraw will not be optimised to heaven.
  349.  */
  350.  
  351. Visible Procedure
  352. trmundefined()
  353. {
  354.     register int y, x;
  355. #ifdef TRACE
  356. fprintf(stderr, "\ttrmundefined();\n");
  357. #endif
  358.  
  359.     cur_y = cur_x = Undefined;
  360.     so_mode = Undefined;
  361.  
  362.     for (y = 0; y < lines; y++) {
  363.         for (x = 0; x < cols; x++)
  364.             line[y][x] = ' ';
  365.             /* they may get printed in scrolling */
  366.         lenline[y] = 0;
  367.     }
  368. }
  369.  
  370. #ifdef DEBUG
  371. Hidden Procedure
  372. check_started(m)
  373.     char *m;
  374. {
  375.     if (!started) {
  376.         printf("Not started: %s\n", m);
  377.         exit(TE_TWICE);
  378.     }
  379. }
  380. #else
  381. #define check_started(m) /*empty*/
  382. #endif
  383.  
  384. /*
  385.  * Sensing the cursor.
  386.  * (NOT IMPLEMENTED, since there is no way to locally move the cursor.)
  387.  */
  388.  
  389. /*
  390.  * Sense the current (y, x) cursor position, after a possible manual
  391.  * change by the user with local cursor motions.
  392.  * If the terminal cannot be asked for the current cursor position,
  393.  * or if the string returned by the terminal is garbled,
  394.  * the position is made Undefined.
  395.  */
  396. Visible Procedure
  397. trmsense(py, px)
  398.     int *py;
  399.     int *px;
  400. {
  401. /*    bool getpos(); */
  402. #ifdef TRACE
  403. fprintf(stderr, "\ttrmsense(&yy, &xx);\n");
  404. #endif
  405.     check_started(MESS(7904, "trmsense called outside trmstart/trmend"));
  406.  
  407.     *py = *px = Undefined;
  408.  
  409. /*
  410.  *    if (flags&CAN_SENSE && getpos(py, px)) {
  411.  *        if (*py < 0 || lines <= *py || *px < 0 || cols <= *px)
  412.  *            *py = *px = Undefined;
  413.  *    }
  414.  */
  415.     cur_y = *py;
  416.     cur_x = *px;
  417. }
  418.  
  419. /*
  420.  * Putting data on the screen.
  421.  */
  422.  
  423. /*
  424.  * Fill screen area with given data.
  425.  * Characters with the SO-bit (0200) set are put in standout mode.
  426.  * (Unfortunately this makes it impossible to display accented characters.
  427.  * The interface should change.)
  428.  */
  429. Visible Procedure
  430. trmputdata(yfirst, ylast, indent, data)
  431. int yfirst;
  432. int ylast;
  433. register int indent;
  434. register string data;
  435. {
  436.     register int y;
  437.     int x, len, lendata, space;
  438.  
  439. #ifdef TRACE
  440. fprintf(stderr, "\ttrmputdata(%d, %d, %d, \"%s\");\n", yfirst, ylast, indent, data);
  441. #endif
  442.     check_started(MESS(7905, "trmputdata called outside trmstart/trmend"));
  443.  
  444.     if (yfirst < 0)
  445.         yfirst = 0;
  446.     if (ylast >= lines)
  447.         ylast = lines-1;
  448.     space = cols*(ylast-yfirst+1) - indent;
  449.     if (space <= 0)
  450.         return;
  451.     yfirst += indent/cols;
  452.     indent %= cols;
  453.     y = yfirst;
  454.     if (data) {
  455.         x = indent;
  456.         lendata = strlen(data);
  457.         if (ylast == lines-1 && lendata >= space)
  458.             lendata = space - 1;
  459.         len = Min(lendata, cols-x);
  460.         while (y <= ylast) {
  461.             put_line(y, x, data, len);
  462.             y++;
  463.             lendata -= len;
  464.             if (lendata > 0) {
  465.                 x = 0;
  466.                 data += len;
  467.                 len = Min(lendata, cols);
  468.             }
  469.             else
  470.                 break;
  471.         }
  472.     }
  473.     if (y <= ylast)
  474.         clear_lines(y, ylast);
  475. }
  476.  
  477. /*
  478.  * We will try to get the picture:
  479.  *
  480.  *            op>>>>>>>>>>>op                       oq
  481.  *            ^         ^                       ^
  482.  *         <xskip><-----m1----><---------------od-------------------->
  483.  *   OLD:   "You're in a maze of twisty little pieces of code, all alike"
  484.  *   NEW:       "in a maze of little twisting pieces of code, all alike"
  485.  *            <-----m1----><----------------nd--------------------->
  486.  *            ^         ^                     ^
  487.  *            np>>>>>>>>>>>np                     nq
  488.  * where
  489.  *    op, oq, np, nq are pointers to start and end of Old and New data,
  490.  * and
  491.  *    xskip = length of indent to be skipped,
  492.  *    m1 = length of Matching part at start,
  493.  *    od = length of Differing end on screen,
  494.  *    nd = length of Differing end in data to be put.
  495.  */
  496. Hidden int
  497. put_line(y, xskip, data, len)
  498. int y, xskip;
  499. string data;
  500. int len;
  501. {
  502.     register char *op, *oq, *np, *nq;
  503.     int m1, od, nd, delta;
  504.  
  505.     /* calculate the magic parameters */
  506.     op = &line[y][xskip];
  507.     oq = &line[y][lenline[y]-1];
  508.     np = data;
  509.     nq = data + len - 1;
  510.     m1 = 0;
  511.     while ((*op&SOCHAR) == (*np&SOCHAR) && op <= oq && np <= nq)
  512.         op++, np++, m1++;
  513.     od = oq - op + 1;
  514.     nd = nq - np + 1;
  515.     /* now we have the picture above */
  516.  
  517.     if (od==0 && nd==0)
  518.         return;
  519.  
  520.     delta = nd - od;
  521.     move(y, xskip + m1);
  522.     if (nd > 0) {
  523.         put_str(np, nd);
  524.     }
  525.     if (delta < 0) {
  526.         clr_to_eol();
  527.         return;
  528.     }
  529.     lenline[y] = xskip + len;
  530.     if (cur_x == cols) {
  531.         cur_y++;
  532.         cur_x = 0;
  533.     }
  534. }
  535.  
  536. /*
  537.  * Scrolling (part of) the screen up (or down, dy<0).
  538.  */
  539.  
  540. Visible Procedure
  541. trmscrollup(yfirst, ylast, by)
  542. register int yfirst;
  543. register int ylast;
  544. register int by;
  545. {
  546. #ifdef TRACE
  547. fprintf(stderr, "\ttrmscrollup(%d, %d, %d);\n", yfirst, ylast, by);
  548. #endif
  549.     check_started(MESS(7906, "trmscrollup called outside trmstart/trmend"));
  550.  
  551.     if (by == 0)
  552.         return;
  553.  
  554.     if (yfirst < 0)
  555.         yfirst = 0;
  556.     if (ylast >= lines)
  557.         ylast = lines-1;
  558.  
  559.     if (yfirst > ylast)
  560.         return;
  561.  
  562.     if (so_mode != Off)
  563.         standend();
  564.  
  565.     if (by > 0 && yfirst + by > ylast
  566.         ||
  567.         by < 0 && yfirst - by > ylast)
  568.     {
  569.         clear_lines(yfirst, ylast);
  570.         return;
  571.     }
  572.  
  573.     switch (scr_mode) {
  574.     case BIOS:
  575.         biosscrollup(yfirst, ylast, by);
  576.         break;
  577.     case ANSI:
  578.         if (by > 0 && yfirst == 0) {
  579.             lf_scroll(ylast, by);
  580.         }
  581.         else if (by > 0) {
  582.             move_lines(yfirst+by, yfirst, ylast-yfirst+1-by, 1);
  583.             clear_lines(ylast-by+1, ylast);
  584.         }
  585.         else {
  586.             move_lines(ylast+by, ylast, ylast-yfirst+1+by, -1);
  587.             clear_lines(yfirst, yfirst-by-1);
  588.         }
  589.         break;
  590.     }
  591. }
  592.  
  593. /*
  594.  * Synchronization, move cursor to given position (or previous if < 0).
  595.  */
  596.  
  597. Visible Procedure
  598. trmsync(y, x)
  599.     int y;
  600.     int x;
  601. {
  602. #ifdef TRACE
  603. fprintf(stderr, "\ttrmsync(%d, %d);\n", y, x);
  604. #endif
  605.     check_started(MESS(7907, "trmsync called outside trmstart/trmend"));
  606.  
  607.     if (0 <= y && y < lines && 0 <= x && x < cols) {
  608.         move(y, x);
  609.     }
  610.     VOID fflush(stdout);
  611. }
  612.  
  613. /*
  614.  * Send a bell, visible if possible.
  615.  */
  616.  
  617. Visible Procedure
  618. trmbell()
  619. {
  620. #ifdef TRACE
  621. fprintf(stderr, "\ttrmbell();\n");
  622. #endif
  623.     check_started(MESS(7908, "trmbell called outside trmstart/trmend"));
  624.     ring_bell();
  625. }
  626.  
  627. /*
  628.  * Now for the real work: here are all low level routines that really
  629.  * differ for BIOS or ANSI mode.
  630.  */
  631.  
  632. /*
  633.  * BIOS video io is called by generating an 8086 software interrupt,
  634.  * using lattice's int86() function.
  635.  * To ease coding, all routines fill in the apropriate parameters in regs,
  636.  * and than call bios10(code), where code is to be placed in ah.
  637.  */
  638.  
  639. Hidden union REGS regs, outregs;
  640.  
  641. /* A macro for speed  */
  642. #define bios10(code) (regs.h.ah = (code), int86(0x10, ®s, ®s))
  643. #define nbios10(code) (regs.h.ah = (code), int86(0x10, ®s, &outregs))
  644.  
  645. /* Video attributes: (see the BASIC manual) (used for standout mode) */
  646.  
  647. Hidden int video_attr;
  648. #ifndef GFX
  649. #define V_NORMAL 7
  650. #else
  651. #define V_NORMAL (gfx_mode == TEXT_MODE ? 7 : 0)
  652. #endif
  653. #define V_STANDOUT (7<<4)
  654.  
  655. /* Some BIOS only routines */
  656.  
  657. Hidden get_cols()
  658. {
  659.     bios10(15);
  660.     return regs.h.ah;
  661. }
  662.  
  663. /*
  664.  * ANSI escape sequences
  665.  */
  666. #define A_CUP    "\033[%d;%dH"   /* cursor position */
  667. #define A_SGR0    "\033[0m"       /* set graphics rendition to normal */
  668. #define A_SGR7    "\033[7m"       /* set graphics rendition to standout */
  669. #define A_ED    "\033[2J"       /* erase display (and cursor home) */
  670. #define A_EL    "\033[K"        /* erase (to end of) line */
  671.  
  672. /*
  673.  * The following routine is the time bottleneck, I believe!
  674.  */
  675.  
  676. Hidden Procedure
  677. put_str(data, n)
  678. char *data;
  679. int n;
  680. {
  681.     register char c, so;
  682.  
  683.     so = so_mode;
  684.     if (scr_mode == BIOS) {
  685.         regs.x.cx = 1;    /* repition count */
  686.         regs.h.bh = 0;    /* page number */
  687.         regs.h.bl = video_attr;
  688.         while (--n >= 0) {
  689.             c = (*data++)&SOCHAR;
  690.             if ((c&SOBIT) != so) {
  691.                 so = c&SOBIT;
  692.                 so ? standout() : standend();
  693.                 regs.h.bl = video_attr;
  694.             }
  695.             regs.h.al = c&CHAR;
  696.             nbios10(9);
  697.             if (cur_x >= cols-1) {
  698.                 line[cur_y][cols-1] = c;
  699.                 continue;
  700.             }
  701.             regs.h.dh = cur_y;
  702.             regs.h.dl = cur_x + 1;
  703.             nbios10(2);
  704.             line[cur_y][cur_x] = c;
  705.             cur_x++;
  706.         }
  707.     }
  708.     else {
  709.         while (--n >= 0) {
  710.             c = (*data++)&SOCHAR;
  711.             if ((c&SOBIT) != so) {
  712.                 so = c&SOBIT;
  713.                 so ? standout() : standend();
  714.             }
  715.             putch(c&CHAR);
  716.             line[cur_y][cur_x] = c;
  717.             cur_x++;
  718.         }
  719.     }
  720. }
  721.  
  722. /*
  723.  * Move to position y,x on the screen
  724.  */
  725.  
  726. Hidden Procedure
  727. move(y, x)
  728. int y, x;
  729. {
  730.     if (scr_mode != BIOS && cur_y == y && cur_x == x)
  731.         return;
  732.     switch (scr_mode) {
  733.     case BIOS:
  734.         regs.h.dh = y;
  735.         regs.h.dl = x;
  736.         regs.h.bh = 0; /* Page; must be 0 for graphics */
  737.         bios10(2);
  738.         break;
  739.     case ANSI:
  740.         cprintf(A_CUP, y+1, x+1);
  741.         break;
  742.     }
  743.     cur_y = y;
  744.     cur_x = x;
  745. }
  746.  
  747. Hidden Procedure
  748. standout()
  749. {
  750.     so_mode = On;
  751.     switch (scr_mode) {
  752.     case BIOS:
  753.         video_attr = V_STANDOUT;
  754.         break;
  755.     case ANSI:
  756.         cputs(A_SGR7);
  757.         break;
  758.     }
  759. }
  760.  
  761. Hidden Procedure
  762. standend()
  763. {
  764.     so_mode = Off;
  765.     switch (scr_mode) {
  766.     case BIOS:
  767.         video_attr = V_NORMAL;
  768.         break;
  769.     case ANSI:
  770.         cputs(A_SGR0);
  771.         break;
  772.     }
  773. }
  774.  
  775. #ifdef UNUSED
  776. Hidden Procedure
  777. put_c(c)
  778. int c;
  779. {
  780.     int ch;
  781.  
  782.     ch = c&CHAR;
  783. #ifndef NDEBUG
  784.     if (!isprint(ch)) {
  785.         ch = '?';
  786.         c = (c&SOBIT)|'?';
  787.     }
  788. #endif
  789.     switch (scr_mode) {
  790.     case BIOS:
  791.         regs.h.al = ch;
  792.         regs.h.bl = video_attr;
  793.         regs.x.cx = 1;    /* repition count */
  794.         regs.h.bh = 0;    /* page number */
  795.         bios10(9);
  796.         if (cur_x >= cols-1) {
  797.             line[cur_y][cols-1] = c;
  798.             return;
  799.         }
  800.         regs.h.dh = cur_y;
  801.         regs.h.dl = cur_x + 1;
  802.         bios10(2);
  803.         break;
  804.     case ANSI:
  805.         putch(ch);
  806.         break;
  807.     }
  808.     line[cur_y][cur_x] = c;
  809.     cur_x++;
  810. }
  811. #endif UNUSED
  812.  
  813. Hidden Procedure
  814. clear_lines(yfirst, ylast)
  815. int yfirst, ylast ;
  816. {
  817.     register int y;
  818.  
  819.     if (scr_mode == BIOS) {
  820.         regs.h.al = 0;    /* scroll with al = 0 means blank window */
  821.         regs.h.ch = yfirst;
  822.         regs.h.cl = 0;
  823.         regs.h.dh = ylast;
  824.         regs.h.dl = cols-1;
  825.         regs.h.bh = V_NORMAL;
  826.         bios10(6);
  827.         for (y = yfirst; y <= ylast; y++)
  828.             lenline[y] = 0;
  829.         return;
  830.     }
  831.     /* scr_mode == ANSI */
  832.     if (yfirst == 0 && ylast == lines-1) {
  833.         if (so_mode == On)
  834.             standend();
  835.         move(0, 0);        /* since some ANSI'd don't move */
  836.         cputs(A_ED);
  837.         cur_y = cur_x = 0;
  838.         for (y = yfirst; y < ylast; y++)
  839.             lenline[y] = 0;
  840.         return;
  841.     }
  842.     for (y = yfirst; y <= ylast; y++) {
  843.         if (lenline[y] > 0) {
  844.             move(y, 0);
  845.             clr_to_eol();
  846.         }
  847.     }
  848. }
  849.  
  850. Hidden Procedure
  851. clr_to_eol()
  852. {
  853.     if (so_mode == On)
  854.         standend();
  855.     switch (scr_mode) {
  856.     case BIOS:
  857.         regs.h.bh = 0;    /* page */
  858.         regs.x.cx = lenline[cur_y] - cur_x;
  859.         regs.h.al = ' ';
  860.         regs.h.bl = V_NORMAL;
  861.         bios10(9);
  862.         break;
  863.     case ANSI:
  864.         cputs(A_EL);
  865.         break;
  866.     }
  867.     lenline[cur_y] = cur_x;
  868. }
  869.  
  870. Hidden Procedure        /* scrolling for BIOS */
  871. biosscrollup(yfirst, ylast, by)
  872. int yfirst;
  873. int ylast;
  874. int by;
  875. {
  876.     regs.h.al = (by < 0 ? -by : by);
  877.     regs.h.ch = yfirst;
  878.     regs.h.cl = 0;
  879.     regs.h.dh = ylast;
  880.     regs.h.dl = cols-1;
  881.     regs.h.bh= V_NORMAL;
  882.     bios10(by < 0 ? 7 : 6);
  883.     cur_y = cur_x = Undefined;
  884.     if (by > 0)
  885.         scr_lines(yfirst, ylast, by, 1);
  886.     else
  887.         scr_lines(ylast, yfirst, -by, -1);
  888. }
  889.  
  890. Hidden Procedure        /* Reset internal administration accordingly */
  891. scr_lines(yfrom, yto, n, dy)
  892. int yfrom, yto, n, dy;
  893. {
  894.     register int y, x;
  895.     char *saveln;
  896.  
  897.     while (n-- > 0) {
  898.         saveln = line[yfrom];
  899.         for (y = yfrom; y != yto; y += dy) {
  900.             line[y] = line[y+dy];
  901.             lenline[y] = lenline[y+dy];
  902.         }
  903.         line[yto] = saveln;
  904.         for (x = 0; x < cols; x++ )
  905.             line[yto][x] = ' ';
  906.         lenline[yto] = 0;
  907.     }
  908. }
  909.  
  910. Hidden Procedure
  911. lf_scroll(yto, by)
  912. int yto;
  913. int by;
  914. {
  915.     register int n = by;
  916.  
  917.     move(lines-1, 0);
  918.     while (n-- > 0) {
  919.         putch('\n');
  920.     }
  921.     scr_lines(0, lines-1, by, 1);
  922.     move_lines(lines-1-by, lines-1, lines-1-yto, -1);
  923.     clear_lines(yto-by+1, yto);
  924. }
  925.  
  926. Hidden Procedure        /* for dumb scrolling, uses and updates */
  927. move_lines(yfrom, yto, n, dy)    /* internal administration */
  928. int yfrom;
  929. int yto;
  930. int n;
  931. int dy;
  932. {
  933.     while (n-- > 0) {
  934.         put_line(yto, 0, line[yfrom], lenline[yfrom]);
  935.         yfrom += dy;
  936.         yto += dy;
  937.     }
  938. }
  939.  
  940. Hidden Procedure ring_bell()
  941. {
  942.     switch (scr_mode) {
  943.     case BIOS:
  944.         regs.h.al = '\007';
  945.         regs.h.bl = V_NORMAL;
  946.         bios10(14);
  947.         break;
  948.     case ANSI:
  949.         putch('\007');
  950.         break;
  951.     }
  952. }
  953.  
  954. /*
  955.  * Show the current internal statuses of the screen on stderr.
  956.  * For debugging only.
  957.  */
  958.  
  959. #ifdef SHOW
  960. Visible Procedure
  961. trmshow(s)
  962. char *s;
  963. {
  964.     int y, x;
  965.  
  966.     fprintf(stderr, "<<< %s >>>\n", s);
  967.     for (y = 0; y < lines; y++) {
  968.         for (x = 0; x <= lenline[y] /*** && x < cols ***/ ; x++) {
  969.             fputc(line[y][x]&CHAR, stderr);
  970.         }
  971.         fputc('\n', stderr);
  972.         for (x = 0; x <= lenline[y] && x < cols-1; x++) {
  973.             if (line[y][x]&SOBIT)
  974.                 fputc('-', stderr);
  975.             else
  976.                 fputc(' ', stderr);
  977.         }
  978.         fputc('\n', stderr);
  979.     }
  980.     fprintf(stderr, "CUR_Y = %d, CUR_X = %d.\n", cur_y, cur_x);
  981.     VOID fflush(stderr);
  982. }
  983. #endif
  984.  
  985. /*
  986.  * Interrupt handling.
  987.  *
  988.  * (This has not properly been tested, nor is it clear that
  989.  * this interface is what we want.  Anyway, it's here for you
  990.  * to experiment with.  What does it do, you may ask?
  991.  * Assume an interactive program which reads its characters
  992.  * through trminput.  Assume ^C is the interrupt character.
  993.  * Normally, ^C is treated just like any other character: when
  994.  * typed, it turns up in the input.  The program may understand
  995.  * input ^C as "quit from the current mode".
  996.  * Occasionally, the program goes into a long period of computation.
  997.  * Now it would be uninterruptible, except if it calls trminterrupt
  998.  * at times in its computational loop.  Trminterrupt magically looks
  999.  * ahead in the input queue, and if it sees a ^C, discards all input
  1000.  * before that point and returns Yes.  It also sets a flag, so that
  1001.  * the interupt "sticks around" until either trminput or trmavail
  1002.  * is called.  It is undefined whether typing ^C several times in
  1003.  * a row is seen as one interrupt, or an interrupt followed by input
  1004.  * of ^C's.  A program should be prepared for either.)
  1005.  */
  1006.  
  1007. static bool intrflag= No;
  1008.  
  1009. static
  1010. handler(sig)
  1011.     int sig;
  1012. {
  1013.     signal(sig, handler);
  1014.     intrflag= Yes;
  1015. }
  1016.  
  1017. static
  1018. set_handler()
  1019. {
  1020.     signal(SIGINT, handler);
  1021. }
  1022.  
  1023. bool
  1024. trminterrupt()
  1025. {
  1026.     /* Force a check for Control-break which will call handler. */
  1027.     kbhit();
  1028.     return intrflag;
  1029. }
  1030.  
  1031. /*
  1032.  * Terminal input without echo.
  1033.  * (This is a recent addition to this module, but will soon be standard).
  1034.  */
  1035.  
  1036. trminput()
  1037. {
  1038.     intrflag= No;
  1039.     return bdos(0x7, 0, 0) & 0377; /* Input, no echo, no ^C checks */
  1040. }
  1041.  
  1042. /*
  1043.  * Check for character available.
  1044.  *
  1045.  * To do this properly, should call DOS function 6,
  1046.  * but the relevant bit (the Z flag) can't be checked from C.
  1047.  * For now, say we don't know.
  1048.  */
  1049.  
  1050. trmavail()
  1051. {
  1052.     intrflag= No;
  1053.     return -1;
  1054. }
  1055.  
  1056. trmsuspend()
  1057. {
  1058.     /* Not implementable on MS-DOS */
  1059. }
  1060.