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