Subversion Repositories planix.SVN

Rev

Rev 115 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
105 7u83 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
#if	!defined(lint) && defined(DOSCCS)
8
static char *sccsid = "@(#)ex_voper.c	7.4 (Berkeley) 6/7/85";
9
#endif
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 (o_speed > 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();
118 7u83 582
			ex_getline(*wdot);
105 7u83 583
			if (wcursor > strend(linebuf))
584
				wcursor = 0;
585
			getDOT();
586
		}
587
		if (o_speed > 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
#ifndef	BIT8
115 7u83 622
		oglobp = globp; CP(vutmp, genbuf); globp = vutmp;
105 7u83 623
#else
115 7u83 624
		oglobp = globp; CP((char*)vutmp, genbuf);
105 7u83 625
		globp = (char*)vutmp;
626
#endif
627
		d = peekc;
628
fromsemi:
629
		ungetchar(0);
630
		fixech();
631
		CATCH
632
#ifndef CBREAK
633
			/*
634
			 * Lose typeahead (ick).
635
			 */
636
			vcook();
637
#endif
638
			addr = address(cursor);
639
#ifndef CBREAK
640
			vraw();
641
#endif
642
		ONERR
643
#ifndef CBREAK
644
			vraw();
645
#endif
646
slerr:
647
			globp = oglobp;
648
			dot = odot;
649
			cursor = ocurs;
650
			ungetchar(d);
651
			splitw = 0;
652
			vclean();
653
			vjumpto(dot, ocurs, 0);
654
			return;
655
		ENDCATCH
656
		if (globp == 0)
657
			globp = "";
658
		else if (peekc)
659
			--globp;
660
		if (*globp == ';') {
661
			/* /foo/;/bar/ */
662
			globp++;
663
			dot = addr;
664
			cursor = loc1;
665
			goto fromsemi;
666
		}
667
		dot = odot;
668
		ungetchar(d);
669
		c = 0;
670
		if (*globp == 'z')
671
			globp++, c = '\n';
672
		if (any(*globp, "^+-."))
673
			c = *globp++;
674
		i = 0;
675
		while (isdigit(*globp))
676
			i = i * 10 + *globp++ - '0';
677
		if (any(*globp, "^+-."))
678
			c = *globp++;
679
		if (*globp) {
680
			/* random junk after the pattern */
681
			beep();
682
			goto slerr;
683
		}
684
		globp = oglobp;
685
		splitw = 0;
686
		vmoving = 0;
687
		wcursor = loc1;
688
		if (i != 0)
689
			vsetsiz(i);
690
		if (opf == vmove) {
691
			if (state == ONEOPEN || state == HARDOPEN)
692
				outline = destline = WBOT;
693
			if (addr != dot || loc1 != cursor)
694
				markDOT();
695
			if (loc1 > linebuf && *loc1 == 0)
696
				loc1--;
697
			if (c)
698
				vjumpto(addr, loc1, c);
699
			else {
700
				vmoving = 0;
701
				if (loc1) {
702
					vmoving++;
703
					vmovcol = column(loc1);
704
				}
705
				getDOT();
706
				if (state == CRTOPEN && addr != dot)
707
					vup1();
708
				vupdown(addr - dot, NOSTR);
709
			}
710
			return;
711
		}
712
		lastcp[-1] = 'n';
713
		getDOT();
714
		wdot = addr;
715
		break;
716
	}
717
	/*
718
	 * Apply.
719
	 */
720
	if (vreg && wdot == 0)
721
		wdot = dot;
722
	(*opf)(c);
723
	wdot = NOLINE;
724
}
725
 
726
/*
727
 * Find single character c, in direction dir from cursor.
728
 */
729
find(c)
730
	char c;
731
{
732
 
733
	for(;;) {
734
		if (edge())
735
			return (0);
736
		wcursor += dir;
737
		if (*wcursor == c)
738
			return (1);
739
	}
740
}
741
 
742
/*
743
 * Do a word motion with operator op, and cnt more words
744
 * to go after this.
745
 */
746
word(op, cnt)
747
	register int (*op)();
748
	int cnt;
749
{
750
	register int which;
751
	register char *iwc;
752
	register line *iwdot = wdot;
753
 
754
	if (dir == 1) {
755
		iwc = wcursor;
756
		which = wordch(wcursor);
757
		while (wordof(which, wcursor)) {
758
			if (cnt == 1 && op != vmove && wcursor[1] == 0) {
759
				wcursor++;
760
				break;
761
			}
762
			if (!lnext())
763
				return (0);
764
			if (wcursor == linebuf)
765
				break;
766
		}
767
		/* Unless last segment of a change skip blanks */
768
		if (op != vchange || cnt > 1)
769
			while (!margin() && blank())
770
				wcursor++;
771
		else
772
			if (wcursor == iwc && iwdot == wdot && *iwc)
773
				wcursor++;
774
		if (op == vmove && margin())
775
			wcursor--;
776
	} else {
777
		if (!lnext())
778
			return (0);
779
		while (blank())
780
			if (!lnext())
781
				return (0);
782
		if (!margin()) {
783
			which = wordch(wcursor);
784
			while (!margin() && wordof(which, wcursor))
785
				wcursor--;
786
		}
787
		if (wcursor < linebuf || !wordof(which, wcursor))
788
			wcursor++;
789
	}
790
	return (1);
791
}
792
 
793
/*
794
 * To end of word, with operator op and cnt more motions
795
 * remaining after this.
796
 */
797
eend(op)
798
	register int (*op)();
799
{
800
	register int which;
801
 
802
	if (!lnext())
803
		return;
804
	while (blank())
805
		if (!lnext())
806
			return;
807
	which = wordch(wcursor);
808
	while (wordof(which, wcursor)) {
809
		if (wcursor[1] == 0) {
810
			wcursor++;
811
			break;
812
		}
813
		if (!lnext())
814
			return;
815
	}
816
	if (op != vchange && op != vdelete && wcursor > linebuf)
817
		wcursor--;
818
}
819
 
820
/*
821
 * Wordof tells whether the character at *wc is in a word of
822
 * kind which (blank/nonblank words are 0, conservative words 1).
823
 */
824
wordof(which, wc)
825
	char which;
826
	register char *wc;
827
{
828
 
829
	if (isspace(*wc))
830
		return (0);
831
	return (!wdkind || wordch(wc) == which);
832
}
833
 
834
/*
835
 * Wordch tells whether character at *wc is a word character
836
 * i.e. an alfa, digit, or underscore.
837
 */
838
wordch(wc)
839
	char *wc;
840
{
841
	register int c;
842
 
843
	c = wc[0];
844
	return (isalpha(c) || isdigit(c) || c == '_'
845
#ifdef	ISO
846
	/*
847
	 * We consider all ISO chars except fpr
848
	 * no-break-space as word characters since
849
	 * we cannot know better.
850
	 */
851
			|| ((c & QUOTE) && !niso(c)
852
				&& ((unsigned char)c) != 240)
853
#endif
854
			);
855
}
856
 
857
/*
858
 * Edge tells when we hit the last character in the current line.
859
 */
860
edge()
861
{
862
 
863
	if (linebuf[0] == 0)
864
		return (1);
865
	if (dir == 1)
866
		return (wcursor[1] == 0);
867
	else
868
		return (wcursor == linebuf);
869
}
870
 
871
/*
872
 * Margin tells us when we have fallen off the end of the line.
873
 */
874
margin()
875
{
876
 
877
	return (wcursor < linebuf || wcursor[0] == 0);
878
}