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