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