release 3.3, Feb 2, 1980
[unix-history] / usr / src / usr.bin / ex / ex_put.c
CommitLineData
e45f2c96
MH
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 */
22int (*Outchar)() = termchar;
23int (*Putchar)() = normchar;
24int (*Pline)() = normline;
25
26int (*
27setlist(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
38int (*
39setnumb(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 */
54listchar(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 */
88normchar(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 */
132numbline(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 */
145normline()
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 */
164slobber(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 */
197static 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};
202static 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 */
210static bool phadnl;
211
212/*
213 * Indirect to current definition of putchar.
214 */
215putchar(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 */
228termchar(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
245flush()
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 */
257flush1()
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
318flush2()
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 */
332fgoto()
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 */
392tab(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
407static int plodcnt, plodflg;
408
409plodput(c)
410{
411
412 if (plodflg)
413 plodcnt--;
414 else
415 putch(c);
416}
417
418plod(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 }
496dontcr:
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) {
887e3e0d
MH
557 /*
558 * move one char to the right. We don't use ND space
559 * because it's better to just print the char we are
560 * moving over. There are various exceptions, however.
561 * If !inopen, vtube contains garbage. If the char is
562 * a null or a tab we want to print a space. Other random
563 * chars we use space for instead, too.
564 */
565#ifdef TRACE
566 if (trace)
567 fprintf(trace, "ND: inopen=%d, i=%d, outline=%d, outcol=%d\n", inopen, i, outline, outcol);
568#endif
569 if (!inopen || vtube[outline]==NULL ||
570 (i=vtube[outline][outcol]) < ' ')
571 i = ' ';
572 if (insmode && ND)
e45f2c96
MH
573 tputs(ND, 0, plodput);
574 else
887e3e0d 575 plodput(i);
e45f2c96
MH
576 outcol++;
577 if (plodcnt < 0)
578 goto out;
579 }
580out:
581 if (plodflg) {
582 outcol = soutcol;
583 outline = soutline;
584 }
585 return(plodcnt);
586}
587
588/*
589 * An input line arrived.
590 * Calculate new (approximate) screen line position.
591 * Approximate because kill character echoes newline with
592 * no feedback and also because of long input lines.
593 */
594noteinp()
595{
596
597 outline++;
598 if (outline > LINES - 1)
599 outline = LINES - 1;
600 destline = outline;
601 destcol = outcol = 0;
602}
603
604/*
605 * Something weird just happened and we
606 * lost track of whats happening out there.
607 * Since we cant, in general, read where we are
608 * we just reset to some known state.
609 * On cursor addressible terminals setting to unknown
610 * will force a cursor address soon.
611 */
612termreset()
613{
614
615 endim();
616 if (TI) /* otherwise it flushes anyway, and 'set tty=dumb' vomits */
617 putpad(TI); /*adb change -- emit terminal initial sequence */
618 destcol = 0;
619 destline = LINES - 1;
620 if (CA) {
621 outcol = UKCOL;
622 outline = UKCOL;
623 } else {
624 outcol = destcol;
625 outline = destline;
626 }
627}
628
629/*
630 * Low level buffering, with the ability to drain
631 * buffered output without printing it.
632 */
633char *obp = obuf;
634
635draino()
636{
637
638 obp = obuf;
639}
640
641flusho()
642{
643
644 if (obp != obuf) {
645 write(1, obuf, obp - obuf);
646 obp = obuf;
647 }
648}
649
650putnl()
651{
652
653 putchar('\n');
654}
655
656putS(cp)
657 char *cp;
658{
659
660 if (cp == NULL)
661 return;
662 while (*cp)
663 putch(*cp++);
664}
665
666
667putch(c)
668 int c;
669{
670
671 *obp++ = c;
672 if (obp >= &obuf[sizeof obuf])
673 flusho();
674}
675
676/*
677 * Miscellaneous routines related to output.
678 */
679
680/*
681 * Cursor motion.
682 */
683char *
684cgoto()
685{
686
687 return (tgoto(CM, destcol, destline));
688}
689
690/*
691 * Put with padding
692 */
693putpad(cp)
694 char *cp;
695{
696
697 flush();
698 tputs(cp, 0, putch);
699}
700
701/*
702 * Set output through normal command mode routine.
703 */
704setoutt()
705{
706
707 Outchar = termchar;
708}
709
710/*
711 * Printf (temporarily) in list mode.
712 */
713/*VARARGS2*/
714lprintf(cp, dp)
715 char *cp, *dp;
716{
717 register int (*P)();
718
719 P = setlist(1);
720 printf(cp, dp);
721 Putchar = P;
722}
723
724/*
725 * Newline + flush.
726 */
727putNFL()
728{
729
730 putnl();
731 flush();
732}
733
734/*
735 * Try to start -nl mode.
736 */
737pstart()
738{
739
740 if (NONL)
741 return;
742 if (!value(OPTIMIZE))
743 return;
744 if (ruptible == 0 || pfast)
745 return;
746 fgoto();
747 flusho();
748 pfast = 1;
749 normtty++;
750 tty.sg_flags = normf & ~(ECHO|XTABS|CRMOD);
751 sTTY(1);
752}
753
754/*
755 * Stop -nl mode.
756 */
757pstop()
758{
759
760 if (inopen)
761 return;
762 phadnl = 0;
763 linp = linb;
764 draino();
765 normal(normf);
766 pfast &= ~1;
767}
768
769/*
770 * Prep tty for open mode.
771 */
772ostart()
773{
774 int f;
775
776 if (!intty)
777 error("Open and visual must be used interactively");
778 gTTY(1);
779 normtty++;
780 f = tty.sg_flags;
781#ifdef CBREAK
782 tty.sg_flags = (normf &~ (ECHO|XTABS|CRMOD)) | CBREAK;
783#else
784 tty.sg_flags = (normf &~ (ECHO|XTABS|CRMOD)) | RAW;
785#endif
887e3e0d 786#ifdef EATQS
e45f2c96
MH
787 nttyc.t_quitc = nttyc.t_startc = nttyc.t_stopc = '\377';
788#endif
789 sTTY(1);
790 putpad(VS);
791 putpad(KS);
792 pfast |= 2;
793 return (f);
794}
795
796/*
797 * Stop open, restoring tty modes.
798 */
799ostop(f)
800 int f;
801{
802
803 pfast = (f & CRMOD) == 0;
804 termreset(), fgoto(), flusho();
805 normal(f);
806 putpad(VE);
807 putpad(KE);
808}
809
810#ifndef CBREAK
811/*
812 * Into cooked mode for interruptibility.
813 */
814vcook()
815{
816
817 tty.sg_flags &= ~RAW;
818 sTTY(1);
819}
820
821/*
822 * Back into raw mode.
823 */
824vraw()
825{
826
827 tty.sg_flags |= RAW;
828 sTTY(1);
829}
830#endif
831
832/*
833 * Restore flags to normal state f.
834 */
835normal(f)
836 int f;
837{
838
839 if (normtty > 0) {
840 setty(f);
841 normtty--;
842 }
843}
844
845/*
846 * Straight set of flags to state f.
847 */
848setty(f)
849 int f;
850{
851 register int ot = tty.sg_flags;
852
887e3e0d 853#ifdef EATQS
e45f2c96
MH
854 if (f == normf)
855 nttyc = ottyc;
856 else
857 nttyc.t_quitc = nttyc.t_startc = nttyc.t_stopc = '\377';
858#endif
859 tty.sg_flags = f;
860 sTTY(1);
861 return (ot);
862}
863
864gTTY(i)
865 int i;
866{
867
868 ignore(gtty(i, &tty));
887e3e0d 869#ifdef EATQS
e45f2c96
MH
870 ioctl(i, TIOCGETC, &ottyc);
871 nttyc = ottyc;
872#endif
873}
874
875sTTY(i)
876 int i;
877{
878
879/*
880 * Bug in USG tty driver, put out a null char as a patch.
881 */
882#ifdef USG
883 if (tty.sg_ospeed == B1200)
884 write(1, "", 1);
885#endif
886#ifdef TIOCSETN
887 ioctl(i, TIOCSETN, &tty);
888#else
889 stty(i, &tty);
890#endif
887e3e0d 891#ifdef EATQS
e45f2c96
MH
892 ioctl(i, TIOCSETC, &nttyc);
893#endif
894}
895
896/*
897 * Print newline, or blank if in open/visual
898 */
899noonl()
900{
901
902 putchar(Outchar != termchar ? ' ' : '\n');
903}