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