|
|
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: #define blank() isspace(wcursor[0])
7: #define forbid(a) if (a) goto errlab;
8:
9: char vscandir[2] = { '/', 0 };
10:
11: /*
12: * Decode an operator/operand type command.
13: * Eventually we switch to an operator subroutine in ex_vops.c.
14: * The work here is setting up a function variable to point
15: * to the routine we want, and manipulation of the variables
16: * wcursor and wdot, which mark the other end of the affected
17: * area. If wdot is zero, then the current line is the other end,
18: * and if wcursor is zero, then the first non-blank location of the
19: * other line is implied.
20: */
21: operate(c, cnt)
22: register int c, cnt;
23: {
24: register int i;
25: int (*moveop)(), (*deleteop)();
26: register int (*opf)();
27: bool subop = 0;
28: char *oglobp, *ocurs;
29: register line *addr;
30: static char lastFKND, lastFCHR;
31: char d;
32:
33: moveop = vmove, deleteop = vdelete;
34: wcursor = cursor;
35: wdot = NOLINE;
36: notecnt = 0;
37: dir = 1;
38: switch (c) {
39:
40: /*
41: * d delete operator.
42: */
43: case 'd':
44: moveop = vdelete;
45: deleteop = beep;
46: break;
47:
48: /*
49: * s substitute characters, like c\040, i.e. change space.
50: */
51: case 's':
52: ungetkey(' ');
53: subop++;
54: /* fall into ... */
55:
56: /*
57: * c Change operator.
58: */
59: case 'c':
60: if (c == 'c' && workcmd[0] == 'C' || workcmd[0] == 'S')
61: subop++;
62: moveop = vchange;
63: deleteop = beep;
64: break;
65:
66: /*
67: * ! Filter through a UNIX command.
68: */
69: case '!':
70: moveop = vfilter;
71: deleteop = beep;
72: break;
73:
74: /*
75: * y Yank operator. Place specified text so that it
76: * can be put back with p/P. Also yanks to named buffers.
77: */
78: case 'y':
79: moveop = vyankit;
80: deleteop = beep;
81: break;
82:
83: /*
84: * = Reformat operator (for LISP).
85: */
86: #ifdef LISPCODE
87: case '=':
88: forbid(!value(LISP));
89: /* fall into ... */
90: #endif
91:
92: /*
93: * > Right shift operator.
94: * < Left shift operator.
95: */
96: case '<':
97: case '>':
98: moveop = vshftop;
99: deleteop = beep;
100: break;
101:
102: /*
103: * r Replace character under cursor with single following
104: * character.
105: */
106: case 'r':
107: vrep(cnt);
108: return;
109:
110: default:
111: goto nocount;
112: }
113: /*
114: * Had an operator, so accept another count.
115: * Multiply counts together.
116: */
117: if (isdigit(peekkey()) && peekkey() != '0') {
118: cnt *= vgetcnt();
119: Xcnt = cnt;
120: forbid (cnt <= 0);
121: }
122:
123: /*
124: * Get next character, mapping it and saving as
125: * part of command for repeat.
126: */
127: c = map(getesc(),arrows);
128: if (c == 0)
129: return;
130: if (!subop)
131: *lastcp++ = c;
132: nocount:
133: opf = moveop;
134: switch (c) {
135:
136: /*
137: * b Back up a word.
138: * B Back up a word, liberal definition.
139: */
140: case 'b':
141: case 'B':
142: dir = -1;
143: /* fall into ... */
144:
145: /*
146: * w Forward a word.
147: * W Forward a word, liberal definition.
148: */
149: case 'W':
150: case 'w':
151: wdkind = c & ' ';
152: forbid(lfind(2, cnt, opf, 0) < 0);
153: vmoving = 0;
154: break;
155:
156: /*
157: * E to end of following blank/nonblank word
158: */
159: case 'E':
160: wdkind = 0;
161: goto ein;
162:
163: /*
164: * e To end of following word.
165: */
166: case 'e':
167: wdkind = 1;
168: ein:
169: forbid(lfind(3, cnt - 1, opf, 0) < 0);
170: vmoving = 0;
171: break;
172:
173: /*
174: * ( Back an s-expression.
175: */
176: case '(':
177: dir = -1;
178: /* fall into... */
179:
180: /*
181: * ) Forward an s-expression.
182: */
183: case ')':
184: forbid(lfind(0, cnt, opf, (line *) 0) < 0);
185: markDOT();
186: break;
187:
188: /*
189: * { Back an s-expression, but don't stop on atoms.
190: * In text mode, a paragraph. For C, a balanced set
191: * of {}'s.
192: */
193: case '{':
194: dir = -1;
195: /* fall into... */
196:
197: /*
198: * } Forward an s-expression, but don't stop on atoms.
199: * In text mode, back paragraph. For C, back a balanced
200: * set of {}'s.
201: */
202: case '}':
203: forbid(lfind(1, cnt, opf, (line *) 0) < 0);
204: markDOT();
205: break;
206:
207: /*
208: * % To matching () or {}. If not at ( or { scan for
209: * first such after cursor on this line.
210: */
211: case '%':
212: vsave();
213: i = lmatchp((line *) 0);
214: getDOT();
215: forbid(!i);
216: if (opf != vmove)
217: if (dir > 0)
218: wcursor++;
219: else
220: cursor++;
221: else
222: markDOT();
223: vmoving = 0;
224: break;
225:
226: /*
227: * [ Back to beginning of defun, i.e. an ( in column 1.
228: * For text, back to a section macro.
229: * For C, back to a { in column 1 (~~ beg of function.)
230: */
231: case '[':
232: dir = -1;
233: /* fall into ... */
234:
235: /*
236: * ] Forward to next defun, i.e. a ( in column 1.
237: * For text, forward section.
238: * For C, forward to a } in column 1 (if delete or such)
239: * or if a move to a { in column 1.
240: */
241: case ']':
242: if (!vglobp)
243: forbid(getkey() != c);
244: if (Xhadcnt)
245: vsetsiz(Xcnt);
246: vsave();
247: i = lbrack(c, opf);
248: getDOT();
249: forbid(!i);
250: markDOT();
251: if (ospeed > B300)
252: hold |= HOLDWIG;
253: break;
254:
255: /*
256: * , Invert last find with f F t or T, like inverse
257: * of ;.
258: */
259: case ',':
260: forbid (lastFKND == 0);
261: c = isupper(lastFKND) ? tolower(lastFKND) : toupper(lastFKND);
262: ungetkey(lastFCHR);
263: if (vglobp == 0)
264: vglobp = "";
265: subop++;
266: goto nocount;
267:
268: /*
269: * 0 To beginning of real line.
270: */
271: case '0':
272: wcursor = linebuf;
273: vmoving = 0;
274: break;
275:
276: /*
277: * ; Repeat last find with f F t or T.
278: */
279: case ';':
280: forbid (lastFKND == 0);
281: c = lastFKND;
282: ungetkey(lastFCHR);
283: subop++;
284: goto nocount;
285:
286: /*
287: * F Find single character before cursor in current line.
288: * T Like F, but stops before character.
289: */
290: case 'F': /* inverted find */
291: case 'T':
292: dir = -1;
293: /* fall into ... */
294:
295: /*
296: * f Find single character following cursor in current line.
297: * t Like f, but stope before character.
298: */
299: case 'f': /* find */
300: case 't':
301: i = getesc();
302: if (i == 0)
303: return;
304: if (!subop)
305: *lastcp++ = i;
306: if (vglobp == 0)
307: lastFKND = c, lastFCHR = i;
308: for (; cnt > 0; cnt--)
309: forbid (find(i) == 0);
310: vmoving = 0;
311: switch (c) {
312:
313: case 'T':
314: wcursor++;
315: break;
316:
317: case 't':
318: wcursor--;
319: case 'f':
320: fixup:
321: if (moveop != vmove)
322: wcursor++;
323: break;
324: }
325: break;
326:
327: /*
328: * | Find specified print column in current line.
329: */
330: case '|':
331: if (Pline == numbline)
332: cnt += 8;
333: vmovcol = cnt;
334: vmoving = 1;
335: wcursor = vfindcol(cnt);
336: break;
337:
338: /*
339: * ^ To beginning of non-white space on line.
340: */
341: case '^':
342: wcursor = vskipwh(linebuf);
343: vmoving = 0;
344: break;
345:
346: /*
347: * $ To end of line.
348: */
349: case '$':
350: if (opf == vmove) {
351: vmoving = 1;
352: vmovcol = 20000;
353: } else
354: vmoving = 0;
355: if (cnt > 1) {
356: if (opf == vmove) {
357: wcursor = 0;
358: cnt--;
359: } else
360: wcursor = linebuf;
361: /* This is wrong at EOF */
362: wdot = dot + cnt;
363: break;
364: }
365: if (linebuf[0]) {
366: wcursor = strend(linebuf) - 1;
367: goto fixup;
368: }
369: wcursor = linebuf;
370: break;
371:
372: /*
373: * h Back a character.
374: * ^H Back a character.
375: */
376: case 'h':
377: case CTRL(h):
378: dir = -1;
379: /* fall into ... */
380:
381: /*
382: * space Forward a character.
383: */
384: case 'l':
385: case ' ':
386: forbid (margin() || opf == vmove && edge());
387: while (cnt > 0 && !margin())
388: wcursor += dir, cnt--;
389: if (margin() && opf == vmove || wcursor < linebuf)
390: wcursor -= dir;
391: vmoving = 0;
392: break;
393:
394: /*
395: * D Delete to end of line, short for d$.
396: */
397: case 'D':
398: cnt = INF;
399: goto deleteit;
400:
401: /*
402: * X Delete character before cursor.
403: */
404: case 'X':
405: dir = -1;
406: /* fall into ... */
407: deleteit:
408: /*
409: * x Delete character at cursor, leaving cursor where it is.
410: */
411: case 'x':
412: if (margin())
413: goto errlab;
414: while (cnt > 0 && !margin())
415: wcursor += dir, cnt--;
416: opf = deleteop;
417: vmoving = 0;
418: break;
419:
420: default:
421: /*
422: * Stuttered operators are equivalent to the operator on
423: * a line, thus turn dd into d_.
424: */
425: if (opf == vmove || c != workcmd[0]) {
426: errlab:
427: beep();
428: vmacp = 0;
429: return;
430: }
431: /* fall into ... */
432:
433: /*
434: * _ Target for a line or group of lines.
435: * Stuttering is more convenient; this is mostly
436: * for aesthetics.
437: */
438: case '_':
439: wdot = dot + cnt - 1;
440: vmoving = 0;
441: wcursor = 0;
442: break;
443:
444: /*
445: * H To first, home line on screen.
446: * Count is for count'th line rather than first.
447: */
448: case 'H':
449: wdot = (dot - vcline) + cnt - 1;
450: if (opf == vmove)
451: markit(wdot);
452: vmoving = 0;
453: wcursor = 0;
454: break;
455:
456: /*
457: * - Backwards lines, to first non-white character.
458: */
459: case '-':
460: wdot = dot - cnt;
461: vmoving = 0;
462: wcursor = 0;
463: break;
464:
465: /*
466: * ^P To previous line same column. Ridiculous on the
467: * console of the VAX since it puts console in LSI mode.
468: */
469: case 'k':
470: case CTRL(p):
471: wdot = dot - cnt;
472: if (vmoving == 0)
473: vmoving = 1, vmovcol = column(cursor);
474: wcursor = 0;
475: break;
476:
477: /*
478: * L To last line on screen, or count'th line from the
479: * bottom.
480: */
481: case 'L':
482: wdot = dot + vcnt - vcline - cnt;
483: if (opf == vmove)
484: markit(wdot);
485: vmoving = 0;
486: wcursor = 0;
487: break;
488:
489: /*
490: * M To the middle of the screen.
491: */
492: case 'M':
493: wdot = dot + ((vcnt + 1) / 2) - vcline - 1;
494: if (opf == vmove)
495: markit(wdot);
496: vmoving = 0;
497: wcursor = 0;
498: break;
499:
500: /*
501: * + Forward line, to first non-white.
502: *
503: * CR Convenient synonym for +.
504: */
505: case '+':
506: case CR:
507: wdot = dot + cnt;
508: vmoving = 0;
509: wcursor = 0;
510: break;
511:
512: /*
513: * ^N To next line, same column if possible.
514: *
515: * LF Linefeed is a convenient synonym for ^N.
516: */
517: case CTRL(n):
518: case 'j':
519: case NL:
520: wdot = dot + cnt;
521: if (vmoving == 0)
522: vmoving = 1, vmovcol = column(cursor);
523: wcursor = 0;
524: break;
525:
526: /*
527: * n Search to next match of current pattern.
528: */
529: case 'n':
530: vglobp = vscandir;
531: c = *vglobp++;
532: goto nocount;
533:
534: /*
535: * N Like n but in reverse direction.
536: */
537: case 'N':
538: vglobp = vscandir[0] == '/' ? "?" : "/";
539: c = *vglobp++;
540: goto nocount;
541:
542: /*
543: * ' Return to line specified by following mark,
544: * first white position on line.
545: *
546: * ` Return to marked line at remembered column.
547: */
548: case '\'':
549: case '`':
550: d = c;
551: c = getesc();
552: if (c == 0)
553: return;
554: c = markreg(c);
555: forbid (c == 0);
556: wdot = getmark(c);
557: forbid (wdot == NOLINE);
558: if (Xhadcnt)
559: vsetsiz(Xcnt);
560: vmoving = 0;
561: wcursor = d == '`' ? ncols[c - 'a'] : 0;
562: if (opf == vmove && (wdot != dot || (d == '`' && wcursor != cursor)))
563: markDOT();
564: if (wcursor) {
565: vsave();
566: getline(*wdot);
567: if (wcursor > strend(linebuf))
568: wcursor = 0;
569: getDOT();
570: }
571: if (ospeed > B300)
572: hold |= HOLDWIG;
573: break;
574:
575: /*
576: * G Goto count'th line, or last line if no count
577: * given.
578: */
579: case 'G':
580: if (!Xhadcnt)
581: cnt = lineDOL();
582: wdot = zero + cnt;
583: forbid (wdot < one || wdot > dol);
584: if (opf == vmove)
585: markit(wdot);
586: vmoving = 0;
587: wcursor = 0;
588: break;
589:
590: /*
591: * / Scan forward for following re.
592: * ? Scan backward for following re.
593: */
594: case '/':
595: case '?':
596: if (Xhadcnt)
597: vsetsiz(Xcnt);
598: vsave();
599: ocurs = cursor;
600: wcursor = 0;
601: if (readecho(c))
602: return;
603: if (!vglobp)
604: vscandir[0] = genbuf[0];
605: oglobp = globp; CP(vutmp, genbuf); globp = vutmp;
606: d = peekc; ungetchar(0); fixech();
607: CATCH
608: #ifndef CBREAK
609: /*
610: * Lose typeahead (ick).
611: */
612: vcook();
613: #endif
614: addr = address(cursor);
615: #ifndef CBREAK
616: vraw();
617: #endif
618: ONERR
619: #ifndef CBREAK
620: vraw();
621: #endif
622: globp = oglobp;
623: ungetchar(d);
624: splitw = 0;
625: vclean();
626: vjumpto(dot, ocurs, 0);
627: return;
628: ENDCATCH
629: if (globp == 0)
630: globp = "";
631: else if (peekc)
632: --globp;
633: ungetchar(d);
634: c = 0;
635: if (*globp == 'z')
636: globp++, c = '\n';
637: if (any(*globp, "^+-."))
638: c = *globp++;
639: i = 0;
640: while (isdigit(*globp))
641: i = i * 10 + *globp++ - '0';
642: if (*globp)
643: c = *globp++;
644: globp = oglobp;
645: splitw = 0;
646: vmoving = 0;
647: wcursor = loc1;
648: if (i != 0)
649: vsetsiz(i);
650: if (opf == vmove) {
651: if (state == ONEOPEN || state == HARDOPEN)
652: outline = destline = WBOT;
653: if (addr != dot || loc1 != cursor)
654: markDOT();
655: if (loc1 > linebuf && *loc1 == 0)
656: loc1--;
657: if (c)
658: vjumpto(addr, loc1, c);
659: else {
660: vmoving = 0;
661: if (loc1) {
662: vmoving++;
663: vmovcol = column(loc1);
664: }
665: getDOT();
666: if (state == CRTOPEN && addr != dot)
667: vup1();
668: vupdown(addr - dot, NOSTR);
669: }
670: return;
671: }
672: lastcp[-1] = 'n';
673: getDOT();
674: wdot = addr;
675: break;
676: }
677: /*
678: * Apply.
679: */
680: if (vreg && wdot == 0)
681: wdot = dot;
682: (*opf)(c);
683: wdot = NOLINE;
684: }
685:
686: /*
687: * Find single character c, in direction dir from cursor.
688: */
689: find(c)
690: char c;
691: {
692:
693: for(;;) {
694: if (edge())
695: return (0);
696: wcursor += dir;
697: if (*wcursor == c)
698: return (1);
699: }
700: }
701:
702: /*
703: * Do a word motion with operator op, and cnt more words
704: * to go after this.
705: */
706: word(op, cnt)
707: register int (*op)();
708: int cnt;
709: {
710: register int which;
711: register char *iwc;
712: register line *iwdot = wdot;
713:
714: if (dir == 1) {
715: iwc = wcursor;
716: which = wordch(wcursor);
717: while (wordof(which, wcursor)) {
718: if (cnt == 1 && op != vmove && wcursor[1] == 0) {
719: wcursor++;
720: break;
721: }
722: if (!lnext())
723: return (0);
724: if (wcursor == linebuf)
725: break;
726: }
727: /* Unless last segment of a change skip blanks */
728: if (op != vchange || cnt > 1)
729: while (!margin() && blank())
730: wcursor++;
731: else
732: if (wcursor == iwc && iwdot == wdot && *iwc)
733: wcursor++;
734: if (op == vmove && margin())
735: wcursor--;
736: } else {
737: if (!lnext())
738: return (0);
739: while (blank())
740: if (!lnext())
741: return (0);
742: if (!margin()) {
743: which = wordch(wcursor);
744: while (!margin() && wordof(which, wcursor))
745: wcursor--;
746: }
747: if (wcursor < linebuf || !wordof(which, wcursor))
748: wcursor++;
749: }
750: return (1);
751: }
752:
753: /*
754: * To end of word, with operator op and cnt more motions
755: * remaining after this.
756: */
757: eend(op)
758: register int (*op)();
759: {
760: register int which;
761:
762: if (!lnext())
763: return;
764: while (blank())
765: if (!lnext())
766: return;
767: which = wordch(wcursor);
768: while (wordof(which, wcursor)) {
769: if (wcursor[1] == 0) {
770: wcursor++;
771: break;
772: }
773: if (!lnext())
774: return;
775: }
776: if (op != vchange && op != vdelete && wcursor > linebuf)
777: wcursor--;
778: }
779:
780: /*
781: * Wordof tells whether the character at *wc is in a word of
782: * kind which (blank/nonblank words are 0, conservative words 1).
783: */
784: wordof(which, wc)
785: char which;
786: register char *wc;
787: {
788:
789: if (isspace(*wc))
790: return (0);
791: return (!wdkind || wordch(wc) == which);
792: }
793:
794: /*
795: * Wordch tells whether character at *wc is a word character
796: * i.e. an alfa, digit, or underscore.
797: */
798: wordch(wc)
799: char *wc;
800: {
801: register int c;
802:
803: c = wc[0];
804: return (isalpha(c) || isdigit(c) || c == '_');
805: }
806:
807: /*
808: * Edge tells when we hit the last character in the current line.
809: */
810: edge()
811: {
812:
813: if (linebuf[0] == 0)
814: return (1);
815: if (dir == 1)
816: return (wcursor[1] == 0);
817: else
818: return (wcursor == linebuf);
819: }
820:
821: /*
822: * Margin tells us when we have fallen off the end of the line.
823: */
824: margin()
825: {
826:
827: return (wcursor < linebuf || wcursor[0] == 0);
828: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.