home *** CD-ROM | disk | FTP | other *** search
/ vsiftp.vmssoftware.com / VSIPUBLIC@vsiftp.vmssoftware.com.tar / FREEWARE / FREEWARE40.ZIP / flistfrontend / src / crt.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-06-04  |  19.6 KB  |  752 lines

  1. #ifndef NO_IDENT
  2. static char *Id = "$Id: crt.c,v 1.7 1995/06/04 18:58:12 tom Exp $";
  3. #endif
  4.  
  5. /*
  6.  * Title:    crt.c - "FLIST" display routines
  7.  * Author:    Thomas E. Dickey
  8.  * Created:    03 May 1984
  9.  * Last update:
  10.  *        27 May 1995, prototypes
  11.  *        07 Oct 1985, ensure that if term-size changed, LPP is ok.
  12.  *        05 Oct 1985, added code to support 80/132-column switch.
  13.  *        15 Jun 1985, reference functions as '(*func)' to make CC2.0 happy
  14.  *        12 Jun 1985, reset keypad mode before entering help-routine.
  15.  *        09 May 1985, moved on-system test to 'whoami'.
  16.  *        04 Apr 1985, forgot length-arg in strncmp-SYS$
  17.  *        28 Mar 1985, added 'status' argument to 'error'
  18.  *        20 Mar 1985, added 'crt_help' interface routine.
  19.  *        14 Mar 1985, added CTRL/C-ast to force crt-reset.  Also, verify
  20.  *                 that screen-height is even, at least four lines.
  21.  *        02 Feb 1985, added 'crt_margin' entry, cleaned up margin-usage
  22.  *                 in 'crt_quit', 'crt_reset'.
  23.  *        27 Jan 1985, added 'crt_qsgr' entry.
  24.  *        11 Jan 1985, added 'crt_reset' routine.
  25.  *        03 Dec 1984, changed 'crt__put' to 'putraw', omitted 'crt__flush'
  26.  *                 (no more Unix-I/O).  This should avoid a bug in
  27.  *                 the VMS terminal driver (see: 'getpass').
  28.  *        04 Nov 1984, merged 'crt_test' into 'crt_text'.  Added logic
  29.  *                 supporting 'to_eql[]', 'to_chg[]', which permits
  30.  *                 'crt_text' to skip over gaps which are similar.
  31.  *        01 Nov 1984, in 'crt_test', skip leading spaces when writing
  32.  *                 text to a null line.
  33.  *        30 Oct 1984, alter threshold for 'crt_scroll'
  34.  *        17 Oct 1984, added 'crt_refresh', 'crt_x', 'crt_y' entries
  35.  *        15 Sep 1984, added 'crt__put', 'crt__flush' entries
  36.  *        24 Aug 1984, use 'crt.h' include
  37.  *        04 Aug 1984, protect 'crt_text' by killing tabs, etc.
  38.  *        27 Jun 1984
  39.  *        28 May 1984, to chop into 'crt' and 'dds' modules
  40.  *
  41.  *    These routines perform screen display/refresh operations for FLIST
  42.  *    and BROWSE.
  43.  */
  44.  
  45. #include    <starlet.h>
  46. #include    <lib$routines.h>
  47. #include    <stdlib.h>
  48. #include    <stdio.h>
  49. #include    <ctype.h>
  50. #include    <descrip.h>
  51. #include    <iodef.h>
  52. #include    <ssdef.h>
  53. #include    <string.h>
  54. #include    <stsdef.h>
  55. #include    <ttdef.h>
  56.  
  57. #include    "bool.h"
  58. #include    "canopen.h"
  59. #include    "names.h"
  60. #include    "whoami.h"
  61.  
  62. #include    "strutils.h"
  63.  
  64. #include    "flist.h"    /* crt.h + error + warn */
  65.  
  66. extern    void    help (char *lib, char *prg, int maxcol);
  67. /*
  68.  * Valid, but no-longer-documented screen package interface
  69.  */
  70. extern    void scr$set_scroll (int lo, int hi);
  71. extern    void scr$set_cursor (int y, int x);
  72. extern    void scr$erase_page (void);
  73. extern    void scr$erase_line (void);
  74. extern    void scr$down_scroll (void);
  75. extern    void scr$up_scroll (void);
  76. extern    void lib$put_screen(struct dsc$descriptor_s *p, ...);
  77. extern    void lib$screen_info (short *term_flags, short *term_type, short *width, short *lpp);
  78.  
  79. /*
  80.  * VAX/VMS terminal-independent routines know about the ANSI "Set Graphics
  81.  * rendition" codes.  Those available via the VMS run-time library are:
  82.  * (VMS coding is additive)
  83.  */
  84. #define    sgrOFF        0
  85. #define    sgrBOLD        1
  86. #define    sgrREVERSE    2
  87. #define    sgrBLINK    4
  88. #define    sgrUNDERL    8
  89.  
  90. /*
  91.  * Local (static) data:
  92.  */
  93.  
  94. #define    FLAG_DEC    1    /* Terminal is a DEC crt        */
  95. #define    FLAG_ANSI    2    /* Terminal is ANSI 3.64-compatible    */
  96.  
  97. static    char    *bigvec    = nullC; /* => crtvec[0] */
  98.  
  99. /* Make the display memory public so it can be examined: */
  100. char    *crtvec[CRT_LINES];    /* prior-state of screen        */
  101.  
  102. static
  103. BYTE    sgrvec[CRT_LINES];    /* last code used via 'crt_text'    */
  104.  
  105. static
  106. short    term_flags,
  107.     term_type,        /* terminal-type (see 'dcdef.h')    */
  108.     width,            /* Current logical-width of screen    */
  109.     v_width,        /* Maximum width reserved in 'crtvec[]'    */
  110.     lpp,            /* Current logical-length of screen    */
  111.     v_lpp;            /* Maximum length reserved in 'crtvec[]'*/
  112. #define    lpp1    (lpp-1)
  113. #define    lpp2    (lpp-2)
  114.  
  115. static
  116. int    top_line,        /* index number of first line displayed    */
  117.     end_line,        /* " " " last line in screen        */
  118.     top_margin,        /* [1..lpp] top scrolling margin    */
  119.     end_margin,        /* [1..lpp] bottom scrolling margin    */
  120.     NLflag,            /* TRUE iff '\n'-adjust needed        */
  121.     lasty, lastx;        /* Last cursor position, for filtering    */
  122.  
  123. static    int    crt__lpp0 (int length);
  124. static    void    crt_ctl_c_ast (short *ttchan_);
  125.  
  126. /* <crt_ctl_c_ast>:
  127.  * CTRL/C AST routine.  Reset terminal characteristics, cancel pending I/O.
  128.  */
  129. static
  130. void    crt_ctl_c_ast (short *ttchan_)
  131. {
  132.     unsigned status;
  133.  
  134.     crt_quit (FALSE);
  135.     crt_move (lpp-1, 1);
  136.     status = sys$cancel(*ttchan_);
  137.     if (! $VMS_STATUS_SUCCESS(status)) lib$stop(status);
  138. }
  139.  
  140. /* <crt_init>:
  141.  * Initialize the screen and this module:
  142.  */
  143. void    crt_init (int (*func)(short *lpp_, short *width_))
  144. {
  145.     register int    j;
  146.     static    short    ttchan;
  147.     unsigned status;
  148.     static    $DESCRIPTOR(terminal,"SYS$COMMAND");
  149.  
  150.     /* Assign channel to terminal    */
  151.     status = sys$assign(&terminal, &ttchan, 0, 0);
  152.     if (! $VMS_STATUS_SUCCESS(status)) lib$stop(status);
  153.  
  154.     /* Enable control-C AST    */
  155.     status = sys$qiow (0,
  156.             ttchan,
  157.             IO$_SETMODE | IO$M_CTRLCAST, 0, 0, 0,
  158.             &crt_ctl_c_ast, &ttchan, 0, 0, 0, 0);
  159.     if (! $VMS_STATUS_SUCCESS(status)) error(status, 0);
  160.  
  161.     /* Get screen size, terminal type: */
  162.     lib$screen_info (&term_flags, &term_type, &width, &lpp);
  163.     if ((term_flags & FLAG_DEC) == 0)
  164.         error (0, "Terminal must be VT52, VT100 or ANSI\n");
  165.  
  166.     lpp = crt__lpp0 (lpp);
  167.  
  168.     crt__NL0 (TRUE);    /* Normally must absorb 1st '\n'    */
  169.     if (func)    (*func)(&lpp, &width);
  170.  
  171.     top_line = end_line = 0;
  172.     lpp    = min(CRT_LINES-1, lpp);
  173.     width    = min(CRT_COLS-1,  width);
  174.     v_width    = max(width, 132);
  175.  
  176.     lastx    = lasty    = 0;
  177.  
  178.     top_margin = 1;
  179.     end_margin = lpp;
  180.  
  181.     bigvec = calloc(lpp, 1+v_width);
  182.     for (j = 0; j <= lpp; j++)
  183.     {
  184.         sgrvec[j] = 0;
  185.         crtvec[j] = &bigvec[(1+v_width) * j];
  186.     }
  187.     crt_clear();
  188. }
  189.  
  190. /*
  191.  * "TT$_" codes for terminals encode specific types in the second hex digit.
  192.  * To return "generic" type, mask this digit first:
  193.  */
  194. #define    GEN_TYPE ((-1) << 4)
  195.  
  196. int    crt_vt100 (void)   { return ((term_type & GEN_TYPE) == TT$_VT100); }
  197. int    crt_vt52 (void)       { return ((term_type & GEN_TYPE) == TT$_VT52); }
  198. int    crt_ansi (void)       { return (term_flags & FLAG_ANSI); }
  199.  
  200. int    crt_top (void)       { return (top_line);    }
  201. int    crt_end (void)       { return (end_line);    }
  202.  
  203. int    crt_lpp (void)       { return (lpp);    }
  204. int    crt_width (void)   { return (width);    }
  205.  
  206. int    crt_topm (void)       { return (top_margin); }
  207. int    crt_endm (void)       { return (end_margin); }
  208.  
  209. int    crt_x (void)       { return (lastx);    }
  210. int    crt_y (void)       { return (lasty);    }
  211.  
  212. int    crt_qsgr(int inx)  { return (sgrvec[inx]); }
  213.  
  214. /* <crt_margin>:
  215.  * If this is an ANSI terminal, we may set scrolling margins.  Both FLIST
  216.  * and BROWSE use the special case of a status line at the end of the page.
  217.  */
  218. void    crt_margin (int lo, int hi)
  219. {
  220.     if (crt_ansi())
  221.         scr$set_scroll (top_margin = lo, end_margin = hi);
  222.     else
  223.         top_margin = 1, end_margin = lpp;
  224. }
  225.  
  226. /* <crt_set>:
  227.  * Set top_line/end_line w/o using global variables:
  228.  */
  229. int    crt_set (int top, int val)
  230. {
  231.     if (top)    top_line = val;
  232.     else        end_line = val;
  233.     return (val);
  234. }
  235.  
  236. /* <crt_quit>:
  237.  * On exit, clear the path-mark line, to set the cursor there.  We also
  238.  * reset the scrolling margins to their default value (without keeping this
  239.  * in our state) so that if this is called from a FLIST-SPAWN command, then
  240.  * we can use 'crt_reset' to restore our scrolling margins.
  241.  *
  242.  * The 'erase' option causes the last line of the screen to be cleared.
  243.  * If the last input was a newline, then VMS will echo this (scrolling up
  244.  * one line on exit).  If not (e.g., CTRL/Z), then the program will exit
  245.  * without scrolling.
  246.  */
  247. void    crt_quit(int erase)
  248. {
  249.     putraw ("\033>");        /* Exit alternate keypad mode */
  250.     scr$set_scroll (1, lpp);
  251.     if (erase)            /* Make a clean exit to command line */
  252.         crt__ED (lpp, 1);
  253. }
  254.  
  255. /* <crt_clear>:
  256.  * Force screen contents to be cleared.  VT100 info-call does a clear-screen
  257.  * for VT100, but not for VT52.
  258.  */
  259. void    crt_clear(void)
  260. {
  261.     register int    j;
  262.  
  263.     crt_reset ();
  264.     crt__ED (1,1);
  265.     for (j = 0; j < lpp; *crtvec[j++] = EOS);
  266. }
  267.  
  268. /* <crt_reset>:
  269.  * Reset scrolling margins and graphics-rendition for ANSI terminals (which
  270.  * may be left in funny modes by other programs).  This code is called on
  271.  * entry to FLIST/BROWSE, and also on return from spawned subprocesses.
  272.  */
  273. void    crt_reset (void)
  274. {
  275.     int    new_w, new_l,
  276.         adj_l, j;
  277.  
  278.     lastx = lasty = 999;    /* We don't really know where cursor is !! */
  279.     termsize (0, &new_w, &new_l);    /* Interrogate current screen size */
  280.     new_w    = min(v_width, new_w);
  281.     new_l    = crt__lpp0 (min(new_l, CRT_LINES));
  282.     if (new_w != width || new_l != lpp)
  283.     {
  284.         if (new_w < width)
  285.             for (j = 0; j < lpp; crtvec[j++][new_w] = EOS);
  286.  
  287.         adj_l    = new_l - lpp;
  288.         if (new_l > v_lpp)
  289.         {
  290.             bigvec = realloc(bigvec, new_l * (1+v_width));
  291.             for (j = 0; j <= new_l; j++)
  292.             {
  293.                 if (j >= lpp) sgrvec[j] = 0;
  294.                 crtvec[j] = &bigvec[(1+v_width) * j];
  295.             }
  296.         }
  297.         for (j = lpp1; j < new_l; j++)
  298.         {
  299.             sgrvec[j] = 0;
  300.             *crtvec[j] = EOS;
  301.         }
  302.         width    = new_w;
  303.         lpp    = new_l;
  304.         end_margin += adj_l;  end_margin = max(top_margin, end_margin);
  305.         end_line   += adj_l;  end_line   = max(top_line, end_line);
  306.     }
  307.  
  308.     crt_margin (top_margin, end_margin);    /* Scrolling margins    */
  309.     if (crt_ansi())
  310.         putraw ("\033[0m\033[20l");    /* SGR, new line */
  311.     if (crt_vt100())
  312.         putraw ("\033[?4l");        /* smooth-scroll */
  313.     putraw ("\033=");        /* Enter alternate Keypad mode */
  314. }
  315.  
  316. /* <crt_refresh>:
  317.  * Refresh the contents of the screen without requiring that the caller code
  318.  * re-generate the data to drive the display.
  319.  */
  320. void    crt_refresh (void)
  321. {
  322.     register
  323.     int    j,
  324.         save_x    = lastx,    /* This is where we think cursor was */
  325.         save_y    = lasty;
  326.     char    sline[CRT_COLS];
  327.  
  328.     lastx = lasty = 999;    /* We don't really know where cursor is !! */
  329.     crt_reset ();        /* Another process may have redefined TERM */
  330.     crt__ED (1,1);        /* clear the physical screen    */
  331.     for (j = 0; j < lpp; j++)
  332.     {
  333.         strcpy (sline, crtvec[j]);
  334.         *crtvec[j] = EOS;
  335.         crt_text (sline, j, sgrvec[j]);
  336.     }
  337.     crt_move (save_y, save_x);
  338. }
  339.  
  340. /* <crt_move>:
  341.  * Set the cursor to the next position from which to manipulate text.
  342.  * Note that the VMS screen routines do not (cannot really) do filtering
  343.  * to reduce the number of cursor movement sequences emitted.  This
  344.  * routine MUST be used for ALL cursor movement to permit it to do the
  345.  * optimization.
  346.  */
  347. void    crt_move (
  348.     int    y,            /* 1 <= y <= lpp    */
  349.     int    x)            /* 1 <= x <= width    */
  350. {
  351.     x = max(1, min(width, x));
  352.     y = max(1, min(lpp,   y));
  353.     if (lasty != y || lastx != x)
  354.     {
  355.         register
  356.         int    len,
  357.             jumpx = lastx - x,
  358.             jumpy = y - (lasty ? lasty : (y + 1)),
  359.             crlfs = (NLflag + jumpy) << 1;
  360.         char    sequence[20];
  361.  
  362.         /*
  363.          * The VMS-library routines are very dumb, not even
  364.          * defaulting parameters.  Use as my threshold an
  365.          * ANSI x3.64 cursor movement:
  366.          */
  367.         if ((y >  end_margin && lasty >  end_margin)
  368.         ||  (y <= end_margin && lasty <= end_margin))
  369.         {
  370.             sprintf (sequence, "?[%d;%d?", y-1, x-1);
  371.             len = strlen (sequence);
  372.         }
  373.         else    /* Disable CRLF if past scrolling margins */
  374.             len = crlfs;
  375.  
  376.         if ((lasty == y) && (jumpx > 0) && (jumpx < len))
  377.         {
  378.             while (jumpx-- > 0)
  379.                 putraw ("\b");
  380.         }
  381.         else if ((1 == x) && (jumpy > 0) && (crlfs < len))
  382.         {
  383.             crt__NL0 (FALSE);
  384.             while (jumpy-- > 0)
  385.                 putraw ("\r\n");
  386.         }
  387.         else
  388.             scr$set_cursor (y, x);
  389.     }
  390.     lasty = y;
  391.     lastx = x;
  392. }
  393.  
  394. /* <crt__lpp0>:
  395.  * We need at least four lines on the screen to run BROWSE.  For the same
  396.  * reason, the screen-length must be even.
  397.  */
  398. static
  399. int    crt__lpp0 (int length)
  400. {
  401.     if (length < 4)
  402.         error (0, "TERMINAL/PAGE must be at least four lines\n");
  403.     if (length & 1)    length--;
  404.     return (length);
  405. }
  406.  
  407. /*
  408.  * One peculiarity (which I work around) is that if I use '\n' to substitute
  409.  * for cursor movement, the first '\n' after a LIB$SCREEN_INFO() is absorbed.
  410.  * The flag 'NLflag' is intended to track this, so I can provide an extra
  411.  * '\n'.
  412.  *
  413.  * This entry point is needed because the Bitgraph init (see BROWSE) cancels
  414.  * the absorption.
  415.  */
  416. void    crt__NL0 (int flg)
  417. {
  418.     NLflag = flg;
  419. }
  420.  
  421. /*
  422.  * ANSI x3.64 defines erase functions:
  423.  */
  424. void    crt__ED (int y, int x)        /* Erase to end of Display    */
  425. {
  426.     crt_move (y, x);
  427.     scr$erase_page ();
  428. }
  429.  
  430. void    crt__EL (int y, int x)        /* Erase to end of Line    */
  431. {
  432.     crt_move (y, x);
  433.     scr$erase_line ();
  434. }
  435.  
  436. /* <crt_help>:
  437.  * We assume that the calling program uses a HELP-library!!
  438.  * This procedure interfaces to the HELP-dialog by clearing the screen,
  439.  * calling HELP, and restoring the screen on exit.
  440.  *
  441.  * If 'library' is null, this program ultimately will be installed, and use the
  442.  * standard VMS help-library.  However, if it is not currently run from the
  443.  * system-directory, then we must assume this is a test-run, from a private
  444.  * directory.  In this case, assume the help-library is in the same directory
  445.  * as the program is run from.
  446.  */
  447. void    crt_help (char *library, char *program)
  448. {
  449.     char    pathname[MAX_PATH], *c_;
  450.  
  451.     if (! library)
  452.     {
  453.         if (! whoami (pathname, 4))
  454.         {
  455.             strcpy (strnull(pathname), "HELPLIB.HLB");
  456.             if (canopen (library = pathname))
  457.             {
  458.                 warn ("Cannot open %s", pathname);
  459.                 return;
  460.             }
  461.         }
  462.     }
  463.  
  464.     crt_quit (FALSE);
  465.     crt__ED (1,1);
  466.     help (library, program, crt_width());
  467.     crt_refresh();
  468. }
  469.  
  470. /* <crt_text>:
  471.  * Display the given text 'bfr', at the specified line (range 0..lpp-1),
  472.  * with the 'mark' code for graphics-rendition (1=highlight, 0=normal).
  473.  *
  474.  * Use the 'crtvec[]' array to store the previous state of the given line,
  475.  * to trade string copy/compare against screen I/O.
  476.  *
  477.  * 'crtvec[]' uses bit-7 as the flag for highlighting, allowing the display
  478.  * driver to highlight part of any line.
  479.  *
  480.  * Do the blanking based on 'col_x' after all other changes have been made
  481.  * to the line, to avoid an extra cursor movement.
  482.  */
  483. void    crt_text(
  484.     char*    bfr,        /* "new" text to display        */
  485.     int    line,        /* index (0 to lpp-1) of line on screen    */
  486.     int    mode)        /* sgr-code if "highlighting"        */
  487. {
  488.     static $DESCRIPTOR(DSC_line,"");
  489.  
  490.     register int    col_l;
  491.     register int    col_r;
  492.     register int    col_x;
  493.     register char    *c1_    = bfr;
  494.     register char    *c2_;
  495.     register int    column = 0;
  496.     register int    eql;
  497.     register int    chg;
  498.  
  499.     char    bfr1    [CRT_COLS];
  500.     char    bfr2    [CRT_COLS];
  501.     static
  502.     BYTE    to_chg[CRT_COLS],    /* distance til changed-column    */
  503.         to_eql[CRT_COLS];    /* distance til difference    */
  504.  
  505.     line = max(0, min(lpp1, line));
  506.     sgrvec[line] = mode;
  507.  
  508.     /*
  509.      * Copy the input string to a temporary buffer, suppressing nongraphic
  510.      * characters, and then trimming trailing blanks.  Explicit test for
  511.      * space is used to avoid problems with highlighting.
  512.      */
  513.     for (c1_ = bfr1, c2_ = bfr; *c2_; c2_++)
  514.     {
  515.         if (isprint(toascii(*c2_)))    *c1_++ = *c2_;
  516.         else if (toascii(*c2_) == '\t')    *c1_++ = *c2_ + (' ' - '\t');
  517.         if ((c1_-bfr1) >= (width-1))    break;
  518.     }
  519.     *c1_ = EOS;
  520.     for (c1_ = bfr1 + strlen(bfr1) - 1;
  521.         (c1_ > bfr1) && (*c1_ == ' '); *c1_-- = EOS);
  522.  
  523.     /*
  524.      * Set highlighting to default, if caller uses defaults:
  525.      */
  526.     if (mode == 0)
  527.         mode    = (line >= lpp1) ? sgrBOLD : sgrREVERSE;
  528.  
  529.     /*
  530.      * Do string comparison to determine the limits of difference between
  531.      * the latest line and the prior contents.  If the older text is longer
  532.      * than the newer, also set index 'col_x' to show the erase-point.
  533.      *
  534.      * If the new text is longer than the old, skip leading spaces in the
  535.      * special case that no difference has been encountered (e.g., writing
  536.      * a word in the right margin on a blank line).
  537.      */
  538.  
  539.     col_l = col_r = col_x = 0;
  540.     for (c1_ = bfr1, c2_ = crtvec[line]; *c1_ && *c2_; c1_++, c2_++)
  541.     {
  542.         if (*c1_ != *c2_)
  543.         {
  544.             column = c1_ - bfr1 + 1;
  545.             if (col_l)    col_r = column;
  546.             else        col_l = column;
  547.         }
  548.     }
  549.  
  550.     if (*c2_)        /* Was the old line longer than new ?    */
  551.         col_x = c1_ - bfr1 + 1;
  552.     else if (*c1_)        /* If new is longer, extend right-col    */
  553.     {
  554.         if (col_l == 0)    /* If no difference yet, skip leading spaces */
  555.         {
  556.             while (*c1_ == ' ')    c1_++;
  557.             col_l = c1_ - bfr1 + 1;
  558.         }
  559.         col_r = strlen(bfr1);
  560.     }
  561.     if (col_l && (col_r == 0))
  562.         col_r = col_l;
  563.  
  564.     /*
  565.      * If we have differences, compute a distance-vector, so that if
  566.      * there are long gaps of similarity, then we can set up a jump.
  567.      */
  568.     if (col_l | col_r)
  569.     {
  570.         int    oldlen = strlen(c2_ = crtvec[line]);
  571.  
  572.         /*
  573.          * Pad the old string out to the length of the new part,
  574.          * so that the difference loop below will work.  (The
  575.          * column-index ranges from 1 to the length of the string.)
  576.          */
  577.         c1_ = bfr1 - 1;
  578.         c2_--;
  579.         while (oldlen < col_r)    c2_[++oldlen] = ' ';
  580.  
  581.         /*
  582.          * First pass: find the length of gaps in the difference,
  583.          * (to_chg) and the length of the difference-sections (to_eql).
  584.          *
  585.          * Example:
  586.          *    'xxx...yyy' - new
  587.          *    'xxx!!!yyy' - old
  588.          *     321000321  - chg
  589.          *     000321000  - eql
  590.          */
  591.         for (column = col_r, chg = eql = 0; column >= col_l; column--)
  592.         {
  593.             if (c1_[column] == c2_[column])
  594.             {
  595.                 to_chg[column] = ++chg;
  596.                 to_eql[column] = eql = 0;
  597.             }
  598.             else
  599.             {
  600.                 to_chg[column] = chg = 0;
  601.                 to_eql[column] = ++eql;
  602.             }
  603.         }
  604.  
  605.         /*
  606.          * Second pass: consolidate difference-sections which are
  607.          * too close together to jump economically.
  608.          * (patch: should make JUMP bigger still if there is a change
  609.          * in graphic-rendition on the seams.)
  610.          */
  611. #define    JUMP    10
  612.         for (column = col_l; column <= col_r;)
  613.         {
  614. loop:            if ((eql = column + to_eql[column]) > col_r)
  615.                 break;
  616.             if ((chg = to_chg[eql]) < JUMP)
  617.             {
  618.                 chg += eql;
  619.                 to_eql[column] += to_chg[eql] + to_eql[chg];
  620.                 goto loop;
  621.             }
  622.             else
  623.                 column = eql + to_chg[eql];
  624.         }
  625.  
  626.         /*
  627.          * Finally, update the actual screen, where it has changed.
  628.          */
  629.         strcpy (crtvec[line], bfr1);    /* Update internal buffer */
  630.         strcpy (bfr2, bfr1);        /* strip parity bit */
  631.         { register char *s = bfr2; while (*s) *s++ &= 0x7f; }
  632.         while (col_l <= col_r)    /* Update partial highlighting */
  633.         {
  634.             int    inx    = col_l - 1,
  635.                 right    = to_eql[col_l] + inx,
  636.                 mark;
  637.  
  638.             while (col_l <= right)
  639.             {
  640. #define    MARK    ishigh(bfr1[inx])
  641.                 inx    = col_l - 1;
  642.                 DSC_line.dsc$a_pointer = &bfr2[inx];
  643.                 for (mark = MARK;
  644.                     (inx < right) && (mark == MARK);
  645.                         inx++);
  646.                 DSC_line.dsc$w_length  = inx - col_l + 1;
  647.                 crt_move (line+1, col_l);
  648.                 lastx    += DSC_line.dsc$w_length;
  649.                 mark    = (mark ? mode : 0);
  650.                 lib$put_screen (&DSC_line, 0, 0, &mark);
  651.                 col_l    = inx + 1;
  652.             }
  653.             col_l    = right + 1;
  654.             if (col_l < col_r)    col_l += to_chg[col_l];
  655.         }
  656.     }
  657.     else if (col_x)        /* (entire line was blanked) */
  658.         strcpy (crtvec[line], bfr1);
  659.  
  660.     if (col_x)    crt__EL (line+1, col_x);
  661. }
  662.  
  663. /* <crt_high>:
  664.  * Mark the indicated character(s) with the highlighting bit (assume all ANSI
  665.  * devices do some form of graphic-rendition):
  666.  */
  667. void    crt_high (char *s_, int len)
  668. {
  669.     if (!crt_vt52())
  670.     {
  671.         while (len > 0)
  672.         {
  673.             if (!s_[--len])    break;
  674.             s_[len] |= 0x80;
  675.         }
  676.     }
  677. }
  678.  
  679. /* <crt_scroll>:
  680.  * Scroll the screen up (down) to permit the index to lie within the screen.
  681.  * If the amount to scroll is less than the screen size, do the scrolling by
  682.  * single-line indexing.  This code works on VT52 and VT100(-compatible) by
  683.  * using a prevailing set of scrolling margins.
  684.  *
  685.  * Return the resulting line in the screen (1..endscroll).
  686.  */
  687. static
  688. void    crt__null(int inx)
  689. {
  690.     /*nothing*/
  691. }
  692.  
  693. int    crt_scroll (int iline, int numlines, void (*func)(int))
  694. {
  695.     register
  696.     int    dif, j;
  697.     int    close    = end_margin - top_margin - 1;
  698.  
  699.     iline = min(numlines-1, max(0, iline));
  700.  
  701.     if (func == 0)    func = crt__null;
  702.  
  703.     if (iline < top_line)
  704.     {
  705.         if ((dif = (top_line - iline)) < close)
  706.         {
  707.             while (dif-- > 0)
  708.             {
  709.                 crt_move (top_margin, 1);
  710.                 top_line--;
  711.                 end_line = min(top_line+lpp2, end_line);
  712.                 scr$down_scroll ();
  713.                 for (j = end_margin-1; j >= top_margin; j--)
  714.                     strcpy (crtvec[j], crtvec[j-1]);
  715.                 *crtvec[0]    = EOS;
  716.                 (*func)(top_line);
  717.             }
  718.         }
  719.         else
  720.         {
  721.             top_line -= dif;
  722.             end_line = min(top_line+lpp2, numlines-1);
  723.             for (j = top_margin-1; j < end_margin; j++)
  724.                 (*func)(top_line+j);
  725.         }
  726.     }
  727.     else if (iline > end_line)
  728.     {
  729.         if ((dif = (iline - end_line)) < close)
  730.         {
  731.             while (dif-- > 0)
  732.             {
  733.                 top_line++, end_line++;
  734.                 crt_move (end_margin, 1);
  735.                 scr$up_scroll ();
  736.                 for (j = top_margin-1; j < end_margin-1; j++)
  737.                     strcpy (crtvec[j], crtvec[j+1]);
  738.                 *crtvec[end_margin-1] = EOS;
  739.                 (*func)(end_line);
  740.             }
  741.         }
  742.         else
  743.         {
  744.             top_line += dif;
  745.             end_line += dif;
  746.             for (j = top_margin-1; j < end_margin; j++)
  747.                 (*func)(top_line+j);
  748.         }
  749.     }
  750.     return (iline-top_line+1);
  751. }
  752.