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