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