date and time created 80/07/31 23:00:53 by mark
[unix-history] / usr / src / usr.bin / ex / ex_vops.c
CommitLineData
79597adf
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 * This file defines the operation sequences which interface the
8 * logical changes to the file buffer with the internal and external
9 * display representations.
10 */
11
12/*
13 * Undo.
14 *
15 * Undo is accomplished in two ways. We often for small changes in the
16 * current line know how (in terms of a change operator) how the change
17 * occurred. Thus on an intelligent terminal we can undo the operation
18 * by another such operation, using insert and delete character
19 * stuff. The pointers vU[AD][12] index the buffer vutmp when this
20 * is possible and provide the necessary information.
21 *
22 * The other case is that the change involved multiple lines or that
23 * we have moved away from the line or forgotten how the change was
24 * accomplished. In this case we do a redisplay and hope that the
25 * low level optimization routines (which don't look for winning
26 * via insert/delete character) will not lose too badly.
27 */
28char *vUA1, *vUA2;
29char *vUD1, *vUD2;
30
31vUndo()
32{
33
34 /*
35 * Avoid UU which clobbers ability to do u.
36 */
37 if (vundkind == VCAPU || vUNDdot != dot) {
38 beep();
39 return;
40 }
41 CP(vutmp, linebuf);
42 vUD1 = linebuf; vUD2 = strend(linebuf);
43 putmk1(dot, vUNDsav);
44 getDOT();
45 vUA1 = linebuf; vUA2 = strend(linebuf);
46 vundkind = VCAPU;
47 if (state == ONEOPEN || state == HARDOPEN) {
48 vjumpto(dot, vUNDcurs, 0);
49 return;
50 }
51 vdirty(vcline, 1);
52 vsyncCL();
53 vfixcurs();
54}
55
56vundo()
57{
58 register int cnt;
59 register line *addr;
60 register char *cp;
61 char temp[LBSIZE];
62 bool savenote;
63 int (*OO)();
64 short oldhold = hold;
65
66 switch (vundkind) {
67
68 case VMANYINS:
69 wcursor = 0;
70 addr1 = undap1;
71 addr2 = undap2 - 1;
72 vsave();
73 YANKreg('1');
74 notecnt = 0;
75 /* fall into ... */
76
77 case VMANY:
78 case VMCHNG:
79 vsave();
80 addr = dot - vcline;
81 notecnt = 1;
82 if (undkind == UNDPUT && undap1 == undap2) {
83 beep();
84 return;
85 }
86 /*
87 * Undo() call below basically replaces undap1 to undap2-1
88 * with dol through unddol-1. Hack screen image to
89 * reflect this replacement.
90 */
91 vreplace(undap1 - addr, undap2 - undap1,
92 undkind == UNDPUT ? 0 : unddol - dol);
93 savenote = notecnt;
94 undo(1);
95 if (vundkind != VMCHNG || addr != dot)
96 killU();
97 vundkind = VMANY;
98 cnt = dot - addr;
99 if (cnt < 0 || cnt > vcnt || state != VISUAL) {
100 vjumpto(dot, NOSTR, '.');
101 return;
102 }
103 if (!savenote)
104 notecnt = 0;
105 vcline = cnt;
106 vrepaint(vmcurs);
107 vmcurs = 0;
108 return;
109
110 case VCHNG:
111 case VCAPU:
112 vundkind = VCHNG;
113 strcpy(temp, vutmp);
114 strcpy(vutmp, linebuf);
115 doomed = column(vUA2 - 1) - column(vUA1 - 1);
116 strcLIN(temp);
117 cp = vUA1; vUA1 = vUD1; vUD1 = cp;
118 cp = vUA2; vUA2 = vUD2; vUD2 = cp;
119 cursor = vUD1;
120 if (state == HARDOPEN) {
121 doomed = 0;
122 vsave();
123 vopen(dot, WBOT);
124 vnline(cursor);
125 return;
126 }
127 /*
128 * Pseudo insert command.
129 */
130 vcursat(cursor);
131 OO = Outchar; Outchar = vinschar; hold |= HOLDQIK;
132 vprepins();
133 temp[vUA2 - linebuf] = 0;
134 for (cp = &temp[vUA1 - linebuf]; *cp;)
135 putchar(*cp++);
136 Outchar = OO; hold = oldhold;
137 endim();
138 physdc(cindent(), cindent() + doomed);
139 doomed = 0;
140 vdirty(vcline, 1);
141 vsyncCL();
142 if (cursor > linebuf && cursor >= strend(linebuf))
143 cursor--;
144 vfixcurs();
145 return;
146
147 case VNONE:
148 beep();
149 return;
150 }
151}
152
153/*
154 * Initialize undo information before an append.
155 */
156vnoapp()
157{
158
159 vUD1 = vUD2 = cursor;
160}
161
162/*
163 * All the rest of the motion sequences have one or more
164 * cases to deal with. In the case wdot == 0, operation
165 * is totally within current line, from cursor to wcursor.
166 * If wdot is given, but wcursor is 0, then operation affects
167 * the inclusive line range. The hardest case is when both wdot
168 * and wcursor are given, then operation affects from line dot at
169 * cursor to line wdot at wcursor.
170 */
171
172/*
173 * Move is simple, except for moving onto new lines in hardcopy open mode.
174 */
175vmove()
176{
177 register int cnt;
178
179 if (wdot) {
180 if (wdot < one || wdot > dol) {
181 beep();
182 return;
183 }
184 cnt = wdot - dot;
185 wdot = NOLINE;
186 if (cnt)
187 killU();
188 vupdown(cnt, wcursor);
189 return;
190 }
191
192 /*
193 * When we move onto a new line, save information for U undo.
194 */
195 if (vUNDdot != dot) {
196 vUNDsav = *dot;
197 vUNDcurs = wcursor;
198 vUNDdot = dot;
199 }
200
201 /*
202 * In hardcopy open, type characters to left of cursor
203 * on new line, or back cursor up if its to left of where we are.
204 * In any case if the current line is ``rubbled'' i.e. has trashy
205 * looking overstrikes on it or \'s from deletes, we reprint
206 * so it is more comprehensible (and also because we can't work
207 * if we let it get more out of sync since column() won't work right.
208 */
209 if (state == HARDOPEN) {
210 register char *cp;
211 if (rubble) {
212 register int c;
213 int oldhold = hold;
214
215 sethard();
216 cp = wcursor;
217 c = *cp;
218 *cp = 0;
219 hold |= HOLDDOL;
220 vreopen(WTOP, lineDOT(), vcline);
221 hold = oldhold;
222 *cp = c;
223 } else if (wcursor > cursor) {
224 vfixcurs();
225 for (cp = cursor; *cp && cp < wcursor;) {
226 register int c = *cp++ & TRIM;
227
228 putchar(c ? c : ' ');
229 }
230 }
231 }
232 vsetcurs(wcursor);
233}
234
235/*
236 * Delete operator.
237 *
238 * Hard case of deleting a range where both wcursor and wdot
239 * are specified is treated as a special case of change and handled
240 * by vchange (although vchange may pass it back if it degenerates
241 * to a full line range delete.)
242 */
243vdelete(c)
244 char c;
245{
246 register char *cp;
247 register int i;
248
249 if (wdot) {
250 if (wcursor) {
251 vchange('d');
252 return;
253 }
254 if ((i = xdw()) < 0)
255 return;
256 if (state != VISUAL) {
257 vgoto(LINE(0), 0);
258 vputchar('@');
259 }
260 wdot = dot;
261 vremote(i, delete, 0);
262 notenam = "delete";
263 DEL[0] = 0;
264 killU();
265 vreplace(vcline, i, 0);
266 if (wdot > dol)
267 vcline--;
268 vrepaint(NOSTR);
269 return;
270 }
271 if (wcursor < linebuf)
272 wcursor = linebuf;
273 if (cursor == wcursor) {
274 beep();
275 return;
276 }
277 i = vdcMID();
278 cp = cursor;
279 setDEL();
280 CP(cp, wcursor);
281 if (cp > linebuf && (cp[0] == 0 || c == '#'))
282 cp--;
283 if (state == HARDOPEN) {
284 bleep(i, cp);
285 cursor = cp;
286 return;
287 }
288 physdc(column(cursor - 1), i);
289 DEPTH(vcline) = 0;
290 vreopen(LINE(vcline), lineDOT(), vcline);
291 vsyncCL();
292 vsetcurs(cp);
293}
294
295/*
296 * Change operator.
297 *
298 * In a single line we mark the end of the changed area with '$'.
299 * On multiple whole lines, we clear the lines first.
300 * Across lines with both wcursor and wdot given, we delete
301 * and sync then append (but one operation for undo).
302 */
303vchange(c)
304 char c;
305{
306 register char *cp;
307 register int i, ind, cnt;
308 line *addr;
309
310 if (wdot) {
311 /*
312 * Change/delete of lines or across line boundaries.
313 */
314 if ((cnt = xdw()) < 0)
315 return;
316 getDOT();
317 if (wcursor && cnt == 1) {
318 /*
319 * Not really.
320 */
321 wdot = 0;
322 if (c == 'd') {
323 vdelete(c);
324 return;
325 }
326 goto smallchange;
327 }
328 if (cursor && wcursor) {
329 /*
330 * Across line boundaries, but not
331 * necessarily whole lines.
332 * Construct what will be left.
333 */
334 *cursor = 0;
335 strcpy(genbuf, linebuf);
336 getline(*wdot);
337 if (strlen(genbuf) + strlen(wcursor) > LBSIZE - 2) {
338 getDOT();
339 beep();
340 return;
341 }
342 strcat(genbuf, wcursor);
343 if (c == 'd' && *vpastwh(genbuf) == 0) {
344 /*
345 * Although this is a delete
346 * spanning line boundaries, what
347 * would be left is all white space,
348 * so take it all away.
349 */
350 wcursor = 0;
351 getDOT();
352 op = 0;
353 notpart(lastreg);
354 notpart('1');
355 vdelete(c);
356 return;
357 }
358 ind = -1;
359 } else if (c == 'd' && wcursor == 0) {
360 vdelete(c);
361 return;
362 } else
363#ifdef LISPCODE
364 /*
365 * We are just substituting text for whole lines,
366 * so determine the first autoindent.
367 */
368 if (value(LISP) && value(AUTOINDENT))
369 ind = lindent(dot);
370 else
371#endif
372 ind = whitecnt(linebuf);
373 i = vcline >= 0 ? LINE(vcline) : WTOP;
374
375 /*
376 * Delete the lines from the buffer,
377 * and remember how the partial stuff came about in
378 * case we are told to put.
379 */
380 addr = dot;
381 vremote(cnt, delete, 0);
382 setpk();
383 notenam = "delete";
384 if (c != 'd')
385 notenam = "change";
386 /*
387 * If DEL[0] were nonzero, put would put it back
388 * rather than the deleted lines.
389 */
390 DEL[0] = 0;
391 if (cnt > 1)
392 killU();
393
394 /*
395 * Now hack the screen image coordination.
396 */
397 vreplace(vcline, cnt, 0);
398 wdot = NOLINE;
399 noteit(0);
400 vcline--;
401 if (addr <= dol)
402 dot--;
403
404 /*
405 * If this is a across line delete/change,
406 * cursor stays where it is; just splice together the pieces
407 * of the new line. Otherwise generate a autoindent
408 * after a S command.
409 */
410 if (ind >= 0) {
411 *genindent(ind) = 0;
412 vdoappend(genbuf);
413 } else {
414 vmcurs = cursor;
415 strcLIN(genbuf);
416 vdoappend(linebuf);
417 }
418
419 /*
420 * Indicate a change on hardcopies by
421 * erasing the current line.
422 */
423 if (c != 'd' && state != VISUAL && state != HARDOPEN) {
424 int oldhold = hold;
425
426 hold |= HOLDAT, vclrlin(i, dot), hold = oldhold;
427 }
428
429 /*
430 * Open the line (logically) on the screen, and
431 * update the screen tail. Unless we are really a delete
432 * go off and gather up inserted characters.
433 */
434 vcline++;
435 if (vcline < 0)
436 vcline = 0;
437 vopen(dot, i);
438 vsyncCL();
439 noteit(1);
440 if (c != 'd') {
441 if (ind >= 0) {
442 cursor = linebuf;
443 linebuf[0] = 0;
444 vfixcurs();
445 } else {
446 ind = 0;
447 vcursat(cursor);
448 }
449 vappend('x', 1, ind);
450 return;
451 }
452 if (*cursor == 0 && cursor > linebuf)
453 cursor--;
454 vrepaint(cursor);
455 return;
456 }
457
458smallchange:
459 /*
460 * The rest of this is just low level hacking on changes
461 * of small numbers of characters.
462 */
463 if (wcursor < linebuf)
464 wcursor = linebuf;
465 if (cursor == wcursor) {
466 beep();
467 return;
468 }
469 i = vdcMID();
470 cp = cursor;
471 if (state != HARDOPEN)
472 vfixcurs();
473
474 /*
475 * Put out the \\'s indicating changed text in hardcopy,
476 * or mark the end of the change with $ if not hardcopy.
477 */
478 if (state == HARDOPEN)
479 bleep(i, cp);
480 else {
481 vcursbef(wcursor);
482 putchar('$');
483 i = cindent();
484 }
485
486 /*
487 * Remember the deleted text for possible put,
488 * and then prepare and execute the input portion of the change.
489 */
490 cursor = cp;
491 setDEL();
492 CP(cursor, wcursor);
493 if (state != HARDOPEN) {
494 vcursaft(cursor - 1);
495 doomed = i - cindent();
496 } else {
497/*
498 sethard();
499 wcursor = cursor;
500 cursor = linebuf;
501 vgoto(outline, value(NUMBER) << 3);
502 vmove();
503*/
504 doomed = 0;
505 }
506 prepapp();
507 vappend('c', 1, 0);
508}
509
510/*
511 * Open new lines.
512 *
513 * Tricky thing here is slowopen. This causes display updating
514 * to be held off so that 300 baud dumb terminals don't lose badly.
515 * This also suppressed counts, which otherwise say how many blank
516 * space to open up. Counts are also suppressed on intelligent terminals.
517 * Actually counts are obsoleted, since if your terminal is slow
518 * you are better off with slowopen.
519 */
520voOpen(c, cnt)
521 char c;
522 register int cnt;
523{
524 register int ind = 0, i;
525 short oldhold = hold;
526
527 if (value(SLOWOPEN) || value(REDRAW) && AL && DL)
528 cnt = 1;
529 vsave();
530 setLAST();
531 if (value(AUTOINDENT))
532 ind = whitecnt(linebuf);
533 if (c == 'O') {
534 vcline--;
535 dot--;
536 if (dot > zero)
537 getDOT();
538 }
539 if (value(AUTOINDENT)) {
540#ifdef LISPCODE
541 if (value(LISP))
542 ind = lindent(dot + 1);
543#endif
544 }
545 killU();
546 prepapp();
547 vundkind = VMANY;
548 if (state != VISUAL)
549 c = WBOT + 1;
550 else {
551 c = vcline < 0 ? WTOP - cnt : LINE(vcline) + DEPTH(vcline);
552 if (c < ZERO)
553 c = ZERO;
554 i = LINE(vcline + 1) - c;
555 if (i < cnt && c <= WBOT && (!AL || !DL))
556 vinslin(c, cnt - i, vcline);
557 }
558 *genindent(ind) = 0;
559 vdoappend(genbuf);
560 vcline++;
561 oldhold = hold;
562 hold |= HOLDROL;
563 vopen(dot, c);
564 hold = oldhold;
565 if (value(SLOWOPEN))
566 /*
567 * Oh, so lazy!
568 */
569 vscrap();
570 else
571 vsync1(LINE(vcline));
572 cursor = linebuf;
573 linebuf[0] = 0;
574 vappend('o', 1, ind);
575}
576
577/*
578 * > < and = shift operators.
579 *
580 * Note that =, which aligns lisp, is just a ragged sort of shift,
581 * since it never distributes text between lines.
582 */
583char vshnam[2] = { 'x', 0 };
584
585vshftop()
586{
587 register line *addr;
588 register int cnt;
589
590 if ((cnt = xdw()) < 0)
591 return;
592 addr = dot;
593 vremote(cnt, vshift, 0);
594 vshnam[0] = op;
595 notenam = vshnam;
596 dot = addr;
597 vreplace(vcline, cnt, cnt);
598 if (state == HARDOPEN)
599 vcnt = 0;
600 vrepaint(NOSTR);
601}
602
603/*
604 * !.
605 *
606 * Filter portions of the buffer through unix commands.
607 */
608vfilter()
609{
610 register line *addr;
611 register int cnt;
612 char *oglobp, d;
613
614 if ((cnt = xdw()) < 0)
615 return;
616 if (vglobp)
617 vglobp = uxb;
618 if (readecho('!'))
619 return;
620 oglobp = globp; globp = genbuf + 1;
621 d = peekc; ungetchar(0);
622 CATCH
623 fixech();
624 unix0(0);
625 ONERR
626 splitw = 0;
627 ungetchar(d);
628 vrepaint(cursor);
629 globp = oglobp;
630 return;
631 ENDCATCH
632 ungetchar(d); globp = oglobp;
633 addr = dot;
634 CATCH
635 vgoto(WECHO, 0); flusho();
636 vremote(cnt, filter, 2);
637 ONERR
638 vdirty(0, LINES);
639 ENDCATCH
640 if (dot == zero && dol > zero)
641 dot = one;
642 splitw = 0;
643 notenam = "";
644 vreplace(vcline, cnt, undap2 - undap1);
645 dot = addr;
646 if (dot > dol) {
647 dot--;
648 vcline--;
649 }
650 vrepaint(NOSTR);
651}
652
653/*
654 * Xdw exchanges dot and wdot if appropriate and also checks
655 * that wdot is reasonable. Its name comes from
656 * xchange dotand wdot
657 */
658xdw()
659{
660 register char *cp;
661 register int cnt;
662/*
663 register int notp = 0;
664 */
665
666 if (wdot == NOLINE || wdot < one || wdot > dol) {
667 beep();
668 return (-1);
669 }
670 vsave();
671 setLAST();
672 if (dot > wdot) {
673 register line *addr;
674
675 vcline -= dot - wdot;
676 addr = dot; dot = wdot; wdot = addr;
677 cp = cursor; cursor = wcursor; wcursor = cp;
678 }
679 /*
680 * If a region is specified but wcursor is at the begining
681 * of the last line, then we move it to be the end of the
682 * previous line (actually off the end).
683 */
684 if (cursor && wcursor == linebuf && wdot > dot) {
685 wdot--;
686 getDOT();
687 if (vpastwh(linebuf) >= cursor)
688 wcursor = 0;
689 else {
690 getline(*wdot);
691 wcursor = strend(linebuf);
692 getDOT();
693 }
694 /*
695 * Should prepare in caller for possible dot == wdot.
696 */
697 }
698 cnt = wdot - dot + 1;
699 if (vreg) {
700 vremote(cnt, YANKreg, vreg);
701/*
702 if (notp)
703 notpart(vreg);
704 */
705 }
706
707 /*
708 * Kill buffer code. If delete operator is c or d, then save
709 * the region in numbered buffers.
710 *
711 * BUG: This may be somewhat inefficient due
712 * to the way named buffer are implemented,
713 * necessitating some optimization.
714 */
715 vreg = 0;
716 if (any(op, "cd")) {
717 vremote(cnt, YANKreg, '1');
718/*
719 if (notp)
720 notpart('1');
721 */
722 }
723 return (cnt);
724}
725
726/*
727 * Routine for vremote to call to implement shifts.
728 */
729vshift()
730{
731
732 shift(op, 1);
733}
734
735/*
736 * Replace a single character with the next input character.
737 * A funny kind of insert.
738 */
739vrep(cnt)
740 register int cnt;
741{
742 register int i, c;
743
744 if (cnt > strlen(cursor)) {
745 beep();
746 return;
747 }
748 i = column(cursor + cnt - 1);
749 vcursat(cursor);
750 doomed = i - cindent();
751 if (!vglobp) {
752 c = getesc();
753 if (c == 0) {
754 vfixcurs();
755 return;
756 }
757 ungetkey(c);
758 }
759 CP(vutmp, linebuf);
760 vundkind = VCHNG;
761 wcursor = cursor + cnt;
762 vUD1 = cursor; vUD2 = wcursor;
763 CP(cursor, wcursor);
764 prepapp();
765 vappend('r', cnt, 0);
766 *lastcp++ = INS[0];
767 setLAST();
768}
769
770/*
771 * Yank.
772 *
773 * Yanking to string registers occurs for free (essentially)
774 * in the routine xdw().
775 */
776vyankit()
777{
778 register int cnt;
779
780 if (wdot) {
781 if ((cnt = xdw()) < 0)
782 return;
783 vremote(cnt, yank, 0);
784 setpk();
785 notenam = "yank";
786 vundkind = VNONE;
787 DEL[0] = 0;
788 wdot = NOLINE;
789 if (notecnt <= vcnt - vcline && notecnt < value(REPORT))
790 notecnt = 0;
791 vrepaint(cursor);
792 return;
793 }
794 takeout(DEL);
795}
796
797/*
798 * Set pkill variables so a put can
799 * know how to put back partial text.
800 * This is necessary because undo needs the complete
801 * line images to be saved, while a put wants to trim
802 * the first and last lines. The compromise
803 * is for put to be more clever.
804 */
805setpk()
806{
807
808 if (wcursor) {
809 pkill[0] = cursor;
810 pkill[1] = wcursor;
811 }
812}