xref: /original-bsd/usr.bin/ex/ex_vput.c (revision 552e81d8)
1 /* Copyright (c) 1980 Regents of the University of California */
2 static char *sccsid = "@(#)ex_vput.c	5.1 08/20/80";
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  */
13 vclear()
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  */
32 vclrbyte(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  */
46 vclrlin(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  */
62 vclreol()
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  */
102 vclrech(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 		 */
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 		}
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  */
154 fixech()
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  */
168 vcursbef(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  */
181 vcursat(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  */
194 vcursaft(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  */
205 vfixcurs()
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  */
215 vsetcurs(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  */
230 vigoto(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  */
242 vcsync()
243 {
244 
245 	vgoto(destline, destcol);
246 }
247 
248 /*
249  * Goto column x of the current line.
250  */
251 vgotoCL(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  */
264 vigotoCL(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  */
277 vgoto(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  */
419 vgotab()
420 {
421 	register int i = tabcol(destcol, value(TABSTOP)) - destcol;
422 
423 	do
424 		(*Outchar)(QUOTE);
425 	while (--i);
426 }
427 
428 /*
429  * Variables for insert mode.
430  */
431 int	linend;			/* The column position of end of line */
432 int	tabstart;		/* Column of start of first following tab */
433 int	tabend;			/* Column of end of following tabs */
434 int	tabsize;		/* Size of the following tabs */
435 int	tabslack;		/* Number of ``spaces'' in following tabs */
436 int	inssiz;			/* Number of characters to be inserted */
437 int	inscol;			/* Column where insertion is taking place */
438 int	shft;			/* Amount tab expansion shifted rest of line */
439 int	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  */
447 vprepins()
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 
458 vmaktop(p, cp)
459 	register int p;
460 	char *cp;
461 {
462 	register int i;
463 	char temp[TUBECOLS];
464 
465 	if (p < 0 || vtube[p] == cp)
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  */
486 vinschar(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 		 */
564 		inssiz = tabcol(inscol, value(TABSTOP)) - inscol;
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 		 */
640 		if (inssiz >= doomed + tabcol(tabstart, value(TABSTOP)) - tabstart) {
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  */
668 vrigid()
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  */
689 vneedpos(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 
701 vnpins(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  */
734 vishft()
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;
789 			do {
790 				if (*up)
791 					vputchar(*up++);
792 				else
793 					break;
794 			} while (--i);
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  */
830 viin(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  */
980 godm()
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  */
999 enddm()
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  */
1016 goim()
1017 {
1018 
1019 	if (!insmode)
1020 		vputp(IM, 0);
1021 	insmode = 1;
1022 }
1023 
1024 endim()
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  */
1043 vputchar(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
1054 	/* Fix problem of >79 chars on echo line. */
1055 	if (destcol >= WCOLS-1 && splitw && destline == WECHO)
1056 		pofix();
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 
1115 def:
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  */
1209 physdc(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
1298 tfixnl()
1299 {
1300 
1301 	if (trubble || techoin)
1302 		fprintf(trace, "\n");
1303 	trubble = 0, techoin = 0;
1304 }
1305 
1306 tvliny()
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 
1326 tracec(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  */
1344 vputch(c)
1345 	int c;
1346 {
1347 
1348 #ifdef TRACE
1349 	if (trace)
1350 		tracec(c);
1351 #endif
1352 	vputc(c);
1353 }
1354