POSIX 1003.2B/D9 symbolic links
[unix-history] / usr / src / usr.bin / ex / ex_vmain.c
CommitLineData
2791ff57 1/*-
eb035710
KB
2 * Copyright (c) 1980, 1993
3 * The Regents of the University of California. All rights reserved.
2791ff57
KB
4 *
5 * %sccs.include.proprietary.c%
19d73a0e
DF
6 */
7
8#ifndef lint
eb035710 9static char sccsid[] = "@(#)ex_vmain.c 8.1 (Berkeley) %G%";
2791ff57 10#endif /* not lint */
19d73a0e 11
2281ca6a
MH
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
26vmain()
27{
28 register int c, cnt, i;
29 char esave[TUBECOLS];
30 char *oglobp;
b8c3ed58 31 short d;
2281ca6a 32 line *addr;
887e3e0d
MH
33 int ind, nlput;
34 int shouldpo = 0;
2281ca6a
MH
35 int onumber, olist, (*OPline)(), (*OPutchar)();
36
d266c416
MH
37 vch_mac = VC_NOTINMAC;
38
2281ca6a
MH
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;
04379bab 73 seenprompt = 1;
2281ca6a
MH
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/*
5a6c967e 83 if (LINE(0) < ex_ZERO) {
2281ca6a
MH
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 (;;) {
104looptop:
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 }
126reread:
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();
d266c416 137 maphopcnt = 0;
2281ca6a
MH
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 }
04379bab
MH
159 if (!value(REMAP)) {
160 c = op;
887e3e0d 161 break;
04379bab 162 }
d266c416
MH
163 if (++maphopcnt > 256)
164 error("Infinite macro loop");
2281ca6a
MH
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 */
2281ca6a
MH
185
186 /*
187 * ^R Retype screen, getting rid of @ lines.
188 * If in open, equivalent to ^L.
887e3e0d
MH
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.
2281ca6a 195 */
65bacefd
KB
196 case CTRL('l'):
197 case CTRL('r'):
198 if (c == CTRL('l') || (KR && *KR==CTRL('l'))) {
887e3e0d
MH
199 vclear();
200 vdirty(0, vcnt);
201 }
2281ca6a
MH
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 '@':
887e3e0d
MH
240 c = getesc();
241 if (c == 0)
242 continue;
2281ca6a
MH
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));
887e3e0d 254 macpush(tmpbuf, 1);
2281ca6a
MH
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 */
65bacefd 304 case CTRL('u'):
2281ca6a 305 if (hadcnt)
5a6c967e
CH
306 ex_vSCROLL = cnt;
307 cnt = ex_vSCROLL;
2281ca6a
MH
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 */
65bacefd 320 case CTRL('d'):
f0f2d980
MH
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
2281ca6a 325 if (hadcnt)
5a6c967e
CH
326 ex_vSCROLL = cnt;
327 cnt = ex_vSCROLL;
2281ca6a
MH
328 if (state == VISUAL)
329 ind = vcnt - vcline - 1, cnt += ind;
330 else
331 ind = 0;
332 vmoving = 0;
333 vdown(cnt, ind, 1);
f0f2d980
MH
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
2281ca6a 338 vnline(NOSTR);
f0f2d980
MH
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
2281ca6a
MH
343 continue;
344
345 /*
346 * ^E Glitch the screen down (one) line.
347 * Cursor left on same line in file.
348 */
65bacefd 349 case CTRL('e'):
2281ca6a
MH
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 */
65bacefd 364 case CTRL('y'):
2281ca6a
MH
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.
d266c416 411 * Count repeats.
2281ca6a 412 */
65bacefd 413 case CTRL('f'):
2281ca6a 414 vsave();
2281ca6a 415 if (vcnt > 2) {
04379bab
MH
416 addr = dot + (vcnt - vcline) - 2 + (cnt-1)*basWLINES;
417 forbid(addr > dol);
418 dot = addr;
2281ca6a
MH
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 */
65bacefd 428 case CTRL('b'):
2281ca6a 429 vsave();
2281ca6a 430 if (one + vcline != dot && vcnt > 2) {
833b2748 431 addr = dot - vcline + 2 - (cnt-1)*basWLINES;
04379bab
MH
432 forbid (addr <= zero);
433 dot = addr;
2281ca6a
MH
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();
d266c416 486 vmacchng(1);
2281ca6a
MH
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':
d266c416 521 vmacchng(1);
04379bab 522 voOpen(c, cnt);
2281ca6a
MH
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];
d266c416 541 setLAST();
2281ca6a
MH
542 mbuf[0] = 'r';
543 mbuf[1] = *cursor;
fa925dac 544 mbuf[2] = cursor[1]==0 ? 0 : 'l';
2281ca6a
MH
545 mbuf[3] = 0;
546 if (isalpha(mbuf[1]))
547 mbuf[1] ^= ' '; /* toggle the case */
887e3e0d 548 macpush(mbuf, 1);
2281ca6a
MH
549 }
550 continue;
551
552
553 /*
554 * A Append at end of line, short for $a.
555 */
556 case 'A':
557 operate('$', 1);
558appnd:
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)
5a6c967e 569 ex_putchar(*cursor);
2281ca6a
MH
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':
602insrt:
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 */
d266c416 610 vmacchng(1);
2281ca6a
MH
611 setLAST();
612 vcursat(cursor);
613 prepapp();
614 vnoapp();
615 doomed = c == 'R' ? 10000 : 0;
887e3e0d
MH
616 if(FIXUNDO)
617 vundkind = VCHNG;
618 vmoving = 0;
2281ca6a
MH
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
887e3e0d 656#ifdef notdef
2281ca6a
MH
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 }
887e3e0d 672#endif
2281ca6a
MH
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();
d266c416
MH
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 }
2281ca6a
MH
692 return;
693
887e3e0d
MH
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
2281ca6a
MH
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;
d266c416 719#ifdef notdef
887e3e0d 720 forbid (!vreg && value(UNDOMACRO) && inopen < 0);
d266c416 721#endif
2281ca6a
MH
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);
d266c416
MH
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);
2281ca6a 744 vsave();
d266c416 745 vmacchng(1);
2281ca6a
MH
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 */
887e3e0d 779 addr = dol; /* old dol */
2281ca6a
MH
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;
887e3e0d 791 nlput = dol - addr + 1;
2281ca6a
MH
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 */
887e3e0d
MH
798 if (FIXUNDO)
799 undap1++, undap2++;
2281ca6a 800 vcline++;
887e3e0d 801 nlput--;
2281ca6a 802
887e3e0d
MH
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);
2281ca6a
MH
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 }
827pfixup:
828 vrepaint(cursor);
829 vfixcurs();
830 continue;
831
832 /*
5e950816
MH
833 * ^^ Return to previous file.
834 * Like a :e #, and thus can be used after a
2281ca6a 835 * "No Write" diagnostic.
2281ca6a 836 */
65bacefd 837 case CTRL('^'):
10499a70 838 forbid (hadcnt);
2281ca6a
MH
839 vsave();
840 ckaw();
841 oglobp = globp;
44232d5b
MH
842 if (value(AUTOWRITE))
843 globp = "e! #";
844 else
845 globp = "e #";
2281ca6a
MH
846 goto gogo;
847
848 /*
849 * ^] Takes word after cursor as tag, and then does
850 * tag command. Read ``go right to''.
851 */
65bacefd 852 case CTRL(']'):
2281ca6a
MH
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 */
65bacefd 872 case CTRL('g'):
2281ca6a
MH
873 oglobp = globp;
874 globp = "file";
875gogo:
876 addr = dot;
877 vsave();
878 goto doinit;
879
bdbbb330 880#ifdef SIGTSTP
d266c416
MH
881 /*
882 * ^Z: suspend editor session and temporarily return
04379bab
MH
883 * to shell. Only works with Berkeley/IIASA process
884 * control in kernel.
d266c416 885 */
65bacefd 886 case CTRL('z'):
db4f89ce 887 forbid(dosusp == 0);
d266c416
MH
888 vsave();
889 oglobp = globp;
890 globp = "stop";
891 goto gogo;
892#endif
893
2281ca6a
MH
894 /*
895 * : Read a command from the echo area and
896 * execute it in command mode.
897 */
898 case ':':
10499a70 899 forbid (hadcnt);
2281ca6a
MH
900 vsave();
901 i = tchng;
902 addr = dot;
903 if (readecho(c)) {
904 esave[0] = 0;
905 goto fixup;
906 }
d266c416 907 getDOT();
2281ca6a
MH
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;
913doinit:
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);
887e3e0d
MH
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 }
2281ca6a
MH
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;
5a6c967e 940 OPutchar = Put_char;
2281ca6a
MH
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;
5a6c967e 958 Put_char = OPutchar;
2281ca6a
MH
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();
5a6c967e 968 ex_sync();
2281ca6a
MH
969 }
970 splitw = 0;
971
972 /*
973 * Special case: did list/number options change?
974 */
975 if (onumber != value(NUMBER))
5a6c967e 976 ignorf(setnumb(value(NUMBER)));
2281ca6a 977 if (olist != value(LIST))
5a6c967e 978 ignorf(setlist(value(LIST)));
2281ca6a
MH
979
980fixup:
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 */
887e3e0d 993 if (FIXUNDO && tchng && tchng != i)
2281ca6a
MH
994 vundkind = VMANY, cursor = 0;
995
996 /*
997 * If we are about to do another :, hold off
998 * updating of screen.
999 */
5a6c967e 1000 if (vcnt < 0 && Peek_key == ':') {
2281ca6a 1001 getDOT();
887e3e0d 1002 shouldpo = 1;
2281ca6a
MH
1003 continue;
1004 }
887e3e0d 1005 shouldpo = 0;
2281ca6a
MH
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();
887e3e0d 1053 else if (state == CRTOPEN) {
2281ca6a 1054 vcnt = 0;
887e3e0d 1055 }
2281ca6a
MH
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':
d266c416 1095 vundo(1);
2281ca6a
MH
1096 continue;
1097
1098 /*
1099 * U restore current line to initial state.
1100 */
1101 case 'U':
5a6c967e 1102 ex_vUndo();
2281ca6a
MH
1103 continue;
1104
1105fonfon:
1106 beep();
1107 vmacp = 0;
44232d5b 1108 inopen = 1; /* might have been -1 */
2281ca6a
MH
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 */
1123grabtag()
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++;
01c761d0
PK
1134 } while (isalpha(*cp) || isdigit(*cp) || *cp == '_'
1135#ifdef LISPCODE
1136 || (value(LISP) && *cp == '-')
1137#endif LISPCODE
1138 );
2281ca6a
MH
1139 *dp++ = 0;
1140 }
1141}
1142
1143/*
1144 * Before appending lines, set up addr1 and
1145 * the command mode undo information.
1146 */
1147prepapp()
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 */
1160vremote(cnt, f, arg)
1161 int cnt, (*f)(), arg;
1162{
1163 register int oing = inglobal;
1164
1165 addr1 = dot;
1166 addr2 = dot + cnt - 1;
2281ca6a 1167 inglobal = 0;
887e3e0d
MH
1168 if (FIXUNDO)
1169 undap1 = undap2 = dot;
2281ca6a
MH
1170 (*f)(arg);
1171 inglobal = oing;
887e3e0d 1172 if (FIXUNDO)
2281ca6a
MH
1173 vundkind = VMANY;
1174 vmcurs = 0;
1175}
1176
1177/*
1178 * Save the current contents of linebuf, if it has changed.
1179 */
1180vsave()
1181{
1182 char temp[LBSIZE];
1183
1184 CP(temp, linebuf);
887e3e0d 1185 if (FIXUNDO && vundkind == VCHNG || vundkind == VCAPU) {
2281ca6a
MH
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 */
1221vzop(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}