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