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