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