remove FLOCK define
[unix-history] / usr / src / usr.bin / ex / ex_voper.c
CommitLineData
19d73a0e
DF
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
5a6c967e 8static char *sccsid = "@(#)ex_voper.c 7.5 (Berkeley) %G%";
19d73a0e
DF
9#endif not lint
10
e4283c49
MH
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
18char 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 */
30operate(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;
887e3e0d 39 line *odot;
e4283c49 40 static char lastFKND, lastFCHR;
b8c3ed58 41 short d;
e4283c49
MH
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':
d266c416 117 vmacchng(1);
04379bab 118 vrep(cnt);
e4283c49
MH
119 return;
120
121 default:
122 goto nocount;
123 }
d266c416 124 vmacchng(1);
e4283c49
MH
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;
144nocount:
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 & ' ';
5a6c967e 164 forbid(lfind(2, cnt, opf, (line *) 0) < 0);
e4283c49
MH
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;
180ein:
5a6c967e 181 forbid(lfind(3, cnt - 1, opf, (line *) 0) < 0);
e4283c49
MH
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);
f0f2d980
MH
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
e4283c49
MH
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);
10499a70 260 forbid (Xhadcnt);
e4283c49
MH
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);
887e3e0d 277 i = lastFCHR;
e4283c49
MH
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;
887e3e0d 297 i = lastFCHR;
e4283c49
MH
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':
887e3e0d
MH
316 if (!subop) {
317 i = getesc();
318 if (i == 0)
319 return;
e4283c49 320 *lastcp++ = i;
887e3e0d 321 }
e4283c49
MH
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':
336fixup:
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 ... */
423deleteit:
424 /*
425 * x Delete character at cursor, leaving cursor where it is.
426 */
427 case 'x':
428 if (margin())
429 goto errlab;
d266c416 430 vmacchng(1);
e4283c49
MH
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]) {
443errlab:
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);
10499a70 575 forbid (Xhadcnt);
e4283c49
MH
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 '?':
10499a70 612 forbid (Xhadcnt);
e4283c49
MH
613 vsave();
614 ocurs = cursor;
887e3e0d 615 odot = dot;
e4283c49
MH
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;
887e3e0d
MH
622 d = peekc;
623fromsemi:
624 ungetchar(0);
625 fixech();
e4283c49
MH
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
887e3e0d 641slerr:
e4283c49 642 globp = oglobp;
887e3e0d
MH
643 dot = odot;
644 cursor = ocurs;
e4283c49
MH
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;
887e3e0d
MH
655 if (*globp == ';') {
656 /* /foo/;/bar/ */
657 globp++;
658 dot = addr;
659 cursor = loc1;
660 goto fromsemi;
661 }
662 dot = odot;
e4283c49
MH
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';
887e3e0d 672 if (any(*globp, "^+-."))
e4283c49 673 c = *globp++;
887e3e0d
MH
674 if (*globp) {
675 /* random junk after the pattern */
676 beep();
677 goto slerr;
678 }
e4283c49
MH
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);
5a6c967e 718flusho();
e4283c49
MH
719 wdot = NOLINE;
720}
721
722/*
723 * Find single character c, in direction dir from cursor.
724 */
725find(c)
726 char c;
727{
728
729 for(;;) {
730 if (edge())
731 return (0);
732 wcursor += dir;
733 if (*wcursor == c)
734 return (1);
735 }
736}
737
738/*
739 * Do a word motion with operator op, and cnt more words
740 * to go after this.
741 */
742word(op, cnt)
743 register int (*op)();
744 int cnt;
745{
746 register int which;
747 register char *iwc;
748 register line *iwdot = wdot;
749
750 if (dir == 1) {
751 iwc = wcursor;
752 which = wordch(wcursor);
753 while (wordof(which, wcursor)) {
754 if (cnt == 1 && op != vmove && wcursor[1] == 0) {
755 wcursor++;
756 break;
757 }
758 if (!lnext())
759 return (0);
760 if (wcursor == linebuf)
761 break;
762 }
763 /* Unless last segment of a change skip blanks */
764 if (op != vchange || cnt > 1)
765 while (!margin() && blank())
766 wcursor++;
767 else
768 if (wcursor == iwc && iwdot == wdot && *iwc)
769 wcursor++;
770 if (op == vmove && margin())
771 wcursor--;
772 } else {
773 if (!lnext())
774 return (0);
775 while (blank())
776 if (!lnext())
777 return (0);
778 if (!margin()) {
779 which = wordch(wcursor);
780 while (!margin() && wordof(which, wcursor))
781 wcursor--;
782 }
783 if (wcursor < linebuf || !wordof(which, wcursor))
784 wcursor++;
785 }
786 return (1);
787}
788
789/*
790 * To end of word, with operator op and cnt more motions
791 * remaining after this.
792 */
793eend(op)
794 register int (*op)();
795{
796 register int which;
797
798 if (!lnext())
799 return;
800 while (blank())
801 if (!lnext())
802 return;
803 which = wordch(wcursor);
804 while (wordof(which, wcursor)) {
805 if (wcursor[1] == 0) {
806 wcursor++;
807 break;
808 }
809 if (!lnext())
810 return;
811 }
812 if (op != vchange && op != vdelete && wcursor > linebuf)
813 wcursor--;
814}
815
816/*
817 * Wordof tells whether the character at *wc is in a word of
818 * kind which (blank/nonblank words are 0, conservative words 1).
819 */
820wordof(which, wc)
821 char which;
822 register char *wc;
823{
824
825 if (isspace(*wc))
826 return (0);
827 return (!wdkind || wordch(wc) == which);
828}
829
830/*
831 * Wordch tells whether character at *wc is a word character
832 * i.e. an alfa, digit, or underscore.
833 */
834wordch(wc)
835 char *wc;
836{
837 register int c;
838
839 c = wc[0];
840 return (isalpha(c) || isdigit(c) || c == '_');
841}
842
843/*
844 * Edge tells when we hit the last character in the current line.
845 */
846edge()
847{
848
849 if (linebuf[0] == 0)
850 return (1);
851 if (dir == 1)
852 return (wcursor[1] == 0);
853 else
854 return (wcursor == linebuf);
855}
856
857/*
858 * Margin tells us when we have fallen off the end of the line.
859 */
860margin()
861{
862
863 return (wcursor < linebuf || wcursor[0] == 0);
864}