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