home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume9 / teco / part04 / te_window.c
Encoding:
C/C++ Source or Header  |  1987-03-11  |  32.2 KB  |  982 lines

  1. /* TECO for Ultrix   Copyright 1986 Matt Fichtenbaum                        */
  2. /* This program and its components belong to GenRad Inc, Concord MA 01742    */
  3. /* They may be copied if this copyright notice is included                    */
  4.  
  5. /* te_window.c   window for teco   10/10/86 */
  6. /* This attempts to be a real window, without unecessary redraw */
  7. /* it is very VT-100 specific, and ought to be rewritten to be general */
  8.  
  9. #include "te_defs.h"
  10.  
  11. /* maximum screen height and width (horiz and vert, not height and vidth) */
  12. #define W_MAX_V 70
  13. #define W_MAX_H 150
  14. #define MAX 0x7fffffff            /* maximum positive integer, for "last modified" location */
  15. #define W_MARK 0200                /* "this loc is special" in screen image */
  16.  
  17.  
  18. /* image of current window */
  19.  
  20. struct w_line            /* data associated with one screen line */
  21.     {
  22.     int start, end;                /* dot at beginning, at end */
  23.     short n, cflag, col;        /* number of char positions used, line continuation flag, starting col */
  24.     char ch[W_MAX_H];            /* image of line */
  25.     }
  26.      w_image[W_MAX_V];
  27.  
  28.  
  29. /* define "this line is continued" / "this line is a continuation" flags */
  30. #define WF_BEG 1
  31. #define WF_CONT 2
  32.  
  33. struct w_line *wlp[W_MAX_V];    /* each word points to the corresponding line's data structure */
  34.  
  35. struct qp w_p1;                    /* pointer for window access to buffer */
  36.  
  37. short curr_x, curr_y;            /* active character position */
  38. short term_x, term_y;            /* current terminal cursor position */
  39. short curs_x, curs_y;            /* current teco dot screen coordinates */
  40. short last_y;                    /* last used line in window */
  41. char curs_c;                    /* code for char at cursor */
  42. char *curs_p;                    /* pointer to cursor loc in window image */
  43. short curs_crflag;                /* flag that cursor is on a CR */
  44. short redraw_sw;                /* forces absolute redraw */
  45.  
  46.  
  47. /* fill characters and terminal speeds: 0th entry used when std out is not a terminal */
  48. char win_speeds[] = { 0, 0, B9600, B4800, B2400, B1800, B1200, B600, B300, B200, B150, B134, B110 };
  49. char win_dlye[] =   { 0, 90, 45, 23, 11, 9, 6, 3, 1, 1, 1, 1, 1 };    /* delay for erase-screen */
  50. char win_dlys[] =   { 0, 60, 30, 15, 7, 6, 4, 2, 1, 1, 0, 0, 0 };    /* delay for scroll ops */
  51. char win_dlyl[] =   { 0, 4, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 };        /* delay for erase line */
  52. char win_dlyc[] =   { 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };        /* delay for other control functions */
  53. short win_speed;
  54. /* routine to perform simple scope operations */
  55. /* (an attempt to concentrate VT-100 specific things in one place) */
  56.  
  57. vt(func)
  58.     int func;
  59.     {
  60.     short t;
  61.     switch (func)
  62.         {
  63.         case VT_CLEAR:            /* clear screen */
  64.             fputs("\033[H\033[J", stdout);
  65.             for (t = 0; t < win_dlye[win_speed]; t++) putchar('\0');    /* fill chars */
  66.             break;
  67.  
  68.         case VT_EEOL:            /* erase to end of line */
  69.             fputs("\033[K", stdout);
  70.             for (t = 0; t < win_dlyl[win_speed]; t++) putchar('\0');    /* fill chars */
  71.             break;
  72.  
  73.         case VT_EBOL:            /* erase from beginning of line */
  74.             fputs("\033[1K", stdout);
  75.             for (t = 0; t < win_dlyl[win_speed]; t++) putchar('\0');    /* fill chars */
  76.             break;
  77.  
  78.         case VT_SETSPEC1:        /* reverse video */
  79.             fputs("\033[7m", stdout);
  80.             break;
  81.  
  82.         case VT_SETSPEC2:        /* bright reverse video */
  83.             fputs("\033[1;7m", stdout);
  84.             break;
  85.  
  86.         case VT_CLRSPEC:        /* normal video */
  87.             fputs("\033[0m", stdout);
  88.             break;
  89.  
  90.         case VT_BS1:            /* backspace 1 spot */
  91.             fputs("\b \b", stdout);
  92.             break;
  93.  
  94.         case VT_BS2:            /* backspace 2 spots */
  95.             fputs("\b \b\b \b", stdout);
  96.             break;
  97.  
  98.         case VT_LINEUP:            /* up one line */
  99.             fputs("\033[1A", stdout);
  100.             break;
  101.         }
  102.     }
  103. /* routine to set window parameters */
  104.  
  105. /* 0: scope type, 1: width, 2: height, 3: seeall, 4: mark position,        */
  106. /* 5: hold mode, 6: upper left corner position, 7: scroll region size    */
  107.  
  108. /*                   0   1        2         3  4      5   6       7    */
  109. int win_min[]  = { 4,  20,        4,         0, 0,     -1,  1,   0 } ;    /* min values for window parameters */
  110. int win_max[]  = { 4,  W_MAX_H,    W_MAX_V, 1, MAX, 12, -1,  20 } ;    /* max values */
  111. int win_data[] = { 4,  132,        24,         0, 0,      0,  0,   0 } ;    /* window parameters    */
  112.  
  113. int window_size;                                /* # of lines in a window */
  114.  
  115. do_window(ref_flag)
  116.     int ref_flag;                    /* nonzero forces "refresh" operation */
  117.     {
  118.     int i;
  119.  
  120.     if (colonflag && !ref_flag)
  121.         {
  122.         i = get_value(0);    /* get sub-function */
  123.         if ((i < 0) || (i > 7)) ERROR(E_IWA);
  124.         if (!esp->flag2)    /* it's a "get" */
  125.             {
  126.             esp->val1 = win_data[i];
  127.             esp->flag1 = 1;
  128.             }
  129.         else
  130.             {
  131.             if ((esp->val2 < win_min[i]) || (esp->val2 > win_max[i]))    /* check range */
  132.                 ERROR(E_IWA);
  133.             if (i == 7)
  134.                 {
  135.                 if (esp->val2)
  136.                     {
  137.                     WN_scroll = esp->val2;
  138.                     window_size = WN_height - WN_scroll;    /* define size of window area */
  139.                     window(WIN_INIT);            /* turn on window */
  140.                     }
  141.                 else window(WIN_OFF);            /* turn off window */
  142.                 }
  143.             win_data[i] = esp->val2;            /* redundant for ~0,7:w, but no harm */
  144.             esp->flag2 = 0;
  145.             window(WIN_REDRAW);                    /* redraw window */
  146.             }
  147.         }
  148.  
  149.     else                                /* no colon, or ^W command */
  150.         {
  151.         if (esp->flag1 || ref_flag)
  152.             {
  153.             if (!ref_flag && (esp->val1 == -1000)) redraw_sw = 0;    /* -1000W: "forget that output was done" */
  154.             else window(WIN_DISP);        /* nW or ^W refreshes window */
  155.             }
  156.          esp->flag2 = esp->flag1 = 0;        /* no colon, consume args */
  157.         }
  158.     colonflag = 0;
  159.     esp->op = OP_START;
  160.     }
  161. /* routine to update screen size with numbers obtained from environment    */
  162. /* (called by main program's initialization)                            */
  163.  
  164. set_term_par(lines, cols)
  165.     int lines, cols;
  166.     {
  167.     if ((lines >= win_min[2]) && (lines <= win_max[2])) window_size = win_data[2] = lines;
  168.     if ((cols >= win_min[1]) && (cols <= win_max[1])) win_data[1] = cols;
  169.     }
  170.  
  171.  
  172. /* window routine.  performs function as indicated by argument                        */
  173. /* WIN_OFF:        disables split-screen scrolling                                        */
  174. /* WIN_SUSP:    disables split-screen scrolling temporarily                            */
  175. /* WIN_INIT:    sets up display support if split-screen scrolling enabled, else nop    */
  176. /* WIN_RESUME:    re-enables display support                                            */
  177. /* WIN_REDRAW:    causes window to be redrawn on next refresh call                    */
  178. /* WIN_REFR:    if scrolling enabled, redoes window, else if ev or es enabled, does    */
  179. /*                that, else nop                                                        */
  180. /* WIN_LINE:    does WIN_REFR unless that wouldn't do anything, in which case        */
  181. /*                it does effective 1EV output                                        */
  182.  
  183. int last_dot = -1;                /* last dot location */
  184.  
  185. window(arg)
  186.     int arg;
  187.     {
  188.     int i;
  189.  
  190.     switch (arg)
  191.         {
  192.         case WIN_OFF:                /* final window off */
  193.         case WIN_SUSP:                /* temp window off */
  194.             if (WN_scroll)            /* if reset/clean up */
  195.                 {
  196.                 /* full margins, cursor to last line, erase line */
  197.                 printf("\033[r\033[%d;0H\033[K", WN_height);
  198.                 }
  199.             break;
  200.  
  201.         case WIN_INIT:                /* initialize window - find output speed */
  202.             if (out_noterm) win_speed = 0;        /* std out is not a terminal */
  203.             else
  204.                 {
  205.                 for (win_speed = 1; (win_speeds[win_speed] != ttybuf.sg_ospeed) && (win_speed < 13); win_speed++);
  206.                 if (win_speed == 13) win_speed = 1;
  207.                 }
  208.             w_init();                /* set up screen image buffer */
  209.             if (WN_scroll) vt(VT_CLEAR);        /* if split-screen is enabled, clear screen */
  210.                                     /* (fall through to "resume") */
  211.  
  212.         case WIN_RESUME:            /* re-enable window */
  213.             if (WN_scroll)            /* set scroll region, cursor to bottom */
  214.                 printf("\033[%d;%dr\033[%d;0H", WN_height - WN_scroll + 1, WN_height, WN_height);
  215.             break;
  216.         case WIN_REDRAW:        /* force redraw of window */
  217.             redraw_sw = 1;
  218.             break;
  219.  
  220.         case WIN_LINE:            /* display one line unless window enabled or ev */
  221.             if (WN_scroll || ev_val) window(WIN_REFR);        /* if a real window is set, do it */
  222.             else if (w_setptr(dot, &w_p1))        /* set pointer to dot... and if there's a buffer */
  223.                 {
  224.                 w_lines(0, &w_p1, &w_p1);        /* get to beginning of line */
  225.                 window0(1);                        /* and type 1 line */
  226.                 }
  227.             break;
  228.  
  229.         case WIN_REFR:            /* if enabled, refresh window; else do ev or es */
  230.             if (WN_scroll) window1();        /* if scrolling enabled, refresh the window */
  231.             else if ((ev_val) || (es_val && search_flag))    /* else if ev or es, do that */
  232.                 {
  233.                 i = (ev_val) ? ev_val : es_val;
  234.                 if (w_setptr(dot, &w_p1))        /* set a pointer at dot... and if there's a buffer */
  235.                     window0(i - w_lines(1 - i, &w_p1, &w_p1));    /* go back (i-1) lines and ahead (i) lines */
  236.                 }
  237.             break;
  238.  
  239.         case WIN_DISP:                    /* display buffer independent of whether scroll mode is enabled */
  240.             window1();
  241.             break;
  242.  
  243.         }        /* end of switch */
  244.  
  245.     fflush(stdout);            /* send output out */
  246.     }                    /* end of window() */
  247. /* routine to type n lines with character at "dot" in reverse video            */
  248. /* used for ev, es, and <BS> or <LF> as immediate commands                    */
  249. /* starting char position is in w_p1; argument is number of lines            */
  250.  
  251. window0(num)
  252.     int num;
  253.     {
  254.     int wi;
  255.     char wc;            /* temp char */
  256.  
  257.     for (wi = w_p1.dot; (num > 0) && (wi < z); wi++)        /* for each character */
  258.         {
  259.         wc = w_p1.p->ch[w_p1.c];        /* get character */
  260.  
  261.         if ((char_count >= WN_width) && (wc != CR) && !(spec_chars[wc] & A_L))    /* if about to exceed width */
  262.             {
  263.             if (et_val & ET_TRUNC) goto w0_noprint;            /* truncate: don't print this */
  264.             else
  265.                 {
  266.                 fputs("\033[K\015\012\033(0h\033(B ", stdout);    /* <eeol> "NL space" */
  267.                 char_count = 2;
  268.                 --num;                    /* one fewer line remaining */
  269.                 }
  270.             }
  271.  
  272.         if (wi == dot)                    /* if this char is at the pointer */
  273.             {
  274.             vt(VT_SETSPEC2);            /* set reverse video */
  275.             if (wc == TAB)
  276.                 {
  277.                 type_char(' ');        /* illuminate the first sp of a tab */
  278.                 vt(VT_CLRSPEC);        /* clear reverse video */
  279.                 if (char_count & tabmask) type_char(TAB);
  280.                 }
  281.             else                        /* not a tab */
  282.                 {
  283.                 if ((wc == CR) && (char_count < WN_width))    /* CR at rh margin: don't display cursor */
  284.                     {
  285.                     type_char(' ');        /* cr: put a space after line */
  286.                     vt(VT_EEOL);        /* erase to eol */
  287.                     }
  288.                 type_char(wc);            /* type the char, or exec CR */
  289.                 if (wc == LF)
  290.                     {
  291.                     fputs("\033(0", stdout);
  292.                     type_char('e');
  293.                     fputs("\033(B", stdout);
  294.                     }
  295.                 vt(VT_CLRSPEC);        /* clear reverse video */
  296.                 }
  297.             }
  298.         else                    /* this is not char at pointer */
  299.             {
  300.             if (wc == CR && curr_x < WN_width) vt(VT_EEOL);        /* erase to EOL */
  301.             type_char(wc);
  302.             }
  303.         if ((wc == FF) || (wc == VT))        /* FF & VT end a line */
  304.             {
  305.             vt(VT_EEOL);            /* erase rest of this line */
  306.             crlf();                    /* and leave a blank one */
  307.             if (!(ez_val & EZ_NOVTFF)) --num;        /* if FF and VT count as line sep's, count them */
  308.             }
  309.  
  310.       w0_noprint:
  311.         if (++w_p1.c > CELLSIZE-1) w_p1.p = w_p1.p->f, w_p1.c = 0;    /* next char */
  312.         if (wc == LF) --num;    /* if this is a line feed, count lines */
  313.         }
  314.  
  315.     if (dot == z) fputs("\033[1;7m \033[0m\033[0K", stdout);    /* type one space and erase rest of line */
  316.     else fputs("\033[0K", stdout);            /* else just erase to EOL */
  317.     }
  318. /* routine to maintain the screen window                                        */
  319. /* if scroll mode is enabled, the VT100 screen is split and only the upper part */
  320. /* is used by this routine; else the whole screen is used.                        */
  321.  
  322. window1()
  323.     {
  324.     int i, j, m, lflag;
  325.  
  326.     if (!redraw_sw && (dot == last_dot) && (buff_mod == MAX)) return;        /* return if nothing has changed */
  327.  
  328.     block_inter(1);                            /* disable ^C interrupts */
  329.     if (WN_scroll) printf("\033[1;%dr", window_size);        /* scroll mode: redefine scroll region */
  330.     printf("\033[H");                        /* home */
  331.     term_y = term_x = 0;                    /* indicate cursor is at home */
  332.  
  333.     if ((redraw_sw) || (z <= wlp[0]->start)) window1_abs();        /* forced redraw, or z before start of screen */
  334.  
  335.  
  336. /* check whether pointer is before modified buffer location */
  337.  
  338.     else if (buff_mod >= dot)    /* yes */
  339.         {
  340.  
  341.         if (dot < wlp[0]->start)            /* if dot is before screen */
  342.             {
  343.             w_setptr(wlp[0]->start, &w_p1);    /* get to beginning of screen */
  344.  
  345.         /* check whether screen begins with the last part of a continued line */
  346.             for (j = 0; (wlp[j]->cflag & WF_CONT) && (j < window_size/2); j++);
  347.             if (j < window_size/2)            /* if so, does it continue less than halfway down the screen? */
  348.                 {
  349.  
  350.                 if (j)                        /* is there a partial line? */
  351.                     {
  352.                     w_lines(0, &w_p1, &w_p1);        /* 0L */
  353.                     j -= w_lines(1, &w_p1, NULL);        /* now j is number of display lines before screen */
  354.                     }
  355.         /* now look for how many lines back "dot" is: if screen starts with partial line, w_p1 has already been moved */
  356.         /* to beginning of the line and j equals the count of extra lines to scroll */
  357.  
  358.                 for (i = 0; (dot < w_p1.dot) && (i < window_size/2); ) i -= w_lines(-1, &w_p1, &w_p1);
  359.                 if ((dot >= w_p1.dot) && (i < window_size))            /* found point within reason */
  360.                     {
  361.                     w_scroll(j - i);            /* scroll screen down that many lines */
  362.                     curr_y = wlp[0]->cflag = wlp[0]->col = curr_x = 0;        /* start from top of screen */
  363.                     wlp[0]->start = w_p1.dot;    /* save starting char position */
  364.                     window2(0);                    /* and rewrite screen */
  365.                     }
  366.  
  367.                 else window1_abs();                /* farther back than that - redraw */
  368.                 }
  369.  
  370.             else window1_abs();                    /* continuation was too long: give up and redraw */
  371.             }                /* end of "dot is before screen" */
  372.  
  373.         else if (dot <= wlp[last_y]->end) window1_inc(dot);        /* on screen - redraw incrementally */
  374.  
  375.         else window1_after();                /* dot is after screen: scroll or redraw */
  376.         }                /* end of "dot is before modified point" */
  377.  
  378.  
  379. /* the modified point in the buffer is before dot */
  380.  
  381.     else
  382.         {
  383.         if (buff_mod < wlp[0]->start) window1_abs();    /* modified point before screen - redraw fully */
  384.  
  385.         else if (buff_mod <= wlp[last_y]->end)            /* modified point on screen */
  386.             {
  387.             for (m = 0; buff_mod > wlp[m]->end; m++);    /* find line with buff_mod */
  388.             w_setptr(wlp[m]->start, &w_p1);                /* set a pointer to start of line with buff_mod */
  389.             j = (m < window_size/2) ? window_size - 1 - m : window_size/2;    /* maximum # of lines between buff_mod & dot */
  390.             for (i = 0; (dot >= w_p1.dot) && (w_p1.dot < z) && (i <= j); )
  391.                 i += (lflag = w_lines(1, &w_p1, &w_p1) ) ? lflag : 1;    /* count lines from buff_mod to first line after dot */
  392.             if (i > j) window1_abs();                    /* too far - redraw */
  393.             else
  394.                 {
  395.                 if (lflag && (dot == z)) i++;            /* if at end, following a LF */
  396.                 w_setptr(wlp[m]->start, &w_p1);            /* pointer to start of area to redraw */
  397.                 if (i >= window_size - m)                /* if there are not enough blank lines on screen */
  398.                     w_scroll(i = i - window_size + m), curr_y = m - i, curs_y -= i;    /* scroll up the difference */
  399.                 else curr_y = m;
  400.                 curr_x = (wlp[curr_y]->cflag & WF_CONT) ? 2 : wlp[curr_y]->col;        /* line starts at left unless continuation */
  401.                 if ((curr_y > curs_y) && (curs_y >= 0)) w_rmcurs();        /* remove old cursor if it won't be written over */
  402.                 window2(0);                            /* rewrite newly cleared region */
  403.                 for (curr_x = 0; ++curr_y < window_size; )        /* clear rest of screen if needed */
  404.                     {
  405.                     wlp[curr_y]->cflag = 0;
  406.                     if (wlp[curr_y]->n) wlp[curr_y]->n = 0, vtm(VT_EEOL);
  407.                     }
  408.                 }
  409.             }            /* end "modified point on screen */
  410.  
  411.         else window1_after();        /* modified point after screen: scroll or redraw as appropriate */
  412.         }
  413. /* done redrawing: do cleanup work */
  414.  
  415.     if (WN_scroll)
  416.         {
  417.         printf("\033[%d;%dr", window_size+1, WN_height);    /* reset margins */
  418.         printf("\033[%d;0H", WN_height);        /* cursor to bottom */
  419.         }
  420.     else printf("\033[H");        /* no split screen: set home */
  421.  
  422.     fflush(stdout);                /* flush output */
  423.     WN_origin = wlp[0]->start;    /* save first char pos on screen */
  424.     redraw_sw = 0;                /* mark screen as updated */
  425.     buff_mod = MAX;
  426.     last_dot = dot;
  427.     block_inter(0);                /* reenable interrupts */
  428.     }
  429. /* routine to redraw screen absolutely */
  430.  
  431. window1_abs()
  432.     {
  433.     int i, j;
  434.  
  435.     curr_y = wlp[0]->col = curr_x = 0;                /* indicate where refresh starts */
  436.     set_pointer(dot, &w_p1);                        /* make a text buffer, if none, and refresh the display */
  437.     w_lines(0, &w_p1, &w_p1);                        /* do 0L */
  438.     if ((i = w_lines(window_size/2, &w_p1, NULL)) == 0) i = 1;        /* check how many lines after dot */
  439.     if (i > window_size/2) i = window_size/2;        /* limit amount after dot */
  440.     for (j = 0; (j < window_size - i) && (w_p1.dot > 0); )        /* find start of display area */
  441.         j -= w_lines(-1, &w_p1, &w_p1);
  442.     if (j > window_size - i) w_lines(1, &w_p1, &w_p1);            /* if too far back, move up one line */
  443.  
  444.     wlp[0]->start = w_p1.dot;        /* indicate where first window line starts */
  445.     window2(0);                        /* refresh the whole display */
  446.  
  447.     for (curr_x = 0; ++curr_y < window_size; )        /* blank out lines not written by window2 */
  448.         if (wlp[curr_y]->n || redraw_sw) wlp[curr_y]->n = 0, vtm(VT_EEOL);
  449.     }
  450.  
  451.  
  452.  
  453.  
  454. /* redraw screen incrementally */
  455.  
  456. window1_inc(wd)
  457.     int wd;                        /* argument is earliest change */
  458.     {
  459.     short temp_y;
  460.  
  461. /* find the line containing the character at wd */
  462.  
  463.     for (temp_y = 0; wd > wlp[temp_y]->end; temp_y++);
  464.  
  465.     if ((curs_y != temp_y) || (buff_mod == MAX) || curs_crflag)        /* if the cursor line won't be rewritten */
  466.         w_rmcurs();                    /* remove the old cursor */
  467.     curr_y = temp_y;                /* and go to work on the beginning of the line with dot */
  468.     curr_x = (wlp[curr_y]->cflag & WF_CONT) ? 2 : wlp[curr_y]->col;        /* line starts at left unless continuation */
  469.  
  470.     w_setptr(wlp[curr_y]->start, &w_p1);        /* make a pointer there */
  471.     window2(buff_mod == MAX);        /* if buffer not modified, redraw only the line with dot */
  472.  
  473.     if (buff_mod < MAX)            /* if buffer has changed, erase display lines beyond end of buffer */
  474.         for (curr_x = 0; ++curr_y < window_size; )
  475.             if ( ((wlp[curr_y]->start >= z) || (wlp[curr_y]->start <= wlp[curr_y-1]->end)) && (wlp[curr_y]->n || redraw_sw) )
  476.                 wlp[curr_y]->n = 0, vtm(VT_EEOL), wlp[curr_y]->cflag = 0;
  477.     }
  478. /* routine to move window downwards: scroll up or redraw as appropriate */
  479.  
  480. window1_after()
  481.     {
  482.     int i, lflag;
  483.  
  484.     w_rmcurs();                        /* remove old cursor */
  485.     w_setptr(wlp[window_size-1]->start, &w_p1);        /* set pointer to start of last line on screen */
  486.  
  487.     for (i = 0; (dot >= w_p1.dot) && (w_p1.dot < z) && (i <= window_size/2); )
  488.         i += (lflag = w_lines(1, &w_p1, &w_p1)) ? lflag : 1;    /* fwd one line at a time until > dot or end of buffer */
  489.  
  490.     if (i <= window_size/2)            /* found within n lines */
  491.         {
  492.         if (lflag && (dot == z)) ++i;                /* if dot is at end of buffer after a LF */
  493.         if (i >= window_size - last_y)                /* if there are not enough blank lines on screen */
  494.             w_scroll(i - window_size + last_y), curr_y = window_size - i;    /* scroll up the difference */
  495.         else curr_y = last_y;
  496.  
  497.         while (curr_y && (wlp[curr_y]->cflag & WF_CONT)) --curr_y;        /* get to start of cont'd lines */
  498.         w_setptr(wlp[curr_y]->start, &w_p1);        /* pointer to start of area to redraw */
  499.         curr_x = wlp[curr_y]->col;                    /* redraw starts at line's first column */
  500.         window2(0);                                    /* rewrite newly cleared region */
  501.         }
  502.  
  503.     else window1_abs();                        /* move down is too far: redraw fully */
  504.     }
  505.  
  506.  
  507.  
  508. /* routine to remove the existing cursor */
  509.  
  510. w_rmcurs()
  511.     {
  512.     if (curs_c)            /* if there was a cursor */
  513.         {
  514.         w_move(curs_y, curs_x);        /* go remove the old cursor */
  515.         if (curs_c & W_MARK) fputs("\033(0", stdout);        /* if prev char was a spec char */
  516.         putchar(*curs_p = curs_c);    /* put back the char that was there */
  517.         if (curs_c & W_MARK) fputs("\033(B", stdout);
  518.         ++term_x;                    /* and keep the terminal cursor loc. happy */
  519.         }
  520.     }
  521. /* routine to do actual display refresh                                                */
  522. /* called with w_p1 at starting char, curr_y, curr_x at starting coordinate            */
  523. /* rewrites to end of screen if arg = 0, or only until line with cursor if arg = 1    */
  524.  
  525. window2(arg)
  526.     int arg;
  527.     {
  528.     register int wdot;
  529.     register char wc;            /* temp char */
  530.     register short dflag;        /* nonzero if this is char at dot */
  531.     short cr_found;                /* indicates a cr found on this line */
  532.  
  533.     cr_found = 0;                /* clear "cr" flag in first line written */
  534.     for (wdot = w_p1.dot; (curr_y < window_size) && (wdot < z); wdot++)        /* for each character */
  535.         {
  536.         wc = w_p1.p->ch[w_p1.c] & 0177;        /* get character */
  537.         if (dflag = (wdot == dot)) if (arg) arg = -1;        /* save "this is char at dot", "on line with dot" */
  538.  
  539.         if (wc < ' ') switch (wc)                /* dispatch control characters */
  540.             {
  541.             case CR:
  542.                 if (dflag)            /* if cursor on this CR */
  543.                     {
  544.                     if (curr_x < WN_width) w_makecurs(' ', 1), w_type(' ', 1);    /* display a space, unless at end */
  545.                     else curs_crflag = curs_c = 0;                /* else set "no cursor displayed" */
  546.                     }
  547.                 /* trim remainder of line if this is first cr and old line was longer */
  548.                 if (!cr_found && ((curr_x < wlp[curr_y]->n) || redraw_sw))
  549.                     {
  550.                     wlp[curr_y]->n = curr_x;
  551.                     if (curr_x < WN_width) vtm(VT_EEOL);
  552.                     }
  553.                 cr_found = 1;            /* set cr flag */
  554.                 wlp[curr_y]->cflag &= ~WF_BEG;        /* this line is not continued */
  555.                 while (curr_y && (wlp[curr_y]->cflag & WF_CONT)) --curr_y;        /* if line is a continuation, scan up */
  556.                 curr_x = 0;
  557.                 break;
  558.  
  559.             case TAB:
  560.                 if (curr_x >= WN_width)
  561.                     {
  562.                     if (et_val & ET_TRUNC) goto noprint;
  563.                     if (w_overflow(wdot)) goto w2_exit;                /* extend line */
  564.                     }
  565.                 if (dflag) w_makecurs(' ', 0);
  566.                 w_type(' ', dflag);                        /* type one space */
  567.                 if (dflag)
  568.                     {
  569.                     vt(VT_CLRSPEC);        /* end reverse video */
  570.                     dflag = 0;
  571.                     }
  572.                 while ((curr_x & tabmask) && (curr_x < WN_width)) w_type(' ', 0);        /* finish tab */
  573.                 break;
  574.             case LF:
  575.                 while ((curr_y < window_size) && (wlp[curr_y]->cflag & WF_BEG)) ++curr_y;    /* last screen row of this line */
  576.                 wlp[curr_y]->end = wdot;        /* save char position that ended this line */
  577.                 if (dflag)        /* if this LF is at dot */
  578.                     {            /* put cursor there, save char that was there */
  579.                     w_makecurs( (curr_x < wlp[curr_y]->n) ? wlp[curr_y]->ch[curr_x] : ' ', 0);
  580.                     fputs("\033(0", stdout);            /* put in a "LF" char */
  581.                     w_type('e', 1);
  582.                     fputs("\033(B", stdout);
  583.                     }            /* if no cr found and not in last column, erase rest of line */
  584.                 if (!cr_found && (curr_x < wlp[curr_y]->n))
  585.                     {
  586.                     wlp[curr_y]->n = curr_x;
  587.                     if (curr_x < WN_width) vtm(VT_EEOL);
  588.                     }
  589.                 if (dflag) --curr_x;            /* put the cursor back before the artificial LF char, if any */
  590.                 if (curr_y >= window_size-1)    /* if at end of screen, exit, but... */
  591.                     {
  592.                     if (dflag) vt(VT_CLRSPEC);    /* if cursor is here, clear reverse video first */
  593.                     goto w2_exit;
  594.                     }
  595.  
  596.                 if ((wlp[curr_y]->cflag & WF_CONT) && (wlp[curr_y]->end - wlp[curr_y]->start == 1))    /* if a now-empty cont. line, */
  597.                     {                                                            /* flush it */
  598.                     if (curr_y > 0) wlp[curr_y-1]->cflag &= ~WF_BEG;            /* remove "cont'd" flag from prev line */
  599.                     arg = 0;                                                    /* and force redraw of rest of screen */
  600.                     if (curs_y == curr_y) curs_c = 0;                            /* if cursor was on this line, it will disappear */
  601.                     }
  602.                 else ++curr_y;                /* down one line if not absorbing blank contin. line */
  603.  
  604.                 wlp[curr_y]->start = wdot + 1;                /* assume line starts with next char */
  605.                 wlp[curr_y]->col = curr_x;                    /* save starting column */
  606.                 cr_found = wlp[curr_y]->cflag = 0;            /* clear line continuation flags */
  607.                 if (curr_x) w_ebol();                        /* if not at left margin, erase beginning of line */
  608.                 if (arg == -1)                                /* finished line with dot... quit if spec'd */
  609.                     {
  610.                     if (dflag)                        /* but first, if at cursor, clear reverse video */
  611.                         {
  612.                         vt(VT_CLRSPEC);
  613.                         dflag = 0;
  614.                         }
  615.                     return;
  616.                     }
  617.                 break;
  618.  
  619.             case ESC:
  620.                 if (curr_x >= WN_width)
  621.                     {
  622.                     if (et_val & ET_TRUNC) goto noprint;
  623.                     if (w_overflow(wdot)) goto w2_exit;                /* extend line */
  624.                     }
  625.                 if (dflag) w_makecurs('$', 0);
  626.                 w_type('$', dflag);
  627.                 break;
  628.             default:                    /* all other control chars print as ^X */
  629.                 if (curr_x >= WN_width - 1)
  630.                     {
  631.                     if (et_val & ET_TRUNC) goto noprint;
  632.                     if (w_overflow(wdot)) goto w2_exit;
  633.                     }
  634.                 if (dflag) w_makecurs('^', 0);
  635.                 w_type('^', dflag);                /* ^ */
  636.                 if (dflag)
  637.                     {
  638.                     vt(VT_CLRSPEC);        /* if at cursor, clear reverse video */
  639.                     dflag = 0;
  640.                     }
  641.                 w_type(wc | 0100, 0);
  642.                 break;
  643.             }                    /* end "switch" */
  644.         else                    /* a printing character */
  645.             {
  646.             if (curr_x >= WN_width)
  647.                 {
  648.                 if (et_val & ET_TRUNC) goto noprint;
  649.                 if (w_overflow(wdot)) goto w2_exit;                /* extend line */
  650.                 }
  651.             if (dflag) w_makecurs(wc, 0);
  652.             w_type(wc, dflag);
  653.             }
  654.  
  655.         if (dflag)
  656.             {
  657.             vt(VT_CLRSPEC);                /* if at cursor, clear reverse video */
  658.             }
  659.  
  660.         if ((wc == FF) || (wc == VT))            /* these chars leave a display line blank */
  661.             {
  662.             if (redraw_sw || (curr_x < wlp[curr_y]->n))
  663.                 {
  664.                 wlp[curr_y]->n = curr_x;
  665.                 if (curr_x < WN_width) vtm(VT_EEOL);    /* erase rest of line */
  666.                 }
  667.             wlp[curr_y]->end = wdot;
  668.             if (curr_y >= window_size-1) goto w2_exit;    /* quit if overflow screen */
  669.             wlp[++curr_y]->start = wdot + 1;
  670.             cr_found = wlp[curr_y]->cflag = 0;        /* init new line */
  671.             if (curr_x -= 2) w_ebol();        /* back up over ^X; if not at left margin, erase beginning of line */
  672.             wlp[curr_y]->col = curr_x;                /* save starting column */
  673.             }
  674.       noprint:
  675.         if (++ w_p1.c > CELLSIZE - 1) w_p1.p = w_p1.p->f, w_p1.c = 0;    /* next char in buffer */
  676.         }        /* end of "for all characters" */
  677.  
  678.     if (dot == z)
  679.         {
  680.         if (curr_x < WN_width) w_makecurs(' ', 1), w_type(' ', 1), vt(VT_CLRSPEC);    /* display a space, unless at end */
  681.         else curs_crflag = curs_c = 0;                /* else set "no cursor displayed" */
  682.         }
  683.  
  684.     /* clear rest of line if needed */
  685.     if (!cr_found && (redraw_sw || (curr_x < wlp[curr_y]->n)))
  686.         {
  687.         wlp[curr_y]->n = curr_x;
  688.         if (curr_x < WN_width) vtm(VT_EEOL);
  689.         }
  690.     wlp[curr_y]->end = wdot;        /* save char at end of last line */
  691.   w2_exit:
  692.     last_y = curr_y;                /* record last used line on screen */
  693.     }
  694. /* routine to move cursor to current location and then call vt */
  695.  
  696. vtm(arg)
  697.     int arg;
  698.     {
  699.     w_move(curr_y, curr_x);
  700.     vt(arg);
  701.     }
  702.  
  703.  
  704.  
  705.  
  706. /* routine to set reverse video and save cursor location */
  707. /* first argument is char at cursor, 2nd is value for curs_crflag */
  708.  
  709. w_makecurs(wc, crflag)
  710.     char wc;
  711.     short crflag;
  712.     {
  713.     curs_y = curr_y, curs_x = curr_x, curs_c = wc;    /* save cursor coord and char */
  714.     curs_p = &wlp[curr_y]->ch[curr_x];                /* save location of cursor spot in window image */
  715.     curs_crflag = crflag;        /* save crflag */
  716.     vt(VT_SETSPEC2);            /* set flag and reverse video */
  717.     }
  718.  
  719.  
  720.  
  721.  
  722. /* routine to handle line overflow */
  723. /* returns nonzero if at end of screen, zero otherwise */
  724. /* arg is current character position */
  725.  
  726. int w_overflow(wd)
  727.     {
  728.     wlp[curr_y]->end = wd-1;            /* last character was end of this line */
  729.     if (wlp[curr_y]->n > curr_x)
  730.         {
  731.         wlp[curr_y]->n = curr_x;
  732.         if (curr_x < WN_width) vtm(VT_EEOL);        /* if old line was wider, erase */
  733.         }
  734.     if (curr_y >= window_size-1) return(1);
  735.     wlp[curr_y]->cflag |= WF_BEG;                /* mark this line as "continued" */
  736.     wlp[++curr_y]->cflag = WF_CONT;                /* next line is a continuation line */
  737.     wlp[curr_y]->start = wd;                    /* char about to be printed is this line's first */
  738.     wlp[curr_y]->col = curr_x = 0;                /* new line starts at left margin */
  739.     fputs("\033(0", stdout);                    /* alternate char set */
  740.     w_type('h', W_MARK);                        /* "NL" space */
  741.     w_type(' ', W_MARK);
  742.     fputs("\033(B", stdout);
  743.     return(0);
  744.     }
  745. /* routine to type one character:  arguments are char and a */
  746. /* "mark" bit.  If mark is set, the char is always retyped  */
  747.  
  748. w_type(c, m)
  749.     char c;
  750.     int m;
  751.     {
  752.     register char *p;
  753.  
  754.     p = &wlp[curr_y]->ch[curr_x];        /* pointer to char image */
  755.     if ((c != *p) || (m) || (redraw_sw) || (curr_x >= wlp[curr_y]->n))
  756.         {
  757.         w_move(curr_y, curr_x);
  758.         putchar(c);
  759.         *p = (m) ? c | W_MARK : c;
  760.         ++term_x;
  761.         }
  762.     ++curr_x;
  763.     if (wlp[curr_y]->n < curr_x) wlp[curr_y]->n = curr_x;    /* if we've lengthened the line, record that fact */
  764.     }
  765.  
  766.  
  767.  
  768.  
  769. /* initialize display image */
  770.  
  771. w_init()
  772.     {
  773.     short i, j;
  774.  
  775.     for (i = 0; i < window_size; i++)        /* for each row */
  776.         {
  777.         wlp[i] = &w_image[i];                /* set pointer to this line's data */
  778.         w_image[i].n = w_image[i].cflag = 0;    /* no chars used, cr flag clear */
  779.         for (j = 0; j < W_MAX_H; w_image[i].ch[j++] = ' ');        /* clear line */
  780.         }
  781.     }
  782.  
  783.  
  784.  
  785.  
  786. /* put character followed by appropriate number of nulls for "other control function" */
  787. /* if argument is 0, output filler chars only */
  788.  
  789. putchar_d(c)
  790.     char c;
  791.     {
  792.     int i;
  793.  
  794.     if (c) putchar(c);                                                /* output character */
  795.     for (i = 0; i < win_dlyc[win_speed]; i++) putchar('\0');        /* output filler */
  796.     }
  797. /* put out appropriate number of filler chars for display function that scrolls (LF, etc.) */
  798.  
  799. scroll_dly()
  800.     {
  801.     int i;
  802.  
  803.     for (i = 0; i < win_dlys[win_speed]; i++) putchar('\0');        /* output filler */
  804.     }
  805.  
  806.  
  807.  
  808. /* move terminal cursor to stated y, x position */
  809. /* uses incremental moves or absolute cursor position, whichever is shorter */
  810.  
  811. w_move(y, x)
  812.     short y, x;
  813.     {
  814.     register short i;
  815.  
  816.     /* if practical, use CR to get to left margin */
  817.     if ((curr_x == 0) && (term_x != 0)) putchar(CR), term_x = 0;
  818.     if ((y == term_y) && (term_x < WN_width))        /* if term x is beyond last char, use abs positioning */
  819.         {
  820.         if (x == term_x) return;
  821.         if (x > term_x)
  822.             {
  823.             if (x - term_x == 1) fputs("\033[C", stdout);
  824.             else printf("\033[%dC", x - term_x);
  825.             }
  826.         else
  827.             {
  828.             if ((i = term_x - x) < 4) for (; i > 0; i--) putchar('\b');    /* use BS */
  829.             else printf("\033[%dD", term_x - x);        /* use incremental jump */
  830.             }
  831.         term_x = x;
  832.         }
  833.     else
  834.         {
  835.         if ((x == term_x) && (term_x < WN_width))
  836.             {
  837.             if (y > term_y)
  838.                 {
  839.                 if ((i = y - term_y) < 4) for (; i >0; i--) putchar(LF);    /* use LF */
  840.                 else printf("\033[%dB", i);        /* use incremental jump */
  841.                 }
  842.             else if ((i = term_y - y) == 1) fputs("\033[A", stdout);    /* move 1 */
  843.             else printf("\033[%dA", i);
  844.             term_y = y;
  845.             }
  846.         else printf("\033[%d;%dH", (term_y = y) + 1, (term_x = x) + 1);        /* absolute jump */
  847.         }
  848.     }
  849. /* scroll screen: argument is count: + up, - down */
  850.  
  851. w_scroll(count)
  852.     int count;
  853.     {
  854.     register int i, ic;
  855.     struct w_line *p[W_MAX_V];        /* temp copy of pointer array */
  856.  
  857.     if (count > 0)        /* scrolling up */
  858.         {
  859.         w_move(window_size-1, 0);    /* cursor to bottom of window */
  860.         for (i = 0; i < count; i++)
  861.             {
  862.             putchar(LF), wlp[i]->n = 0;        /* scroll terminal, blank out image line */
  863.             }
  864.         }
  865.     else        /* scroll down */
  866.         {
  867.         w_move(0, 0);        /* cursor to top */
  868.         for (i = 0; i > count; i--)
  869.             {
  870.             fputs("\033M", stdout), wlp[window_size-1+i]->n = 0;
  871.             }
  872.         }
  873.     for (i = 0; i < window_size; i++) p[i] = wlp[(window_size + i + count) % window_size];    /* rearrange */
  874.     for (i = 0; i < window_size; i++) wlp[i] = p[i];
  875.     }
  876.  
  877.  
  878.  
  879. /* clear line to left of curr_x */
  880. /* if some chars nonblank, does erase from start of line */
  881.  
  882. w_ebol()
  883.     {
  884.     short i, j;
  885.  
  886.     for (j = i = 0; i < curr_x; i++) if (wlp[curr_y]->ch[i] != ' ') wlp[curr_y]->ch[i] = ' ', j++;
  887.     if (j || redraw_sw) w_move(curr_y, curr_x-1), vt(VT_EBOL);
  888.     }
  889.  
  890.  
  891.  
  892. /* routine to set a pointer to a given location (like set_pointer) */
  893. /* returns nonzero if a text buffer exists, otherwise 0 */
  894.  
  895. int w_setptr(loc, pp)
  896.     register int loc;                /* location */
  897.     register struct qp *pp;            /* address of pointer */
  898.     {
  899.     register int i;
  900.  
  901.     if (buff.f)
  902.         {
  903.         for (i = loc / CELLSIZE, pp->p = buff.f; i > 0; i--) pp->p = pp->p->f;
  904.         pp->c = loc % CELLSIZE;
  905.         pp->dot = loc;
  906.         }
  907.     return( (int) buff.f);
  908.     }
  909. /* routine to move N lines (back, forward, or 0)                */
  910. /* w_lines(n, &source, &dest) where n is the line count, source    */
  911. /* points to a qp at the current pointer, dest, if nonzero,        */
  912. /* it points to a qp where the result is to go.                    */
  913. /* routine returns actual number of display lines                */
  914.  
  915. struct qp w_lines_p;                /* to compute # of display lines in -N lines */
  916.  
  917. int w_lines(n, ps, pd)
  918.     int n;                            /* number of lines */
  919.     register struct qp *ps, *pd;    /* source, destination qp's */
  920.     {
  921.     register struct buffcell *tp;    /* local copy of the qp */
  922.     register int tc, tdot, tn;
  923.     int tcnt, tl;                    /* chars/line and display line count */
  924.     char tch;
  925.  
  926.     tdot = ps->dot;
  927.     tp = ps->p;
  928.     tc = ps->c;
  929.  
  930.     if (n > 0)            /* argument is positive */
  931.         {
  932.         for (tcnt = tl = tn = 0; (tn < n) && (tdot < z); tdot++)    /* forward over N line separators */
  933.             {
  934.             if (spec_chars[ tch = tp->ch[tc] ] & A_L) ++tl, ++tn;        /* count separators */
  935.             else if (!(et_val & ET_TRUNC))        /* if text lines can overflow screen lines */
  936.                 {
  937.                 if (!(tch & 0140))                /* if character is a control char */
  938.                     {
  939.                     if (tch == CR)                                        /* CR resets count */
  940.                         {
  941.                         if (tcnt > WN_width) ++tl;
  942.                         tcnt = 0;
  943.                         }
  944.                     else if (tch == TAB) tcnt = (tcnt | tabmask) +1;    /* tab to next tab stop */
  945.                     else if (tch == ESC) ++tcnt;                        /* ESC takes one space */
  946.                     else tcnt += 2;                                        /* normal control chars take 2 spaces */
  947.                     }
  948.                 else ++tcnt;                /* not a control char: takes one space */
  949.                 if (tcnt > WN_width) ++tl, tcnt = 2;        /* if overflow, one more line */
  950.                 }
  951.  
  952.             if (++tc > CELLSIZE-1) tp = tp->f, tc = 0;        /* next character position */
  953.             }
  954.         if (tl > tn) tn = tl;        /* if counting display lines and there's more of them, return that */
  955.         }
  956.     else                /* argument is zero or negative */
  957.         {
  958.         for (tn = 0; (tn >= n) && (tdot > 0); )        /* back up over (n+1) line feeds */
  959.             {
  960.             --tdot;
  961.             if (--tc < 0) tp = tp->b, tc = CELLSIZE -1;
  962.             if (spec_chars[tp->ch[tc]] & A_L) --tn;
  963.             }
  964.         if (tn < n)            /* if stopped on a line sep, fwd over it */
  965.             {
  966.             ++tn;
  967.             ++tdot;
  968.             if (++tc > CELLSIZE-1) tp = tp->f, tc = 0;
  969.             }
  970.  
  971.         if (!(et_val & ET_TRUNC) && (n != 0))            /* if text line can overflow display line */
  972.             {
  973.             w_lines_p.dot = tdot, w_lines_p.p = tp, w_lines_p.c = tc;        /* then count the number of display */
  974.             tn = -w_lines(-n, &w_lines_p, 0);                /* lines in the N text lines we just backed up over */
  975.             }
  976.         }
  977.  
  978.     if (pd) pd->dot = tdot, pd->p = tp, pd->c = tc;        /* if an "after" pointer given, update it */
  979.     return(tn);
  980.     }
  981.  
  982.