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