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