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 */
64int	(*Outchar)() = termchar;
65int	(*Putchar)() = normchar;
66int	(*Pline)() = normline;
67static unsigned char multic[MULTI_BYTE_MAX];
68bool putoctal; /* flag to say if byte should be printed as octal */
69int termiosflag = -1; /* flag for using termios ioctl
70			      * structure */
71
72int (*
73setlist(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
84int (*
85setnumb(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 */
100int
101listchar(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 */
134int
135normchar(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 */
185int
186numbline(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 */
199int
200normline(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 */
228void
229slobber(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 */
258static	wchar_t linb[66];
259wchar_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 */
267static	bool phadnl;
268
269/*
270 * Indirect to current definition of putchar.
271 */
272int
273putchar(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 */
284int
285termchar(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
302void
303flush(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 */
315void
316flush1(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
428void
429flush2(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 */
443void
444fgoto(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 */
521void
522gotab(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
536static int plodcnt, plodflg;
537
538int
539#ifdef __STDC__
540plodput(char c)
541#else
542plodput(c)
543char c;
544#endif
545{
546
547	if (plodflg)
548		plodcnt--;
549	else
550		(void) putch(c);
551	return (0);
552}
553
554int
555plod(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	}
676dontcr:
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	}
819out:
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 */
833void
834noteinp(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 */
852void
853termreset(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 */
874unsigned char	*obp = obuf;
875
876void
877draino(void)
878{
879
880	obp = obuf;
881}
882
883void
884flusho(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
896void
897putnl(void)
898{
899
900	putchar('\n');
901}
902
903void
904putS(unsigned char *cp)
905{
906
907	if (cp == NULL)
908		return;
909	while (*cp)
910		(void) putch(*cp++);
911}
912
913int
914putch(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 */
934void
935putpad(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 */
945void
946setoutt(void)
947{
948
949	Outchar = termchar;
950}
951
952/*
953 * Printf (temporarily) in list mode.
954 */
955/*VARARGS2*/
956void
957lprintf(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 */
973void
974putNFL()
975{
976
977	putnl();
978	flush();
979}
980
981/*
982 * Try to start -nl mode.
983 */
984void
985pstart(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 */
1008void
1009pstop(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 */
1024ttymode
1025ostart()
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 */
1051void
1052tostart(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
1082void
1083ttcharoff(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 */
1106void
1107ostop(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. */
1117void
1118tostop(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 */
1131vcook()
1132{
1133
1134	tty.sg_flags &= ~RAW;
1135	sTTY(2);
1136}
1137
1138/*
1139 * Back into raw mode.
1140 */
1141vraw()
1142{
1143
1144	tty.sg_flags |= RAW;
1145	sTTY(2);
1146}
1147#endif
1148
1149/*
1150 * Restore flags to normal state f.
1151 */
1152void
1153normal(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 */
1165ttymode
1166setty(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
1184static struct termio termio;
1185
1186int
1187gTTY(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 */
1222void
1223sTTY(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 */
1242void
1243noonl(void)
1244{
1245
1246	putchar(Outchar != termchar ? ' ' : '\n');
1247}
1248