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