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