|
|
1.1 root 1: /* Copyright (c) 1979 Regents of the University of California */
2: #include "ex.h"
3: #include "ex_tty.h"
4: #include "ex_vis.h"
5:
6: /*
7: * Terminal driving and line formatting routines.
8: * Basic motion optimizations are done here as well
9: * as formatting of lines (printing of control characters,
10: * line numbering and the like).
11: */
12:
13: /*
14: * The routines outchar, putchar and pline are actually
15: * variables, and these variables point at the current definitions
16: * of the routines. See the routine setflav.
17: * We sometimes make outchar be routines which catch the characters
18: * to be printed, e.g. if we want to see how long a line is.
19: * During open/visual, outchar and putchar will be set to
20: * routines in the file ex_vput.c (vputchar, vinschar, etc.).
21: */
22: int (*Outchar)() = termchar;
23: int (*Putchar)() = normchar;
24: int (*Pline)() = normline;
25:
26: int (*
27: setlist(t))()
28: bool t;
29: {
30: register int (*P)();
31:
32: listf = t;
33: P = Putchar;
34: Putchar = t ? listchar : normchar;
35: return (P);
36: }
37:
38: int (*
39: setnumb(t))()
40: bool t;
41: {
42: register int (*P)();
43:
44: numberf = t;
45: P = Pline;
46: Pline = t ? numbline : normline;
47: return (P);
48: }
49:
50: /*
51: * Format c for list mode; leave things in common
52: * with normal print mode to be done by normchar.
53: */
54: listchar(c)
55: register short c;
56: {
57:
58: c &= (TRIM|QUOTE);
59: switch (c) {
60:
61: case '\t':
62: case '\b':
63: outchar('^');
64: c = ctlof(c);
65: break;
66:
67: case '\n':
68: break;
69:
70: case '\n' | QUOTE:
71: outchar('$');
72: break;
73:
74: default:
75: if (c & QUOTE)
76: break;
77: if (c < ' ' && c != '\n' || c == DELETE)
78: outchar('^'), c = ctlof(c);
79: break;
80: }
81: normchar(c);
82: }
83:
84: /*
85: * Format c for printing. Handle funnies of upper case terminals
86: * and crocky hazeltines which don't have ~.
87: */
88: normchar(c)
89: register short c;
90: {
91: register char *colp;
92:
93: c &= (TRIM|QUOTE);
94: if (c == '~' && HZ) {
95: normchar('\\');
96: c = '^';
97: }
98: if (c & QUOTE)
99: switch (c) {
100:
101: case ' ' | QUOTE:
102: case '\b' | QUOTE:
103: break;
104:
105: case QUOTE:
106: return;
107:
108: default:
109: c &= TRIM;
110: }
111: else if (c < ' ' && (c != '\b' || !OS) && c != '\n' && c != '\t' || c == DELETE)
112: putchar('^'), c = ctlof(c);
113: else if (UPPERCASE)
114: if (isupper(c)) {
115: outchar('\\');
116: c = tolower(c);
117: } else {
118: colp = "({)}!|^~'`";
119: while (*colp++)
120: if (c == *colp++) {
121: outchar('\\');
122: c = colp[-2];
123: break;
124: }
125: }
126: outchar(c);
127: }
128:
129: /*
130: * Print a line with a number.
131: */
132: numbline(i)
133: int i;
134: {
135:
136: if (shudclob)
137: slobber(' ');
138: printf("%6d ", i);
139: normline();
140: }
141:
142: /*
143: * Normal line output, no numbering.
144: */
145: normline()
146: {
147: register char *cp;
148:
149: if (shudclob)
150: slobber(linebuf[0]);
151: /* pdp-11 doprnt is not reentrant so can't use "printf" here
152: in case we are tracing */
153: for (cp = linebuf; *cp;)
154: putchar(*cp++);
155: if (!inopen)
156: putchar('\n' | QUOTE);
157: }
158:
159: /*
160: * Given c at the beginning of a line, determine whether
161: * the printing of the line will erase or otherwise obliterate
162: * the prompt which was printed before. If it won't, do it now.
163: */
164: slobber(c)
165: int c;
166: {
167:
168: shudclob = 0;
169: switch (c) {
170:
171: case '\t':
172: if (Putchar == listchar)
173: return;
174: break;
175:
176: default:
177: return;
178:
179: case ' ':
180: case 0:
181: break;
182: }
183: if (OS)
184: return;
185: flush();
186: putch(' ');
187: if (BC)
188: tputs(BC, 0, putch);
189: else
190: putch('\b');
191: }
192:
193: /*
194: * The output buffer is initialized with a useful error
195: * message so we don't have to keep it in data space.
196: */
197: static char linb[66] = {
198: 'E', 'r', 'r', 'o', 'r', ' ', 'm', 'e', 's', 's', 'a', 'g', 'e', ' ',
199: 'f', 'i', 'l', 'e', ' ', 'n', 'o', 't', ' ',
200: 'a', 'v', 'a', 'i', 'l', 'a', 'b', 'l', 'e', '\n', 0
201: };
202: static char *linp = linb + 33;
203:
204: /*
205: * Phadnl records when we have already had a complete line ending with \n.
206: * If another line starts without a flush, and the terminal suggests it,
207: * we switch into -nl mode so that we can send lineffeeds to avoid
208: * a lot of spacing.
209: */
210: static bool phadnl;
211:
212: /*
213: * Indirect to current definition of putchar.
214: */
215: putchar(c)
216: int c;
217: {
218:
219: (*Putchar)(c);
220: }
221:
222: /*
223: * Termchar routine for command mode.
224: * Watch for possible switching to -nl mode.
225: * Otherwise flush into next level of buffering when
226: * small buffer fills or at a newline.
227: */
228: termchar(c)
229: int c;
230: {
231:
232: if (pfast == 0 && phadnl)
233: pstart();
234: if (c == '\n')
235: phadnl = 1;
236: else if (linp >= &linb[63])
237: flush1();
238: *linp++ = c;
239: if (linp >= &linb[63]) {
240: fgoto();
241: flush1();
242: }
243: }
244:
245: flush()
246: {
247:
248: flush1();
249: flush2();
250: }
251:
252: /*
253: * Flush from small line buffer into output buffer.
254: * Work here is destroying motion into positions, and then
255: * letting fgoto do the optimized motion.
256: */
257: flush1()
258: {
259: register char *lp;
260: register short c;
261:
262: *linp = 0;
263: lp = linb;
264: while (*lp)
265: switch (c = *lp++) {
266:
267: case '\r':
268: destline += destcol / COLUMNS;
269: destcol = 0;
270: continue;
271:
272: case '\b':
273: if (destcol)
274: destcol--;
275: continue;
276:
277: case ' ':
278: destcol++;
279: continue;
280:
281: case '\t':
282: destcol += value(TABSTOP) - destcol % value(TABSTOP);
283: continue;
284:
285: case '\n':
286: destline += destcol / COLUMNS + 1;
287: if (destcol != 0 && destcol % COLUMNS == 0)
288: destline--;
289: destcol = 0;
290: continue;
291:
292: default:
293: fgoto();
294: for (;;) {
295: if (AM == 0 && outcol == COLUMNS)
296: fgoto();
297: c &= TRIM;
298: putch(c);
299: if (c == '\b') {
300: outcol--;
301: destcol--;
302: } else if (c >= ' ' && c != DELETE) {
303: outcol++;
304: destcol++;
305: if (XN && outcol % COLUMNS == 0)
306: putch('\n');
307: }
308: c = *lp++;
309: if (c <= ' ')
310: break;
311: }
312: --lp;
313: continue;
314: }
315: linp = linb;
316: }
317:
318: flush2()
319: {
320:
321: fgoto();
322: flusho();
323: pstop();
324: }
325:
326: /*
327: * Sync the position of the output cursor.
328: * Most work here is rounding for terminal boundaries getting the
329: * column position implied by wraparound or the lack thereof and
330: * rolling up the screen to get destline on the screen.
331: */
332: fgoto()
333: {
334: register int l, c;
335:
336: if (destcol > COLUMNS - 1) {
337: destline += destcol / COLUMNS;
338: destcol %= COLUMNS;
339: }
340: if (outcol > COLUMNS - 1) {
341: l = (outcol + 1) / COLUMNS;
342: outline += l;
343: outcol %= COLUMNS;
344: if (AM == 0) {
345: while (l > 0) {
346: if (pfast)
347: putch('\r');
348: putch('\n');
349: l--;
350: }
351: outcol = 0;
352: }
353: if (outline > LINES - 1) {
354: destline -= outline - (LINES - 1);
355: outline = LINES - 1;
356: }
357: }
358: if (destline > LINES - 1) {
359: l = destline;
360: destline = LINES - 1;
361: if (outline < LINES - 1) {
362: c = destcol;
363: if (pfast == 0 && (!CA || holdcm))
364: destcol = 0;
365: fgoto();
366: destcol = c;
367: }
368: while (l > LINES - 1) {
369: putch('\n');
370: l--;
371: if (pfast == 0)
372: outcol = 0;
373: }
374: }
375: if (destline < outline && !(CA && !holdcm || UP != NOSTR))
376: destline = outline;
377: if (CA && !holdcm)
378: if (plod(costCM) > 0)
379: plod(0);
380: else
381: tputs(tgoto(CM, destcol, destline), 0, putch);
382: else
383: plod(0);
384: outline = destline;
385: outcol = destcol;
386: }
387:
388: /*
389: * Tab to column col by flushing and then setting destcol.
390: * Used by "set all".
391: */
392: tab(col)
393: int col;
394: {
395:
396: flush1();
397: destcol = col;
398: }
399:
400: /*
401: * Move (slowly) to destination.
402: * Hard thing here is using home cursor on really deficient terminals.
403: * Otherwise just use cursor motions, hacking use of tabs and overtabbing
404: * and backspace.
405: */
406:
407: static int plodcnt, plodflg;
408:
409: plodput(c)
410: {
411:
412: if (plodflg)
413: plodcnt--;
414: else
415: putch(c);
416: }
417:
418: plod(cnt)
419: {
420: register int i, j, k;
421: register int soutcol, soutline;
422:
423: plodcnt = plodflg = cnt;
424: soutcol = outcol;
425: soutline = outline;
426: if (HO) {
427: if (GT)
428: i = (destcol / value(HARDTABS)) + (destcol % value(HARDTABS));
429: else
430: i = destcol;
431: if (destcol >= outcol) {
432: j = destcol / value(HARDTABS) - outcol / value(HARDTABS);
433: if (GT && j)
434: j += destcol % value(HARDTABS);
435: else
436: j = destcol - outcol;
437: } else
438: if (outcol - destcol <= i && (BS || BC))
439: i = j = outcol - destcol;
440: else
441: j = i + 1;
442: k = outline - destline;
443: if (k < 0)
444: k = -k;
445: j += k;
446: if (i + destline < j) {
447: tputs(HO, 0, plodput);
448: outcol = outline = 0;
449: } else if (LL) {
450: k = (LINES - 1) - destline;
451: if (i + k + 2 < j) {
452: tputs(LL, 0, plodput);
453: outcol = 0;
454: outline = LINES - 1;
455: }
456: }
457: }
458: if (GT)
459: i = destcol % value(HARDTABS) + destcol / value(HARDTABS);
460: else
461: i = destcol;
462: /*
463: if (BT && outcol > destcol && (j = (((outcol+7) & ~7) - destcol - 1) >> 3)) {
464: j *= (k = strlen(BT));
465: if ((k += (destcol&7)) > 4)
466: j += 8 - (destcol&7);
467: else
468: j += k;
469: } else
470: */
471: j = outcol - destcol;
472: /*
473: * If we will later need a \n which will turn into a \r\n by
474: * the system or the terminal, then don't bother to try to \r.
475: */
476: if ((NONL || !pfast) && outline < destline)
477: goto dontcr;
478: /*
479: * If the terminal will do a \r\n and there isn't room for it,
480: * then we can't afford a \r.
481: */
482: if (NC && outline >= destline)
483: goto dontcr;
484: /*
485: * If it will be cheaper, or if we can't back up, then send
486: * a return preliminarily.
487: */
488: if (j > i + 1 || outcol > destcol && !BS && !BC) {
489: plodput('\r');
490: if (NC) {
491: plodput('\n');
492: outline++;
493: }
494: outcol = 0;
495: }
496: dontcr:
497: while (outline < destline) {
498: outline++;
499: plodput('\n');
500: if (plodcnt < 0)
501: goto out;
502: if (NONL || pfast == 0)
503: outcol = 0;
504: }
505: if (BT)
506: k = strlen(BT);
507: while (outcol > destcol) {
508: if (plodcnt < 0)
509: goto out;
510: /*
511: if (BT && !insmode && outcol - destcol > 4+k) {
512: tputs(BT, 0, plodput);
513: outcol--;
514: outcol &= ~7;
515: continue;
516: }
517: */
518: outcol--;
519: if (BC)
520: tputs(BC, 0, plodput);
521: else
522: plodput('\b');
523: }
524: while (outline > destline) {
525: outline--;
526: tputs(UP, 0, plodput);
527: if (plodcnt < 0)
528: goto out;
529: }
530: if (GT && !insmode && destcol - outcol > 1) {
531: for (;;) {
532: i = (outcol / value(HARDTABS) + 1) * value(HARDTABS);
533: if (i > destcol)
534: break;
535: if (TA)
536: tputs(TA, 0, plodput);
537: else
538: plodput('\t');
539: outcol = i;
540: }
541: if (destcol - outcol > 4 && i < COLUMNS && (BC || BS)) {
542: if (TA)
543: tputs(TA, 0, plodput);
544: else
545: plodput('\t');
546: outcol = i;
547: while (outcol > destcol) {
548: outcol--;
549: if (BC)
550: tputs(BC, 0, plodput);
551: else
552: plodput('\b');
553: }
554: }
555: }
556: while (outcol < destcol) {
557: if (inopen && ND)
558: tputs(ND, 0, plodput);
559: else
560: plodput(' ');
561: outcol++;
562: if (plodcnt < 0)
563: goto out;
564: }
565: out:
566: if (plodflg) {
567: outcol = soutcol;
568: outline = soutline;
569: }
570: return(plodcnt);
571: }
572:
573: /*
574: * An input line arrived.
575: * Calculate new (approximate) screen line position.
576: * Approximate because kill character echoes newline with
577: * no feedback and also because of long input lines.
578: */
579: noteinp()
580: {
581:
582: outline++;
583: if (outline > LINES - 1)
584: outline = LINES - 1;
585: destline = outline;
586: destcol = outcol = 0;
587: }
588:
589: /*
590: * Something weird just happened and we
591: * lost track of whats happening out there.
592: * Since we cant, in general, read where we are
593: * we just reset to some known state.
594: * On cursor addressible terminals setting to unknown
595: * will force a cursor address soon.
596: */
597: termreset()
598: {
599:
600: endim();
601: if (TI) /* otherwise it flushes anyway, and 'set tty=dumb' vomits */
602: putpad(TI); /*adb change -- emit terminal initial sequence */
603: destcol = 0;
604: destline = LINES - 1;
605: if (CA) {
606: outcol = UKCOL;
607: outline = UKCOL;
608: } else {
609: outcol = destcol;
610: outline = destline;
611: }
612: }
613:
614: /*
615: * Low level buffering, with the ability to drain
616: * buffered output without printing it.
617: */
618: char *obp = obuf;
619:
620: draino()
621: {
622:
623: obp = obuf;
624: }
625:
626: flusho()
627: {
628:
629: if (obp != obuf) {
630: write(1, obuf, obp - obuf);
631: obp = obuf;
632: }
633: }
634:
635: putnl()
636: {
637:
638: putchar('\n');
639: }
640:
641: putS(cp)
642: char *cp;
643: {
644:
645: if (cp == NULL)
646: return;
647: while (*cp)
648: putch(*cp++);
649: }
650:
651:
652: putch(c)
653: int c;
654: {
655:
656: *obp++ = c;
657: if (obp >= &obuf[sizeof obuf])
658: flusho();
659: }
660:
661: /*
662: * Miscellaneous routines related to output.
663: */
664:
665: /*
666: * Cursor motion.
667: */
668: char *
669: cgoto()
670: {
671:
672: return (tgoto(CM, destcol, destline));
673: }
674:
675: /*
676: * Put with padding
677: */
678: putpad(cp)
679: char *cp;
680: {
681:
682: flush();
683: tputs(cp, 0, putch);
684: }
685:
686: /*
687: * Set output through normal command mode routine.
688: */
689: setoutt()
690: {
691:
692: Outchar = termchar;
693: }
694:
695: /*
696: * Printf (temporarily) in list mode.
697: */
698: /*VARARGS2*/
699: lprintf(cp, dp)
700: char *cp, *dp;
701: {
702: register int (*P)();
703:
704: P = setlist(1);
705: printf(cp, dp);
706: Putchar = P;
707: }
708:
709: /*
710: * Newline + flush.
711: */
712: putNFL()
713: {
714:
715: putnl();
716: flush();
717: }
718:
719: /*
720: * Try to start -nl mode.
721: */
722: pstart()
723: {
724:
725: if (NONL)
726: return;
727: if (!value(OPTIMIZE))
728: return;
729: if (ruptible == 0 || pfast)
730: return;
731: fgoto();
732: flusho();
733: pfast = 1;
734: normtty++;
735: tty.sg_flags = normf & ~(ECHO|XTABS|CRMOD);
736: sTTY(1);
737: }
738:
739: /*
740: * Stop -nl mode.
741: */
742: pstop()
743: {
744:
745: if (inopen)
746: return;
747: phadnl = 0;
748: linp = linb;
749: draino();
750: normal(normf);
751: pfast &= ~1;
752: }
753:
754: /*
755: * Prep tty for open mode.
756: */
757: ostart()
758: {
759: int f;
760:
761: if (!intty)
762: error("Open and visual must be used interactively");
763: gTTY(1);
764: normtty++;
765: f = tty.sg_flags;
766: #ifdef CBREAK
767: tty.sg_flags = (normf &~ (ECHO|XTABS|CRMOD)) | CBREAK;
768: #else
769: tty.sg_flags = (normf &~ (ECHO|XTABS|CRMOD)) | RAW;
770: #endif
771: #ifdef TIOCGETC
772: nttyc.t_quitc = nttyc.t_startc = nttyc.t_stopc = '\377';
773: #endif
774: sTTY(1);
775: putpad(VS);
776: putpad(KS);
777: pfast |= 2;
778: return (f);
779: }
780:
781: /*
782: * Stop open, restoring tty modes.
783: */
784: ostop(f)
785: int f;
786: {
787:
788: pfast = (f & CRMOD) == 0;
789: termreset(), fgoto(), flusho();
790: normal(f);
791: putpad(VE);
792: putpad(KE);
793: }
794:
795: #ifndef CBREAK
796: /*
797: * Into cooked mode for interruptibility.
798: */
799: vcook()
800: {
801:
802: tty.sg_flags &= ~RAW;
803: sTTY(1);
804: }
805:
806: /*
807: * Back into raw mode.
808: */
809: vraw()
810: {
811:
812: tty.sg_flags |= RAW;
813: sTTY(1);
814: }
815: #endif
816:
817: /*
818: * Restore flags to normal state f.
819: */
820: normal(f)
821: int f;
822: {
823:
824: if (normtty > 0) {
825: setty(f);
826: normtty--;
827: }
828: }
829:
830: /*
831: * Straight set of flags to state f.
832: */
833: setty(f)
834: int f;
835: {
836: register int ot = tty.sg_flags;
837:
838: #ifdef TIOCGETC
839: if (f == normf)
840: nttyc = ottyc;
841: else
842: nttyc.t_quitc = nttyc.t_startc = nttyc.t_stopc = '\377';
843: #endif
844: tty.sg_flags = f;
845: sTTY(1);
846: return (ot);
847: }
848:
849: gTTY(i)
850: int i;
851: {
852:
853: ignore(gtty(i, &tty));
854: #ifdef TIOCGETC
855: ioctl(i, TIOCGETC, &ottyc);
856: nttyc = ottyc;
857: #endif
858: }
859:
860: sTTY(i)
861: int i;
862: {
863:
864: /*
865: * Bug in USG tty driver, put out a null char as a patch.
866: */
867: #ifdef USG
868: if (tty.sg_ospeed == B1200)
869: write(1, "", 1);
870: #endif
871: #ifdef TIOCSETN
872: ioctl(i, TIOCSETN, &tty);
873: #else
874: stty(i, &tty);
875: #endif
876: #ifdef TIOCSETC
877: ioctl(i, TIOCSETC, &nttyc);
878: #endif
879: }
880:
881: /*
882: * Print newline, or blank if in open/visual
883: */
884: noonl()
885: {
886:
887: putchar(Outchar != termchar ? ' ' : '\n');
888: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.