home *** CD-ROM | disk | FTP | other *** search
/ minnie.tuhs.org / unixen.tar / unixen / PDP-11 / Distributions / ucb / spencer_2bsd.tar.gz / 2bsd.tar / src / ex / ex_vput.c < prev    next >
C/C++ Source or Header  |  1980-02-17  |  29KB  |  1,299 lines

  1. /* Copyright (c) 1979 Regents of the University of California */
  2. #include "ex.h"
  3. #include "ex_tty.h"
  4. #include "ex_vis.h"
  5.  
  6. /*
  7.  * Deal with the screen, clearing, cursor positioning, putting characters
  8.  * into the screen image, and deleting characters.
  9.  * Really hard stuff here is utilizing insert character operations
  10.  * on intelligent terminals which differs widely from terminal to terminal.
  11.  */
  12. vclear()
  13. {
  14.  
  15. #ifdef ADEBUG
  16.     if (trace)
  17.         tfixnl(), fprintf(trace, "------\nvclear\n");
  18. #endif
  19.     tputs(CL, LINES, putch);
  20.     destcol = 0;
  21.     outcol = 0;
  22.     destline = 0;
  23.     outline = 0;
  24.     if (inopen)
  25.         vclrbyte(vtube0, WCOLS * (WECHO - ZERO + 1));
  26. }
  27.  
  28. /*
  29.  * Clear memory.
  30.  */
  31. vclrbyte(cp, i)
  32.     register char *cp;
  33.     register int i;
  34. {
  35.  
  36.     if (i > 0)
  37.         do
  38.             *cp++ = 0;
  39.         while (--i != 0);
  40. }
  41.  
  42. /*
  43.  * Clear a physical display line, high level.
  44.  */
  45. vclrlin(l, tp)
  46.     int l;
  47.     line *tp;
  48. {
  49.  
  50.     vigoto(l, 0);
  51.     if ((hold & HOLDAT) == 0)
  52.         putchar(tp > dol ? ((UPPERCASE || HZ) ? '^' : '~') : '@');
  53.     if (state == HARDOPEN)
  54.         sethard();
  55.     vclreol();
  56. }
  57.  
  58. /*
  59.  * Clear to the end of the current physical line
  60.  */
  61. vclreol()
  62. {
  63.     register int i, j;
  64.     register char *tp;
  65.  
  66.     if (destcol == WCOLS)
  67.         return;
  68.     destline += destcol / WCOLS;
  69.     destcol %= WCOLS;
  70.     if (destline < 0 || destline > WECHO)
  71.         error("Internal error: vclreol");
  72.     i = WCOLS - destcol;
  73.     tp = vtube[destline] + destcol;
  74.     if (CE) {
  75.         if (IN && *tp || !ateopr()) {
  76.             vcsync();
  77.             vputp(CE, 1);
  78.         }
  79.         vclrbyte(tp, i);
  80.         return;
  81.     }
  82.     if (*tp == 0)
  83.         return;
  84.     while (i > 0 && (j = *tp & (QUOTE|TRIM))) {
  85.         if (j != ' ' && (j & QUOTE) == 0) {
  86.             destcol = WCOLS - i;
  87.             vputchar(' ');
  88.         }
  89.         --i, *tp++ = 0;
  90.     }
  91. }
  92.  
  93. /*
  94.  * Clear the echo line.
  95.  * If didphys then its been cleared physically (as
  96.  * a side effect of a clear to end of display, e.g.)
  97.  * so just do it logically.
  98.  * If work here is being held off, just remember, in
  99.  * heldech, if work needs to be done, don't do anything.
  100.  */
  101. vclrech(didphys)
  102.     bool didphys;
  103. {
  104.  
  105.     if (Peekkey == ATTN)
  106.         return;
  107.     if (hold & HOLDECH) {
  108.         heldech = !didphys;
  109.         return;
  110.     }
  111.     if (!didphys && (CD || CE)) {
  112.         splitw++;
  113.         /*
  114.          * If display is retained below, then MUST use CD or CE
  115.          * since we don't really know whats out there.
  116.          * Vigoto might decide (incorrectly) to do nothing.
  117.          */
  118.         if (DB)
  119.             vgoto(WECHO, 0), vputp(CD ? CD : CE, 1);
  120.         else
  121.             vigoto(WECHO, 0), vclreol();
  122.         splitw = 0;
  123.         didphys = 1;
  124.     }
  125.     if (didphys)
  126.         vclrbyte(vtube[WECHO], WCOLS);
  127.     heldech = 0;
  128. }
  129.  
  130. /*
  131.  * Fix the echo area for use, setting
  132.  * the state variable splitw so we wont rollup
  133.  * when we move the cursor there.
  134.  */
  135. fixech()
  136. {
  137.  
  138.     splitw++;
  139.     if (state != VISUAL && state != CRTOPEN) {
  140.         vclean();
  141.         vcnt = 0;
  142.     }
  143.     vgoto(WECHO, 0); flusho();
  144. }
  145.  
  146. /*
  147.  * Put the cursor ``before'' cp.
  148.  */
  149. vcursbef(cp)
  150.     register char *cp;
  151. {
  152.  
  153.     if (cp <= linebuf)
  154.         vgotoCL(value(NUMBER) << 3);
  155.     else
  156.         vgotoCL(column(cp - 1) - 1);
  157. }
  158.  
  159. /*
  160.  * Put the cursor ``at'' cp.
  161.  */
  162. vcursat(cp)
  163.     register char *cp;
  164. {
  165.  
  166.     if (cp <= linebuf && linebuf[0] == 0)
  167.         vgotoCL(value(NUMBER) << 3);
  168.     else
  169.         vgotoCL(column(cp - 1));
  170. }
  171.  
  172. /*
  173.  * Put the cursor ``after'' cp.
  174.  */
  175. vcursaft(cp)
  176.     register char *cp;
  177. {
  178.  
  179.     vgotoCL(column(cp));
  180. }
  181.  
  182. /*
  183.  * Fix the cursor to be positioned in the correct place
  184.  * to accept a command.
  185.  */
  186. vfixcurs()
  187. {
  188.  
  189.     vsetcurs(cursor);
  190. }
  191.  
  192. /*
  193.  * Compute the column position implied by the cursor at ``nc'',
  194.  * and move the cursor there.
  195.  */
  196. vsetcurs(nc)
  197.     register char *nc;
  198. {
  199.     register int col;
  200.  
  201.     col = column(nc);
  202.     if (linebuf[0])
  203.         col--;
  204.     vgotoCL(col);
  205.     cursor = nc;
  206. }
  207.  
  208. /*
  209.  * Move the cursor invisibly, i.e. only remember to do it.
  210.  */
  211. vigoto(y, x)
  212.     int y, x;
  213. {
  214.  
  215.     destline = y;
  216.     destcol = x;
  217. }
  218.  
  219. /*
  220.  * Move the cursor to the position implied by any previous
  221.  * vigoto (or low level hacking with destcol/destline as in readecho).
  222.  */
  223. vcsync()
  224. {
  225.  
  226.     vgoto(destline, destcol);
  227. }
  228.  
  229. /*
  230.  * Goto column x of the current line.
  231.  */
  232. vgotoCL(x)
  233.     register int x;
  234. {
  235.  
  236.     if (splitw)
  237.         vgoto(WECHO, x);
  238.     else
  239.         vgoto(LINE(vcline), x);
  240. }
  241.  
  242. /*
  243.  * Invisible goto column x of current line.
  244.  */
  245. vigotoCL(x)
  246.     register int x;
  247. {
  248.  
  249.     if (splitw)
  250.         vigoto(WECHO, x);
  251.     else
  252.         vigoto(LINE(vcline), x);
  253. }
  254.  
  255. /*
  256.  * Move cursor to line y, column x, handling wraparound and scrolling.
  257.  */
  258. vgoto(y, x)
  259.     register int y, x;
  260. {
  261.     register char *tp;
  262.     register int c;
  263.  
  264.     /*
  265.      * Fold the possibly too large value of x.
  266.      */
  267.     if (x >= WCOLS) {
  268.         y += x / WCOLS;
  269.         x %= WCOLS;
  270.     }
  271.     if (y < 0)
  272.         error("Internal error: vgoto");
  273.     if (outcol >= WCOLS) {
  274.         if (AM) {
  275.             outline += outcol / WCOLS;
  276.             outcol %= WCOLS;
  277.         } else
  278.             outcol = WCOLS - 1;
  279.     }
  280.  
  281.     /*
  282.      * In a hardcopy or glass crt open, print the stuff
  283.      * implied by a motion, or backspace.
  284.      */
  285.     if (state == HARDOPEN || state == ONEOPEN) {
  286.         if (y != outline)
  287.             error("Line too long for open");
  288.         if (x + 1 < outcol - x || (outcol > x && !BS))
  289.             destcol = 0, fgoto();
  290.         tp = vtube[WBOT] + outcol;
  291.         while (outcol != x)
  292.             if (outcol < x) {
  293.                 if (*tp == 0)
  294.                     *tp = ' ';
  295.                 c = *tp++ & TRIM;
  296.                 vputc(c && (!OS || EO) ? c : ' '), outcol++;
  297.             } else {
  298.                 if (BC)
  299.                     vputp(BC, 0);
  300.                 else
  301.                     vputc('\b');
  302.                 outcol--;
  303.             }
  304.         destcol = outcol = x;
  305.         destline = outline;
  306.         return;
  307.     }
  308.  
  309.     /*
  310.      * If the destination position implies a scroll, do it.
  311.      */
  312.     destline = y;
  313.     if (destline > WBOT && (!splitw || destline > WECHO)) {
  314.         endim();
  315.         vrollup(destline);
  316.     }
  317.  
  318.     /*
  319.      * If there really is a motion involved, do it.
  320.      * The check here is an optimization based on profiling.
  321.      */
  322.     destcol = x;
  323.     if ((destline - outline) * WCOLS != destcol - outcol) {
  324.         if (!MI)
  325.             endim();
  326.         fgoto();
  327.     }
  328. }
  329.  
  330. /*
  331.  * This is the hardest code in the editor, and deals with insert modes
  332.  * on different kinds of intelligent terminals.  The complexity is due
  333.  * to the cross product of three factors:
  334.  *
  335.  *    1. Lines may display as more than one segment on the screen.
  336.  *    2. There are 2 kinds of intelligent terminal insert modes.
  337.  *    3. Tabs squash when you insert characters in front of them,
  338.  *       in a way in which current intelligent terminals don't handle.
  339.  *
  340.  * The two kinds of terminals are typified by the DM2500 or HP2645 for
  341.  * one and the CONCEPT-100 or the FOX for the other.
  342.  *
  343.  * The first (HP2645) kind has an insert mode where the characters
  344.  * fall off the end of the line and the screen is shifted rigidly
  345.  * no matter how the display came about.
  346.  *
  347.  * The second (CONCEPT-100) kind comes from terminals which are designed
  348.  * for forms editing and which distinguish between blanks and ``spaces''
  349.  * on the screen, spaces being like blank, but never having had
  350.  * and data typed into that screen position (since, e.g. a clear operation
  351.  * like clear screen).  On these terminals, when you insert a character,
  352.  * the characters from where you are to the end of the screen shift
  353.  * over till a ``space'' is found, and the null character there gets
  354.  * eaten up.
  355.  *
  356.  *
  357.  * The code here considers the line as consisting of several parts
  358.  * the first part is the ``doomed'' part, i.e. a part of the line
  359.  * which is being typed over.  Next comes some text up to the first
  360.  * following tab.  The tab is the next segment of the line, and finally
  361.  * text after the tab.
  362.  *
  363.  * We have to consider each of these segments and the effect of the
  364.  * insertion of a character on them.  On terminals like HP2645's we
  365.  * must simulate a multi-line insert mode using the primitive one
  366.  * line insert mode.  If we are inserting in front of a tab, we have
  367.  * to either delete characters from the tab or insert white space
  368.  * (when the tab reaches a new spot where it gets larger) before we
  369.  * insert the new character.
  370.  *
  371.  * On a terminal like a CONCEPT our strategy is to make all
  372.  * blanks be displayed, while trying to keep the screen having ``spaces''
  373.  * for portions of tabs.  In this way the terminal hardward does some
  374.  * of the hacking for compression of tabs, although this tends to
  375.  * disappear as you work on the line and spaces change into blanks.
  376.  *
  377.  * There are a number of boundary conditions (like typing just before
  378.  * the first following tab) where we can avoid a lot of work.  Most
  379.  * of them have to be dealt with explicitly because performance is
  380.  * much, much worse if we don't.
  381.  *
  382.  * A final thing which is hacked here is two flavors of insert mode.
  383.  * Datamedia's do this by an insert mode which you enter and leave
  384.  * and by having normal motion character operate differently in this
  385.  * mode, notably by having a newline insert a line on the screen in
  386.  * this mode.  This generally means it is unsafe to move around
  387.  * the screen ignoring the fact that we are in this mode.
  388.  * This is possible on some terminals, and wins big (e.g. HP), so
  389.  * we encode this as a ``can move in insert capability'' mi,
  390.  * and terminals which have it can do insert mode with much less
  391.  * work when tabs are present following the cursor on the current line.
  392.  */
  393.  
  394. /*
  395.  * Routine to expand a tab, calling the normal Outchar routine
  396.  * to put out each implied character.  Note that we call outchar
  397.  * with a QUOTE.  We use QUOTE internally to represent a position
  398.  * which is part of the expansion of a tab.
  399.  */
  400. vgotab()
  401. {
  402.     register int i = (LINE(vcline) - destline) * WCOLS + destcol;
  403.  
  404.     do
  405.         (*Outchar)(QUOTE);
  406.     while (++i % value(TABSTOP));
  407. }
  408.  
  409. /*
  410.  * Variables for insert mode.
  411.  */
  412. int    linend;            /* The column position of end of line */
  413. int    tabstart;        /* Column of start of first following tab */
  414. int    tabend;            /* Column of end of following tabs */
  415. int    tabsize;        /* Size of the following tabs */
  416. int    tabslack;        /* Number of ``spaces'' in following tabs */
  417. int    inssiz;            /* Number of characters to be inserted */
  418. int    inscol;            /* Column where insertion is taking place */
  419. int    shft;            /* Amount tab expansion shifted rest of line */
  420. int    slakused;        /* This much of tabslack will be used up */
  421.  
  422. /*
  423.  * This routine MUST be called before insert mode is run,
  424.  * and brings all segments of the current line to the top
  425.  * of the screen image buffer so it is easier for us to
  426.  * maniuplate them.
  427.  */
  428. vprepins()
  429. {
  430.     register int i;
  431.     register char *cp = vtube0;
  432.  
  433.     for (i = 0; i < DEPTH(vcline); i++) {
  434.         vmaktop(LINE(vcline) + i, cp);
  435.         cp += WCOLS;
  436.     }
  437. }
  438.  
  439. vmaktop(p, cp)
  440.     register int p;
  441.     char *cp;
  442. {
  443.     register int i;
  444.     char temp[TUBECOLS];
  445.  
  446.     if (vtube[p] == cp)
  447.         return;
  448.     for (i = ZERO; i <= WECHO; i++)
  449.         if (vtube[i] == cp) {
  450.             copy(temp, vtube[i], WCOLS);
  451.             copy(vtube[i], vtube[p], WCOLS);
  452.             copy(vtube[p], temp, WCOLS);
  453.             vtube[i] = vtube[p];
  454.             vtube[p] = cp;
  455.             return;
  456.         }
  457.     error("Line too long");
  458. }
  459.  
  460. /*
  461.  * Insert character c at current cursor position.
  462.  * Multi-character inserts occur only as a result
  463.  * of expansion of tabs (i.e. inssize == 1 except
  464.  * for tabs) and code assumes this in several place
  465.  * to make life simpler.
  466.  */
  467. vinschar(c)
  468.     char c;
  469. {
  470.     register int i;
  471.     register char *tp;
  472.  
  473.     if ((!IM || !EI) && ((hold & HOLDQIK) || !value(REDRAW) || value(SLOWOPEN))) {
  474.         /*
  475.          * Don't want to try to use terminal
  476.          * insert mode, or to try to fake it.
  477.          * Just put the character out; the screen
  478.          * will probably be wrong but we will fix it later.
  479.          */
  480.         if (c == '\t') {
  481.             vgotab();
  482.             return;
  483.         }
  484.         vputchar(c);
  485.         if (DEPTH(vcline) * WCOLS + !value(REDRAW) > destcol)
  486.             return;
  487.         /*
  488.          * The next line is about to be clobbered
  489.          * make space for another segment of this line
  490.          * (on an intelligent terminal) or just remember
  491.          * that next line was clobbered (on a dumb one
  492.          * if we don't care to redraw the tail.
  493.          */
  494.         c = LINE(vcline) + DEPTH(vcline);
  495.         if (c < LINE(vcline + 1) || c > WBOT)
  496.             return;
  497.         vinslin(c, 1, vcline);
  498.         DEPTH(vcline)++;
  499.         return;
  500.     }
  501.     /*
  502.      * Compute the number of positions in the line image of the
  503.      * current line.  This is done from the physical image
  504.      * since that is faster.  Note that we have no memory
  505.      * from insertion to insertion so that routines which use
  506.      * us don't have to worry about moving the cursor around.
  507.      */
  508.     if (*vtube0 == 0)
  509.         linend = 0;
  510.     else {
  511.         /*
  512.          * Search backwards for a non-null character
  513.          * from the end of the displayed line.
  514.          */
  515.         i = WCOLS * DEPTH(vcline);
  516.         if (i == 0)
  517.             i = WCOLS;
  518.         tp = vtube0 + i;
  519.         while (*--tp == 0)
  520.             if (--i == 0)
  521.                 break;
  522.         linend = i;
  523.     }
  524.  
  525.     /*
  526.      * We insert at a position based on the physical location
  527.      * of the output cursor.
  528.      */
  529.     inscol = destcol + (destline - LINE(vcline)) * WCOLS;
  530.     if (c == '\t') {
  531.         /*
  532.          * Characters inserted from a tab must be
  533.          * remembered as being part of a tab, but we can't
  534.          * use QUOTE here since we really need to print blanks.
  535.          * QUOTE|' ' is the representation of this.
  536.          */
  537.         inssiz = value(TABSTOP) - inscol % value(TABSTOP);
  538.         c = ' ' | QUOTE;
  539.     } else
  540.         inssiz = 1;
  541.  
  542.     /*
  543.      * If the text to be inserted is less than the number
  544.      * of doomed positions, then we don't need insert mode,
  545.      * rather we can just typeover.
  546.      */
  547.     if (inssiz <= doomed) {
  548.         endim();
  549.         if (inscol != linend)
  550.             doomed -= inssiz;
  551.         do
  552.             vputchar(c);
  553.         while (--inssiz);
  554.         return;
  555.     }
  556.  
  557.     /*
  558.      * Have to really do some insertion, thus
  559.      * stake out the bounds of the first following
  560.      * group of tabs, computing starting position,
  561.      * ending position, and the number of ``spaces'' therein
  562.      * so we can tell how much it will squish.
  563.      */
  564.     tp = vtube0 + inscol;
  565.     for (i = inscol; i < linend; i++)
  566.         if (*tp++ & QUOTE) {
  567.             --tp;
  568.             break;
  569.         }
  570.     tabstart = tabend = i;
  571.     tabslack = 0;
  572.     while (tabend < linend) {
  573.         i = *tp++;
  574.         if ((i & QUOTE) == 0)
  575.             break;
  576.         if ((i & TRIM) == 0)
  577.             tabslack++;
  578.         tabsize++;
  579.         tabend++;
  580.     }
  581.     tabsize = tabend - tabstart;
  582.  
  583.     /*
  584.      * For HP's and DM's, e.g. tabslack has no meaning.
  585.      */
  586.     if (!IN)
  587.         tabslack = 0;
  588. #ifdef IDEBUG
  589.     if (trace)
  590.         fprintf(trace, "inscol %d, inssiz %d, tabstart %d,\
  591. tabend %d, tabslack %d, linend %d\n", inscol, inssiz, tabstart, tabend, tabslack, linend);
  592. #endif
  593.  
  594.     /*
  595.      * The real work begins.
  596.      */
  597.     slakused = 0;
  598.     shft = 0;
  599.     if (tabsize) {
  600.         /*
  601.          * There are tabs on this line.
  602.          * If they need to expand, then the rest of the line
  603.          * will have to be shifted over.  In this case,
  604.          * we will need to make sure there are no ``spaces''
  605.          * in the rest of the line (on e.g. CONCEPT-100)
  606.          * and then grab another segment on the screen if this
  607.          * line is now deeper.  We then do the shift
  608.          * implied by the insertion.
  609.          */
  610.         if (inssiz >= doomed + value(TABSTOP) - tabstart % value(TABSTOP)) {
  611.             if (IN)
  612.                 vrigid();
  613.             vneedpos(value(TABSTOP));
  614.             vishft();
  615.         }
  616.     } else if (inssiz > doomed)
  617.         /*
  618.          * No tabs, but line may still get deeper.
  619.          */
  620.         vneedpos(inssiz - doomed);
  621.     /*
  622.      * Now put in the inserted characters.
  623.      */
  624.     viin(c);
  625.  
  626.     /*
  627.      * Now put the cursor in its final resting place.
  628.      */
  629.     destline = LINE(vcline);
  630.     destcol = inscol + inssiz;
  631.     vcsync();
  632. }
  633.  
  634. /*
  635.  * Rigidify the rest of the line after the first
  636.  * group of following tabs, typing blanks over ``spaces''.
  637.  */
  638. vrigid()
  639. {
  640.     register int col;
  641.     register char *tp = vtube0 + tabend;
  642.  
  643.     for (col = tabend; col < linend; col++)
  644.         if ((*tp++ & TRIM) == 0) {
  645.             endim();
  646.             vgotoCL(col);
  647.             vputchar(' ' | QUOTE);
  648.         }
  649. }
  650.  
  651. /*
  652.  * We need cnt more positions on this line.
  653.  * Open up new space on the screen (this may in fact be a
  654.  * screen rollup).
  655.  *
  656.  * On a dumb terminal we may infact redisplay the rest of the
  657.  * screen here brute force to keep it pretty.
  658.  */
  659. vneedpos(cnt)
  660.     int cnt;
  661. {
  662.     register int d = DEPTH(vcline);
  663.     register int rmdr = d * WCOLS - linend;
  664.     register int e;
  665.  
  666.     if (cnt <= rmdr - IN)
  667.         return;
  668.     e = LINE(vcline) + DEPTH(vcline);
  669.     if (e < LINE(vcline + 1)) {
  670.         vigoto(e, 0);
  671.         vclreol();
  672.         return;
  673.     }
  674.     DEPTH(vcline)++;
  675.     if (e < WECHO) {
  676.         e = vglitchup(vcline, d);
  677.         vigoto(e, 0); vclreol();
  678.         Outchar = vputchar; vsync(e + 1); Outchar = vinschar;
  679.     } else {
  680.         vup1();
  681.         vigoto(WBOT, 0);
  682.         vclreol();
  683.     }
  684.     vprepins();
  685. }
  686.  
  687. /*
  688.  * Do the shift of the next tabstop implied by
  689.  * insertion so it expands.
  690.  */
  691. vishft()
  692. {
  693.     int tshft = 0;
  694.     int j;
  695.     register int i;
  696.     register char *tp = vtube0;
  697.     register char *up;
  698.     short oldhold = hold;
  699.  
  700.     shft = value(TABSTOP);
  701.     hold |= HOLDPUPD;
  702.     if (!IM && !EI) {
  703.         /*
  704.          * Dumb terminals are easy, we just have
  705.          * to retype the text.
  706.          */
  707.         vigotoCL(tabend + shft);
  708.         up = tp + tabend;
  709.         for (i = tabend; i < linend; i++)
  710.             vputchar(*up++);
  711.     } else if (IN) {
  712.         /*
  713.          * CONCEPT-like terminals do most of the work for us,
  714.          * we don't have to muck with simulation of multi-line
  715.          * insert mode.  Some of the shifting may come for free
  716.          * also if the tabs don't have enough slack to take up
  717.          * all the inserted characters.
  718.          */
  719.         i = shft;
  720.         slakused = inssiz - doomed;
  721.         if (slakused > tabslack && inscol + doomed != tabstart) {
  722.             i -= slakused - tabslack;
  723.             slakused -= tabslack;
  724.         }
  725.         if (i > 0 && tabend != linend) {
  726.             tshft = i;
  727.             vgotoCL(tabend);
  728.             goim();
  729.             do
  730.                 vputchar(' ' | QUOTE);
  731.             while (--i);
  732.         }
  733.     } else {
  734.         /*
  735.          * HP and Datamedia type terminals have to have multi-line
  736.          * insert faked.  Hack each segment after where we are
  737.          * (going backwards to where we are.)  We then can
  738.          * hack the segment where the end of the first following
  739.          * tab group is.
  740.          */
  741.         for (j = DEPTH(vcline) - 1; j > (tabend + shft) / WCOLS; j--) {
  742.             vgotoCL(j * WCOLS);
  743.             goim();
  744.             up = tp + j * WCOLS - shft;
  745.             i = shft;
  746.             do
  747.                 vputchar(*up++);
  748.             while (--i);
  749.         }
  750.         vigotoCL(tabstart);
  751.         i = shft - (inssiz - doomed);
  752.         if (i > 0 && inscol + doomed != tabstart) {
  753.             tabslack = inssiz - doomed;
  754.             vcsync();
  755.             goim();
  756.             do
  757.                 vputchar(' ');
  758.             while (--i);
  759.         }
  760.     }
  761.     /*
  762.      * Now do the data moving in the internal screen
  763.      * image which is common to all three cases.
  764.      */
  765.     tp += linend;
  766.     up = tp + shft;
  767.     i = linend - tabend;
  768.     if (i > 0)
  769.         do
  770.             *--up = *--tp;
  771.         while (--i);
  772.     if (IN && tshft) {
  773.         i = tshft;
  774.         do
  775.             *--up = ' ' | QUOTE;
  776.         while (--i);
  777.     }
  778.     hold = oldhold;
  779. }
  780.  
  781. /*
  782.  * Now do the insert of the characters (finally).
  783.  */
  784. viin(c)
  785.     char c;
  786. {
  787.     register char *tp, *up;
  788.     register int i, j;
  789.     int remdoom;
  790.     short oldhold = hold;
  791.  
  792.     hold |= HOLDPUPD;
  793.     if (tabsize && (IM && EI) && inssiz - doomed > tabslack)
  794.         /*
  795.          * There is a tab out there which will be affected
  796.          * by the insertion since there aren't enough doomed
  797.          * characters to take up all the insertion and we do
  798.          * have insert mode capability.
  799.          */
  800.         if (inscol + doomed == tabstart)
  801.             /*
  802.              * The end of the doomed characters sits right at the
  803.              * start of the tabs, then we don't need to use insert
  804.              * mode; the tab has already been expanded as
  805.              * appropriate and we can just overwrite it.
  806.              */
  807.             slakused = 0;
  808.         else {
  809.             /*
  810.              * The last really special case to handle is case
  811.              * where the tab is just sitting there and doesn't
  812.              * have enough slack to let the insertion take
  813.              * place without shifting the rest of the line
  814.              * over.  In this case we have to go out and
  815.              * delete some characters of the tab before we start
  816.              * or the answer will be wrong, as the rest of the
  817.              * line will have been shifted.  This code means
  818.              * that terminals with only insert chracter (no
  819.              * delete character) won't work correctly.
  820.              */
  821.             i = inssiz - doomed - tabslack - slakused;
  822.             if (i > 0) {
  823.                 vgotoCL(tabstart);
  824.                 godm();
  825.                 for (i = inssiz - doomed - tabslack; i > 0; i--)
  826.                     vputp(DC, DEPTH(vcline));
  827.                 enddm();
  828.             }
  829.         }
  830.  
  831.     /* 
  832.      * Now put out the characters of the actual insertion.
  833.      */
  834.     vigotoCL(inscol);
  835.     remdoom = doomed;
  836.     for (i = inssiz; i > 0; i--) {
  837.         if (remdoom > 0) {
  838.             remdoom--;
  839.             endim();
  840.         } else if (IM && EI) {
  841.             vcsync();
  842.             goim();
  843.         }
  844.         vputchar(c);
  845.     }
  846.  
  847.     if (!IM || !EI) {
  848.         /*
  849.          * We are a dumb terminal; brute force update
  850.          * the rest of the line; this is very much an n^^2 process,
  851.          * and totally unreasonable at low speed.
  852.          *
  853.          * You asked for it, you get it.
  854.          */
  855.         tp = vtube0 + inscol + doomed;
  856.         for (i = inscol + doomed; i < tabstart; i++)
  857.             vputchar(*tp++);
  858.         hold = oldhold;
  859.         vigotoCL(tabstart + inssiz - doomed);
  860.         for (i = tabsize - (inssiz - doomed) + shft; i > 0; i--)
  861.             vputchar(' ' | QUOTE);
  862.     } else {
  863.         if (!IN) {
  864.             /*
  865.              * On terminals without multi-line
  866.              * insert in the hardware, we must go fix the segments
  867.              * between the inserted text and the following
  868.              * tabs, if they are on different lines.
  869.              *
  870.              * Aaargh.
  871.              */
  872.             tp = vtube0;
  873.             for (j = (inscol + inssiz - 1) / WCOLS + 1; j <= (tabstart + inssiz - doomed - 1) / WCOLS; j++) {
  874.                 vgotoCL(j * WCOLS);
  875.                 i = inssiz - doomed;
  876.                 up = tp + j * WCOLS - i;
  877.                 goim();
  878.                 do
  879.                     vputchar(*up++);
  880.                 while (--i && *up);
  881.             }
  882.         } else {
  883.             /*
  884.              * On terminals with multi line inserts,
  885.              * life is simpler, just reflect eating of
  886.              * the slack.
  887.              */
  888.             tp = vtube0 + tabend;
  889.             for (i = tabsize - (inssiz - doomed); i >= 0; i--) {
  890.                 if ((*--tp & (QUOTE|TRIM)) == QUOTE) {
  891.                     --tabslack;
  892.                     if (tabslack >= slakused)
  893.                         continue;
  894.                 }
  895.                 *tp = ' ' | QUOTE;
  896.             }
  897.         }
  898.         /*
  899.          * Blank out the shifted positions to be tab positions.
  900.          */
  901.         if (shft) {
  902.             tp = vtube0 + tabend + shft;
  903.             for (i = tabsize - (inssiz - doomed) + shft; i > 0; i--)
  904.                 if ((*--tp & QUOTE) == 0)
  905.                     *tp = ' ' | QUOTE;
  906.         }
  907.     }
  908.  
  909.     /*
  910.      * Finally, complete the screen image update
  911.      * to reflect the insertion.
  912.      */
  913.     hold = oldhold;
  914.     tp = vtube0 + tabstart; up = tp + inssiz - doomed;
  915.     for (i = tabstart; i > inscol + doomed; i--)
  916.         *--up = *--tp;
  917.     for (i = inssiz; i > 0; i--)
  918.         *--up = c;
  919.     doomed = 0;
  920. }
  921.  
  922. /*
  923.  * Go into ``delete mode''.  If the
  924.  * sequence which goes into delete mode
  925.  * is the same as that which goes into insert
  926.  * mode, then we are in delete mode already.
  927.  */
  928. godm()
  929. {
  930.  
  931.     if (insmode) {
  932.         if (strcmp(DM, IM) == 0)
  933.             return;
  934.         endim();
  935.     }
  936.     vputp(DM, 0);
  937. }
  938.  
  939. /*
  940.  * If we are coming out of delete mode, but
  941.  * delete and insert mode end with the same sequence,
  942.  * it wins to pretend we are now in insert mode,
  943.  * since we will likely want to be there again soon
  944.  * if we just moved over to delete space from part of
  945.  * a tab (above).
  946.  */
  947. enddm()
  948. {
  949.  
  950.     if (strcmp(DM, IM) == 0) {
  951.         insmode = 1;
  952.         return;
  953.     }
  954.     vputp(ED, 0);
  955. }
  956.  
  957. /*
  958.  * In and out of insert mode.
  959.  * Note that the code here demands that there be
  960.  * a string for insert mode (the null string) even
  961.  * if the terminal does all insertions a single character
  962.  * at a time, since it branches based on whether IM is null.
  963.  */
  964. goim()
  965. {
  966.  
  967.     if (!insmode)
  968.         vputp(IM, 0);
  969.     insmode = 1;
  970. }
  971.  
  972. endim()
  973. {
  974.  
  975.     if (insmode) {
  976.         vputp(EI, 0);
  977.         insmode = 0;
  978.     }
  979. }
  980.  
  981. /*
  982.  * Put the character c on the screen at the current cursor position.
  983.  * This routine handles wraparound and scrolling and understands not
  984.  * to roll when splitw is set, i.e. we are working in the echo area.
  985.  * There is a bunch of hacking here dealing with the difference between
  986.  * QUOTE, QUOTE|' ', and ' ' for CONCEPT-100 like terminals, and also
  987.  * code to deal with terminals which overstrike, including CRT's where
  988.  * you can erase overstrikes with some work.  CRT's which do underlining
  989.  * implicitly which has to be erased (like CONCEPTS) are also handled.
  990.  */
  991. vputchar(c)
  992.     register int c;
  993. {
  994.     register char *tp;
  995.     register int d;
  996.  
  997.     c &= (QUOTE|TRIM);
  998. #ifdef TRACE
  999.     if (trace)
  1000.         tracec(c);
  1001. #endif
  1002.     if (destcol >= WCOLS) {
  1003.         destline += destcol / WCOLS;
  1004.         destcol %= WCOLS;
  1005.     }
  1006.     if (destline > WBOT && (!splitw || destline > WECHO))
  1007.         vrollup(destline);
  1008.     tp = vtube[destline] + destcol;
  1009.     switch (c) {
  1010.  
  1011.     case '\t':
  1012.         vgotab();
  1013.         return;
  1014.  
  1015.     case ' ':
  1016.         /*
  1017.          * We can get away without printing a space in a number
  1018.          * of cases, but not always.  We get away with doing nothing
  1019.          * if we are not in insert mode, and not on a CONCEPT-100
  1020.          * like terminal, and either not in hardcopy open or in hardcopy
  1021.          * open on a terminal with no overstriking, provided,
  1022.          * in all cases, that nothing has ever been displayed
  1023.          * at this position.  Ugh.
  1024.          */
  1025.         if (!insmode && !IN && (state != HARDOPEN || OS) && (*tp&TRIM) == 0) {
  1026.             *tp = ' ';
  1027.             destcol++;
  1028.             return;
  1029.         }
  1030.         goto def;
  1031.  
  1032.     case QUOTE:
  1033.         if (insmode) {
  1034.             /*
  1035.              * When in insert mode, tabs have to expand
  1036.              * to real, printed blanks.
  1037.              */
  1038.             c = ' ' | QUOTE;
  1039.             goto def;
  1040.         }
  1041.         if (*tp == 0) {
  1042.             /*
  1043.              * A ``space''.
  1044.              */
  1045.             if ((hold & HOLDPUPD) == 0)
  1046.                 *tp = QUOTE;
  1047.             destcol++;
  1048.             return;
  1049.         }
  1050.         /*
  1051.          * A ``space'' ontop of a part of a tab.
  1052.          */
  1053.         if (*tp & QUOTE) {
  1054.             destcol++;
  1055.             return;
  1056.         }
  1057.         c = ' ' | QUOTE;
  1058.         /* fall into ... */
  1059.  
  1060. def:
  1061.     default:
  1062.         d = *tp & TRIM;
  1063.         /*
  1064.          * Now get away with doing nothing if the characters
  1065.          * are the same, provided we are not in insert mode
  1066.          * and if we are in hardopen, that the terminal has overstrike.
  1067.          */
  1068.         if (d == (c & TRIM) && !insmode && (state != HARDOPEN || OS)) {
  1069.             if ((hold & HOLDPUPD) == 0)
  1070.                 *tp = c;
  1071.             destcol++;
  1072.             return;
  1073.         }
  1074.         /*
  1075.          * Backwards looking optimization.
  1076.          * The low level cursor motion routines will use
  1077.          * a cursor motion right sequence to step 1 character
  1078.          * right.  On, e.g., a DM3025A this is 2 characters
  1079.          * and printing is noticeably slower at 300 baud.
  1080.          * Since the low level routines are not allowed to use
  1081.          * spaces for positioning, we discover the common
  1082.          * case of a single space here and force a space
  1083.          * to be printed.
  1084.          */
  1085.         if (destcol == outcol + 1 && tp[-1] == ' ' && outline == destline) {
  1086.             vputc(' ');
  1087.             outcol++;
  1088.         }
  1089.  
  1090.         /*
  1091.          * This is an inline expansion a call to vcsync() dictated
  1092.          * by high frequency in a profile.
  1093.          */
  1094.         if (outcol != destcol || outline != destline)
  1095.             vgoto(destline, destcol);
  1096.  
  1097.         /*
  1098.          * Deal with terminals which have overstrike.
  1099.          * We handle erasing general overstrikes, erasing
  1100.          * underlines on terminals (such as CONCEPTS) which
  1101.          * do underlining correctly automatically (e.g. on nroff
  1102.          * output), and remembering, in hardcopy mode,
  1103.          * that we have overstruct something.
  1104.          */
  1105.         if (!insmode && d && d != ' ' && d != (c & TRIM)) {
  1106.             if (EO && (OS || UL && (c == '_' || d == '_'))) {
  1107.                 vputc(' ');
  1108.                 outcol++, destcol++;
  1109.                 back1();
  1110.             } else
  1111.                 rubble = 1;
  1112.         }
  1113.  
  1114.         /*
  1115.          * Unless we are just bashing characters around for
  1116.          * inner working of insert mode, update the display.
  1117.          */
  1118.         if ((hold & HOLDPUPD) == 0)
  1119.             *tp = c;
  1120.  
  1121.         /*
  1122.          * In insert mode, put out the IC sequence, padded
  1123.          * based on the depth of the current line.
  1124.          * A terminal which had no real insert mode, rather
  1125.          * opening a character position at a time could do this.
  1126.          * Actually should use depth to end of current line
  1127.          * but this rarely matters.
  1128.          */
  1129.         if (insmode)
  1130.             vputp(IC, DEPTH(vcline));
  1131.         vputc(c & TRIM);
  1132.  
  1133.         /*
  1134.          * In insert mode, IP is a post insert pad.
  1135.          */
  1136.         if (insmode)
  1137.             vputp(IP, DEPTH(vcline));
  1138.         destcol++, outcol++;
  1139.  
  1140.         /*
  1141.          * CONCEPT braindamage in early models:  after a wraparound
  1142.          * the next newline is eaten.  It's hungry so we just
  1143.          * feed it now rather than worrying about it.
  1144.          */
  1145.         if (XN && outcol % WCOLS == 0)
  1146.             vputc('\n');
  1147.     }
  1148. }
  1149.  
  1150. /*
  1151.  * Delete display positions stcol through endcol.
  1152.  * Amount of use of special terminal features here is limited.
  1153.  */
  1154. physdc(stcol, endcol)
  1155.     int stcol, endcol;
  1156. {
  1157.     register char *tp, *up;
  1158.     char *tpe;
  1159.     register int i;
  1160.     register int nc = endcol - stcol;
  1161.  
  1162. #ifdef IDEBUG
  1163.     if (trace)
  1164.         tfixnl(), fprintf(trace, "physdc(%d, %d)\n", stcol, endcol);
  1165. #endif
  1166.     if (!DC || nc <= 0)
  1167.         return;
  1168.     if (IN) {
  1169.         /*
  1170.          * CONCEPT-100 like terminal.
  1171.          * If there are any ``spaces'' in the material to be
  1172.          * deleted, then this is too hard, just retype.
  1173.          */
  1174.         vprepins();
  1175.         up = vtube0 + stcol;
  1176.         i = nc;
  1177.         do
  1178.             if ((*up++ & (QUOTE|TRIM)) == QUOTE)
  1179.                 return;
  1180.         while (--i);
  1181.         i = 2 * nc;
  1182.         do
  1183.             if (*up == 0 || (*up++ & QUOTE) == QUOTE)
  1184.                 return;
  1185.         while (--i);
  1186.         vgotoCL(stcol);
  1187.     } else {
  1188.         /*
  1189.          * HP like delete mode.
  1190.          * Compute how much text we are moving over by deleting.
  1191.          * If it appears to be faster to just retype
  1192.          * the line, do nothing and that will be done later.
  1193.          * We are assuming 2 output characters per deleted
  1194.          * characters and that clear to end of line is available.
  1195.          */
  1196.         i = stcol / WCOLS;
  1197.         if (i != endcol / WCOLS)
  1198.             return;
  1199.         i += LINE(vcline);
  1200.         stcol %= WCOLS;
  1201.         endcol %= WCOLS;
  1202.         up = vtube[i]; tp = up + endcol; tpe = up + WCOLS;
  1203.         while (tp < tpe && *tp)
  1204.             tp++;
  1205.         if (tp - (up + stcol) < 2 * nc)
  1206.             return;
  1207.         vgoto(i, stcol);
  1208.     }
  1209.  
  1210.     /*
  1211.      * Go into delete mode and do the actual delete.
  1212.      * Padding is on DC itself.
  1213.      */
  1214.     godm();
  1215.     for (i = nc; i > 0; i--)
  1216.         vputp(DC, DEPTH(vcline));
  1217.     vputp(ED, 0);
  1218.  
  1219.     /*
  1220.      * Straighten up.
  1221.      * With CONCEPT like terminals, characters are pulled left
  1222.      * from first following null.  HP like terminals shift rest of
  1223.      * this (single physical) line rigidly.
  1224.      */
  1225.     if (IN) {
  1226.         up = vtube0 + stcol;
  1227.         tp = vtube0 + endcol;
  1228.         while (i = *tp++) {
  1229.             if ((i & (QUOTE|TRIM)) == QUOTE)
  1230.                 break;
  1231.             *up++ = i;
  1232.         }
  1233.         do
  1234.             *up++ = i;
  1235.         while (--nc);
  1236.     } else {
  1237.         copy(up + stcol, up + endcol, WCOLS - endcol);
  1238.         vclrbyte(tpe - nc, nc);
  1239.     }
  1240. }
  1241.  
  1242. #ifdef TRACE
  1243. tfixnl()
  1244. {
  1245.  
  1246.     if (trubble || techoin)
  1247.         fprintf(trace, "\n");
  1248.     trubble = 0, techoin = 0;
  1249. }
  1250.  
  1251. tvliny()
  1252. {
  1253.     register int i;
  1254.  
  1255.     if (!trace)
  1256.         return;
  1257.     tfixnl();
  1258.     fprintf(trace, "vcnt = %d, vcline = %d, vliny = ", vcnt, vcline);
  1259.     for (i = 0; i <= vcnt; i++) {
  1260.         fprintf(trace, "%d", LINE(i));
  1261.         if (FLAGS(i) & VDIRT)
  1262.             fprintf(trace, "*");
  1263.         if (DEPTH(i) != 1)
  1264.             fprintf(trace, "<%d>", DEPTH(i));
  1265.         if (i < vcnt)
  1266.             fprintf(trace, " ");
  1267.     }
  1268.     fprintf(trace, "\n");
  1269. }
  1270.  
  1271. tracec(c)
  1272.     char c;
  1273. {
  1274.  
  1275.     if (!techoin)
  1276.         trubble = 1;
  1277.     if (c == ESCAPE)
  1278.         fprintf(trace, "$");
  1279.     else if (c < ' ' || c == DELETE)
  1280.         fprintf(trace, "^%c", ctlof(c));
  1281.     else
  1282.         fprintf(trace, "%c", c);
  1283. }
  1284. #endif
  1285.  
  1286. /*
  1287.  * Put a character with possible tracing.
  1288.  */
  1289. vputch(c)
  1290.     int c;
  1291. {
  1292.  
  1293. #ifdef TRACE
  1294.     if (trace)
  1295.         tracec(c);
  1296. #endif
  1297.     vputc(c);
  1298. }
  1299.