BSD 3 development
[unix-history] / .ref-BSD-2 / src / ex / ex_vmain.c
CommitLineData
8c102237
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 * This is the main routine for visual.
8 * We here decode the count and possible named buffer specification
9 * preceding a command and interpret a few of the commands.
10 * Commands which involve a target (i.e. an operator) are decoded
11 * in the routine operate in ex_voperate.c.
12 */
13
14#define forbid(a) { if (a) goto fonfon; }
15
16vmain()
17{
18 register int c, cnt, i;
19 char esave[TUBECOLS];
20 char *oglobp;
21 char d;
22 line *addr;
23 int ind;
24 int onumber, olist, (*OPline)(), (*OPutchar)();
25
26 /*
27 * If we started as a vi command (on the command line)
28 * then go process initial commands (recover, next or tag).
29 */
30 if (initev) {
31 oglobp = globp;
32 globp = initev;
33 hadcnt = cnt = 0;
34 i = tchng;
35 addr = dot;
36 goto doinit;
37 }
38
39 /*
40 * NB:
41 *
42 * The current line is always in the line buffer linebuf,
43 * and the cursor at the position cursor. You should do
44 * a vsave() before moving off the line to make sure the disk
45 * copy is updated if it has changed, and a getDOT() to get
46 * the line back if you mung linebuf. The motion
47 * routines in ex_vwind.c handle most of this.
48 */
49 for (;;) {
50 /*
51 * Decode a visual command.
52 * First sync the temp file if there has been a reasonable
53 * amount of change. Clear state for decoding of next
54 * command.
55 */
56 TSYNC();
57 vglobp = 0;
58 vreg = 0;
59 hold = 0;
60 wcursor = 0;
61 Xhadcnt = hadcnt = 0;
62 Xcnt = cnt = 1;
63 splitw = 0;
64 if (i = holdupd) {
65 if (state == VISUAL)
66 ignore(peekkey());
67 holdupd = 0;
68/*
69 if (LINE(0) < ZERO) {
70 vclear();
71 vcnt = 0;
72 i = 3;
73 }
74*/
75 if (state != VISUAL) {
76 vcnt = 0;
77 vsave();
78 vrepaint(cursor);
79 } else if (i == 3)
80 vredraw(WTOP);
81 else
82 vsync(WTOP);
83 vfixcurs();
84 }
85
86 /*
87 * Gobble up counts and named buffer specifications.
88 */
89 for (;;) {
90 if (isdigit(peekkey()) && peekkey() != '0') {
91 hadcnt = 1;
92 cnt = vgetcnt();
93 forbid (cnt <= 0);
94 }
95 if (peekkey() != '"')
96 break;
97 ignore(getkey()), c = getkey();
98 /*
99 * Buffer names be letters or digits.
100 * But not '0' as that is the source of
101 * an 'empty' named buffer spec in the routine
102 * kshift (see ex_temp.c).
103 */
104 forbid (c == '0' || !isalpha(c) && !isdigit(c));
105 vreg = c;
106 }
107reread:
108 /*
109 * Come to reread from below after some macro expansions.
110 * The call to map allows use of function key pads
111 * by performing a terminal dependent mapping of inputs,
112 * but notably not of two character inputs starting with
113 * an escape (as from a VT52 keypad). This is useful for
114 * e.g. DM1520's however.
115 */
116 op = c = map(getkey());
117
118 /*
119 * Begin to build an image of this command for possible
120 * later repeat in the buffer workcmd. It will be copied
121 * to lastcmd by the routine setLAST
122 * if/when completely specified.
123 */
124 lastcp = workcmd;
125 if (!vglobp)
126 *lastcp++ = c;
127
128 /*
129 * First level command decode.
130 */
131 switch (c) {
132
133 /*
134 * ^L Clear screen e.g. after transmission error.
135 */
136 case CTRL(l):
137 vclear();
138 vdirty(0, vcnt);
139 /* fall into... */
140
141 /*
142 * ^R Retype screen, getting rid of @ lines.
143 * If in open, equivalent to ^L.
144 */
145 case CTRL(r):
146 if (state != VISUAL) {
147 /*
148 * Get a clean line, throw away the
149 * memory of what is displayed now,
150 * and move back onto the current line.
151 */
152 vclean();
153 vcnt = 0;
154 vmoveto(dot, cursor, 0);
155 continue;
156 }
157 vredraw(WTOP);
158 /*
159 * Weird glitch -- when we enter visual
160 * in a very small window we may end up with
161 * no lines on the screen because the line
162 * at the top is too long. This forces the screen
163 * to be expanded to make room for it (after
164 * we have printed @'s ick showing we goofed).
165 */
166 if (vcnt == 0)
167 vrepaint(cursor);
168 vfixcurs();
169 continue;
170
171 /*
172 * $ Escape just cancels the current command
173 * with a little feedback. It would be
174 * nice to handle function pads sending ESC-X;
175 * here, using something else to replace ESC.
176 */
177 case ESCAPE:
178 beep();
179 continue;
180
181 /*
182 * . Repeat the last (modifying) open/visual command.
183 */
184 case '.':
185 /*
186 * Check that there was a last command, and
187 * take its count and named buffer unless they
188 * were given anew. Special case if last command
189 * referenced a numeric named buffer -- increment
190 * the number and go to a named buffer again.
191 * This allows a sequence like "1pu.u.u...
192 * to successively look for stuff in the kill chain
193 * much as one does in EMACS with C-Y and M-Y.
194 */
195 forbid (lastcmd[0] == 0);
196 if (hadcnt)
197 lastcnt = cnt;
198 if (vreg)
199 lastreg = vreg;
200 else if (isdigit(lastreg) && lastreg < '9')
201 lastreg++;
202 vreg = lastreg;
203 cnt = lastcnt;
204 hadcnt = lasthad;
205 vglobp = lastcmd;
206 goto reread;
207
208 /*
209 * ^U Scroll up. A count sticks around for
210 * future scrolls as the scroll amount.
211 * Attempt to hold the indentation from the
212 * top of the screen (in logical lines).
213 *
214 * BUG: A ^U near the bottom of the screen
215 * on a dumb terminal (which can't roll back)
216 * causes the screen to be cleared and then
217 * redrawn almost as it was. In this case
218 * one should simply move the cursor.
219 */
220 case CTRL(u):
221 if (hadcnt)
222 vSCROLL = cnt;
223 cnt = vSCROLL;
224 if (state == VISUAL)
225 ind = vcline, cnt += ind;
226 else
227 ind = 0;
228 vmoving = 0;
229 vup(cnt, ind, 1);
230 vnline(NOSTR);
231 continue;
232
233 /*
234 * ^D Scroll down. Like scroll up.
235 */
236 case CTRL(d):
237 if (hadcnt)
238 vSCROLL = cnt;
239 cnt = vSCROLL;
240 if (state == VISUAL)
241 ind = vcnt - vcline - 1, cnt += ind;
242 else
243 ind = 0;
244 vmoving = 0;
245 vdown(cnt, ind, 1);
246 vnline(NOSTR);
247 continue;
248
249 /*
250 * m Mark position in mark register given
251 * by following letter. Return is
252 * accomplished via ' or `; former
253 * to beginning of line where mark
254 * was set, latter to column where marked.
255 */
256 case 'm':
257 /*
258 * Getesc is generally used when a character
259 * is read as a latter part of a command
260 * to allow one to hit rubout/escape to cancel
261 * what you have typed so far. These characters
262 * are mapped to 0 by the subroutine.
263 */
264 c = getesc();
265 if (c == 0)
266 continue;
267
268 /*
269 * Markreg checks that argument is a letter
270 * and also maps ' and ` to the end of the range
271 * to allow '' or `` to reference the previous
272 * context mark.
273 */
274 c = markreg(c);
275 forbid (c == 0);
276 vsave();
277 names[c - 'a'] = (*dot &~ 01);
278 ncols[c - 'a'] = cursor;
279 anymarks = 1;
280 continue;
281
282 /*
283 * ^F Window forwards, with 2 lines of continuity.
284 * Count gives new screen size.
285 */
286 case CTRL(f):
287 vsave();
288 if (hadcnt)
289 vsetsiz(cnt);
290 if (vcnt > 2) {
291 dot += (vcnt - vcline) - 2;
292 vcnt = vcline = 0;
293 }
294 vzop(0, 0, '+');
295 continue;
296
297 /*
298 * ^B Window backwards, with 2 lines of continuity.
299 * Inverse of ^F.
300 */
301 case CTRL(b):
302 vsave();
303 if (hadcnt)
304 vsetsiz(cnt);
305 if (one + vcline != dot && vcnt > 2) {
306 dot -= vcline - 2;
307 vcnt = vcline = 0;
308 }
309 vzop(0, 0, '^');
310 continue;
311
312 /*
313 * z Screen adjustment, taking a following character:
314 * z<CR> current line to top
315 * z<NL> like z<CR>
316 * z- current line to bottom
317 * also z+, z^ like ^F and ^B.
318 * A preceding count is line to use rather
319 * than current line. A count between z and
320 * specifier character changes the screen size
321 * for the redraw.
322 *
323 */
324 case 'z':
325 if (state == VISUAL) {
326 i = vgetcnt();
327 if (i > 0)
328 vsetsiz(i);
329 c = getesc();
330 if (c == 0)
331 continue;
332 }
333 vsave();
334 vzop(hadcnt, cnt, c);
335 continue;
336
337 /*
338 * Y Yank lines, abbreviation for y_ or yy.
339 * Yanked lines can be put later if no
340 * changes intervene, or can be put in named
341 * buffers and put anytime in this session.
342 */
343 case 'Y':
344 ungetkey('_');
345 c = 'y';
346 break;
347
348 /*
349 * J Join lines, 2 by default. Count is number
350 * of lines to join (no join operator sorry.)
351 */
352 case 'J':
353 forbid (dot == dol);
354 if (cnt == 1)
355 cnt = 2;
356 if (cnt > (i = dol - dot + 1))
357 cnt = i;
358 vsave();
359 setLAST();
360 cursor = strend(linebuf);
361 vremote(cnt, join, 0);
362 notenam = "join";
363 vmoving = 0;
364 killU();
365 vreplace(vcline, cnt, 1);
366 if (!*cursor && cursor > linebuf)
367 cursor--;
368 if (notecnt == 2)
369 notecnt = 0;
370 vrepaint(cursor);
371 continue;
372
373 /*
374 * S Substitute text for whole lines, abbrev for c_.
375 * Count is number of lines to change.
376 */
377 case 'S':
378 ungetkey('_');
379 c = 'c';
380 break;
381
382 /*
383 * O Create a new line above current and accept new
384 * input text, to an escape, there.
385 * A count specifies, for dumb terminals when
386 * slowopen is not set, the number of physical
387 * line space to open on the screen.
388 *
389 * o Like O, but opens lines below.
390 */
391 case 'O':
392 case 'o':
393 voOpen(c, cnt);
394 continue;
395
396 /*
397 * C Change text to end of line, short for c$.
398 */
399 case 'C':
400 if (*cursor) {
401 ungetkey('$'), c = 'c';
402 break;
403 }
404 goto appnd;
405
406 /*
407 * A Append at end of line, short for $a.
408 */
409 case 'A':
410 operate('$', 1);
411appnd:
412 c = 'a';
413 /* fall into ... */
414
415 /*
416 * a Appends text after cursor. Text can continue
417 * through arbitrary number of lines.
418 */
419 case 'a':
420 if (*cursor) {
421 if (state == HARDOPEN)
422 putchar(*cursor);
423 cursor++;
424 }
425 goto insrt;
426
427 /*
428 * I Insert at beginning of whitespace of line,
429 * short for ^i.
430 */
431 case 'I':
432 operate('^', 1);
433 c = 'i';
434 /* fall into ... */
435
436 /*
437 * R Replace characters, one for one, by input
438 * (logically), like repeated r commands.
439 *
440 * BUG: This is like the typeover mode of many other
441 * editors, and is only rarely useful. Its
442 * implementation is a hack in a low level
443 * routine and it doesn't work very well, e.g.
444 * you can't move around within a R, etc.
445 */
446 case 'R':
447 /* fall into... */
448
449 /*
450 * i Insert text to an escape in the buffer.
451 * Text is arbitrary. This command reminds of
452 * the i command in bare teco.
453 */
454 case 'i':
455insrt:
456 /*
457 * Common code for all the insertion commands.
458 * Save for redo, position cursor, prepare for append
459 * at command and in visual undo. Note that nothing
460 * is doomed, unless R when all is, and save the
461 * current line in a the undo temporary buffer.
462 */
463 setLAST();
464 vcursat(cursor);
465 prepapp();
466 vnoapp();
467 doomed = c == 'R' ? 10000 : 0;
468 vundkind = VCHNG;
469 CP(vutmp, linebuf);
470
471 /*
472 * If this is a repeated command, then suppress
473 * fake insert mode on dumb terminals which looks
474 * ridiculous and wastes lots of time even at 9600B.
475 */
476 if (vglobp)
477 hold = HOLDQIK;
478 vappend(c, cnt, 0);
479 continue;
480
481 /*
482 * ^? An attention, normally a ^?, just beeps.
483 * If you are a vi command within ex, then
484 * two ATTN's will drop you back to command mode.
485 */
486 case ATTN:
487 beep();
488 if (initev || peekkey() != ATTN)
489 continue;
490 /* fall into... */
491
492 /*
493 * ^\ A quit always gets command mode.
494 */
495 case QUIT:
496 /*
497 * Have to be careful if we were called
498 * g/xxx/vi
499 * since a return will just start up again.
500 * So we simulate an interrupt.
501 */
502 if (inglobal)
503 onintr();
504 /* fall into... */
505
506 /*
507 * q Quit back to command mode, unless called as
508 * vi on command line in which case dont do it
509 */
510 case 'q': /* quit */
511 if (initev) {
512 vsave();
513 CATCH
514 error("Q gets ex command mode, :q leaves vi");
515 ENDCATCH
516 splitw = 0;
517 getDOT();
518 vrepaint(cursor);
519 continue;
520 }
521 /* fall into... */
522
523 /*
524 * Q Is like q, but always gets to command mode
525 * even if command line invocation was as vi.
526 */
527 case 'Q':
528 vsave();
529 return;
530
531 /*
532 * P Put back text before cursor or before current
533 * line. If text was whole lines goes back
534 * as whole lines. If part of a single line
535 * or parts of whole lines splits up current
536 * line to form many new lines.
537 * May specify a named buffer, or the delete
538 * saving buffers 1-9.
539 *
540 * p Like P but after rather than before.
541 */
542 case 'P':
543 case 'p':
544 vmoving = 0;
545 /*
546 * If previous delete was partial line, use an
547 * append or insert to put it back so as to
548 * use insert mode on intelligent terminals.
549 */
550 if (!vreg && DEL[0]) {
551 forbid ((DEL[0] & (QUOTE|TRIM)) == OVERBUF);
552 vglobp = DEL;
553 ungetkey(c == 'p' ? 'a' : 'i');
554 goto reread;
555 }
556
557 /*
558 * If a register wasn't specified, then make
559 * sure there is something to put back.
560 */
561 forbid (!vreg && unddol == dol);
562 vsave();
563 setLAST();
564 i = 0;
565 if (vreg && partreg(vreg) || !vreg && pkill[0]) {
566 /*
567 * Restoring multiple lines which were partial
568 * lines; will leave cursor in middle
569 * of line after shoving restored text in to
570 * split the current line.
571 */
572 i++;
573 if (c == 'p' && *cursor)
574 cursor++;
575 } else {
576 /*
577 * In whole line case, have to back up dot
578 * for P; also want to clear cursor so
579 * cursor will eventually be positioned
580 * at the beginning of the first put line.
581 */
582 cursor = 0;
583 if (c == 'P') {
584 dot--, vcline--;
585 c = 'p';
586 }
587 }
588 killU();
589
590 /*
591 * The call to putreg can potentially
592 * bomb since there may be nothing in a named buffer.
593 * We thus put a catch in here. If we didn't and
594 * there was an error we would end up in command mode.
595 */
596 CATCH
597 vremote(1, vreg ? putreg : put, vreg);
598 ONERR
599 if (vreg == -1) {
600 splitw = 0;
601 if (op == 'P')
602 dot++, vcline++;
603 goto pfixup;
604 }
605 ENDCATCH
606 splitw = 0;
607 if (!i) {
608 /*
609 * Increment undap1, undap2 to make up
610 * for their incorrect initialization in the
611 * routine vremote before calling put/putreg.
612 */
613 undap1++, undap2++;
614 vcline++;
615 }
616
617 /*
618 * After a put want current line first line,
619 * and dot was made the last line put in code run
620 * so far. This is why we increment vcline above,
621 * and decrease (usually) dot here.
622 */
623 dot = undap1;
624 vreplace(vcline, i, undap2 - undap1);
625 if (state != VISUAL) {
626 /*
627 * Special case in open mode.
628 * Force action on the screen when a single
629 * line is put even if it is identical to
630 * the current line, e.g. on YP; otherwise
631 * you can't tell anything happened.
632 */
633 vjumpto(dot, cursor, '.');
634 continue;
635 }
636pfixup:
637 vrepaint(cursor);
638 vfixcurs();
639 continue;
640
641 /*
642 * ^^ Return to previous context. Like a 't
643 * if that mark is set since tag sets that
644 * mark if it stays in same file. Else
645 * like a :e #, and thus can be used after a
646 * "No Write" diagnostic.
647 */
648 case CTRL(^):
649 if (hadcnt)
650 vsetsiz(cnt);
651 addr = getmark('t');
652 if (addr != 0) {
653 markit(addr);
654 vupdown(addr - dot, NOSTR);
655 continue;
656 }
657 oglobp = globp;
658 globp = "e #";
659 goto gogo;
660
661 /*
662 * ^] Takes word after cursor as tag, and then does
663 * tag command. Read ``go right to''.
664 */
665 case CTRL(]):
666 grabtag();
667 oglobp = globp;
668 globp = "tag";
669 goto gogo;
670
671 /*
672 * ^G Bring up a status line at the bottom of
673 * the screen, like a :file command.
674 *
675 * BUG: Was ^S but doesn't work in cbreak mode
676 */
677 case CTRL(g):
678 oglobp = globp;
679 globp = "file";
680gogo:
681 addr = dot;
682 vsave();
683 goto doinit;
684
685 /*
686 * : Read a command from the echo area and
687 * execute it in command mode.
688 */
689 case ':':
690 if (hadcnt)
691 vsetsiz(cnt);
692 vsave();
693 i = tchng;
694 addr = dot;
695 if (readecho(c)) {
696 esave[0] = 0;
697 goto fixup;
698 }
699 /*
700 * Use the visual undo buffer to store the global
701 * string for command mode, since it is idle right now.
702 */
703 oglobp = globp; strcpy(vutmp, genbuf+1); globp = vutmp;
704doinit:
705 esave[0] = 0;
706 fixech();
707
708 /*
709 * Have to finagle around not to lose last
710 * character after this command (when run from ex
711 * command mode). This is clumsy.
712 */
713 d = peekc; ungetchar(0);
714 CATCH
715 /*
716 * Save old values of options so we can
717 * notice when they change; switch into
718 * cooked mode so we are interruptible.
719 */
720 onumber = value(NUMBER);
721 olist = value(LIST);
722 OPline = Pline;
723 OPutchar = Putchar;
724#ifdef V6
725 vcook();
726#endif
727 commands(1, 1);
728 if (dot == zero && dol > zero)
729 dot = one;
730#ifdef V6
731 vraw();
732#endif
733 ONERR
734#ifdef V6
735 vraw();
736#endif
737 copy(esave, vtube[WECHO], TUBECOLS);
738 ENDCATCH
739 fixol();
740 Pline = OPline;
741 Putchar = OPutchar;
742 ungetchar(d);
743 globp = oglobp;
744
745 /*
746 * If we ended up with no lines in the buffer, make
747 * a line, and don't consider the buffer changed.
748 */
749 if (dot == zero) {
750 fixzero();
751 sync();
752 }
753 splitw = 0;
754
755 /*
756 * Special case: did list/number options change?
757 */
758 if (onumber != value(NUMBER))
759 setnumb(value(NUMBER));
760 if (olist != value(LIST))
761 setlist(value(LIST));
762
763fixup:
764 /*
765 * If a change occurred, other than
766 * a write which clears changes, then
767 * we should allow an undo even if .
768 * didn't move.
769 *
770 * BUG: You can make this wrong by
771 * tricking around with multiple commands
772 * on one line of : escape, and including
773 * a write command there, but its not
774 * worth worrying about.
775 */
776 if (tchng && tchng != i)
777 vundkind = VMANY, cursor = 0;
778
779 /*
780 * If we are about to do another :, hold off
781 * updating of screen.
782 */
783 if (vcnt < 0 && Peekkey == ':') {
784 getDOT();
785 continue;
786 }
787
788 /*
789 * In the case where the file being edited is
790 * new; e.g. if the initial state hasn't been
791 * saved yet, then do so now.
792 */
793 if (unddol == truedol) {
794 vundkind = VNONE;
795 Vlines = lineDOL();
796 if (!inglobal)
797 savevis();
798 addr = zero;
799 vcnt = 0;
800 if (esave[0] == 0)
801 copy(esave, vtube[WECHO], TUBECOLS);
802 }
803
804 /*
805 * If the current line moved reset the cursor position.
806 */
807 if (dot != addr) {
808 vmoving = 0;
809 cursor = 0;
810 }
811
812 /*
813 * If current line is not on screen or if we are
814 * in open mode and . moved, then redraw.
815 */
816 i = vcline + (dot - addr);
817 if (i < 0 || i >= vcnt && i >= -vcnt || state != VISUAL && dot != addr) {
818 if (state == CRTOPEN)
819 vup1();
820 if (vcnt > 0)
821 vcnt = 0;
822 vjumpto(dot, (char *) 0, '.');
823 } else {
824 /*
825 * Current line IS on screen.
826 * If we did a [Hit return...] then
827 * restore vcnt and clear screen if in visual
828 */
829 vcline = i;
830 if (vcnt < 0) {
831 vcnt = -vcnt;
832 if (state == VISUAL)
833 vclear();
834 else if (state == CRTOPEN)
835 vcnt = 0;
836 }
837
838 /*
839 * Limit max value of vcnt based on $
840 */
841 i = vcline + lineDOL() - lineDOT() + 1;
842 if (i < vcnt)
843 vcnt = i;
844
845 /*
846 * Dirty and repaint.
847 */
848 vdirty(0, LINES);
849 vrepaint(cursor);
850 }
851
852 /*
853 * If in visual, put back the echo area
854 * if it was clobberred.
855 */
856 if (state == VISUAL) {
857 int sdc = destcol, sdl = destline;
858
859 splitw++;
860 vigoto(WECHO, 0);
861 for (i = 0; i < TUBECOLS - 1; i++) {
862 if (esave[i] == 0)
863 break;
864 vputchar(esave[i]);
865 }
866 splitw = 0;
867 vgoto(sdl, sdc);
868 }
869 continue;
870
871 /*
872 * u undo the last changing command.
873 */
874 case 'u':
875 vundo();
876 continue;
877
878 /*
879 * U restore current line to initial state.
880 */
881 case 'U':
882 vUndo();
883 continue;
884
885fonfon:
886 beep();
887 continue;
888 }
889
890 /*
891 * Rest of commands are decoded by the operate
892 * routine.
893 */
894 operate(c, cnt);
895 }
896}
897
898/*
899 * Grab the word after the cursor so we can look for it as a tag.
900 */
901grabtag()
902{
903 register char *cp, *dp;
904
905 cp = vpastwh(cursor);
906 if (*cp) {
907 dp = lasttag;
908 do {
909 if (dp < &lasttag[sizeof lasttag - 2])
910 *dp++ = *cp;
911 cp++;
912 } while (isalpha(*cp) || isdigit(*cp) || *cp == '_');
913 *dp++ = 0;
914 }
915}
916
917/*
918 * Before appending lines, set up addr1 and
919 * the command mode undo information.
920 */
921prepapp()
922{
923
924 addr1 = dot + 1;
925 deletenone();
926 appendnone();
927}
928
929/*
930 * Execute function f with the address bounds addr1
931 * and addr2 surrounding cnt lines starting at dot.
932 */
933vremote(cnt, f, arg)
934 int cnt, (*f)(), arg;
935{
936 register int oing = inglobal;
937
938 addr1 = dot;
939 addr2 = dot + cnt - 1;
940 undap1 = undap2 = dot;
941 inglobal = 0;
942 (*f)(arg);
943 inglobal = oing;
944 vundkind = VMANY;
945 vmcurs = 0;
946}
947
948/*
949 * Save the current contents of linebuf, if it has changed.
950 */
951vsave()
952{
953 char temp[LBSIZE];
954
955 CP(temp, linebuf);
956 if (vundkind == VCHNG || vundkind == VCAPU) {
957 /*
958 * If the undo state is saved in the temporary buffer
959 * vutmp, then we sync this into the temp file so that
960 * we will be able to undo even after we have moved off
961 * the line. It would be possible to associate a line
962 * with vutmp but we assume that vutmp is only associated
963 * with line dot (e.g. in case ':') above, so beware.
964 */
965 prepapp();
966 strcLIN(vutmp);
967 putmark(dot);
968 vremote(1, yank, 0);
969 vundkind = VMCHNG;
970 notecnt = 0;
971 undkind = UNDCHANGE;
972 }
973 /*
974 * Get the line out of the temp file and do nothing if it hasn't
975 * changed. This may seem like a loss, but the line will
976 * almost always be in a read buffer so this may well avoid disk i/o.
977 */
978 getDOT();
979 if (strcmp(linebuf, temp) == 0)
980 return;
981 strcLIN(temp);
982 putmark(dot);
983}
984
985#undef forbid
986#define forbid(a) if (a) { beep(); return; }
987
988/*
989 * Do a z operation.
990 * Code here is rather long, and very uninteresting.
991 */
992vzop(hadcnt, cnt, c)
993 bool hadcnt;
994 int cnt;
995 register int c;
996{
997 register line *addr;
998
999 if (state != VISUAL) {
1000 /*
1001 * Z from open; always like a z=.
1002 * This code is a mess and should be cleaned up.
1003 */
1004 vmoveitup(1);
1005 vgoto(outline, 0);
1006 ostop(normf);
1007 setoutt();
1008 addr2 = dot;
1009 vclear();
1010 destline = WECHO;
1011 zop2(Xhadcnt ? Xcnt : value(WINDOW) - 1, '=');
1012 if (state == CRTOPEN)
1013 putnl();
1014 putNFL();
1015 termreset();
1016 Outchar = vputchar;
1017 ignore(ostart());
1018 vcnt = 0;
1019 outline = destline = 0;
1020 vjumpto(dot, cursor, 0);
1021 return;
1022 }
1023 if (hadcnt) {
1024 addr = zero + cnt;
1025 if (addr < one)
1026 addr = one;
1027 if (addr > dol)
1028 addr = dol;
1029 markit(addr);
1030 } else
1031 switch (c) {
1032
1033 case '+':
1034 addr = dot + vcnt - vcline;
1035 break;
1036
1037 case '^':
1038 addr = dot - vcline - 1;
1039 forbid (addr < one);
1040 c = '-';
1041 break;
1042
1043 default:
1044 addr = dot;
1045 break;
1046 }
1047 switch (c) {
1048
1049 case '.':
1050 case '-':
1051 break;
1052
1053 case '^':
1054 forbid (addr <= one);
1055 break;
1056
1057 case '+':
1058 forbid (addr >= dol);
1059 /* fall into ... */
1060
1061 case CR:
1062 case NL:
1063 c = CR;
1064 break;
1065
1066 default:
1067 beep();
1068 return;
1069 }
1070 vmoving = 0;
1071 vjumpto(addr, NOSTR, c);
1072}