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