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