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