Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
99 7u83 1
/*
2
 * This code contains changes by
3
 *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
4
 *
5
 * Conditions 1, 2, and 4 and the no-warranty notice below apply
6
 * to these changes.
7
 *
8
 *
9
 * Copyright (c) 1980, 1993
10
 * 	The Regents of the University of California.  All rights reserved.
11
 *
12
 * Redistribution and use in source and binary forms, with or without
13
 * modification, are permitted provided that the following conditions
14
 * are met:
15
 * 1. Redistributions of source code must retain the above copyright
16
 *    notice, this list of conditions and the following disclaimer.
17
 * 2. Redistributions in binary form must reproduce the above copyright
18
 *    notice, this list of conditions and the following disclaimer in the
19
 *    documentation and/or other materials provided with the distribution.
20
 * 3. All advertising materials mentioning features or use of this software
21
 *    must display the following acknowledgement:
22
 * 	This product includes software developed by the University of
23
 * 	California, Berkeley and its contributors.
24
 * 4. Neither the name of the University nor the names of its contributors
25
 *    may be used to endorse or promote products derived from this software
26
 *    without specific prior written permission.
27
 *
28
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38
 * SUCH DAMAGE.
39
 *
40
 *
41
 * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
42
 *
43
 * Redistribution and use in source and binary forms, with or without
44
 * modification, are permitted provided that the following conditions
45
 * are met:
46
 *   Redistributions of source code and documentation must retain the
47
 *    above copyright notice, this list of conditions and the following
48
 *    disclaimer.
49
 *   Redistributions in binary form must reproduce the above copyright
50
 *    notice, this list of conditions and the following disclaimer in the
51
 *    documentation and/or other materials provided with the distribution.
52
 *   All advertising materials mentioning features or use of this software
53
 *    must display the following acknowledgement:
54
 *      This product includes software developed or owned by Caldera
55
 *      International, Inc.
56
 *   Neither the name of Caldera International, Inc. nor the names of
57
 *    other contributors may be used to endorse or promote products
58
 *    derived from this software without specific prior written permission.
59
 *
60
 * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
61
 * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
62
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
63
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
64
 * ARE DISCLAIMED. IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE
65
 * LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
66
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
67
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
68
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
69
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
70
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
71
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
72
 */
73
 
74
#ifndef	lint
75
#ifdef	DOSCCS
76
static char sccsid[] = "@(#)ex_voper.c	1.27 (gritter) 2/15/05";
77
#endif
78
#endif
79
 
80
/* from ex_voper.c	7.4 (Berkeley) 6/7/85 */
81
 
82
#include "ex.h"
83
#include "ex_re.h"
84
#include "ex_tty.h"
85
#include "ex_vis.h"
86
 
87
#ifdef	MB
88
static int
89
cblank(char *cp)
90
{
91
	if (mb_cur_max > 1 && *cp & 0200) {
92
		int	c;
93
		return mbtowi(&c, cp, mb_cur_max) > 0 && iswspace(c);
94
	} else
95
		return isspace(*cp&0377);
96
}
97
#define	blank()		cblank(wcursor)
98
#else	/* !MB */
99
#define	cblank(cp)	isspace(*cp&0377)
100
#define	blank()		xisspace(wcursor[0]&TRIM)
101
#endif	/* !MB */
102
#define	forbid(a)	if (a) goto errlab;
103
 
104
cell	vscandir[2] =	{ '/', 0 };
105
 
106
/*
107
 * Decode an operator/operand type command.
108
 * Eventually we switch to an operator subroutine in ex_vops.c.
109
 * The work here is setting up a function variable to point
110
 * to the routine we want, and manipulation of the variables
111
 * wcursor and wdot, which mark the other end of the affected
112
 * area.  If wdot is zero, then the current line is the other end,
113
 * and if wcursor is zero, then the first non-blank location of the
114
 * other line is implied.
115
 */
116
void 
117
operate(register int c, register int cnt)
118
{
119
	register int i = 0;
120
	void (*moveop)(int), (*deleteop)(int);
121
	void (*opf)(int);
122
	bool subop = 0;
123
	char *oglobp, *ocurs;
124
	register line *addr;
125
	line *odot;
126
	static int lastFKND, lastFCHR;
127
	short d;
128
	cell nullcell[1], qmarkcell[2], slashcell[2];
129
 
130
	CLOBBGRD(opf);
131
	CLOBBGRD(d);
132
	qmarkcell[0] = '?';
133
	slashcell[0] = '/';
134
	nullcell[0] = qmarkcell[1] = slashcell[1] = 0;
135
	moveop = vmove, deleteop = vdelete;
136
	wcursor = cursor;
137
	wdot = NOLINE;
138
	notecnt = 0;
139
	dir = 1;
140
	switch (c) {
141
 
142
	/*
143
	 * d		delete operator.
144
	 */
145
	case 'd':
146
		moveop = vdelete;
147
		deleteop = (void (*)(int))beep;
148
		break;
149
 
150
	/*
151
	 * s		substitute characters, like c\040, i.e. change space.
152
	 */
153
	case 's':
154
		ungetkey(' ');
155
		subop++;
156
		/* fall into ... */
157
 
158
	/*
159
	 * c		Change operator.
160
	 */
161
	case 'c':
162
		if (c == 'c' && workcmd[0] == 'C' || workcmd[0] == 'S')
163
			subop++;
164
		moveop = vchange;
165
		deleteop = (void (*)(int))beep;
166
		break;
167
 
168
	/*
169
	 * !		Filter through a UNIX command.
170
	 */
171
	case '!':
172
		moveop = vfilter;
173
		deleteop = (void (*)(int))beep;
174
		break;
175
 
176
	/*
177
	 * y		Yank operator.  Place specified text so that it
178
	 *		can be put back with p/P.  Also yanks to named buffers.
179
	 */
180
	case 'y':
181
		moveop = vyankit;
182
		deleteop = (void (*)(int))beep;
183
		break;
184
 
185
	/*
186
	 * =		Reformat operator (for LISP).
187
	 */
188
#ifdef LISPCODE
189
	case '=':
190
		forbid(!value(LISP));
191
		/* fall into ... */
192
#endif
193
 
194
	/*
195
	 * >		Right shift operator.
196
	 * <		Left shift operator.
197
	 */
198
	case '<':
199
	case '>':
200
		moveop = vshftop;
201
		deleteop = (void (*)(int))beep;
202
		break;
203
 
204
	/*
205
	 * r		Replace character under cursor with single following
206
	 *		character.
207
	 */
208
	case 'r':
209
		vmacchng(1);
210
		vrep(cnt);
211
		return;
212
 
213
	default:
214
		goto nocount;
215
	}
216
	vmacchng(1);
217
	/*
218
	 * Had an operator, so accept another count.
219
	 * Multiply counts together.
220
	 */
221
	if (xisdigit(peekkey()) && peekkey() != '0') {
222
		cnt *= vgetcnt();
223
		Xcnt = cnt;
224
		forbid (cnt <= 0);
225
	}
226
 
227
	/*
228
	 * Get next character, mapping it and saving as
229
	 * part of command for repeat.
230
	 */
231
	c = map(getesc(),arrows);
232
	if (c == 0)
233
		return;
234
	if (!subop)
235
		*lastcp++ = c;
236
nocount:
237
	opf = moveop;
238
	switch (c) {
239
 
240
	/*
241
	 * b		Back up a word.
242
	 * B		Back up a word, liberal definition.
243
	 */
244
	case 'b':
245
	case 'B':
246
		dir = -1;
247
		/* fall into ... */
248
 
249
	/*
250
	 * w		Forward a word.
251
	 * W		Forward a word, liberal definition.
252
	 */
253
	case 'W':
254
	case 'w':
255
		wdkind = c & ' ';
256
		forbid(llfind(2, cnt, opf, 0) < 0);
257
		vmoving = 0;
258
		break;
259
 
260
	/*
261
	 * E		to end of following blank/nonblank word
262
	 */
263
	case 'E':
264
		wdkind = 0;
265
		goto ein;
266
 
267
	/*
268
	 * e		To end of following word.
269
	 */
270
	case 'e':
271
		wdkind = 1;
272
ein:
273
		forbid(llfind(3, cnt - 1, opf, 0) < 0);
274
		vmoving = 0;
275
		break;
276
 
277
	/*
278
	 * (		Back an s-expression.
279
	 */
280
	case '(':
281
		dir = -1;
282
		/* fall into... */
283
 
284
	/*
285
	 * )		Forward an s-expression.
286
	 */
287
	case ')':
288
		forbid(llfind(0, cnt, opf, (line *) 0) < 0);
289
		markDOT();
290
		break;
291
 
292
	/*
293
	 * {		Back an s-expression, but don't stop on atoms.
294
	 *		In text mode, a paragraph.  For C, a balanced set
295
	 *		of {}'s.
296
	 */
297
	case '{':
298
		dir = -1;
299
		/* fall into... */
300
 
301
	/*
302
	 * }		Forward an s-expression, but don't stop on atoms.
303
	 *		In text mode, back paragraph.  For C, back a balanced
304
	 *		set of {}'s.
305
	 */
306
	case '}':
307
		forbid(llfind(1, cnt, opf, (line *) 0) < 0);
308
		markDOT();
309
		break;
310
 
311
	/*
312
	 * %		To matching () or {}.  If not at ( or { scan for
313
	 *		first such after cursor on this line.
314
	 */
315
	case '%':
316
		vsave();
317
		i = lmatchp((line *) 0);
318
#ifdef TRACE
319
		if (trace)
320
			fprintf(trace, "after lmatchp in %, dot=%d, wdot=%d, dol=%d\n", lineno(dot), lineno(wdot), lineno(dol));
321
#endif
322
		getDOT();
323
		forbid(!i);
324
		if (opf != vmove)
325
			if (dir > 0)
326
				wcursor += skipright(linebuf, wcursor);
327
			else
328
				cursor += skipright(linebuf, cursor);
329
		else
330
			markDOT();
331
		vmoving = 0;
332
		break;
333
 
334
	/*
335
	 * [		Back to beginning of defun, i.e. an ( in column 1.
336
	 *		For text, back to a section macro.
337
	 *		For C, back to a { in column 1 (~~ beg of function.)
338
	 */
339
	case '[':
340
		dir = -1;
341
		/* fall into ... */
342
 
343
	/*
344
	 * ]		Forward to next defun, i.e. a ( in column 1.
345
	 *		For text, forward section.
346
	 *		For C, forward to a } in column 1 (if delete or such)
347
	 *		or if a move to a { in column 1.
348
	 */
349
	case ']':
350
		if (!vglobp)
351
			forbid(getkey() != c);
352
		forbid (Xhadcnt);
353
		vsave();
354
		i = lbrack(c, opf);
355
		getDOT();
356
		forbid(!i);
357
		markDOT();
358
		if (ospeed > B300)
359
			hold |= HOLDWIG;
360
		break;
361
 
362
	/*
363
	 * ,		Invert last find with f F t or T, like inverse
364
	 *		of ;.
365
	 */
366
	case ',':
367
		forbid (lastFKND == 0);
368
		c = xisupper(lastFKND&TRIM)
369
			? xtolower(lastFKND&TRIM) : xtoupper(lastFKND&TRIM);
370
		i = lastFCHR;
371
		if (vglobp == 0)
372
			vglobp = nullcell;
373
		subop++;
374
		goto nocount;
375
 
376
	/*
377
	 * 0		To beginning of real line.
378
	 */
379
	case '0':
380
		wcursor = linebuf;
381
		vmoving = 0;
382
		break;
383
 
384
	/*
385
	 * ;		Repeat last find with f F t or T.
386
	 */
387
	case ';':
388
		forbid (lastFKND == 0);
389
		c = lastFKND;
390
		i = lastFCHR;
391
		subop++;
392
		goto nocount;
393
 
394
	/*
395
	 * F		Find single character before cursor in current line.
396
	 * T		Like F, but stops before character.
397
	 */
398
	case 'F':	/* inverted find */
399
	case 'T':
400
		dir = -1;
401
		/* fall into ... */
402
 
403
	/*
404
	 * f		Find single character following cursor in current line.
405
	 * t		Like f, but stope before character.
406
	 */
407
	case 'f':	/* find */
408
	case 't':
409
		if (!subop) {
410
			i = getesc();
411
			if (i == 0)
412
				return;
413
			*lastcp++ = i;
414
		}
415
		if (vglobp == 0)
416
			lastFKND = c, lastFCHR = i;
417
		for (; cnt > 0; cnt--)
418
			forbid (find(i) == 0);
419
		vmoving = 0;
420
		switch (c) {
421
 
422
		case 'T':
423
			wcursor += skipright(linebuf, wcursor);
424
			break;
425
 
426
		case 't':
427
			wcursor += skipleft(linebuf, wcursor);
428
		case 'f':
429
fixup:
430
			if (moveop != vmove)
431
				wcursor += skipright(linebuf, wcursor);
432
			break;
433
		}
434
		break;
435
 
436
	/*
437
	 * |		Find specified print column in current line.
438
	 */
439
	case '|':
440
		if (Pline == numbline)
441
			cnt += 8;
442
		vmovcol = cnt;
443
		vmoving = 1;
444
		wcursor = vfindcol(cnt);
445
		break;
446
 
447
	/*
448
	 * ^		To beginning of non-white space on line.
449
	 */
450
	case '^':
451
		wcursor = vskipwh(linebuf);
452
		vmoving = 0;
453
		break;
454
 
455
	/*
456
	 * $		To end of line.
457
	 */
458
	case '$':
459
		if (opf == vmove) {
460
			vmoving = 1;
461
			vmovcol = 20000;
462
		} else
463
			vmoving = 0;
464
		if (cnt > 1) {
465
			if (opf == vmove) {
466
				wcursor = 0;
467
				cnt--;
468
			} else
469
				wcursor = linebuf;
470
			/* This is wrong at EOF */
471
			wdot = dot + cnt;
472
			break;
473
		}
474
		if (linebuf[0]) {
475
			wcursor = strend(linebuf) - 1;
476
			goto fixup;
477
		}
478
		wcursor = linebuf;
479
		break;
480
 
481
	/*
482
	 * h		Back a character.
483
	 * ^H		Back a character.
484
	 */
485
	case 'h':
486
	case CTRL('h'):
487
		dir = -1;
488
		/* fall into ... */
489
 
490
	/*
491
	 * space	Forward a character.
492
	 */
493
	case 'l':
494
	case ' ':
495
		forbid (margin() || opf == vmove && edge());
496
		while (cnt > 0 && !margin()) {
497
			wcursor += dir>0 ? skipright(linebuf, wcursor) :
498
				skipleft(linebuf, wcursor);
499
			cnt--;
500
		}
501
		if (margin() && opf == vmove || wcursor < linebuf)
502
			wcursor -= dir;
503
		vmoving = 0;
504
		break;
505
 
506
	/*
507
	 * D		Delete to end of line, short for d$.
508
	 */
509
	case 'D':
510
		cnt = INF;
511
		goto deleteit;
512
 
513
	/*
514
	 * X		Delete character before cursor.
515
	 */
516
	case 'X':
517
		dir = -1;
518
		/* fall into ... */
519
deleteit:
520
	/*
521
	 * x		Delete character at cursor, leaving cursor where it is.
522
	 */
523
	case 'x':
524
		if (margin())
525
			goto errlab;
526
		vmacchng(1);
527
		while (cnt > 0 && !margin()) {
528
			wcursor += dir > 0 ? skipright(linebuf, wcursor) :
529
				skipleft(linebuf, wcursor);
530
			cnt--;
531
		}
532
		opf = deleteop;
533
		vmoving = 0;
534
		break;
535
 
536
	default:
537
		/*
538
		 * Stuttered operators are equivalent to the operator on
539
		 * a line, thus turn dd into d_.
540
		 */
541
		if (opf == vmove || c != workcmd[0]) {
542
errlab:
543
			beep();
544
			vmacp = 0;
545
			return;
546
		}
547
		/* fall into ... */
548
 
549
	/*
550
	 * _		Target for a line or group of lines.
551
	 *		Stuttering is more convenient; this is mostly
552
	 *		for aesthetics.
553
	 */
554
	case '_':
555
		wdot = dot + cnt - 1;
556
		vmoving = 0;
557
		wcursor = 0;
558
		break;
559
 
560
	/*
561
	 * H		To first, home line on screen.
562
	 *		Count is for count'th line rather than first.
563
	 */
564
	case 'H':
565
		wdot = (dot - vcline) + cnt - 1;
566
		if (opf == vmove)
567
			markit(wdot);
568
		vmoving = 0;
569
		wcursor = 0;
570
		break;
571
 
572
	/*
573
	 * -		Backwards lines, to first non-white character.
574
	 */
575
	case '-':
576
		wdot = dot - cnt;
577
		vmoving = 0;
578
		wcursor = 0;
579
		break;
580
 
581
	/*
582
	 * ^P		To previous line same column.  Ridiculous on the
583
	 *		console of the VAX since it puts console in LSI mode.
584
	 */
585
	case 'k':
586
	case CTRL('p'):
587
		wdot = dot - cnt;
588
		if (vmoving == 0)
589
			vmoving = 1, vmovcol = lcolumn(cursor);
590
		wcursor = 0;
591
		break;
592
 
593
	/*
594
	 * L		To last line on screen, or count'th line from the
595
	 *		bottom.
596
	 */
597
	case 'L':
598
		wdot = dot + vcnt - vcline - cnt;
599
		if (opf == vmove)
600
			markit(wdot);
601
		vmoving = 0;
602
		wcursor = 0;
603
		break;
604
 
605
	/*
606
	 * M		To the middle of the screen.
607
	 */
608
	case 'M':
609
		wdot = dot + ((vcnt + 1) / 2) - vcline - 1;
610
		if (opf == vmove)
611
			markit(wdot);
612
		vmoving = 0;
613
		wcursor = 0;
614
		break;
615
 
616
	/*
617
	 * +		Forward line, to first non-white.
618
	 *
619
	 * CR		Convenient synonym for +.
620
	 */
621
	case '+':
622
	case CR:
623
		wdot = dot + cnt;
624
		vmoving = 0;
625
		wcursor = 0;
626
		break;
627
 
628
	/*
629
	 * ^N		To next line, same column if possible.
630
	 *
631
	 * LF		Linefeed is a convenient synonym for ^N.
632
	 */
633
	case CTRL('n'):
634
	case 'j':
635
	case NL:
636
		wdot = dot + cnt;
637
		if (vmoving == 0)
638
			vmoving = 1, vmovcol = lcolumn(cursor);
639
		wcursor = 0;
640
		break;
641
 
642
	/*
643
	 * n		Search to next match of current pattern.
644
	 */
645
	case 'n':
646
		vglobp = vscandir;
647
		c = *vglobp++;
648
		goto nocount;
649
 
650
	/*
651
	 * N		Like n but in reverse direction.
652
	 */
653
	case 'N':
654
		vglobp = vscandir[0] == '/' ? qmarkcell : slashcell;
655
		c = *vglobp++;
656
		goto nocount;
657
 
658
	/*
659
	 * '		Return to line specified by following mark,
660
	 *		first white position on line.
661
	 *
662
	 * `		Return to marked line at remembered column.
663
	 */
664
	case '\'':
665
	case '`':
666
		d = c;
667
		c = getesc();
668
		if (c == 0)
669
			return;
670
		c = markreg(c);
671
		forbid (c == 0);
672
		wdot = getmark(c);
673
		forbid (wdot == NOLINE);
674
		forbid (Xhadcnt);
675
		vmoving = 0;
676
		wcursor = d == '`' ? ncols[c - 'a'] : 0;
677
		if (opf == vmove && (wdot != dot || (d == '`' && wcursor != cursor)))
678
			markDOT();
679
		if (wcursor) {
680
			vsave();
681
			getline(*wdot);
682
			if (wcursor > strend(linebuf))
683
				wcursor = 0;
684
			getDOT();
685
		}
686
		if (ospeed > B300)
687
			hold |= HOLDWIG;
688
		break;
689
 
690
	/*
691
	 * G		Goto count'th line, or last line if no count
692
	 *		given.
693
	 */
694
	case 'G':
695
		if (!Xhadcnt)
696
			cnt = lineDOL();
697
		wdot = zero + cnt;
698
		forbid (wdot < one || wdot > dol);
699
		if (opf == vmove)
700
			markit(wdot);
701
		vmoving = 0;
702
		wcursor = 0;
703
		break;
704
 
705
	/*
706
	 * /		Scan forward for following re.
707
	 * ?		Scan backward for following re.
708
	 */
709
	case '/':
710
	case '?':
711
		forbid (Xhadcnt);
712
		vsave();
713
		ocurs = cursor;
714
		odot = dot;
715
		wcursor = 0;
716
		if (readecho(c))
717
			return;
718
		if (!vglobp)
719
			vscandir[0] = genbuf[0];
720
		oglobp = globp;
721
		CP(vutmp, genbuf);
722
		globp = vutmp;
723
		d = peekc;
724
fromsemi:
725
		ungetchar(0);
726
		fixech();
727
		CATCH
728
			addr = address(cursor);
729
		ONERR
730
slerr:
731
			globp = oglobp;
732
			dot = odot;
733
			cursor = ocurs;
734
			ungetchar(d);
735
			splitw = 0;
736
			vclean();
737
			vjumpto(dot, ocurs, 0);
738
			return;
739
		ENDCATCH
740
		if (globp == 0)
741
			globp = "";
742
		else if (peekc)
743
			--globp;
744
		if (*globp == ';') {
745
			/* /foo/;/bar/ */
746
			globp++;
747
			dot = addr;
748
			cursor = loc1;
749
			goto fromsemi;
750
		}
751
		dot = odot;
752
		ungetchar(d);
753
		c = 0;
754
		if (*globp == 'z')
755
			globp++, c = '\n';
756
		if (any(*globp, "^+-."))
757
			c = *globp++;
758
		i = 0;
759
		while (xisdigit(*globp&TRIM))
760
			i = i * 10 + *globp++ - '0';
761
		if (any(*globp, "^+-."))
762
			c = *globp++;
763
		if (*globp) {
764
			/* random junk after the pattern */
765
			beep();
766
			goto slerr;
767
		}
768
		globp = oglobp;
769
		splitw = 0;
770
		vmoving = 0;
771
		wcursor = loc1;
772
		if (i != 0)
773
			vsetsiz(i);
774
		if (opf == vmove) {
775
			if (state == ONEOPEN || state == HARDOPEN)
776
				outline = destline = WBOT;
777
			if (addr != dot || loc1 != cursor)
778
				markDOT();
779
			if (loc1 > linebuf && *loc1 == 0)
780
				loc1--;
781
			if (c)
782
				vjumpto(addr, loc1, c);
783
			else {
784
				vmoving = 0;
785
				if (loc1) {
786
					vmoving++;
787
					vmovcol = column(loc1);
788
				}
789
				getDOT();
790
				if (state == CRTOPEN && addr != dot)
791
					vup1();
792
				vupdown(addr - dot, NOSTR);
793
			}
794
			return;
795
		}
796
		lastcp[-1] = 'n';
797
		getDOT();
798
		wdot = addr;
799
		break;
800
	}
801
	/*
802
	 * Apply.
803
	 */
804
	if (vreg && wdot == 0)
805
		wdot = dot;
806
	(*opf)(c);
807
	wdot = NOLINE;
808
}
809
 
810
/*
811
 * Find single character c, in direction dir from cursor.
812
 */
813
int 
814
find(int c)
815
{
816
 
817
	for(;;) {
818
		if (edge())
819
			return (0);
820
		wcursor += dir>0 ? skipright(linebuf, wcursor) :
821
			skipleft(linebuf, wcursor-1);
822
		if (samechar(wcursor, c))
823
			return (1);
824
	}
825
}
826
 
827
/*
828
 * Do a word motion with operator op, and cnt more words
829
 * to go after this.
830
 */
831
int 
832
word(register void (*op)(int), int cnt)
833
{
834
	register int which = 0, i;
835
	register char *iwc;
836
	register line *iwdot = wdot;
837
 
838
	if (dir == 1) {
839
		iwc = wcursor;
840
		which = wordch(wcursor);
841
		while (wordof(which, wcursor)) {
842
			if (cnt == 1 && op != vmove &&
843
					wcursor[i = skipright(linebuf, wcursor)]
844
					== 0) {
845
				wcursor += i;
846
				break;
847
			}
848
			if (!lnext())
849
				return (0);
850
			if (wcursor == linebuf)
851
				break;
852
		}
853
		/* Unless last segment of a change skip blanks */
854
		if (op != vchange || cnt > 1)
855
			while (!margin() && blank())
856
				wcursor += skipright(linebuf, wcursor);
857
		else
858
			if (wcursor == iwc && iwdot == wdot && *iwc)
859
				wcursor += skipright(linebuf, wcursor);
860
		if (op == vmove && margin()) {
861
			if (wcursor == linebuf)
862
				wcursor--;
863
			else if (!lnext())
864
				return (0);
865
		}
866
	} else {
867
		if (!lnext())
868
			return (0);
869
		while (blank())
870
			if (!lnext())
871
				return (0);
872
		if (!margin()) {
873
			which = wordch(wcursor);
874
			while (!margin() && wordof(which, wcursor))
875
				wcursor--;
876
		}
877
		if (wcursor < linebuf || !wordof(which, wcursor))
878
			wcursor += skipright(linebuf, wcursor);
879
	}
880
	return (1);
881
}
882
 
883
/*
884
 * To end of word, with operator op and cnt more motions
885
 * remaining after this.
886
 */
887
void 
888
eend(register void (*op)(int))
889
{
890
	register int which;
891
 
892
	if (!lnext())
893
		return;
894
	while (blank())
895
		if (!lnext())
896
			return;
897
	which = wordch(wcursor);
898
	while (wordof(which, wcursor)) {
899
		if (wcursor[1] == 0) {
900
			wcursor++;
901
			break;
902
		}
903
		if (!lnext())
904
			return;
905
	}
906
	if (op != vchange && op != vdelete && wcursor > linebuf)
907
		wcursor--;
908
}
909
 
910
/*
911
 * Wordof tells whether the character at *wc is in a word of
912
 * kind which (blank/nonblank words are 0, conservative words 1).
913
 */
914
int 
915
wordof(int which, register char *wc)
916
{
917
 
918
	if (cblank(wc))
919
		return (0);
920
	return (!wdkind || wordch(wc) == which);
921
}
922
 
923
/*
924
 * Wordch tells whether character at *wc is a word character
925
 * i.e. an alfa, digit, or underscore.
926
 */
927
int 
928
wordch(char *wc)
929
{
930
	int c;
931
 
932
#ifdef	MB
933
	if (mb_cur_max > 0 && *wc & 0200) {
934
		mbtowi(&c, wc, mb_cur_max);
935
		if (c & INVBIT)
936
			return 1;
937
	} else
938
#endif
939
		c = wc[0]&0377;
940
	return (xisalnum(c) || c == '_'
941
#ifdef	BIT8
942
#ifdef	ISO8859_1
943
	/*
944
	 * We consider all ISO 8859-1 characters except for
945
	 * no-break-space as word characters.
946
	 */
947
			|| c&0200 && (!(c&QUOTE) && (c&TRIM) != 0240)
948
#endif
949
#endif
950
			);
951
}
952
 
953
/*
954
 * Edge tells when we hit the last character in the current line.
955
 */
956
int 
957
edge(void)
958
{
959
 
960
	if (linebuf[0] == 0)
961
		return (1);
962
	if (dir == 1)
963
		return (wcursor[skipright(linebuf, wcursor)] == 0);
964
	else
965
		return (wcursor == linebuf);
966
}
967
 
968
/*
969
 * Margin tells us when we have fallen off the end of the line.
970
 */
971
int 
972
margin(void)
973
{
974
 
975
	return (wcursor < linebuf || wcursor[0] == 0);
976
}