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