BSD 4_3_Tahoe release
[unix-history] / usr / src / ucb / ex / ex_vput.c
CommitLineData
f9679f70
DF
1/*
2 * Copyright (c) 1980 Regents of the University of California.
3 * All rights reserved. The Berkeley software License Agreement
4 * specifies the terms and conditions for redistribution.
5 */
6
7#ifndef lint
ca67e7b4 8static char *sccsid = "@(#)ex_vput.c 7.5 (Berkeley) 3/9/87";
f9679f70
DF
9#endif not lint
10
98fb7faa
MH
11#include "ex.h"
12#include "ex_tty.h"
13#include "ex_vis.h"
14
15/*
16 * Deal with the screen, clearing, cursor positioning, putting characters
17 * into the screen image, and deleting characters.
18 * Really hard stuff here is utilizing insert character operations
19 * on intelligent terminals which differs widely from terminal to terminal.
20 */
21vclear()
22{
23
24#ifdef ADEBUG
25 if (trace)
26 tfixnl(), fprintf(trace, "------\nvclear\n");
27#endif
28 tputs(CL, LINES, putch);
29 destcol = 0;
30 outcol = 0;
31 destline = 0;
32 outline = 0;
33 if (inopen)
5a6c967e 34 vclrbyte(vtube0, WCOLS * (WECHO - ex_ZERO + 1));
98fb7faa
MH
35}
36
37/*
38 * Clear memory.
39 */
40vclrbyte(cp, i)
41 register char *cp;
42 register int i;
43{
44
45 if (i > 0)
46 do
47 *cp++ = 0;
48 while (--i != 0);
49}
50
51/*
52 * Clear a physical display line, high level.
53 */
54vclrlin(l, tp)
55 int l;
56 line *tp;
57{
58
59 vigoto(l, 0);
60 if ((hold & HOLDAT) == 0)
5a6c967e 61 ex_putchar(tp > dol ? ((UPPERCASE || HZ) ? '^' : '~') : '@');
98fb7faa
MH
62 if (state == HARDOPEN)
63 sethard();
64 vclreol();
65}
66
67/*
68 * Clear to the end of the current physical line
69 */
70vclreol()
71{
72 register int i, j;
73 register char *tp;
74
75 if (destcol == WCOLS)
76 return;
77 destline += destcol / WCOLS;
78 destcol %= WCOLS;
79 if (destline < 0 || destline > WECHO)
80 error("Internal error: vclreol");
81 i = WCOLS - destcol;
82 tp = vtube[destline] + destcol;
83 if (CE) {
84 if (IN && *tp || !ateopr()) {
85 vcsync();
86 vputp(CE, 1);
87 }
88 vclrbyte(tp, i);
89 return;
90 }
91 if (*tp == 0)
92 return;
93 while (i > 0 && (j = *tp & (QUOTE|TRIM))) {
94 if (j != ' ' && (j & QUOTE) == 0) {
95 destcol = WCOLS - i;
96 vputchar(' ');
97 }
98 --i, *tp++ = 0;
99 }
100}
101
102/*
103 * Clear the echo line.
104 * If didphys then its been cleared physically (as
105 * a side effect of a clear to end of display, e.g.)
106 * so just do it logically.
107 * If work here is being held off, just remember, in
108 * heldech, if work needs to be done, don't do anything.
109 */
110vclrech(didphys)
111 bool didphys;
112{
113
5a6c967e 114 if (Peek_key == ATTN)
98fb7faa
MH
115 return;
116 if (hold & HOLDECH) {
117 heldech = !didphys;
118 return;
119 }
120 if (!didphys && (CD || CE)) {
121 splitw++;
122 /*
123 * If display is retained below, then MUST use CD or CE
124 * since we don't really know whats out there.
125 * Vigoto might decide (incorrectly) to do nothing.
126 */
d266c416
MH
127 if (DB) {
128 vgoto(WECHO, 0);
129 vputp(CD ? CD : CE, 1);
130 } else {
131 if (XT) {
132 /*
133 * This code basically handles the t1061
134 * where positioning at (0, 0) won't work
135 * because the terminal won't let you put
136 * the cursor on it's magic cookie.
137 *
138 * Should probably be XS above, or even a
139 * new X? glitch, but right now t1061 is the
140 * only terminal with XT.
141 */
142 vgoto(WECHO, 0);
143 vputp(DL, 1);
144 } else {
145 vigoto(WECHO, 0);
146 vclreol();
147 }
148 }
98fb7faa
MH
149 splitw = 0;
150 didphys = 1;
151 }
152 if (didphys)
153 vclrbyte(vtube[WECHO], WCOLS);
154 heldech = 0;
155}
156
157/*
158 * Fix the echo area for use, setting
159 * the state variable splitw so we wont rollup
160 * when we move the cursor there.
161 */
162fixech()
163{
164
165 splitw++;
166 if (state != VISUAL && state != CRTOPEN) {
167 vclean();
168 vcnt = 0;
169 }
170 vgoto(WECHO, 0); flusho();
171}
172
173/*
174 * Put the cursor ``before'' cp.
175 */
176vcursbef(cp)
177 register char *cp;
178{
179
180 if (cp <= linebuf)
181 vgotoCL(value(NUMBER) << 3);
182 else
183 vgotoCL(column(cp - 1) - 1);
184}
185
186/*
187 * Put the cursor ``at'' cp.
188 */
189vcursat(cp)
190 register char *cp;
191{
192
193 if (cp <= linebuf && linebuf[0] == 0)
194 vgotoCL(value(NUMBER) << 3);
195 else
196 vgotoCL(column(cp - 1));
197}
198
199/*
200 * Put the cursor ``after'' cp.
201 */
202vcursaft(cp)
203 register char *cp;
204{
205
206 vgotoCL(column(cp));
207}
208
209/*
210 * Fix the cursor to be positioned in the correct place
211 * to accept a command.
212 */
213vfixcurs()
214{
215
216 vsetcurs(cursor);
217}
218
219/*
220 * Compute the column position implied by the cursor at ``nc'',
221 * and move the cursor there.
222 */
223vsetcurs(nc)
224 register char *nc;
225{
226 register int col;
227
228 col = column(nc);
229 if (linebuf[0])
230 col--;
231 vgotoCL(col);
232 cursor = nc;
233}
234
235/*
236 * Move the cursor invisibly, i.e. only remember to do it.
237 */
238vigoto(y, x)
239 int y, x;
240{
241
242 destline = y;
243 destcol = x;
244}
245
246/*
247 * Move the cursor to the position implied by any previous
248 * vigoto (or low level hacking with destcol/destline as in readecho).
249 */
250vcsync()
251{
252
253 vgoto(destline, destcol);
254}
255
256/*
257 * Goto column x of the current line.
258 */
259vgotoCL(x)
260 register int x;
261{
262
263 if (splitw)
264 vgoto(WECHO, x);
265 else
266 vgoto(LINE(vcline), x);
267}
268
269/*
270 * Invisible goto column x of current line.
271 */
272vigotoCL(x)
273 register int x;
274{
275
276 if (splitw)
277 vigoto(WECHO, x);
278 else
279 vigoto(LINE(vcline), x);
280}
281
282/*
283 * Move cursor to line y, column x, handling wraparound and scrolling.
284 */
285vgoto(y, x)
286 register int y, x;
287{
288 register char *tp;
289 register int c;
290
291 /*
292 * Fold the possibly too large value of x.
293 */
294 if (x >= WCOLS) {
295 y += x / WCOLS;
296 x %= WCOLS;
297 }
298 if (y < 0)
299 error("Internal error: vgoto");
300 if (outcol >= WCOLS) {
301 if (AM) {
302 outline += outcol / WCOLS;
303 outcol %= WCOLS;
304 } else
305 outcol = WCOLS - 1;
306 }
307
308 /*
309 * In a hardcopy or glass crt open, print the stuff
310 * implied by a motion, or backspace.
311 */
312 if (state == HARDOPEN || state == ONEOPEN) {
313 if (y != outline)
314 error("Line too long for open");
315 if (x + 1 < outcol - x || (outcol > x && !BS))
316 destcol = 0, fgoto();
317 tp = vtube[WBOT] + outcol;
318 while (outcol != x)
319 if (outcol < x) {
320 if (*tp == 0)
321 *tp = ' ';
322 c = *tp++ & TRIM;
323 vputc(c && (!OS || EO) ? c : ' '), outcol++;
324 } else {
325 if (BC)
326 vputp(BC, 0);
327 else
328 vputc('\b');
329 outcol--;
330 }
331 destcol = outcol = x;
332 destline = outline;
333 return;
334 }
335
336 /*
337 * If the destination position implies a scroll, do it.
338 */
339 destline = y;
340 if (destline > WBOT && (!splitw || destline > WECHO)) {
341 endim();
342 vrollup(destline);
343 }
344
345 /*
346 * If there really is a motion involved, do it.
347 * The check here is an optimization based on profiling.
348 */
349 destcol = x;
350 if ((destline - outline) * WCOLS != destcol - outcol) {
351 if (!MI)
352 endim();
353 fgoto();
354 }
355}
356
357/*
358 * This is the hardest code in the editor, and deals with insert modes
359 * on different kinds of intelligent terminals. The complexity is due
360 * to the cross product of three factors:
361 *
362 * 1. Lines may display as more than one segment on the screen.
363 * 2. There are 2 kinds of intelligent terminal insert modes.
364 * 3. Tabs squash when you insert characters in front of them,
365 * in a way in which current intelligent terminals don't handle.
366 *
367 * The two kinds of terminals are typified by the DM2500 or HP2645 for
368 * one and the CONCEPT-100 or the FOX for the other.
369 *
370 * The first (HP2645) kind has an insert mode where the characters
371 * fall off the end of the line and the screen is shifted rigidly
372 * no matter how the display came about.
373 *
374 * The second (CONCEPT-100) kind comes from terminals which are designed
375 * for forms editing and which distinguish between blanks and ``spaces''
376 * on the screen, spaces being like blank, but never having had
377 * and data typed into that screen position (since, e.g. a clear operation
378 * like clear screen). On these terminals, when you insert a character,
379 * the characters from where you are to the end of the screen shift
380 * over till a ``space'' is found, and the null character there gets
381 * eaten up.
382 *
383 *
384 * The code here considers the line as consisting of several parts
385 * the first part is the ``doomed'' part, i.e. a part of the line
386 * which is being typed over. Next comes some text up to the first
387 * following tab. The tab is the next segment of the line, and finally
388 * text after the tab.
389 *
390 * We have to consider each of these segments and the effect of the
391 * insertion of a character on them. On terminals like HP2645's we
392 * must simulate a multi-line insert mode using the primitive one
393 * line insert mode. If we are inserting in front of a tab, we have
394 * to either delete characters from the tab or insert white space
395 * (when the tab reaches a new spot where it gets larger) before we
396 * insert the new character.
397 *
398 * On a terminal like a CONCEPT our strategy is to make all
399 * blanks be displayed, while trying to keep the screen having ``spaces''
400 * for portions of tabs. In this way the terminal hardward does some
401 * of the hacking for compression of tabs, although this tends to
402 * disappear as you work on the line and spaces change into blanks.
403 *
404 * There are a number of boundary conditions (like typing just before
405 * the first following tab) where we can avoid a lot of work. Most
406 * of them have to be dealt with explicitly because performance is
407 * much, much worse if we don't.
408 *
409 * A final thing which is hacked here is two flavors of insert mode.
410 * Datamedia's do this by an insert mode which you enter and leave
411 * and by having normal motion character operate differently in this
412 * mode, notably by having a newline insert a line on the screen in
413 * this mode. This generally means it is unsafe to move around
414 * the screen ignoring the fact that we are in this mode.
415 * This is possible on some terminals, and wins big (e.g. HP), so
416 * we encode this as a ``can move in insert capability'' mi,
417 * and terminals which have it can do insert mode with much less
418 * work when tabs are present following the cursor on the current line.
419 */
420
421/*
422 * Routine to expand a tab, calling the normal Outchar routine
423 * to put out each implied character. Note that we call outchar
424 * with a QUOTE. We use QUOTE internally to represent a position
425 * which is part of the expansion of a tab.
426 */
427vgotab()
428{
d266c416 429 register int i = tabcol(destcol, value(TABSTOP)) - destcol;
98fb7faa
MH
430
431 do
432 (*Outchar)(QUOTE);
d266c416 433 while (--i);
98fb7faa
MH
434}
435
436/*
437 * Variables for insert mode.
438 */
439int linend; /* The column position of end of line */
440int tabstart; /* Column of start of first following tab */
441int tabend; /* Column of end of following tabs */
442int tabsize; /* Size of the following tabs */
443int tabslack; /* Number of ``spaces'' in following tabs */
444int inssiz; /* Number of characters to be inserted */
445int inscol; /* Column where insertion is taking place */
446int shft; /* Amount tab expansion shifted rest of line */
447int slakused; /* This much of tabslack will be used up */
448
449/*
450 * This routine MUST be called before insert mode is run,
451 * and brings all segments of the current line to the top
452 * of the screen image buffer so it is easier for us to
453 * maniuplate them.
454 */
455vprepins()
456{
457 register int i;
458 register char *cp = vtube0;
459
460 for (i = 0; i < DEPTH(vcline); i++) {
461 vmaktop(LINE(vcline) + i, cp);
462 cp += WCOLS;
463 }
464}
465
466vmaktop(p, cp)
467 register int p;
468 char *cp;
469{
470 register int i;
471 char temp[TUBECOLS];
472
d266c416 473 if (p < 0 || vtube[p] == cp)
98fb7faa 474 return;
5a6c967e 475 for (i = ex_ZERO; i <= WECHO; i++)
98fb7faa
MH
476 if (vtube[i] == cp) {
477 copy(temp, vtube[i], WCOLS);
478 copy(vtube[i], vtube[p], WCOLS);
479 copy(vtube[p], temp, WCOLS);
480 vtube[i] = vtube[p];
481 vtube[p] = cp;
482 return;
483 }
484 error("Line too long");
485}
486
487/*
488 * Insert character c at current cursor position.
489 * Multi-character inserts occur only as a result
490 * of expansion of tabs (i.e. inssize == 1 except
491 * for tabs) and code assumes this in several place
492 * to make life simpler.
493 */
494vinschar(c)
299f2784 495 int c; /* mjm: char --> int */
98fb7faa
MH
496{
497 register int i;
498 register char *tp;
499
500 if ((!IM || !EI) && ((hold & HOLDQIK) || !value(REDRAW) || value(SLOWOPEN))) {
501 /*
502 * Don't want to try to use terminal
503 * insert mode, or to try to fake it.
504 * Just put the character out; the screen
505 * will probably be wrong but we will fix it later.
506 */
507 if (c == '\t') {
508 vgotab();
509 return;
510 }
511 vputchar(c);
512 if (DEPTH(vcline) * WCOLS + !value(REDRAW) >
513 (destline - LINE(vcline)) * WCOLS + destcol)
514 return;
515 /*
516 * The next line is about to be clobbered
517 * make space for another segment of this line
518 * (on an intelligent terminal) or just remember
519 * that next line was clobbered (on a dumb one
520 * if we don't care to redraw the tail.
521 */
522 if (AL) {
523 vnpins(0);
524 } else {
525 c = LINE(vcline) + DEPTH(vcline);
526 if (c < LINE(vcline + 1) || c > WBOT)
527 return;
528 i = destcol;
529 vinslin(c, 1, vcline);
530 DEPTH(vcline)++;
531 vigoto(c, i);
532 vprepins();
533 }
534 return;
535 }
536 /*
537 * Compute the number of positions in the line image of the
538 * current line. This is done from the physical image
539 * since that is faster. Note that we have no memory
540 * from insertion to insertion so that routines which use
541 * us don't have to worry about moving the cursor around.
542 */
543 if (*vtube0 == 0)
544 linend = 0;
545 else {
546 /*
547 * Search backwards for a non-null character
548 * from the end of the displayed line.
549 */
550 i = WCOLS * DEPTH(vcline);
551 if (i == 0)
552 i = WCOLS;
553 tp = vtube0 + i;
554 while (*--tp == 0)
555 if (--i == 0)
556 break;
557 linend = i;
558 }
559
560 /*
561 * We insert at a position based on the physical location
562 * of the output cursor.
563 */
564 inscol = destcol + (destline - LINE(vcline)) * WCOLS;
565 if (c == '\t') {
566 /*
567 * Characters inserted from a tab must be
568 * remembered as being part of a tab, but we can't
569 * use QUOTE here since we really need to print blanks.
570 * QUOTE|' ' is the representation of this.
571 */
d266c416 572 inssiz = tabcol(inscol, value(TABSTOP)) - inscol;
98fb7faa
MH
573 c = ' ' | QUOTE;
574 } else
575 inssiz = 1;
576
577 /*
578 * If the text to be inserted is less than the number
579 * of doomed positions, then we don't need insert mode,
580 * rather we can just typeover.
581 */
582 if (inssiz <= doomed) {
583 endim();
584 if (inscol != linend)
585 doomed -= inssiz;
586 do
587 vputchar(c);
588 while (--inssiz);
589 return;
590 }
591
592 /*
593 * Have to really do some insertion, thus
594 * stake out the bounds of the first following
595 * group of tabs, computing starting position,
596 * ending position, and the number of ``spaces'' therein
597 * so we can tell how much it will squish.
598 */
599 tp = vtube0 + inscol;
600 for (i = inscol; i < linend; i++)
601 if (*tp++ & QUOTE) {
602 --tp;
603 break;
604 }
605 tabstart = tabend = i;
606 tabslack = 0;
607 while (tabend < linend) {
608 i = *tp++;
609 if ((i & QUOTE) == 0)
610 break;
611 if ((i & TRIM) == 0)
612 tabslack++;
613 tabsize++;
614 tabend++;
615 }
616 tabsize = tabend - tabstart;
617
618 /*
619 * For HP's and DM's, e.g. tabslack has no meaning.
620 */
621 if (!IN)
622 tabslack = 0;
623#ifdef IDEBUG
624 if (trace) {
625 fprintf(trace, "inscol %d, inssiz %d, tabstart %d, ",
626 inscol, inssiz, tabstart);
627 fprintf(trace, "tabend %d, tabslack %d, linend %d\n",
628 tabend, tabslack, linend);
629 }
630#endif
631
632 /*
633 * The real work begins.
634 */
635 slakused = 0;
636 shft = 0;
637 if (tabsize) {
638 /*
639 * There are tabs on this line.
640 * If they need to expand, then the rest of the line
641 * will have to be shifted over. In this case,
642 * we will need to make sure there are no ``spaces''
643 * in the rest of the line (on e.g. CONCEPT-100)
644 * and then grab another segment on the screen if this
645 * line is now deeper. We then do the shift
646 * implied by the insertion.
647 */
d266c416 648 if (inssiz >= doomed + tabcol(tabstart, value(TABSTOP)) - tabstart) {
98fb7faa
MH
649 if (IN)
650 vrigid();
651 vneedpos(value(TABSTOP));
652 vishft();
653 }
654 } else if (inssiz > doomed)
655 /*
656 * No tabs, but line may still get deeper.
657 */
658 vneedpos(inssiz - doomed);
659 /*
660 * Now put in the inserted characters.
661 */
662 viin(c);
663
664 /*
665 * Now put the cursor in its final resting place.
666 */
667 destline = LINE(vcline);
668 destcol = inscol + inssiz;
669 vcsync();
670}
671
672/*
673 * Rigidify the rest of the line after the first
674 * group of following tabs, typing blanks over ``spaces''.
675 */
676vrigid()
677{
678 register int col;
679 register char *tp = vtube0 + tabend;
680
681 for (col = tabend; col < linend; col++)
682 if ((*tp++ & TRIM) == 0) {
683 endim();
684 vgotoCL(col);
685 vputchar(' ' | QUOTE);
686 }
687}
688
689/*
690 * We need cnt more positions on this line.
691 * Open up new space on the screen (this may in fact be a
692 * screen rollup).
693 *
694 * On a dumb terminal we may infact redisplay the rest of the
695 * screen here brute force to keep it pretty.
696 */
697vneedpos(cnt)
698 int cnt;
699{
700 register int d = DEPTH(vcline);
701 register int rmdr = d * WCOLS - linend;
702
703 if (cnt <= rmdr - IN)
704 return;
705 endim();
706 vnpins(1);
707}
708
709vnpins(dosync)
710 int dosync;
711{
712 register int d = DEPTH(vcline);
713 register int e;
714
715 e = LINE(vcline) + DEPTH(vcline);
716 if (e < LINE(vcline + 1)) {
717 vigoto(e, 0);
718 vclreol();
719 return;
720 }
721 DEPTH(vcline)++;
722 if (e < WECHO) {
723 e = vglitchup(vcline, d);
724 vigoto(e, 0); vclreol();
725 if (dosync) {
9cc49269 726 int (*Ooutchar)() = Outchar;
98fb7faa
MH
727 Outchar = vputchar;
728 vsync(e + 1);
9cc49269 729 Outchar = Ooutchar;
98fb7faa
MH
730 }
731 } else {
732 vup1();
733 vigoto(WBOT, 0);
734 vclreol();
735 }
736 vprepins();
737}
738
739/*
740 * Do the shift of the next tabstop implied by
741 * insertion so it expands.
742 */
743vishft()
744{
745 int tshft = 0;
746 int j;
747 register int i;
748 register char *tp = vtube0;
749 register char *up;
750 short oldhold = hold;
751
752 shft = value(TABSTOP);
753 hold |= HOLDPUPD;
754 if (!IM && !EI) {
755 /*
756 * Dumb terminals are easy, we just have
757 * to retype the text.
758 */
759 vigotoCL(tabend + shft);
760 up = tp + tabend;
761 for (i = tabend; i < linend; i++)
762 vputchar(*up++);
763 } else if (IN) {
764 /*
765 * CONCEPT-like terminals do most of the work for us,
766 * we don't have to muck with simulation of multi-line
767 * insert mode. Some of the shifting may come for free
768 * also if the tabs don't have enough slack to take up
769 * all the inserted characters.
770 */
771 i = shft;
772 slakused = inssiz - doomed;
773 if (slakused > tabslack) {
774 i -= slakused - tabslack;
775 slakused -= tabslack;
776 }
777 if (i > 0 && tabend != linend) {
778 tshft = i;
779 vgotoCL(tabend);
780 goim();
781 do
782 vputchar(' ' | QUOTE);
783 while (--i);
784 }
785 } else {
786 /*
787 * HP and Datamedia type terminals have to have multi-line
788 * insert faked. Hack each segment after where we are
789 * (going backwards to where we are.) We then can
790 * hack the segment where the end of the first following
791 * tab group is.
792 */
793 for (j = DEPTH(vcline) - 1; j > (tabend + shft) / WCOLS; j--) {
794 vgotoCL(j * WCOLS);
795 goim();
796 up = tp + j * WCOLS - shft;
797 i = shft;
887e3e0d
MH
798 do {
799 if (*up)
800 vputchar(*up++);
801 else
802 break;
803 } while (--i);
98fb7faa
MH
804 }
805 vigotoCL(tabstart);
806 i = shft - (inssiz - doomed);
807 if (i > 0) {
808 tabslack = inssiz - doomed;
809 vcsync();
810 goim();
811 do
812 vputchar(' ');
813 while (--i);
814 }
815 }
816 /*
817 * Now do the data moving in the internal screen
818 * image which is common to all three cases.
819 */
820 tp += linend;
821 up = tp + shft;
822 i = linend - tabend;
823 if (i > 0)
824 do
825 *--up = *--tp;
826 while (--i);
827 if (IN && tshft) {
828 i = tshft;
829 do
830 *--up = ' ' | QUOTE;
831 while (--i);
832 }
833 hold = oldhold;
834}
835
836/*
837 * Now do the insert of the characters (finally).
838 */
839viin(c)
299f2784 840 int c; /* mjm: char --> int */
98fb7faa
MH
841{
842 register char *tp, *up;
843 register int i, j;
844 register bool noim = 0;
845 int remdoom;
846 short oldhold = hold;
847
848 hold |= HOLDPUPD;
849 if (tabsize && (IM && EI) && inssiz - doomed > tabslack)
850 /*
851 * There is a tab out there which will be affected
852 * by the insertion since there aren't enough doomed
853 * characters to take up all the insertion and we do
854 * have insert mode capability.
855 */
856 if (inscol + doomed == tabstart) {
857 /*
858 * The end of the doomed characters sits right at the
859 * start of the tabs, then we don't need to use insert
860 * mode; unless the tab has already been expanded
861 * in which case we MUST use insert mode.
862 */
863 slakused = 0;
864 noim = !shft;
865 } else {
866 /*
867 * The last really special case to handle is case
868 * where the tab is just sitting there and doesn't
869 * have enough slack to let the insertion take
870 * place without shifting the rest of the line
871 * over. In this case we have to go out and
872 * delete some characters of the tab before we start
873 * or the answer will be wrong, as the rest of the
874 * line will have been shifted. This code means
875 * that terminals with only insert chracter (no
876 * delete character) won't work correctly.
877 */
878 i = inssiz - doomed - tabslack - slakused;
879 i %= value(TABSTOP);
880 if (i > 0) {
881 vgotoCL(tabstart);
882 godm();
883 for (i = inssiz - doomed - tabslack; i > 0; i--)
884 vputp(DC, DEPTH(vcline));
885 enddm();
886 }
887 }
888
889 /*
890 * Now put out the characters of the actual insertion.
891 */
892 vigotoCL(inscol);
893 remdoom = doomed;
894 for (i = inssiz; i > 0; i--) {
895 if (remdoom > 0) {
896 remdoom--;
897 endim();
898 } else if (noim)
899 endim();
900 else if (IM && EI) {
901 vcsync();
902 goim();
903 }
904 vputchar(c);
905 }
906
907 if (!IM || !EI) {
908 /*
909 * We are a dumb terminal; brute force update
910 * the rest of the line; this is very much an n^^2 process,
911 * and totally unreasonable at low speed.
912 *
913 * You asked for it, you get it.
914 */
915 tp = vtube0 + inscol + doomed;
916 for (i = inscol + doomed; i < tabstart; i++)
917 vputchar(*tp++);
918 hold = oldhold;
919 vigotoCL(tabstart + inssiz - doomed);
920 for (i = tabsize - (inssiz - doomed) + shft; i > 0; i--)
921 vputchar(' ' | QUOTE);
922 } else {
923 if (!IN) {
924 /*
925 * On terminals without multi-line
926 * insert in the hardware, we must go fix the segments
927 * between the inserted text and the following
928 * tabs, if they are on different lines.
929 *
930 * Aaargh.
931 */
932 tp = vtube0;
933 for (j = (inscol + inssiz - 1) / WCOLS + 1;
934 j <= (tabstart + inssiz - doomed - 1) / WCOLS; j++) {
935 vgotoCL(j * WCOLS);
936 i = inssiz - doomed;
937 up = tp + j * WCOLS - i;
938 goim();
939 do
940 vputchar(*up++);
941 while (--i && *up);
942 }
943 } else {
944 /*
945 * On terminals with multi line inserts,
946 * life is simpler, just reflect eating of
947 * the slack.
948 */
949 tp = vtube0 + tabend;
950 for (i = tabsize - (inssiz - doomed); i >= 0; i--) {
951 if ((*--tp & (QUOTE|TRIM)) == QUOTE) {
952 --tabslack;
953 if (tabslack >= slakused)
954 continue;
955 }
956 *tp = ' ' | QUOTE;
957 }
958 }
959 /*
960 * Blank out the shifted positions to be tab positions.
961 */
962 if (shft) {
963 tp = vtube0 + tabend + shft;
964 for (i = tabsize - (inssiz - doomed) + shft; i > 0; i--)
965 if ((*--tp & QUOTE) == 0)
966 *tp = ' ' | QUOTE;
967 }
968 }
969
970 /*
971 * Finally, complete the screen image update
972 * to reflect the insertion.
973 */
974 hold = oldhold;
975 tp = vtube0 + tabstart; up = tp + inssiz - doomed;
976 for (i = tabstart; i > inscol + doomed; i--)
977 *--up = *--tp;
978 for (i = inssiz; i > 0; i--)
979 *--up = c;
980 doomed = 0;
981}
982
983/*
984 * Go into ``delete mode''. If the
985 * sequence which goes into delete mode
986 * is the same as that which goes into insert
987 * mode, then we are in delete mode already.
988 */
989godm()
990{
991
992 if (insmode) {
993 if (eq(DM, IM))
994 return;
995 endim();
996 }
997 vputp(DM, 0);
998}
999
1000/*
1001 * If we are coming out of delete mode, but
1002 * delete and insert mode end with the same sequence,
1003 * it wins to pretend we are now in insert mode,
1004 * since we will likely want to be there again soon
1005 * if we just moved over to delete space from part of
1006 * a tab (above).
1007 */
1008enddm()
1009{
1010
1011 if (eq(DM, IM)) {
1012 insmode = 1;
1013 return;
1014 }
1015 vputp(ED, 0);
1016}
1017
1018/*
1019 * In and out of insert mode.
1020 * Note that the code here demands that there be
1021 * a string for insert mode (the null string) even
1022 * if the terminal does all insertions a single character
1023 * at a time, since it branches based on whether IM is null.
1024 */
1025goim()
1026{
1027
1028 if (!insmode)
1029 vputp(IM, 0);
1030 insmode = 1;
1031}
1032
1033endim()
1034{
1035
1036 if (insmode) {
1037 vputp(EI, 0);
1038 insmode = 0;
1039 }
1040}
1041
1042/*
1043 * Put the character c on the screen at the current cursor position.
1044 * This routine handles wraparound and scrolling and understands not
1045 * to roll when splitw is set, i.e. we are working in the echo area.
1046 * There is a bunch of hacking here dealing with the difference between
1047 * QUOTE, QUOTE|' ', and ' ' for CONCEPT-100 like terminals, and also
1048 * code to deal with terminals which overstrike, including CRT's where
1049 * you can erase overstrikes with some work. CRT's which do underlining
1050 * implicitly which has to be erased (like CONCEPTS) are also handled.
1051 */
1052vputchar(c)
1053 register int c;
1054{
1055 register char *tp;
1056 register int d;
1057
1058 c &= (QUOTE|TRIM);
1059#ifdef TRACE
1060 if (trace)
1061 tracec(c);
1062#endif
887e3e0d 1063 /* Fix problem of >79 chars on echo line. */
98fb7faa 1064 if (destcol >= WCOLS-1 && splitw && destline == WECHO)
887e3e0d 1065 pofix();
98fb7faa
MH
1066 if (destcol >= WCOLS) {
1067 destline += destcol / WCOLS;
1068 destcol %= WCOLS;
1069 }
1070 if (destline > WBOT && (!splitw || destline > WECHO))
1071 vrollup(destline);
1072 tp = vtube[destline] + destcol;
1073 switch (c) {
1074
1075 case '\t':
1076 vgotab();
1077 return;
1078
1079 case ' ':
1080 /*
1081 * We can get away without printing a space in a number
1082 * of cases, but not always. We get away with doing nothing
1083 * if we are not in insert mode, and not on a CONCEPT-100
1084 * like terminal, and either not in hardcopy open or in hardcopy
1085 * open on a terminal with no overstriking, provided,
1086 * in all cases, that nothing has ever been displayed
1087 * at this position. Ugh.
1088 */
1089 if (!insmode && !IN && (state != HARDOPEN || OS) && (*tp&TRIM) == 0) {
1090 *tp = ' ';
1091 destcol++;
1092 return;
1093 }
1094 goto def;
1095
1096 case QUOTE:
1097 if (insmode) {
1098 /*
1099 * When in insert mode, tabs have to expand
1100 * to real, printed blanks.
1101 */
1102 c = ' ' | QUOTE;
1103 goto def;
1104 }
1105 if (*tp == 0) {
1106 /*
1107 * A ``space''.
1108 */
1109 if ((hold & HOLDPUPD) == 0)
1110 *tp = QUOTE;
1111 destcol++;
1112 return;
1113 }
1114 /*
1115 * A ``space'' ontop of a part of a tab.
1116 */
1117 if (*tp & QUOTE) {
1118 destcol++;
1119 return;
1120 }
1121 c = ' ' | QUOTE;
1122 /* fall into ... */
1123
1124def:
1125 default:
1126 d = *tp & TRIM;
1127 /*
1128 * Now get away with doing nothing if the characters
1129 * are the same, provided we are not in insert mode
1130 * and if we are in hardopen, that the terminal has overstrike.
1131 */
1132 if (d == (c & TRIM) && !insmode && (state != HARDOPEN || OS)) {
1133 if ((hold & HOLDPUPD) == 0)
1134 *tp = c;
1135 destcol++;
1136 return;
1137 }
1138 /*
1139 * Backwards looking optimization.
1140 * The low level cursor motion routines will use
1141 * a cursor motion right sequence to step 1 character
1142 * right. On, e.g., a DM3025A this is 2 characters
1143 * and printing is noticeably slower at 300 baud.
1144 * Since the low level routines are not allowed to use
1145 * spaces for positioning, we discover the common
1146 * case of a single space here and force a space
1147 * to be printed.
1148 */
1149 if (destcol == outcol + 1 && tp[-1] == ' ' && outline == destline) {
1150 vputc(' ');
1151 outcol++;
1152 }
1153
1154 /*
1155 * This is an inline expansion a call to vcsync() dictated
1156 * by high frequency in a profile.
1157 */
1158 if (outcol != destcol || outline != destline)
1159 vgoto(destline, destcol);
1160
1161 /*
1162 * Deal with terminals which have overstrike.
1163 * We handle erasing general overstrikes, erasing
1164 * underlines on terminals (such as CONCEPTS) which
1165 * do underlining correctly automatically (e.g. on nroff
1166 * output), and remembering, in hardcopy mode,
1167 * that we have overstruct something.
1168 */
1169 if (!insmode && d && d != ' ' && d != (c & TRIM)) {
1170 if (EO && (OS || UL && (c == '_' || d == '_'))) {
1171 vputc(' ');
1172 outcol++, destcol++;
1173 back1();
1174 } else
1175 rubble = 1;
1176 }
1177
1178 /*
1179 * Unless we are just bashing characters around for
1180 * inner working of insert mode, update the display.
1181 */
1182 if ((hold & HOLDPUPD) == 0)
1183 *tp = c;
1184
1185 /*
1186 * In insert mode, put out the IC sequence, padded
1187 * based on the depth of the current line.
1188 * A terminal which had no real insert mode, rather
1189 * opening a character position at a time could do this.
1190 * Actually should use depth to end of current line
1191 * but this rarely matters.
1192 */
1193 if (insmode)
1194 vputp(IC, DEPTH(vcline));
1195 vputc(c & TRIM);
1196
1197 /*
1198 * In insert mode, IP is a post insert pad.
1199 */
1200 if (insmode)
1201 vputp(IP, DEPTH(vcline));
1202 destcol++, outcol++;
1203
1204 /*
1205 * CONCEPT braindamage in early models: after a wraparound
1206 * the next newline is eaten. It's hungry so we just
1207 * feed it now rather than worrying about it.
299f2784
MH
1208 * Fixed to use return linefeed to work right
1209 * on vt100/tab132 as well as concept.
98fb7faa 1210 */
299f2784 1211 if (XN && outcol % WCOLS == 0) {
455e66d9
MH
1212 vputc('\r');
1213 vputc('\n');
299f2784 1214 }
98fb7faa
MH
1215 }
1216}
1217
1218/*
1219 * Delete display positions stcol through endcol.
1220 * Amount of use of special terminal features here is limited.
1221 */
1222physdc(stcol, endcol)
1223 int stcol, endcol;
1224{
1225 register char *tp, *up;
1226 char *tpe;
1227 register int i;
1228 register int nc = endcol - stcol;
1229
1230#ifdef IDEBUG
1231 if (trace)
1232 tfixnl(), fprintf(trace, "physdc(%d, %d)\n", stcol, endcol);
1233#endif
1234 if (!DC || nc <= 0)
1235 return;
1236 if (IN) {
1237 /*
1238 * CONCEPT-100 like terminal.
1239 * If there are any ``spaces'' in the material to be
1240 * deleted, then this is too hard, just retype.
1241 */
1242 vprepins();
1243 up = vtube0 + stcol;
1244 i = nc;
1245 do
1246 if ((*up++ & (QUOTE|TRIM)) == QUOTE)
1247 return;
1248 while (--i);
1249 i = 2 * nc;
1250 do
1251 if (*up == 0 || (*up++ & QUOTE) == QUOTE)
1252 return;
1253 while (--i);
1254 vgotoCL(stcol);
1255 } else {
1256 /*
1257 * HP like delete mode.
1258 * Compute how much text we are moving over by deleting.
1259 * If it appears to be faster to just retype
1260 * the line, do nothing and that will be done later.
1261 * We are assuming 2 output characters per deleted
1262 * characters and that clear to end of line is available.
1263 */
1264 i = stcol / WCOLS;
1265 if (i != endcol / WCOLS)
1266 return;
1267 i += LINE(vcline);
1268 stcol %= WCOLS;
1269 endcol %= WCOLS;
1270 up = vtube[i]; tp = up + endcol; tpe = up + WCOLS;
1271 while (tp < tpe && *tp)
1272 tp++;
1273 if (tp - (up + stcol) < 2 * nc)
1274 return;
1275 vgoto(i, stcol);
1276 }
1277
1278 /*
1279 * Go into delete mode and do the actual delete.
1280 * Padding is on DC itself.
1281 */
1282 godm();
1283 for (i = nc; i > 0; i--)
1284 vputp(DC, DEPTH(vcline));
1285 vputp(ED, 0);
1286
1287 /*
1288 * Straighten up.
1289 * With CONCEPT like terminals, characters are pulled left
1290 * from first following null. HP like terminals shift rest of
1291 * this (single physical) line rigidly.
1292 */
1293 if (IN) {
1294 up = vtube0 + stcol;
1295 tp = vtube0 + endcol;
1296 while (i = *tp++) {
1297 if ((i & (QUOTE|TRIM)) == QUOTE)
1298 break;
1299 *up++ = i;
1300 }
1301 do
1302 *up++ = i;
1303 while (--nc);
1304 } else {
1305 copy(up + stcol, up + endcol, WCOLS - endcol);
1306 vclrbyte(tpe - nc, nc);
1307 }
1308}
1309
1310#ifdef TRACE
1311tfixnl()
1312{
1313
1314 if (trubble || techoin)
1315 fprintf(trace, "\n");
1316 trubble = 0, techoin = 0;
1317}
1318
1319tvliny()
1320{
1321 register int i;
1322
1323 if (!trace)
1324 return;
1325 tfixnl();
1326 fprintf(trace, "vcnt = %d, vcline = %d, vliny = ", vcnt, vcline);
1327 for (i = 0; i <= vcnt; i++) {
1328 fprintf(trace, "%d", LINE(i));
1329 if (FLAGS(i) & VDIRT)
1330 fprintf(trace, "*");
1331 if (DEPTH(i) != 1)
1332 fprintf(trace, "<%d>", DEPTH(i));
1333 if (i < vcnt)
1334 fprintf(trace, " ");
1335 }
1336 fprintf(trace, "\n");
1337}
1338
1339tracec(c)
299f2784 1340 int c; /* mjm: char --> int */
98fb7faa
MH
1341{
1342
1343 if (!techoin)
1344 trubble = 1;
1345 if (c == ESCAPE)
1346 fprintf(trace, "$");
299f2784
MH
1347 else if (c & QUOTE) /* mjm: for 3B (no sign extension) */
1348 fprintf(trace, "~%c", ctlof(c&TRIM));
98fb7faa
MH
1349 else if (c < ' ' || c == DELETE)
1350 fprintf(trace, "^%c", ctlof(c));
1351 else
1352 fprintf(trace, "%c", c);
1353}
1354#endif
1355
1356/*
1357 * Put a character with possible tracing.
1358 */
1359vputch(c)
1360 int c;
1361{
1362
1363#ifdef TRACE
1364 if (trace)
1365 tracec(c);
1366#endif
1367 vputc(c);
1368}