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