BSD 3 development
[unix-history] / usr / src / cmd / ex / ex_put.c
CommitLineData
27704fe4
BJ
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) {
557 if (inopen && ND)
558 tputs(ND, 0, plodput);
559 else
560 plodput(' ');
561 outcol++;
562 if (plodcnt < 0)
563 goto out;
564 }
565out:
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 */
579noteinp()
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 */
597termreset()
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 */
618char *obp = obuf;
619
620draino()
621{
622
623 obp = obuf;
624}
625
626flusho()
627{
628
629 if (obp != obuf) {
630 write(1, obuf, obp - obuf);
631 obp = obuf;
632 }
633}
634
635putnl()
636{
637
638 putchar('\n');
639}
640
641putS(cp)
642 char *cp;
643{
644
645 if (cp == NULL)
646 return;
647 while (*cp)
648 putch(*cp++);
649}
650
651
652putch(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 */
668char *
669cgoto()
670{
671
672 return (tgoto(CM, destcol, destline));
673}
674
675/*
676 * Put with padding
677 */
678putpad(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 */
689setoutt()
690{
691
692 Outchar = termchar;
693}
694
695/*
696 * Printf (temporarily) in list mode.
697 */
698/*VARARGS2*/
699lprintf(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 */
712putNFL()
713{
714
715 putnl();
716 flush();
717}
718
719/*
720 * Try to start -nl mode.
721 */
722pstart()
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 */
742pstop()
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 */
757ostart()
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 */
784ostop(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 */
799vcook()
800{
801
802 tty.sg_flags &= ~RAW;
803 sTTY(1);
804}
805
806/*
807 * Back into raw mode.
808 */
809vraw()
810{
811
812 tty.sg_flags |= RAW;
813 sTTY(1);
814}
815#endif
816
817/*
818 * Restore flags to normal state f.
819 */
820normal(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 */
833setty(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
849gTTY(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
860sTTY(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 */
884noonl()
885{
886
887 putchar(Outchar != termchar ? ' ' : '\n');
888}