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