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