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