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