preliminary release 3.6 10/18/80
[unix-history] / usr / src / usr.bin / ex / ex_vput.c
CommitLineData
7c4625ef 1/* Copyright (c) 1980 Regents of the University of California */
b41bbcb5 2static char *sccsid = "@(#)ex_vput.c 5.1 %G%";
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) {
718 Outchar = vputchar;
719 vsync(e + 1);
720 Outchar = vinschar;
721 }
722 } else {
723 vup1();
724 vigoto(WBOT, 0);
725 vclreol();
726 }
727 vprepins();
728}
729
730/*
731 * Do the shift of the next tabstop implied by
732 * insertion so it expands.
733 */
734vishft()
735{
736 int tshft = 0;
737 int j;
738 register int i;
739 register char *tp = vtube0;
740 register char *up;
741 short oldhold = hold;
742
743 shft = value(TABSTOP);
744 hold |= HOLDPUPD;
745 if (!IM && !EI) {
746 /*
747 * Dumb terminals are easy, we just have
748 * to retype the text.
749 */
750 vigotoCL(tabend + shft);
751 up = tp + tabend;
752 for (i = tabend; i < linend; i++)
753 vputchar(*up++);
754 } else if (IN) {
755 /*
756 * CONCEPT-like terminals do most of the work for us,
757 * we don't have to muck with simulation of multi-line
758 * insert mode. Some of the shifting may come for free
759 * also if the tabs don't have enough slack to take up
760 * all the inserted characters.
761 */
762 i = shft;
763 slakused = inssiz - doomed;
764 if (slakused > tabslack) {
765 i -= slakused - tabslack;
766 slakused -= tabslack;
767 }
768 if (i > 0 && tabend != linend) {
769 tshft = i;
770 vgotoCL(tabend);
771 goim();
772 do
773 vputchar(' ' | QUOTE);
774 while (--i);
775 }
776 } else {
777 /*
778 * HP and Datamedia type terminals have to have multi-line
779 * insert faked. Hack each segment after where we are
780 * (going backwards to where we are.) We then can
781 * hack the segment where the end of the first following
782 * tab group is.
783 */
784 for (j = DEPTH(vcline) - 1; j > (tabend + shft) / WCOLS; j--) {
785 vgotoCL(j * WCOLS);
786 goim();
787 up = tp + j * WCOLS - shft;
788 i = shft;
887e3e0d
MH
789 do {
790 if (*up)
791 vputchar(*up++);
792 else
793 break;
794 } while (--i);
98fb7faa
MH
795 }
796 vigotoCL(tabstart);
797 i = shft - (inssiz - doomed);
798 if (i > 0) {
799 tabslack = inssiz - doomed;
800 vcsync();
801 goim();
802 do
803 vputchar(' ');
804 while (--i);
805 }
806 }
807 /*
808 * Now do the data moving in the internal screen
809 * image which is common to all three cases.
810 */
811 tp += linend;
812 up = tp + shft;
813 i = linend - tabend;
814 if (i > 0)
815 do
816 *--up = *--tp;
817 while (--i);
818 if (IN && tshft) {
819 i = tshft;
820 do
821 *--up = ' ' | QUOTE;
822 while (--i);
823 }
824 hold = oldhold;
825}
826
827/*
828 * Now do the insert of the characters (finally).
829 */
830viin(c)
831 char c;
832{
833 register char *tp, *up;
834 register int i, j;
835 register bool noim = 0;
836 int remdoom;
837 short oldhold = hold;
838
839 hold |= HOLDPUPD;
840 if (tabsize && (IM && EI) && inssiz - doomed > tabslack)
841 /*
842 * There is a tab out there which will be affected
843 * by the insertion since there aren't enough doomed
844 * characters to take up all the insertion and we do
845 * have insert mode capability.
846 */
847 if (inscol + doomed == tabstart) {
848 /*
849 * The end of the doomed characters sits right at the
850 * start of the tabs, then we don't need to use insert
851 * mode; unless the tab has already been expanded
852 * in which case we MUST use insert mode.
853 */
854 slakused = 0;
855 noim = !shft;
856 } else {
857 /*
858 * The last really special case to handle is case
859 * where the tab is just sitting there and doesn't
860 * have enough slack to let the insertion take
861 * place without shifting the rest of the line
862 * over. In this case we have to go out and
863 * delete some characters of the tab before we start
864 * or the answer will be wrong, as the rest of the
865 * line will have been shifted. This code means
866 * that terminals with only insert chracter (no
867 * delete character) won't work correctly.
868 */
869 i = inssiz - doomed - tabslack - slakused;
870 i %= value(TABSTOP);
871 if (i > 0) {
872 vgotoCL(tabstart);
873 godm();
874 for (i = inssiz - doomed - tabslack; i > 0; i--)
875 vputp(DC, DEPTH(vcline));
876 enddm();
877 }
878 }
879
880 /*
881 * Now put out the characters of the actual insertion.
882 */
883 vigotoCL(inscol);
884 remdoom = doomed;
885 for (i = inssiz; i > 0; i--) {
886 if (remdoom > 0) {
887 remdoom--;
888 endim();
889 } else if (noim)
890 endim();
891 else if (IM && EI) {
892 vcsync();
893 goim();
894 }
895 vputchar(c);
896 }
897
898 if (!IM || !EI) {
899 /*
900 * We are a dumb terminal; brute force update
901 * the rest of the line; this is very much an n^^2 process,
902 * and totally unreasonable at low speed.
903 *
904 * You asked for it, you get it.
905 */
906 tp = vtube0 + inscol + doomed;
907 for (i = inscol + doomed; i < tabstart; i++)
908 vputchar(*tp++);
909 hold = oldhold;
910 vigotoCL(tabstart + inssiz - doomed);
911 for (i = tabsize - (inssiz - doomed) + shft; i > 0; i--)
912 vputchar(' ' | QUOTE);
913 } else {
914 if (!IN) {
915 /*
916 * On terminals without multi-line
917 * insert in the hardware, we must go fix the segments
918 * between the inserted text and the following
919 * tabs, if they are on different lines.
920 *
921 * Aaargh.
922 */
923 tp = vtube0;
924 for (j = (inscol + inssiz - 1) / WCOLS + 1;
925 j <= (tabstart + inssiz - doomed - 1) / WCOLS; j++) {
926 vgotoCL(j * WCOLS);
927 i = inssiz - doomed;
928 up = tp + j * WCOLS - i;
929 goim();
930 do
931 vputchar(*up++);
932 while (--i && *up);
933 }
934 } else {
935 /*
936 * On terminals with multi line inserts,
937 * life is simpler, just reflect eating of
938 * the slack.
939 */
940 tp = vtube0 + tabend;
941 for (i = tabsize - (inssiz - doomed); i >= 0; i--) {
942 if ((*--tp & (QUOTE|TRIM)) == QUOTE) {
943 --tabslack;
944 if (tabslack >= slakused)
945 continue;
946 }
947 *tp = ' ' | QUOTE;
948 }
949 }
950 /*
951 * Blank out the shifted positions to be tab positions.
952 */
953 if (shft) {
954 tp = vtube0 + tabend + shft;
955 for (i = tabsize - (inssiz - doomed) + shft; i > 0; i--)
956 if ((*--tp & QUOTE) == 0)
957 *tp = ' ' | QUOTE;
958 }
959 }
960
961 /*
962 * Finally, complete the screen image update
963 * to reflect the insertion.
964 */
965 hold = oldhold;
966 tp = vtube0 + tabstart; up = tp + inssiz - doomed;
967 for (i = tabstart; i > inscol + doomed; i--)
968 *--up = *--tp;
969 for (i = inssiz; i > 0; i--)
970 *--up = c;
971 doomed = 0;
972}
973
974/*
975 * Go into ``delete mode''. If the
976 * sequence which goes into delete mode
977 * is the same as that which goes into insert
978 * mode, then we are in delete mode already.
979 */
980godm()
981{
982
983 if (insmode) {
984 if (eq(DM, IM))
985 return;
986 endim();
987 }
988 vputp(DM, 0);
989}
990
991/*
992 * If we are coming out of delete mode, but
993 * delete and insert mode end with the same sequence,
994 * it wins to pretend we are now in insert mode,
995 * since we will likely want to be there again soon
996 * if we just moved over to delete space from part of
997 * a tab (above).
998 */
999enddm()
1000{
1001
1002 if (eq(DM, IM)) {
1003 insmode = 1;
1004 return;
1005 }
1006 vputp(ED, 0);
1007}
1008
1009/*
1010 * In and out of insert mode.
1011 * Note that the code here demands that there be
1012 * a string for insert mode (the null string) even
1013 * if the terminal does all insertions a single character
1014 * at a time, since it branches based on whether IM is null.
1015 */
1016goim()
1017{
1018
1019 if (!insmode)
1020 vputp(IM, 0);
1021 insmode = 1;
1022}
1023
1024endim()
1025{
1026
1027 if (insmode) {
1028 vputp(EI, 0);
1029 insmode = 0;
1030 }
1031}
1032
1033/*
1034 * Put the character c on the screen at the current cursor position.
1035 * This routine handles wraparound and scrolling and understands not
1036 * to roll when splitw is set, i.e. we are working in the echo area.
1037 * There is a bunch of hacking here dealing with the difference between
1038 * QUOTE, QUOTE|' ', and ' ' for CONCEPT-100 like terminals, and also
1039 * code to deal with terminals which overstrike, including CRT's where
1040 * you can erase overstrikes with some work. CRT's which do underlining
1041 * implicitly which has to be erased (like CONCEPTS) are also handled.
1042 */
1043vputchar(c)
1044 register int c;
1045{
1046 register char *tp;
1047 register int d;
1048
1049 c &= (QUOTE|TRIM);
1050#ifdef TRACE
1051 if (trace)
1052 tracec(c);
1053#endif
887e3e0d 1054 /* Fix problem of >79 chars on echo line. */
98fb7faa 1055 if (destcol >= WCOLS-1 && splitw && destline == WECHO)
887e3e0d 1056 pofix();
98fb7faa
MH
1057 if (destcol >= WCOLS) {
1058 destline += destcol / WCOLS;
1059 destcol %= WCOLS;
1060 }
1061 if (destline > WBOT && (!splitw || destline > WECHO))
1062 vrollup(destline);
1063 tp = vtube[destline] + destcol;
1064 switch (c) {
1065
1066 case '\t':
1067 vgotab();
1068 return;
1069
1070 case ' ':
1071 /*
1072 * We can get away without printing a space in a number
1073 * of cases, but not always. We get away with doing nothing
1074 * if we are not in insert mode, and not on a CONCEPT-100
1075 * like terminal, and either not in hardcopy open or in hardcopy
1076 * open on a terminal with no overstriking, provided,
1077 * in all cases, that nothing has ever been displayed
1078 * at this position. Ugh.
1079 */
1080 if (!insmode && !IN && (state != HARDOPEN || OS) && (*tp&TRIM) == 0) {
1081 *tp = ' ';
1082 destcol++;
1083 return;
1084 }
1085 goto def;
1086
1087 case QUOTE:
1088 if (insmode) {
1089 /*
1090 * When in insert mode, tabs have to expand
1091 * to real, printed blanks.
1092 */
1093 c = ' ' | QUOTE;
1094 goto def;
1095 }
1096 if (*tp == 0) {
1097 /*
1098 * A ``space''.
1099 */
1100 if ((hold & HOLDPUPD) == 0)
1101 *tp = QUOTE;
1102 destcol++;
1103 return;
1104 }
1105 /*
1106 * A ``space'' ontop of a part of a tab.
1107 */
1108 if (*tp & QUOTE) {
1109 destcol++;
1110 return;
1111 }
1112 c = ' ' | QUOTE;
1113 /* fall into ... */
1114
1115def:
1116 default:
1117 d = *tp & TRIM;
1118 /*
1119 * Now get away with doing nothing if the characters
1120 * are the same, provided we are not in insert mode
1121 * and if we are in hardopen, that the terminal has overstrike.
1122 */
1123 if (d == (c & TRIM) && !insmode && (state != HARDOPEN || OS)) {
1124 if ((hold & HOLDPUPD) == 0)
1125 *tp = c;
1126 destcol++;
1127 return;
1128 }
1129 /*
1130 * Backwards looking optimization.
1131 * The low level cursor motion routines will use
1132 * a cursor motion right sequence to step 1 character
1133 * right. On, e.g., a DM3025A this is 2 characters
1134 * and printing is noticeably slower at 300 baud.
1135 * Since the low level routines are not allowed to use
1136 * spaces for positioning, we discover the common
1137 * case of a single space here and force a space
1138 * to be printed.
1139 */
1140 if (destcol == outcol + 1 && tp[-1] == ' ' && outline == destline) {
1141 vputc(' ');
1142 outcol++;
1143 }
1144
1145 /*
1146 * This is an inline expansion a call to vcsync() dictated
1147 * by high frequency in a profile.
1148 */
1149 if (outcol != destcol || outline != destline)
1150 vgoto(destline, destcol);
1151
1152 /*
1153 * Deal with terminals which have overstrike.
1154 * We handle erasing general overstrikes, erasing
1155 * underlines on terminals (such as CONCEPTS) which
1156 * do underlining correctly automatically (e.g. on nroff
1157 * output), and remembering, in hardcopy mode,
1158 * that we have overstruct something.
1159 */
1160 if (!insmode && d && d != ' ' && d != (c & TRIM)) {
1161 if (EO && (OS || UL && (c == '_' || d == '_'))) {
1162 vputc(' ');
1163 outcol++, destcol++;
1164 back1();
1165 } else
1166 rubble = 1;
1167 }
1168
1169 /*
1170 * Unless we are just bashing characters around for
1171 * inner working of insert mode, update the display.
1172 */
1173 if ((hold & HOLDPUPD) == 0)
1174 *tp = c;
1175
1176 /*
1177 * In insert mode, put out the IC sequence, padded
1178 * based on the depth of the current line.
1179 * A terminal which had no real insert mode, rather
1180 * opening a character position at a time could do this.
1181 * Actually should use depth to end of current line
1182 * but this rarely matters.
1183 */
1184 if (insmode)
1185 vputp(IC, DEPTH(vcline));
1186 vputc(c & TRIM);
1187
1188 /*
1189 * In insert mode, IP is a post insert pad.
1190 */
1191 if (insmode)
1192 vputp(IP, DEPTH(vcline));
1193 destcol++, outcol++;
1194
1195 /*
1196 * CONCEPT braindamage in early models: after a wraparound
1197 * the next newline is eaten. It's hungry so we just
1198 * feed it now rather than worrying about it.
1199 */
1200 if (XN && outcol % WCOLS == 0)
1201 vputc('\n');
1202 }
1203}
1204
1205/*
1206 * Delete display positions stcol through endcol.
1207 * Amount of use of special terminal features here is limited.
1208 */
1209physdc(stcol, endcol)
1210 int stcol, endcol;
1211{
1212 register char *tp, *up;
1213 char *tpe;
1214 register int i;
1215 register int nc = endcol - stcol;
1216
1217#ifdef IDEBUG
1218 if (trace)
1219 tfixnl(), fprintf(trace, "physdc(%d, %d)\n", stcol, endcol);
1220#endif
1221 if (!DC || nc <= 0)
1222 return;
1223 if (IN) {
1224 /*
1225 * CONCEPT-100 like terminal.
1226 * If there are any ``spaces'' in the material to be
1227 * deleted, then this is too hard, just retype.
1228 */
1229 vprepins();
1230 up = vtube0 + stcol;
1231 i = nc;
1232 do
1233 if ((*up++ & (QUOTE|TRIM)) == QUOTE)
1234 return;
1235 while (--i);
1236 i = 2 * nc;
1237 do
1238 if (*up == 0 || (*up++ & QUOTE) == QUOTE)
1239 return;
1240 while (--i);
1241 vgotoCL(stcol);
1242 } else {
1243 /*
1244 * HP like delete mode.
1245 * Compute how much text we are moving over by deleting.
1246 * If it appears to be faster to just retype
1247 * the line, do nothing and that will be done later.
1248 * We are assuming 2 output characters per deleted
1249 * characters and that clear to end of line is available.
1250 */
1251 i = stcol / WCOLS;
1252 if (i != endcol / WCOLS)
1253 return;
1254 i += LINE(vcline);
1255 stcol %= WCOLS;
1256 endcol %= WCOLS;
1257 up = vtube[i]; tp = up + endcol; tpe = up + WCOLS;
1258 while (tp < tpe && *tp)
1259 tp++;
1260 if (tp - (up + stcol) < 2 * nc)
1261 return;
1262 vgoto(i, stcol);
1263 }
1264
1265 /*
1266 * Go into delete mode and do the actual delete.
1267 * Padding is on DC itself.
1268 */
1269 godm();
1270 for (i = nc; i > 0; i--)
1271 vputp(DC, DEPTH(vcline));
1272 vputp(ED, 0);
1273
1274 /*
1275 * Straighten up.
1276 * With CONCEPT like terminals, characters are pulled left
1277 * from first following null. HP like terminals shift rest of
1278 * this (single physical) line rigidly.
1279 */
1280 if (IN) {
1281 up = vtube0 + stcol;
1282 tp = vtube0 + endcol;
1283 while (i = *tp++) {
1284 if ((i & (QUOTE|TRIM)) == QUOTE)
1285 break;
1286 *up++ = i;
1287 }
1288 do
1289 *up++ = i;
1290 while (--nc);
1291 } else {
1292 copy(up + stcol, up + endcol, WCOLS - endcol);
1293 vclrbyte(tpe - nc, nc);
1294 }
1295}
1296
1297#ifdef TRACE
1298tfixnl()
1299{
1300
1301 if (trubble || techoin)
1302 fprintf(trace, "\n");
1303 trubble = 0, techoin = 0;
1304}
1305
1306tvliny()
1307{
1308 register int i;
1309
1310 if (!trace)
1311 return;
1312 tfixnl();
1313 fprintf(trace, "vcnt = %d, vcline = %d, vliny = ", vcnt, vcline);
1314 for (i = 0; i <= vcnt; i++) {
1315 fprintf(trace, "%d", LINE(i));
1316 if (FLAGS(i) & VDIRT)
1317 fprintf(trace, "*");
1318 if (DEPTH(i) != 1)
1319 fprintf(trace, "<%d>", DEPTH(i));
1320 if (i < vcnt)
1321 fprintf(trace, " ");
1322 }
1323 fprintf(trace, "\n");
1324}
1325
1326tracec(c)
1327 char c;
1328{
1329
1330 if (!techoin)
1331 trubble = 1;
1332 if (c == ESCAPE)
1333 fprintf(trace, "$");
1334 else if (c < ' ' || c == DELETE)
1335 fprintf(trace, "^%c", ctlof(c));
1336 else
1337 fprintf(trace, "%c", c);
1338}
1339#endif
1340
1341/*
1342 * Put a character with possible tracing.
1343 */
1344vputch(c)
1345 int c;
1346{
1347
1348#ifdef TRACE
1349 if (trace)
1350 tracec(c);
1351#endif
1352 vputc(c);
1353}