xref: /illumos-gate/usr/src/cmd/vi/port/ex_put.c (revision d48be21240dfd051b689384ce2b23479d757f2d8)
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 #ifndef PRESUNEUC
37 #include <wctype.h>
38 /* Undef putchar/getchar if they're defined. */
39 #ifdef putchar
40 #	undef putchar
41 #endif
42 #ifdef getchar
43 #	undef getchar
44 #endif
45 #endif /* PRESUNEUC */
46 
47 /*
48  * Terminal driving and line formatting routines.
49  * Basic motion optimizations are done here as well
50  * as formatting of lines (printing of control characters,
51  * line numbering and the like).
52  */
53 
54 /*
55  * The routines outchar, putchar and pline are actually
56  * variables, and these variables point at the current definitions
57  * of the routines.  See the routine setflav.
58  * We sometimes make outchar be routines which catch the characters
59  * to be printed, e.g. if we want to see how long a line is.
60  * During open/visual, outchar and putchar will be set to
61  * routines in the file ex_vput.c (vputchar, vinschar, etc.).
62  */
63 int	(*Outchar)() = termchar;
64 int	(*Putchar)() = normchar;
65 int	(*Pline)() = normline;
66 static unsigned char multic[MULTI_BYTE_MAX];
67 bool putoctal; /* flag to say if byte should be printed as octal */
68 int termiosflag = -1; /* flag for using termios ioctl
69 			      * structure */
70 
71 int (*
72 setlist(t))()
73 	bool t;
74 {
75 	int (*P)();
76 
77 	listf = t;
78 	P = Putchar;
79 	Putchar = t ? listchar : normchar;
80 	return (P);
81 }
82 
83 int (*
84 setnumb(t))()
85 	bool t;
86 {
87 	int (*P)();
88 
89 	numberf = t;
90 	P = Pline;
91 	Pline = t ? (int (*)())numbline : normline;
92 	return (P);
93 }
94 
95 /*
96  * Format c for list mode; leave things in common
97  * with normal print mode to be done by normchar.
98  */
99 int
100 listchar(wchar_t c)
101 {
102 
103 	c &= (int)(TRIM|QUOTE);
104 	switch (c) {
105 
106 	case '\t':
107 	case '\b':
108 		outchar('^');
109 		c = ctlof(c);
110 		break;
111 
112 	case '\n':
113 		break;
114 
115 	case (int)('\n' | QUOTE):
116 		outchar('$');
117 		break;
118 
119 	default:
120 		if((int)(c & QUOTE))
121 			break;
122 		if (c < ' ' && c != '\n' || c == DELETE)
123 			outchar('^'), c = ctlof(c);
124 	}
125 	(void) normchar(c);
126 	return (0);
127 }
128 
129 /*
130  * Format c for printing.  Handle funnies of upper case terminals
131  * and hazeltines which don't have ~.
132  */
133 int
134 normchar(wchar_t c)
135 {
136 	char *colp;
137 
138 	c &= (int)(TRIM|QUOTE);
139 	if (c == '~' && tilde_glitch) {
140 		(void) normchar('\\');
141 		c = '^';
142 	}
143 	if ((int)(c & QUOTE))
144 		switch (c) {
145 
146 		case (int)(' ' | QUOTE):
147 		case (int)('\b' | QUOTE):
148 			break;
149 
150 		case (int)QUOTE:
151 			return (0);
152 
153 		default:
154 			c &= (int)TRIM;
155 		}
156 	else if (c < ' ' && (c != '\b' || !over_strike) && c != '\n' && c != '\t' || c == DELETE)
157 		putchar('^'), c = ctlof(c);
158 	else if (c >= 0200 && (putoctal || !iswprint(c))) {
159 		outchar('\\');
160 		outchar(((c >> 6) & 07) + '0');
161 		outchar(((c >> 3) & 07) + '0');
162 		outchar((c & 07) + '0');
163 		return (0);
164 	} else if (UPPERCASE)
165 		if (isupper(c)) {
166 			outchar('\\');
167 			c = tolower(c);
168 		} else {
169 			colp = "({)}!|^~'`";
170 			while (*colp++)
171 				if (c == *colp++) {
172 					outchar('\\');
173 					c = colp[-2];
174 					break;
175 				}
176 		}
177 	outchar(c);
178 	return (0);
179 }
180 
181 /*
182  * Print a line with a number.
183  */
184 int
185 numbline(int i)
186 {
187 
188 	if (shudclob)
189 		slobber(' ');
190 	viprintf("%6d  ", i);
191 	(void) normline();
192 	return (0);
193 }
194 
195 /*
196  * Normal line output, no numbering.
197  */
198 int
199 normline(void)
200 {
201 	unsigned char *cp;
202 	int n;
203 	wchar_t wchar;
204 	if (shudclob)
205 		slobber(linebuf[0]);
206 	/* pdp-11 doprnt is not reentrant so can't use "printf" here
207 	   in case we are tracing */
208 	for (cp = linebuf; *cp;)
209 		if((n = mbtowc(&wchar, (char *)cp, MULTI_BYTE_MAX)) < 0) {
210 			putoctal = 1;
211 			putchar(*cp++);
212 			putoctal = 0;
213 		} else {
214 			cp += n;
215 			putchar(wchar);
216 		}
217 	if (!inopen)
218 		putchar((int)('\n' | QUOTE));
219 	return (0);
220 }
221 
222 /*
223  * Given c at the beginning of a line, determine whether
224  * the printing of the line will erase or otherwise obliterate
225  * the prompt which was printed before.  If it won't, do it now.
226  */
227 void
228 slobber(int c)
229 {
230 
231 	shudclob = 0;
232 	switch (c) {
233 
234 	case '\t':
235 		if (Putchar == listchar)
236 			return;
237 		break;
238 
239 	default:
240 		return;
241 
242 	case ' ':
243 	case 0:
244 		break;
245 	}
246 	if (over_strike)
247 		return;
248 	flush();
249 	(void) putch(' ');
250 	tputs(cursor_left, 0, putch);
251 }
252 
253 /*
254  * The output buffer is initialized with a useful error
255  * message so we don't have to keep it in data space.
256  */
257 static	wchar_t linb[66];
258 wchar_t *linp = linb;
259 
260 /*
261  * Phadnl records when we have already had a complete line ending with \n.
262  * If another line starts without a flush, and the terminal suggests it,
263  * we switch into -nl mode so that we can send linefeeds to avoid
264  * a lot of spacing.
265  */
266 static	bool phadnl;
267 
268 /*
269  * Indirect to current definition of putchar.
270  */
271 int
272 putchar(int c)
273 {
274 	return ((*Putchar)((wchar_t)c));
275 }
276 
277 /*
278  * Termchar routine for command mode.
279  * Watch for possible switching to -nl mode.
280  * Otherwise flush into next level of buffering when
281  * small buffer fills or at a newline.
282  */
283 int
284 termchar(wchar_t c)
285 {
286 
287 	if (pfast == 0 && phadnl)
288 		pstart();
289 	if (c == '\n')
290 		phadnl = 1;
291 	else if (linp >= &linb[63])
292 		flush1();
293 	*linp++ = c;
294 	if (linp >= &linb[63]) {
295 		fgoto();
296 		flush1();
297 	}
298 	return (0);
299 }
300 
301 void
302 flush(void)
303 {
304 
305 	flush1();
306 	flush2();
307 }
308 
309 /*
310  * Flush from small line buffer into output buffer.
311  * Work here is destroying motion into positions, and then
312  * letting fgoto do the optimized motion.
313  */
314 void
315 flush1(void)
316 {
317 	wchar_t *lp;
318 	wchar_t c;
319 #ifdef PRESUNEUC
320 	/* used for multibyte characters split between lines */
321 	int splitcnt = 0;
322 #else
323 	/* used for multicolumn character substitution and padding */
324 	int fillercnt = 0;
325 #endif /* PRESUNEUC */
326 	*linp = 0;
327 	lp = linb;
328 	while (*lp)
329 		switch (c = *lp++) {
330 
331 		case '\r':
332 			destline += destcol / columns;
333 			destcol = 0;
334 			continue;
335 
336 		case '\b':
337 			if (destcol)
338 				destcol--;
339 			continue;
340 
341 		case ' ':
342 			destcol++;
343 			continue;
344 
345 		case '\t':
346 			destcol += value(vi_TABSTOP) - destcol % value(vi_TABSTOP);
347 			continue;
348 
349 		case '\n':
350 			destline += destcol / columns + 1;
351 			if (destcol != 0 && destcol % columns == 0)
352 				destline--;
353 			destcol = 0;
354 			continue;
355 
356 		default:
357 			fgoto();
358 			for (;;) {
359 				int length, length2;
360 				unsigned char *p;
361 				c &= TRIM;
362 				if ((length = wcwidth(c)) < 0)
363 					length = 0;
364 				if (auto_right_margin == 0 && outcol >= columns)
365 					fgoto();
366 				if((destcol % columns) + length - 1 >= columns) {
367 #ifdef PRESUNEUC
368 					/* represent split chars by '>' */
369 					splitcnt = length - 1;
370 					c = '>';
371 #else
372 					/* substitute/wrap multicolumn char */
373 					if(mc_wrap) {
374 						fillercnt = columns -
375 							    (destcol % columns);
376 						while(fillercnt) {
377 							(void) putch(mc_filler);
378 							outcol++;
379 							destcol++;
380 							fillercnt--;
381 						}
382 					} else {
383 						fillercnt = length - 1;
384 						c = mc_filler;
385 					}
386 #endif /* PRESUNEUC */
387 					continue;
388 				}
389 				length2 = wctomb((char *)multic, c);
390 				p = multic;
391 				while(length2--)
392 					(void) putch(*p++);
393 				if (c == '\b') {
394 					outcol--;
395 					destcol--;
396 				} else if (c >= ' ' && c != DELETE) {
397 					outcol += length;
398 					destcol += length;
399 					if (eat_newline_glitch && outcol % columns == 0)
400 						(void) putch('\r'),
401 						    (void) putch('\n');
402 				}
403 #ifdef PRESUNEUC
404 				if(splitcnt) {
405 					splitcnt--;
406 					c = '>';
407 				} else
408 					c = *lp++;
409 #else
410 				if(fillercnt) {
411 					fillercnt--;
412 					c = mc_filler;
413 					if(c == ' ')
414 						continue;
415 				} else
416 					c = *lp++;
417 #endif /* PRESUNEUC */
418 				if (c <= ' ')
419 					break;
420 			}
421 			--lp;
422 			continue;
423 		}
424 	linp = linb;
425 }
426 
427 void
428 flush2(void)
429 {
430 
431 	fgoto();
432 	flusho();
433 	pstop();
434 }
435 
436 /*
437  * Sync the position of the output cursor.
438  * Most work here is rounding for terminal boundaries getting the
439  * column position implied by wraparound or the lack thereof and
440  * rolling up the screen to get destline on the screen.
441  */
442 void
443 fgoto(void)
444 {
445 	int l, c;
446 
447 	if (destcol > columns - 1) {
448 		destline += destcol / columns;
449 		destcol %= columns;
450 	}
451 	if (outcol > columns - 1) {
452 		l = (outcol + 1) / columns;
453 		outline += l;
454 		outcol %= columns;
455 		if (auto_right_margin == 0) {
456 			while (l > 0) {
457 				if (pfast)
458 					tputs(carriage_return, 0, putch);
459 				tputs(cursor_down, 0, putch);
460 				l--;
461 			}
462 			outcol = 0;
463 		}
464 		if (outline > lines - 1) {
465 			destline -= outline - (lines - 1);
466 			outline = lines - 1;
467 		}
468 	}
469 	if (destline > lines - 1) {
470 		l = destline;
471 		destline = lines - 1;
472 		if (outline < lines - 1) {
473 			c = destcol;
474 			if (pfast == 0 && (!cursor_address || holdcm))
475 				destcol = 0;
476 			fgoto();
477 			destcol = c;
478 		}
479 		while (l > lines - 1) {
480 			/*
481 			 * The following linefeed (or simulation thereof)
482 			 * is supposed to scroll up the screen, since we
483 			 * are on the bottom line.
484 			 *
485 			 * Superbee glitch:  in the middle of the screen we
486 			 * have to use esc B (down) because linefeed messes up
487 			 * in "Efficient Paging" mode (which is essential in
488 			 * some SB's because CRLF mode puts garbage
489 			 * in at end of memory), but you must use linefeed to
490 			 * scroll since down arrow won't go past memory end.
491 			 * I turned this off after receiving Paul Eggert's
492 			 * Superbee description which wins better.
493 			 */
494 			if (scroll_forward /* && !beehive_glitch */ && pfast)
495 				tputs(scroll_forward, 0, putch);
496 			else
497 				(void) putch('\n');
498 			l--;
499 			if (pfast == 0)
500 				outcol = 0;
501 		}
502 	}
503 	if (destline < outline && !(cursor_address && !holdcm || cursor_up || cursor_home))
504 		destline = outline;
505 	if (cursor_address && !holdcm)
506 		if (plod(costCM) > 0)
507 			plod(0);
508 		else
509 			tputs(tparm(cursor_address, destline, destcol), 0, putch);
510 	else
511 		plod(0);
512 	outline = destline;
513 	outcol = destcol;
514 }
515 
516 /*
517  * Tab to column col by flushing and then setting destcol.
518  * Used by "set all".
519  */
520 void
521 gotab(int col)
522 {
523 
524 	flush1();
525 	destcol = col;
526 }
527 
528 /*
529  * Move (slowly) to destination.
530  * Hard thing here is using home cursor on really deficient terminals.
531  * Otherwise just use cursor motions, hacking use of tabs and overtabbing
532  * and backspace.
533  */
534 
535 static int plodcnt, plodflg;
536 
537 int
538 #ifdef __STDC__
539 plodput(char c)
540 #else
541 plodput(c)
542 char c;
543 #endif
544 {
545 
546 	if (plodflg)
547 		plodcnt--;
548 	else
549 		(void) putch(c);
550 	return (0);
551 }
552 
553 int
554 plod(int cnt)
555 {
556 	int i, j, k;
557 	int soutcol, soutline;
558 
559 	plodcnt = plodflg = cnt;
560 	soutcol = outcol;
561 	soutline = outline;
562 	/*
563 	 * Consider homing and moving down/right from there, vs moving
564 	 * directly with local motions to the right spot.
565 	 */
566 	if (cursor_home) {
567 		/*
568 		 * i is the cost to home and tab/space to the right to
569 		 * get to the proper column.  This assumes cursor_right costs
570 		 * 1 char.  So i+destcol is cost of motion with home.
571 		 */
572 		if (tab && value(vi_HARDTABS))
573 			i = (destcol / value(vi_HARDTABS)) + (destcol % value(vi_HARDTABS));
574 		else
575 			i = destcol;
576 		/*
577 		 * j is cost to move locally without homing
578 		 */
579 		if (destcol >= outcol) {	/* if motion is to the right */
580 			if (value(vi_HARDTABS)) {
581 				j = destcol / value(vi_HARDTABS) - outcol / value(vi_HARDTABS);
582 				if (tab && j)
583 					j += destcol % value(vi_HARDTABS);
584 				else
585 					j = destcol - outcol;
586 			} else
587 				j = destcol - outcol;
588 		} else
589 			/* leftward motion only works if we can backspace. */
590 			if (outcol - destcol <= i && (cursor_left))
591 				i = j = outcol - destcol; /* cheaper to backspace */
592 			else
593 				j = i + 1; /* impossibly expensive */
594 
595 		/* k is the absolute value of vertical distance */
596 		k = outline - destline;
597 		if (k < 0)
598 			k = -k;
599 		j += k;
600 
601 		/*
602 		 * Decision.  We may not have a choice if no cursor_up.
603 		 */
604 		if (i + destline < j || (!cursor_up && destline < outline)) {
605 			/*
606 			 * Cheaper to home.  Do it now and pretend it's a
607 			 * regular local motion.
608 			 */
609 			tputs(cursor_home, 0, plodput);
610 			outcol = outline = 0;
611 		} else if (cursor_to_ll) {
612 			/*
613 			 * Quickly consider homing down and moving from there.
614 			 * Assume cost of cursor_to_ll is 2.
615 			 */
616 			k = (lines - 1) - destline;
617 			if (i + k + 2 < j && (k<=0 || cursor_up)) {
618 				tputs(cursor_to_ll, 0, plodput);
619 				outcol = 0;
620 				outline = lines - 1;
621 			}
622 		}
623 	} else
624 		/*
625 		 * No home and no up means it's impossible, so we return an
626 		 * incredibly big number to make cursor motion win out.
627 		 */
628 		if (!cursor_up && destline < outline)
629 			return (500);
630 	if (tab && value(vi_HARDTABS))
631 		i = destcol % value(vi_HARDTABS)
632 		    + destcol / value(vi_HARDTABS);
633 	else
634 		i = destcol;
635 /*
636 	if (back_tab && outcol > destcol && (j = (((outcol+7) & ~7) - destcol - 1) >> 3)) {
637 		j *= (k = strlen(back_tab));
638 		if ((k += (destcol&7)) > 4)
639 			j += 8 - (destcol&7);
640 		else
641 			j += k;
642 	} else
643 */
644 		j = outcol - destcol;
645 	/*
646 	 * If we will later need a \n which will turn into a \r\n by
647 	 * the system or the terminal, then don't bother to try to \r.
648 	 */
649 	if ((NONL || !pfast) && outline < destline)
650 		goto dontcr;
651 	/*
652 	 * If the terminal will do a \r\n and there isn't room for it,
653 	 * then we can't afford a \r.
654 	 */
655 	if (!carriage_return && outline >= destline)
656 		goto dontcr;
657 	/*
658 	 * If it will be cheaper, or if we can't back up, then send
659 	 * a return preliminarily.
660 	 */
661 	if (j > i + 1 || outcol > destcol && !cursor_left) {
662 		/*
663 		 * BUG: this doesn't take the (possibly long) length
664 		 * of carriage_return into account.
665 		 */
666 		if (carriage_return) {
667 			tputs(carriage_return, 0, plodput);
668 			outcol = 0;
669 		} else if (newline) {
670 			tputs(newline, 0, plodput);
671 			outline++;
672 			outcol = 0;
673 		}
674 	}
675 dontcr:
676 	/* Move down, if necessary, until we are at the desired line */
677 	while (outline < destline) {
678 		j = destline - outline;
679 		if (j > costDP && parm_down_cursor) {
680 			/* Win big on Tek 4025 */
681 			tputs(tparm(parm_down_cursor, j), j, plodput);
682 			outline += j;
683 		}
684 		else {
685 			outline++;
686 			if (cursor_down && pfast)
687 				tputs(cursor_down, 0, plodput);
688 			else
689 				(void) plodput('\n');
690 		}
691 		if (plodcnt < 0)
692 			goto out;
693 		if (NONL || pfast == 0)
694 			outcol = 0;
695 	}
696 	if (back_tab)
697 		k = strlen(back_tab);	/* should probably be cost(back_tab) and moved out */
698 	/* Move left, if necessary, to desired column */
699 	while (outcol > destcol) {
700 		if (plodcnt < 0)
701 			goto out;
702 		if (back_tab && !insmode && outcol - destcol > 4+k) {
703 			tputs(back_tab, 0, plodput);
704 			outcol--;
705 			if (value(vi_HARDTABS))
706 				outcol -= outcol % value(vi_HARDTABS); /* outcol &= ~7; */
707 			continue;
708 		}
709 		j = outcol - destcol;
710 		if (j > costLP && parm_left_cursor) {
711 			tputs(tparm(parm_left_cursor, j), j, plodput);
712 			outcol -= j;
713 		}
714 		else {
715 			outcol--;
716 			tputs(cursor_left, 0, plodput);
717 		}
718 	}
719 	/* Move up, if necessary, to desired row */
720 	while (outline > destline) {
721 		j = outline - destline;
722 		if (parm_up_cursor && j > 1) {
723 			/* Win big on Tek 4025 */
724 			tputs(tparm(parm_up_cursor, j), j, plodput);
725 			outline -= j;
726 		}
727 		else {
728 			outline--;
729 			tputs(cursor_up, 0, plodput);
730 		}
731 		if (plodcnt < 0)
732 			goto out;
733 	}
734 	/*
735 	 * Now move to the right, if necessary.  We first tab to
736 	 * as close as we can get.
737 	 */
738 	if (value(vi_HARDTABS) && tab && !insmode && destcol - outcol > 1) {
739 		/* tab to right as far as possible without passing col */
740 		for (;;) {
741 			i = tabcol(outcol, value(vi_HARDTABS));
742 			if (i > destcol)
743 				break;
744 			if (tab)
745 				tputs(tab, 0, plodput);
746 			else
747 				(void) plodput('\t');
748 			outcol = i;
749 		}
750 		/* consider another tab and then some backspaces */
751 		if (destcol - outcol > 4 && i < columns && cursor_left) {
752 			tputs(tab, 0, plodput);
753 			outcol = i;
754 			/*
755 			 * Back up.  Don't worry about parm_left_cursor because
756 			 * it's never more than 4 spaces anyway.
757 			 */
758 			while (outcol > destcol) {
759 				outcol--;
760 				tputs(cursor_left, 0, plodput);
761 			}
762 		}
763 	}
764 	/*
765 	 * We've tabbed as much as possible.  If we still need to go
766 	 * further (not exact or can't tab) space over.  This is a
767 	 * very common case when moving to the right with space.
768 	 */
769 	while (outcol < destcol) {
770 		j = destcol - outcol;
771 		if (j > costRP && parm_right_cursor) {
772 			/*
773 			 * This probably happens rarely, if at all.
774 			 * It seems mainly useful for ANSI terminals
775 			 * with no hardware tabs, and I don't know
776 			 * of any such terminal at the moment.
777 			 */
778 			tputs(tparm(parm_right_cursor, j), j, plodput);
779 			outcol += j;
780 		}
781 		else {
782 			/*
783 			 * move one char to the right.  We don't use right
784 			 * because it's better to just print the char we are
785 			 * moving over.  There are various exceptions, however.
786 			 * If !inopen, vtube contains garbage.  If the char is
787 			 * a null or a tab we want to print a space.  Other
788 			 * random chars we use space for instead, too.
789 			 */
790 			wchar_t wchar;
791 			int length, scrlength;
792 			unsigned char multic[MB_LEN_MAX];
793 
794 			if (!inopen || vtube[outline]==NULL ||
795 				(wchar=vtube[outline][outcol]) < ' ')
796 				wchar = ' ';
797 			if((int)(wchar & QUOTE))	/* no sign extension on 3B */
798 				wchar = ' ';
799 			length = wctomb((char *)multic, wchar);
800 			if ((scrlength = wcwidth(wchar)) < 0)
801 				scrlength = 0;
802 			/* assume multibyte terminals have cursor_right */
803 			if (insmode && cursor_right || length > 1 || wchar == FILLER) {
804 				int diff = destcol - outcol;
805 				j = (wchar == FILLER ? 1 : scrlength > diff ? diff : scrlength);
806 				while(j--) {
807 					outcol++;
808 					tputs(cursor_right, 0, plodput);
809 				}
810 			} else {
811 				(void) plodput((char)multic[0]);
812 				outcol++;
813 			}
814 		}
815 		if (plodcnt < 0)
816 			goto out;
817 	}
818 out:
819 	if(plodflg) {
820 		outcol = soutcol;
821 		outline = soutline;
822 	}
823 	return(plodcnt);
824 }
825 
826 /*
827  * An input line arrived.
828  * Calculate new (approximate) screen line position.
829  * Approximate because kill character echoes newline with
830  * no feedback and also because of long input lines.
831  */
832 void
833 noteinp(void)
834 {
835 
836 	outline++;
837 	if (outline > lines - 1)
838 		outline = lines - 1;
839 	destline = outline;
840 	destcol = outcol = 0;
841 }
842 
843 /*
844  * Something weird just happened and we
845  * lost track of what's happening out there.
846  * Since we can't, in general, read where we are
847  * we just reset to some known state.
848  * On cursor addressable terminals setting to unknown
849  * will force a cursor address soon.
850  */
851 void
852 termreset(void)
853 {
854 
855 	endim();
856 	if (enter_ca_mode)
857 		putpad((unsigned char *)enter_ca_mode);
858 	destcol = 0;
859 	destline = lines - 1;
860 	if (cursor_address) {
861 		outcol = UKCOL;
862 		outline = UKCOL;
863 	} else {
864 		outcol = destcol;
865 		outline = destline;
866 	}
867 }
868 
869 /*
870  * Low level buffering, with the ability to drain
871  * buffered output without printing it.
872  */
873 unsigned char	*obp = obuf;
874 
875 void
876 draino(void)
877 {
878 
879 	obp = obuf;
880 }
881 
882 void
883 flusho(void)
884 {
885 	if (obp != obuf) {
886 		write(1, obuf, obp - obuf);
887 #ifdef TRACE
888 		if (trace)
889 			fwrite(obuf, 1, obp-obuf, trace);
890 #endif
891 		obp = obuf;
892 	}
893 }
894 
895 void
896 putnl(void)
897 {
898 
899 	putchar('\n');
900 }
901 
902 void
903 putS(unsigned char *cp)
904 {
905 
906 	if (cp == NULL)
907 		return;
908 	while (*cp)
909 		(void) putch(*cp++);
910 }
911 
912 int
913 putch(char c)
914 {
915 
916 #ifdef OLD3BTTY
917 	if(c == '\n')	/* Fake "\n\r" for '\n' til fix in 3B firmware */
918 		(void) putch('\r'); /* vi does "stty -icanon" => -onlcr !! */
919 #endif
920 	*obp++ = c;
921 	if (obp >= &obuf[sizeof obuf])
922 		flusho();
923 	return (0);
924 }
925 
926 /*
927  * Miscellaneous routines related to output.
928  */
929 
930 /*
931  * Put with padding
932  */
933 void
934 putpad(unsigned char *cp)
935 {
936 
937 	flush();
938 	tputs((char *)cp, 0, putch);
939 }
940 
941 /*
942  * Set output through normal command mode routine.
943  */
944 void
945 setoutt(void)
946 {
947 
948 	Outchar = termchar;
949 }
950 
951 /*
952  * Printf (temporarily) in list mode.
953  */
954 /*VARARGS2*/
955 void
956 lprintf(unsigned char *cp, unsigned char *dp, ...)
957 {
958 	int (*P)();
959 
960 	P = setlist(1);
961 #ifdef PRESUNEUC
962 	viprintf(cp, dp);
963 #else
964 	viprintf((char *)cp, (char *)dp);
965 #endif /* PRESUNEUC */
966 	Putchar = P;
967 }
968 
969 /*
970  * Newline + flush.
971  */
972 void
973 putNFL()
974 {
975 
976 	putnl();
977 	flush();
978 }
979 
980 /*
981  * Try to start -nl mode.
982  */
983 void
984 pstart(void)
985 {
986 
987 	if (NONL)
988 		return;
989  	if (!value(vi_OPTIMIZE))
990 		return;
991 	if (ruptible == 0 || pfast)
992 		return;
993 	fgoto();
994 	flusho();
995 	pfast = 1;
996 	normtty++;
997 	tty = normf;
998 	tty.c_oflag &= ~(ONLCR|TAB3);
999 	tty.c_lflag &= ~ECHO;
1000 	saveterm();
1001 	sTTY(2);
1002 }
1003 
1004 /*
1005  * Stop -nl mode.
1006  */
1007 void
1008 pstop(void)
1009 {
1010 
1011 	if (inopen)
1012 		return;
1013 	phadnl = 0;
1014 	linp = linb;
1015 	draino();
1016 	normal(normf);
1017 	pfast &= ~1;
1018 }
1019 
1020 /*
1021  * Prep tty for open mode.
1022  */
1023 ttymode
1024 ostart()
1025 {
1026 	ttymode f;
1027 
1028 	/*
1029 	if (!intty)
1030 		error("Open and visual must be used interactively");
1031 	*/
1032 	(void) gTTY(2);
1033 	normtty++;
1034 	f = tty;
1035 	tty = normf;
1036 	tty.c_iflag &= ~ICRNL;
1037 	tty.c_lflag &= ~(ECHO|ICANON);
1038 	tty.c_oflag &= ~(TAB3|ONLCR);
1039 	tty.c_cc[VMIN] = 1;
1040 	tty.c_cc[VTIME] = 1;
1041 	ttcharoff();
1042 	sTTY(2);
1043 	tostart();
1044 	pfast |= 2;
1045 	saveterm();
1046 	return (f);
1047 }
1048 
1049 /* actions associated with putting the terminal in open mode */
1050 void
1051 tostart(void)
1052 {
1053 	putpad((unsigned char *)cursor_visible);
1054 	putpad((unsigned char *)keypad_xmit);
1055 	if (!value(vi_MESG)) {
1056 		if (ttynbuf[0] == 0) {
1057 			char *tn;
1058 			if ((tn=ttyname(2)) == NULL &&
1059 			    (tn=ttyname(1)) == NULL &&
1060 			    (tn=ttyname(0)) == NULL)
1061 				ttynbuf[0] = 1;
1062 			else
1063 				strcpy(ttynbuf, tn);
1064 		}
1065 		if (ttynbuf[0] != 1) {
1066 			struct stat64 sbuf;
1067 			stat64((char *)ttynbuf, &sbuf);
1068 			ttymesg = FMODE(sbuf) & 0777;
1069 			chmod((char *)ttynbuf, 0600);
1070 		}
1071 	}
1072 }
1073 
1074 /*
1075  * Turn off start/stop chars if they aren't the default ^S/^Q.
1076  * This is so people who make esc their start/stop don't lose.
1077  * We always turn off quit since datamedias send ^\ for their
1078  * right arrow key.
1079  */
1080 
1081 void
1082 ttcharoff(void)
1083 {
1084 	/*
1085 	 * use 200 instead of 377 because 377 is y-umlaut
1086 	 * in ISO 8859/1
1087 	 */
1088 	tty.c_cc[VQUIT] = termiosflag ? _POSIX_VDISABLE : '\200';
1089 	if (tty.c_cc[VSTART] != CTRL('q'))
1090 		tty.c_cc[VSTART] = _POSIX_VDISABLE;
1091 	if (tty.c_cc[VSTOP] != CTRL('s'))
1092 		tty.c_cc[VSTOP] = _POSIX_VDISABLE;
1093 	/* We will read ^z and suspend ourselves via kill */
1094 	tty.c_cc[VSUSP] = _POSIX_VDISABLE;
1095 	tty.c_cc[VDSUSP] = _POSIX_VDISABLE;
1096 	tty.c_cc[VREPRINT] = _POSIX_VDISABLE;
1097 	tty.c_cc[VDISCARD] = _POSIX_VDISABLE;
1098 	tty.c_cc[VWERASE] = _POSIX_VDISABLE;
1099 	tty.c_cc[VLNEXT] = _POSIX_VDISABLE;
1100 }
1101 
1102 /*
1103  * Stop open, restoring tty modes.
1104  */
1105 void
1106 ostop(ttymode f)
1107 {
1108 
1109 	pfast = (f.c_oflag & ONLCR) == 0;
1110 	termreset(), fgoto(), flusho();
1111 	normal(f);
1112 	tostop();
1113 }
1114 
1115 /* Actions associated with putting the terminal in the right mode. */
1116 void
1117 tostop(void)
1118 {
1119 	putpad((unsigned char *)clr_eos);
1120 	putpad((unsigned char *)cursor_normal);
1121 	putpad((unsigned char *)keypad_local);
1122 	if (!value(vi_MESG) && ttynbuf[0]>1)
1123 		chmod((char *)ttynbuf, ttymesg);
1124 }
1125 
1126 #ifndef CBREAK
1127 /*
1128  * Into cooked mode for interruptibility.
1129  */
1130 vcook()
1131 {
1132 
1133 	tty.sg_flags &= ~RAW;
1134 	sTTY(2);
1135 }
1136 
1137 /*
1138  * Back into raw mode.
1139  */
1140 vraw()
1141 {
1142 
1143 	tty.sg_flags |= RAW;
1144 	sTTY(2);
1145 }
1146 #endif
1147 
1148 /*
1149  * Restore flags to normal state f.
1150  */
1151 void
1152 normal(ttymode f)
1153 {
1154 
1155 	if (normtty > 0) {
1156 		setty(f);
1157 		normtty--;
1158 	}
1159 }
1160 
1161 /*
1162  * Straight set of flags to state f.
1163  */
1164 ttymode
1165 setty(f)
1166 	ttymode f;
1167 {
1168 	int isnorm = 0;
1169 	ttymode ot;
1170 	ot = tty;
1171 
1172 	if (tty.c_lflag & ICANON)
1173 		ttcharoff();
1174 	else
1175 		isnorm = 1;
1176 	tty = f;
1177 	sTTY(2);
1178 	if (!isnorm)
1179 		saveterm();
1180 	return (ot);
1181 }
1182 
1183 static struct termio termio;
1184 
1185 int
1186 gTTY(int i)
1187 {
1188 	if(termiosflag < 0) {
1189 		if(ioctl(i, TCGETS, &tty) == 0)
1190 			termiosflag = 1;
1191 		else  {
1192 			termiosflag = 0;
1193 			if(ioctl(i, TCGETA, &termio) < 0)
1194 				return (-1);
1195 			tty.c_iflag = termio.c_iflag;
1196 			tty.c_oflag = termio.c_oflag;
1197 			tty.c_cflag = termio.c_cflag;
1198 			tty.c_lflag = termio.c_lflag;
1199 			for(i = 0; i < NCC; i++)
1200 				tty.c_cc[i] = termio.c_cc[i];
1201 		}
1202 		return (0);
1203 	}
1204 	if(termiosflag)
1205 		return (ioctl(i, TCGETS, &tty));
1206 	if(ioctl(i, TCGETA, &termio) < 0)
1207 		return (-1);
1208 	tty.c_iflag = termio.c_iflag;
1209 	tty.c_oflag = termio.c_oflag;
1210 	tty.c_cflag = termio.c_cflag;
1211 	tty.c_lflag = termio.c_lflag;
1212 	for(i = 0; i < NCC; i++)
1213 		tty.c_cc[i] = termio.c_cc[i];
1214 	return (0);
1215 }
1216 
1217 /*
1218  * sTTY: set the tty modes on file descriptor i to be what's
1219  * currently in global "tty".  (Also use nttyc if needed.)
1220  */
1221 void
1222 sTTY(int i)
1223 {
1224 	int j;
1225 	if(termiosflag)
1226 		ioctl(i, TCSETSW, &tty);
1227 	else {
1228 		termio.c_iflag = tty.c_iflag;
1229 		termio.c_oflag = tty.c_oflag;
1230 		termio.c_cflag = tty.c_cflag;
1231 		termio.c_lflag = tty.c_lflag;
1232 		for(j = 0; j < NCC; j++)
1233 			termio.c_cc[j] = tty.c_cc[j];
1234 		ioctl(i, TCSETAW, &termio);
1235 	}
1236 }
1237 
1238 /*
1239  * Print newline, or blank if in open/visual
1240  */
1241 void
1242 noonl(void)
1243 {
1244 
1245 	putchar(Outchar != termchar ? ' ' : '\n');
1246 }
1247