BSD 3 development
[unix-history] / usr / src / cmd / ex / ex_vput.c
CommitLineData
27704fe4
BJ
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 */
12vclear()
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 */
31vclrbyte(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 */
45vclrlin(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 */
61vclreol()
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 */
101vclrech(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 */
135fixech()
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 */
149vcursbef(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 */
162vcursat(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 */
175vcursaft(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 */
186vfixcurs()
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 */
196vsetcurs(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 */
211vigoto(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 */
223vcsync()
224{
225
226 vgoto(destline, destcol);
227}
228
229/*
230 * Goto column x of the current line.
231 */
232vgotoCL(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 */
245vigotoCL(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 */
258vgoto(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 */
400vgotab()
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 */
412int linend; /* The column position of end of line */
413int tabstart; /* Column of start of first following tab */
414int tabend; /* Column of end of following tabs */
415int tabsize; /* Size of the following tabs */
416int tabslack; /* Number of ``spaces'' in following tabs */
417int inssiz; /* Number of characters to be inserted */
418int inscol; /* Column where insertion is taking place */
419int shft; /* Amount tab expansion shifted rest of line */
420int 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 */
428vprepins()
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
439vmaktop(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 */
467vinschar(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) >
486 (destline - LINE(vcline)) * WCOLS + destcol)
487 return;
488 /*
489 * The next line is about to be clobbered
490 * make space for another segment of this line
491 * (on an intelligent terminal) or just remember
492 * that next line was clobbered (on a dumb one
493 * if we don't care to redraw the tail.
494 */
495 if (AL) {
496 vnpins(0);
497 } else {
498 c = LINE(vcline) + DEPTH(vcline);
499 if (c < LINE(vcline + 1) || c > WBOT)
500 return;
501 i = destcol;
502 vinslin(c, 1, vcline);
503 DEPTH(vcline)++;
504 vigoto(c, i);
505 vprepins();
506 }
507 return;
508 }
509 /*
510 * Compute the number of positions in the line image of the
511 * current line. This is done from the physical image
512 * since that is faster. Note that we have no memory
513 * from insertion to insertion so that routines which use
514 * us don't have to worry about moving the cursor around.
515 */
516 if (*vtube0 == 0)
517 linend = 0;
518 else {
519 /*
520 * Search backwards for a non-null character
521 * from the end of the displayed line.
522 */
523 i = WCOLS * DEPTH(vcline);
524 if (i == 0)
525 i = WCOLS;
526 tp = vtube0 + i;
527 while (*--tp == 0)
528 if (--i == 0)
529 break;
530 linend = i;
531 }
532
533 /*
534 * We insert at a position based on the physical location
535 * of the output cursor.
536 */
537 inscol = destcol + (destline - LINE(vcline)) * WCOLS;
538 if (c == '\t') {
539 /*
540 * Characters inserted from a tab must be
541 * remembered as being part of a tab, but we can't
542 * use QUOTE here since we really need to print blanks.
543 * QUOTE|' ' is the representation of this.
544 */
545 inssiz = value(TABSTOP) - inscol % value(TABSTOP);
546 c = ' ' | QUOTE;
547 } else
548 inssiz = 1;
549
550 /*
551 * If the text to be inserted is less than the number
552 * of doomed positions, then we don't need insert mode,
553 * rather we can just typeover.
554 */
555 if (inssiz <= doomed) {
556 endim();
557 if (inscol != linend)
558 doomed -= inssiz;
559 do
560 vputchar(c);
561 while (--inssiz);
562 return;
563 }
564
565 /*
566 * Have to really do some insertion, thus
567 * stake out the bounds of the first following
568 * group of tabs, computing starting position,
569 * ending position, and the number of ``spaces'' therein
570 * so we can tell how much it will squish.
571 */
572 tp = vtube0 + inscol;
573 for (i = inscol; i < linend; i++)
574 if (*tp++ & QUOTE) {
575 --tp;
576 break;
577 }
578 tabstart = tabend = i;
579 tabslack = 0;
580 while (tabend < linend) {
581 i = *tp++;
582 if ((i & QUOTE) == 0)
583 break;
584 if ((i & TRIM) == 0)
585 tabslack++;
586 tabsize++;
587 tabend++;
588 }
589 tabsize = tabend - tabstart;
590
591 /*
592 * For HP's and DM's, e.g. tabslack has no meaning.
593 */
594 if (!IN)
595 tabslack = 0;
596#ifdef IDEBUG
597 if (trace) {
598 fprintf(trace, "inscol %d, inssiz %d, tabstart %d, ",
599 inscol, inssiz, tabstart);
600 fprintf(trace, "tabend %d, tabslack %d, linend %d\n",
601 tabend, tabslack, linend);
602 }
603#endif
604
605 /*
606 * The real work begins.
607 */
608 slakused = 0;
609 shft = 0;
610 if (tabsize) {
611 /*
612 * There are tabs on this line.
613 * If they need to expand, then the rest of the line
614 * will have to be shifted over. In this case,
615 * we will need to make sure there are no ``spaces''
616 * in the rest of the line (on e.g. CONCEPT-100)
617 * and then grab another segment on the screen if this
618 * line is now deeper. We then do the shift
619 * implied by the insertion.
620 */
621 if (inssiz >= doomed + value(TABSTOP) - tabstart % value(TABSTOP)) {
622 if (IN)
623 vrigid();
624 vneedpos(value(TABSTOP));
625 vishft();
626 }
627 } else if (inssiz > doomed)
628 /*
629 * No tabs, but line may still get deeper.
630 */
631 vneedpos(inssiz - doomed);
632 /*
633 * Now put in the inserted characters.
634 */
635 viin(c);
636
637 /*
638 * Now put the cursor in its final resting place.
639 */
640 destline = LINE(vcline);
641 destcol = inscol + inssiz;
642 vcsync();
643}
644
645/*
646 * Rigidify the rest of the line after the first
647 * group of following tabs, typing blanks over ``spaces''.
648 */
649vrigid()
650{
651 register int col;
652 register char *tp = vtube0 + tabend;
653
654 for (col = tabend; col < linend; col++)
655 if ((*tp++ & TRIM) == 0) {
656 endim();
657 vgotoCL(col);
658 vputchar(' ' | QUOTE);
659 }
660}
661
662/*
663 * We need cnt more positions on this line.
664 * Open up new space on the screen (this may in fact be a
665 * screen rollup).
666 *
667 * On a dumb terminal we may infact redisplay the rest of the
668 * screen here brute force to keep it pretty.
669 */
670vneedpos(cnt)
671 int cnt;
672{
673 register int d = DEPTH(vcline);
674 register int rmdr = d * WCOLS - linend;
675
676 if (cnt <= rmdr - IN)
677 return;
678 endim();
679 vnpins(1);
680}
681
682vnpins(dosync)
683 int dosync;
684{
685 register int d = DEPTH(vcline);
686 register int e;
687
688 e = LINE(vcline) + DEPTH(vcline);
689 if (e < LINE(vcline + 1)) {
690 vigoto(e, 0);
691 vclreol();
692 return;
693 }
694 DEPTH(vcline)++;
695 if (e < WECHO) {
696 e = vglitchup(vcline, d);
697 vigoto(e, 0); vclreol();
698 if (dosync) {
699 Outchar = vputchar;
700 vsync(e + 1);
701 Outchar = vinschar;
702 }
703 } else {
704 vup1();
705 vigoto(WBOT, 0);
706 vclreol();
707 }
708 vprepins();
709}
710
711/*
712 * Do the shift of the next tabstop implied by
713 * insertion so it expands.
714 */
715vishft()
716{
717 int tshft = 0;
718 int j;
719 register int i;
720 register char *tp = vtube0;
721 register char *up;
722 short oldhold = hold;
723
724 shft = value(TABSTOP);
725 hold |= HOLDPUPD;
726 if (!IM && !EI) {
727 /*
728 * Dumb terminals are easy, we just have
729 * to retype the text.
730 */
731 vigotoCL(tabend + shft);
732 up = tp + tabend;
733 for (i = tabend; i < linend; i++)
734 vputchar(*up++);
735 } else if (IN) {
736 /*
737 * CONCEPT-like terminals do most of the work for us,
738 * we don't have to muck with simulation of multi-line
739 * insert mode. Some of the shifting may come for free
740 * also if the tabs don't have enough slack to take up
741 * all the inserted characters.
742 */
743 i = shft;
744 slakused = inssiz - doomed;
745 if (slakused > tabslack) {
746 i -= slakused - tabslack;
747 slakused -= tabslack;
748 }
749 if (i > 0 && tabend != linend) {
750 tshft = i;
751 vgotoCL(tabend);
752 goim();
753 do
754 vputchar(' ' | QUOTE);
755 while (--i);
756 }
757 } else {
758 /*
759 * HP and Datamedia type terminals have to have multi-line
760 * insert faked. Hack each segment after where we are
761 * (going backwards to where we are.) We then can
762 * hack the segment where the end of the first following
763 * tab group is.
764 */
765 for (j = DEPTH(vcline) - 1; j > (tabend + shft) / WCOLS; j--) {
766 vgotoCL(j * WCOLS);
767 goim();
768 up = tp + j * WCOLS - shft;
769 i = shft;
770 do
771 vputchar(*up++);
772 while (--i);
773 }
774 vigotoCL(tabstart);
775 i = shft - (inssiz - doomed);
776 if (i > 0) {
777 tabslack = inssiz - doomed;
778 vcsync();
779 goim();
780 do
781 vputchar(' ');
782 while (--i);
783 }
784 }
785 /*
786 * Now do the data moving in the internal screen
787 * image which is common to all three cases.
788 */
789 tp += linend;
790 up = tp + shft;
791 i = linend - tabend;
792 if (i > 0)
793 do
794 *--up = *--tp;
795 while (--i);
796 if (IN && tshft) {
797 i = tshft;
798 do
799 *--up = ' ' | QUOTE;
800 while (--i);
801 }
802 hold = oldhold;
803}
804
805/*
806 * Now do the insert of the characters (finally).
807 */
808viin(c)
809 char c;
810{
811 register char *tp, *up;
812 register int i, j;
813 register bool noim = 0;
814 int remdoom;
815 short oldhold = hold;
816
817 hold |= HOLDPUPD;
818 if (tabsize && (IM && EI) && inssiz - doomed > tabslack)
819 /*
820 * There is a tab out there which will be affected
821 * by the insertion since there aren't enough doomed
822 * characters to take up all the insertion and we do
823 * have insert mode capability.
824 */
825 if (inscol + doomed == tabstart) {
826 /*
827 * The end of the doomed characters sits right at the
828 * start of the tabs, then we don't need to use insert
829 * mode; unless the tab has already been expanded
830 * in which case we MUST use insert mode.
831 */
832 slakused = 0;
833 noim = !shft;
834 } else {
835 /*
836 * The last really special case to handle is case
837 * where the tab is just sitting there and doesn't
838 * have enough slack to let the insertion take
839 * place without shifting the rest of the line
840 * over. In this case we have to go out and
841 * delete some characters of the tab before we start
842 * or the answer will be wrong, as the rest of the
843 * line will have been shifted. This code means
844 * that terminals with only insert chracter (no
845 * delete character) won't work correctly.
846 */
847 i = inssiz - doomed - tabslack - slakused;
848 i %= value(TABSTOP);
849 if (i > 0) {
850 vgotoCL(tabstart);
851 godm();
852 for (i = inssiz - doomed - tabslack; i > 0; i--)
853 vputp(DC, DEPTH(vcline));
854 enddm();
855 }
856 }
857
858 /*
859 * Now put out the characters of the actual insertion.
860 */
861 vigotoCL(inscol);
862 remdoom = doomed;
863 for (i = inssiz; i > 0; i--) {
864 if (remdoom > 0) {
865 remdoom--;
866 endim();
867 } else if (noim)
868 endim();
869 else if (IM && EI) {
870 vcsync();
871 goim();
872 }
873 vputchar(c);
874 }
875
876 if (!IM || !EI) {
877 /*
878 * We are a dumb terminal; brute force update
879 * the rest of the line; this is very much an n^^2 process,
880 * and totally unreasonable at low speed.
881 *
882 * You asked for it, you get it.
883 */
884 tp = vtube0 + inscol + doomed;
885 for (i = inscol + doomed; i < tabstart; i++)
886 vputchar(*tp++);
887 hold = oldhold;
888 vigotoCL(tabstart + inssiz - doomed);
889 for (i = tabsize - (inssiz - doomed) + shft; i > 0; i--)
890 vputchar(' ' | QUOTE);
891 } else {
892 if (!IN) {
893 /*
894 * On terminals without multi-line
895 * insert in the hardware, we must go fix the segments
896 * between the inserted text and the following
897 * tabs, if they are on different lines.
898 *
899 * Aaargh.
900 */
901 tp = vtube0;
902 for (j = (inscol + inssiz - 1) / WCOLS + 1;
903 j <= (tabstart + inssiz - doomed - 1) / WCOLS; j++) {
904 vgotoCL(j * WCOLS);
905 i = inssiz - doomed;
906 up = tp + j * WCOLS - i;
907 goim();
908 do
909 vputchar(*up++);
910 while (--i && *up);
911 }
912 } else {
913 /*
914 * On terminals with multi line inserts,
915 * life is simpler, just reflect eating of
916 * the slack.
917 */
918 tp = vtube0 + tabend;
919 for (i = tabsize - (inssiz - doomed); i >= 0; i--) {
920 if ((*--tp & (QUOTE|TRIM)) == QUOTE) {
921 --tabslack;
922 if (tabslack >= slakused)
923 continue;
924 }
925 *tp = ' ' | QUOTE;
926 }
927 }
928 /*
929 * Blank out the shifted positions to be tab positions.
930 */
931 if (shft) {
932 tp = vtube0 + tabend + shft;
933 for (i = tabsize - (inssiz - doomed) + shft; i > 0; i--)
934 if ((*--tp & QUOTE) == 0)
935 *tp = ' ' | QUOTE;
936 }
937 }
938
939 /*
940 * Finally, complete the screen image update
941 * to reflect the insertion.
942 */
943 hold = oldhold;
944 tp = vtube0 + tabstart; up = tp + inssiz - doomed;
945 for (i = tabstart; i > inscol + doomed; i--)
946 *--up = *--tp;
947 for (i = inssiz; i > 0; i--)
948 *--up = c;
949 doomed = 0;
950}
951
952/*
953 * Go into ``delete mode''. If the
954 * sequence which goes into delete mode
955 * is the same as that which goes into insert
956 * mode, then we are in delete mode already.
957 */
958godm()
959{
960
961 if (insmode) {
962 if (eq(DM, IM))
963 return;
964 endim();
965 }
966 vputp(DM, 0);
967}
968
969/*
970 * If we are coming out of delete mode, but
971 * delete and insert mode end with the same sequence,
972 * it wins to pretend we are now in insert mode,
973 * since we will likely want to be there again soon
974 * if we just moved over to delete space from part of
975 * a tab (above).
976 */
977enddm()
978{
979
980 if (eq(DM, IM)) {
981 insmode = 1;
982 return;
983 }
984 vputp(ED, 0);
985}
986
987/*
988 * In and out of insert mode.
989 * Note that the code here demands that there be
990 * a string for insert mode (the null string) even
991 * if the terminal does all insertions a single character
992 * at a time, since it branches based on whether IM is null.
993 */
994goim()
995{
996
997 if (!insmode)
998 vputp(IM, 0);
999 insmode = 1;
1000}
1001
1002endim()
1003{
1004
1005 if (insmode) {
1006 vputp(EI, 0);
1007 insmode = 0;
1008 }
1009}
1010
1011/*
1012 * Put the character c on the screen at the current cursor position.
1013 * This routine handles wraparound and scrolling and understands not
1014 * to roll when splitw is set, i.e. we are working in the echo area.
1015 * There is a bunch of hacking here dealing with the difference between
1016 * QUOTE, QUOTE|' ', and ' ' for CONCEPT-100 like terminals, and also
1017 * code to deal with terminals which overstrike, including CRT's where
1018 * you can erase overstrikes with some work. CRT's which do underlining
1019 * implicitly which has to be erased (like CONCEPTS) are also handled.
1020 */
1021vputchar(c)
1022 register int c;
1023{
1024 register char *tp;
1025 register int d;
1026
1027 c &= (QUOTE|TRIM);
1028#ifdef TRACE
1029 if (trace)
1030 tracec(c);
1031#endif
1032 /* Patch to fix problem of >79 chars on echo line: don't echo extras */
1033 if (destcol >= WCOLS-1 && splitw && destline == WECHO)
1034 return;
1035 if (destcol >= WCOLS) {
1036 destline += destcol / WCOLS;
1037 destcol %= WCOLS;
1038 }
1039 if (destline > WBOT && (!splitw || destline > WECHO))
1040 vrollup(destline);
1041 tp = vtube[destline] + destcol;
1042 switch (c) {
1043
1044 case '\t':
1045 vgotab();
1046 return;
1047
1048 case ' ':
1049 /*
1050 * We can get away without printing a space in a number
1051 * of cases, but not always. We get away with doing nothing
1052 * if we are not in insert mode, and not on a CONCEPT-100
1053 * like terminal, and either not in hardcopy open or in hardcopy
1054 * open on a terminal with no overstriking, provided,
1055 * in all cases, that nothing has ever been displayed
1056 * at this position. Ugh.
1057 */
1058 if (!insmode && !IN && (state != HARDOPEN || OS) && (*tp&TRIM) == 0) {
1059 *tp = ' ';
1060 destcol++;
1061 return;
1062 }
1063 goto def;
1064
1065 case QUOTE:
1066 if (insmode) {
1067 /*
1068 * When in insert mode, tabs have to expand
1069 * to real, printed blanks.
1070 */
1071 c = ' ' | QUOTE;
1072 goto def;
1073 }
1074 if (*tp == 0) {
1075 /*
1076 * A ``space''.
1077 */
1078 if ((hold & HOLDPUPD) == 0)
1079 *tp = QUOTE;
1080 destcol++;
1081 return;
1082 }
1083 /*
1084 * A ``space'' ontop of a part of a tab.
1085 */
1086 if (*tp & QUOTE) {
1087 destcol++;
1088 return;
1089 }
1090 c = ' ' | QUOTE;
1091 /* fall into ... */
1092
1093def:
1094 default:
1095 d = *tp & TRIM;
1096 /*
1097 * Now get away with doing nothing if the characters
1098 * are the same, provided we are not in insert mode
1099 * and if we are in hardopen, that the terminal has overstrike.
1100 */
1101 if (d == (c & TRIM) && !insmode && (state != HARDOPEN || OS)) {
1102 if ((hold & HOLDPUPD) == 0)
1103 *tp = c;
1104 destcol++;
1105 return;
1106 }
1107 /*
1108 * Backwards looking optimization.
1109 * The low level cursor motion routines will use
1110 * a cursor motion right sequence to step 1 character
1111 * right. On, e.g., a DM3025A this is 2 characters
1112 * and printing is noticeably slower at 300 baud.
1113 * Since the low level routines are not allowed to use
1114 * spaces for positioning, we discover the common
1115 * case of a single space here and force a space
1116 * to be printed.
1117 */
1118 if (destcol == outcol + 1 && tp[-1] == ' ' && outline == destline) {
1119 vputc(' ');
1120 outcol++;
1121 }
1122
1123 /*
1124 * This is an inline expansion a call to vcsync() dictated
1125 * by high frequency in a profile.
1126 */
1127 if (outcol != destcol || outline != destline)
1128 vgoto(destline, destcol);
1129
1130 /*
1131 * Deal with terminals which have overstrike.
1132 * We handle erasing general overstrikes, erasing
1133 * underlines on terminals (such as CONCEPTS) which
1134 * do underlining correctly automatically (e.g. on nroff
1135 * output), and remembering, in hardcopy mode,
1136 * that we have overstruct something.
1137 */
1138 if (!insmode && d && d != ' ' && d != (c & TRIM)) {
1139 if (EO && (OS || UL && (c == '_' || d == '_'))) {
1140 vputc(' ');
1141 outcol++, destcol++;
1142 back1();
1143 } else
1144 rubble = 1;
1145 }
1146
1147 /*
1148 * Unless we are just bashing characters around for
1149 * inner working of insert mode, update the display.
1150 */
1151 if ((hold & HOLDPUPD) == 0)
1152 *tp = c;
1153
1154 /*
1155 * In insert mode, put out the IC sequence, padded
1156 * based on the depth of the current line.
1157 * A terminal which had no real insert mode, rather
1158 * opening a character position at a time could do this.
1159 * Actually should use depth to end of current line
1160 * but this rarely matters.
1161 */
1162 if (insmode)
1163 vputp(IC, DEPTH(vcline));
1164 vputc(c & TRIM);
1165
1166 /*
1167 * In insert mode, IP is a post insert pad.
1168 */
1169 if (insmode)
1170 vputp(IP, DEPTH(vcline));
1171 destcol++, outcol++;
1172
1173 /*
1174 * CONCEPT braindamage in early models: after a wraparound
1175 * the next newline is eaten. It's hungry so we just
1176 * feed it now rather than worrying about it.
1177 */
1178 if (XN && outcol % WCOLS == 0)
1179 vputc('\n');
1180 }
1181}
1182
1183/*
1184 * Delete display positions stcol through endcol.
1185 * Amount of use of special terminal features here is limited.
1186 */
1187physdc(stcol, endcol)
1188 int stcol, endcol;
1189{
1190 register char *tp, *up;
1191 char *tpe;
1192 register int i;
1193 register int nc = endcol - stcol;
1194
1195#ifdef IDEBUG
1196 if (trace)
1197 tfixnl(), fprintf(trace, "physdc(%d, %d)\n", stcol, endcol);
1198#endif
1199 if (!DC || nc <= 0)
1200 return;
1201 if (IN) {
1202 /*
1203 * CONCEPT-100 like terminal.
1204 * If there are any ``spaces'' in the material to be
1205 * deleted, then this is too hard, just retype.
1206 */
1207 vprepins();
1208 up = vtube0 + stcol;
1209 i = nc;
1210 do
1211 if ((*up++ & (QUOTE|TRIM)) == QUOTE)
1212 return;
1213 while (--i);
1214 i = 2 * nc;
1215 do
1216 if (*up == 0 || (*up++ & QUOTE) == QUOTE)
1217 return;
1218 while (--i);
1219 vgotoCL(stcol);
1220 } else {
1221 /*
1222 * HP like delete mode.
1223 * Compute how much text we are moving over by deleting.
1224 * If it appears to be faster to just retype
1225 * the line, do nothing and that will be done later.
1226 * We are assuming 2 output characters per deleted
1227 * characters and that clear to end of line is available.
1228 */
1229 i = stcol / WCOLS;
1230 if (i != endcol / WCOLS)
1231 return;
1232 i += LINE(vcline);
1233 stcol %= WCOLS;
1234 endcol %= WCOLS;
1235 up = vtube[i]; tp = up + endcol; tpe = up + WCOLS;
1236 while (tp < tpe && *tp)
1237 tp++;
1238 if (tp - (up + stcol) < 2 * nc)
1239 return;
1240 vgoto(i, stcol);
1241 }
1242
1243 /*
1244 * Go into delete mode and do the actual delete.
1245 * Padding is on DC itself.
1246 */
1247 godm();
1248 for (i = nc; i > 0; i--)
1249 vputp(DC, DEPTH(vcline));
1250 vputp(ED, 0);
1251
1252 /*
1253 * Straighten up.
1254 * With CONCEPT like terminals, characters are pulled left
1255 * from first following null. HP like terminals shift rest of
1256 * this (single physical) line rigidly.
1257 */
1258 if (IN) {
1259 up = vtube0 + stcol;
1260 tp = vtube0 + endcol;
1261 while (i = *tp++) {
1262 if ((i & (QUOTE|TRIM)) == QUOTE)
1263 break;
1264 *up++ = i;
1265 }
1266 do
1267 *up++ = i;
1268 while (--nc);
1269 } else {
1270 copy(up + stcol, up + endcol, WCOLS - endcol);
1271 vclrbyte(tpe - nc, nc);
1272 }
1273}
1274
1275#ifdef TRACE
1276tfixnl()
1277{
1278
1279 if (trubble || techoin)
1280 fprintf(trace, "\n");
1281 trubble = 0, techoin = 0;
1282}
1283
1284tvliny()
1285{
1286 register int i;
1287
1288 if (!trace)
1289 return;
1290 tfixnl();
1291 fprintf(trace, "vcnt = %d, vcline = %d, vliny = ", vcnt, vcline);
1292 for (i = 0; i <= vcnt; i++) {
1293 fprintf(trace, "%d", LINE(i));
1294 if (FLAGS(i) & VDIRT)
1295 fprintf(trace, "*");
1296 if (DEPTH(i) != 1)
1297 fprintf(trace, "<%d>", DEPTH(i));
1298 if (i < vcnt)
1299 fprintf(trace, " ");
1300 }
1301 fprintf(trace, "\n");
1302}
1303
1304tracec(c)
1305 char c;
1306{
1307
1308 if (!techoin)
1309 trubble = 1;
1310 if (c == ESCAPE)
1311 fprintf(trace, "$");
1312 else if (c < ' ' || c == DELETE)
1313 fprintf(trace, "^%c", ctlof(c));
1314 else
1315 fprintf(trace, "%c", c);
1316}
1317#endif
1318
1319/*
1320 * Put a character with possible tracing.
1321 */
1322vputch(c)
1323 int c;
1324{
1325
1326#ifdef TRACE
1327 if (trace)
1328 tracec(c);
1329#endif
1330 vputc(c);
1331}