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