|
|
1.1 root 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
8: static char *sccsid = "@(#)ex_vmain.c 7.7 (Berkeley) 6/7/85";
9: #endif not lint
10:
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:
25: vmain()
26: {
27: register int c, cnt, i;
28: char esave[TUBECOLS];
29: char *oglobp;
30: short d;
31: line *addr;
32: int ind, nlput;
33: int shouldpo = 0;
34: int onumber, olist, (*OPline)(), (*OPutchar)();
35:
36: vch_mac = VC_NOTINMAC;
37:
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;
72: seenprompt = 1;
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: /*
82: if (LINE(0) < ZERO) {
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 (;;) {
103: looptop:
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: }
125: reread:
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();
136: maphopcnt = 0;
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: }
158: if (!value(REMAP)) {
159: c = op;
160: break;
161: }
162: if (++maphopcnt > 256)
163: error("Infinite macro loop");
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: */
184:
185: /*
186: * ^R Retype screen, getting rid of @ lines.
187: * If in open, equivalent to ^L.
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.
194: */
195: case CTRL(l):
196: case CTRL(r):
197: if (c == CTRL(l) || (KR && *KR==CTRL(l))) {
198: vclear();
199: vdirty(0, vcnt);
200: }
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 '@':
239: c = getesc();
240: if (c == 0)
241: continue;
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));
253: macpush(tmpbuf, 1);
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: */
303: case CTRL(u):
304: if (hadcnt)
305: vSCROLL = cnt;
306: cnt = vSCROLL;
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: */
319: case CTRL(d):
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
324: if (hadcnt)
325: vSCROLL = cnt;
326: cnt = vSCROLL;
327: if (state == VISUAL)
328: ind = vcnt - vcline - 1, cnt += ind;
329: else
330: ind = 0;
331: vmoving = 0;
332: vdown(cnt, ind, 1);
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
337: vnline(NOSTR);
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
342: continue;
343:
344: /*
345: * ^E Glitch the screen down (one) line.
346: * Cursor left on same line in file.
347: */
348: case CTRL(e):
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: */
363: case CTRL(y):
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.
410: * Count repeats.
411: */
412: case CTRL(f):
413: vsave();
414: if (vcnt > 2) {
415: addr = dot + (vcnt - vcline) - 2 + (cnt-1)*basWLINES;
416: forbid(addr > dol);
417: dot = addr;
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: */
427: case CTRL(b):
428: vsave();
429: if (one + vcline != dot && vcnt > 2) {
430: addr = dot - vcline + 2 - (cnt-1)*basWLINES;
431: forbid (addr <= zero);
432: dot = addr;
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();
485: vmacchng(1);
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':
520: vmacchng(1);
521: voOpen(c, cnt);
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];
540: setLAST();
541: mbuf[0] = 'r';
542: mbuf[1] = *cursor;
543: mbuf[2] = cursor[1]==0 ? 0 : ' ';
544: mbuf[3] = 0;
545: if (isalpha(mbuf[1]))
546: mbuf[1] ^= ' '; /* toggle the case */
547: macpush(mbuf, 1);
548: }
549: continue;
550:
551:
552: /*
553: * A Append at end of line, short for $a.
554: */
555: case 'A':
556: operate('$', 1);
557: appnd:
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)
568: putchar(*cursor);
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':
601: insrt:
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: */
609: vmacchng(1);
610: setLAST();
611: vcursat(cursor);
612: prepapp();
613: vnoapp();
614: doomed = c == 'R' ? 10000 : 0;
615: if(FIXUNDO)
616: vundkind = VCHNG;
617: vmoving = 0;
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:
655: #ifdef notdef
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: }
671: #endif
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();
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: }
691: return;
692:
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:
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;
718: #ifdef notdef
719: forbid (!vreg && value(UNDOMACRO) && inopen < 0);
720: #endif
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);
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);
743: vsave();
744: vmacchng(1);
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: */
778: addr = dol; /* old dol */
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;
790: nlput = dol - addr + 1;
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: */
797: if (FIXUNDO)
798: undap1++, undap2++;
799: vcline++;
800: nlput--;
801:
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);
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: }
826: pfixup:
827: vrepaint(cursor);
828: vfixcurs();
829: continue;
830:
831: /*
832: * ^^ Return to previous file.
833: * Like a :e #, and thus can be used after a
834: * "No Write" diagnostic.
835: */
836: case CTRL(^):
837: forbid (hadcnt);
838: vsave();
839: ckaw();
840: oglobp = globp;
841: if (value(AUTOWRITE))
842: globp = "e! #";
843: else
844: globp = "e #";
845: goto gogo;
846:
847: /*
848: * ^] Takes word after cursor as tag, and then does
849: * tag command. Read ``go right to''.
850: */
851: case CTRL(]):
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: */
871: case CTRL(g):
872: oglobp = globp;
873: globp = "file";
874: gogo:
875: addr = dot;
876: vsave();
877: goto doinit;
878:
879: #ifdef SIGTSTP
880: /*
881: * ^Z: suspend editor session and temporarily return
882: * to shell. Only works with Berkeley/IIASA process
883: * control in kernel.
884: */
885: case CTRL(z):
886: forbid(dosusp == 0 || !ldisc);
887: vsave();
888: oglobp = globp;
889: globp = "stop";
890: goto gogo;
891: #endif
892:
893: /*
894: * : Read a command from the echo area and
895: * execute it in command mode.
896: */
897: case ':':
898: forbid (hadcnt);
899: vsave();
900: i = tchng;
901: addr = dot;
902: if (readecho(c)) {
903: esave[0] = 0;
904: goto fixup;
905: }
906: getDOT();
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;
912: doinit:
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);
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: }
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;
939: OPutchar = Putchar;
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;
957: Putchar = OPutchar;
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();
967: sync();
968: }
969: splitw = 0;
970:
971: /*
972: * Special case: did list/number options change?
973: */
974: if (onumber != value(NUMBER))
975: setnumb(value(NUMBER));
976: if (olist != value(LIST))
977: setlist(value(LIST));
978:
979: fixup:
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: */
992: if (FIXUNDO && tchng && tchng != i)
993: vundkind = VMANY, cursor = 0;
994:
995: /*
996: * If we are about to do another :, hold off
997: * updating of screen.
998: */
999: if (vcnt < 0 && Peekkey == ':') {
1000: getDOT();
1001: shouldpo = 1;
1002: continue;
1003: }
1004: shouldpo = 0;
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();
1052: else if (state == CRTOPEN) {
1053: vcnt = 0;
1054: }
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':
1094: vundo(1);
1095: continue;
1096:
1097: /*
1098: * U restore current line to initial state.
1099: */
1100: case 'U':
1101: vUndo();
1102: continue;
1103:
1104: fonfon:
1105: beep();
1106: vmacp = 0;
1107: inopen = 1; /* might have been -1 */
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: */
1122: grabtag()
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++;
1133: } while (isalpha(*cp) || isdigit(*cp) || *cp == '_'
1134: #ifdef LISPCODE
1135: || (value(LISP) && *cp == '-')
1136: #endif LISPCODE
1137: );
1138: *dp++ = 0;
1139: }
1140: }
1141:
1142: /*
1143: * Before appending lines, set up addr1 and
1144: * the command mode undo information.
1145: */
1146: prepapp()
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: */
1159: vremote(cnt, f, arg)
1160: int cnt, (*f)(), arg;
1161: {
1162: register int oing = inglobal;
1163:
1164: addr1 = dot;
1165: addr2 = dot + cnt - 1;
1166: inglobal = 0;
1167: if (FIXUNDO)
1168: undap1 = undap2 = dot;
1169: (*f)(arg);
1170: inglobal = oing;
1171: if (FIXUNDO)
1172: vundkind = VMANY;
1173: vmcurs = 0;
1174: }
1175:
1176: /*
1177: * Save the current contents of linebuf, if it has changed.
1178: */
1179: vsave()
1180: {
1181: char temp[LBSIZE];
1182:
1183: CP(temp, linebuf);
1184: if (FIXUNDO && vundkind == VCHNG || vundkind == VCAPU) {
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: */
1220: vzop(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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.