119304Speter/*-
219304Speter * Copyright (c) 1992, 1993, 1994
319304Speter *	The Regents of the University of California.  All rights reserved.
419304Speter * Copyright (c) 1992, 1993, 1994, 1995, 1996
519304Speter *	Keith Bostic.  All rights reserved.
619304Speter *
719304Speter * See the LICENSE file for redistribution information.
819304Speter */
919304Speter
1019304Speter#include "config.h"
1119304Speter
1219304Speter#ifndef lint
13254225Speterstatic const char sccsid[] = "$Id: v_search.c,v 10.31 2012/02/08 07:26:59 zy Exp $";
1419304Speter#endif /* not lint */
1519304Speter
1619304Speter#include <sys/types.h>
1719304Speter#include <sys/queue.h>
1819304Speter#include <sys/time.h>
1919304Speter
2019304Speter#include <bitstring.h>
2119304Speter#include <ctype.h>
2219304Speter#include <errno.h>
2319304Speter#include <limits.h>
2419304Speter#include <stdio.h>
2519304Speter#include <stdlib.h>
2619304Speter#include <string.h>
2719304Speter
2819304Speter#include "../common/common.h"
2919304Speter#include "vi.h"
3019304Speter
3119304Speterstatic int v_exaddr __P((SCR *, VICMD *, dir_t));
32254225Speterstatic int v_search __P((SCR *, VICMD *, CHAR_T *, size_t, u_int, dir_t));
3319304Speter
3419304Speter/*
3519304Speter * v_srch -- [count]?RE[? offset]
3619304Speter *	Ex address search backward.
3719304Speter *
3819304Speter * PUBLIC: int v_searchb __P((SCR *, VICMD *));
3919304Speter */
4019304Speterint
41254225Speterv_searchb(SCR *sp, VICMD *vp)
4219304Speter{
4319304Speter	return (v_exaddr(sp, vp, BACKWARD));
4419304Speter}
4519304Speter
4619304Speter/*
4719304Speter * v_searchf -- [count]/RE[/ offset]
4819304Speter *	Ex address search forward.
4919304Speter *
5019304Speter * PUBLIC: int v_searchf __P((SCR *, VICMD *));
5119304Speter */
5219304Speterint
53254225Speterv_searchf(SCR *sp, VICMD *vp)
5419304Speter{
5519304Speter	return (v_exaddr(sp, vp, FORWARD));
5619304Speter}
5719304Speter
5819304Speter/*
5919304Speter * v_exaddr --
6019304Speter *	Do a vi search (which is really an ex address).
6119304Speter */
6219304Speterstatic int
63254225Speterv_exaddr(SCR *sp, VICMD *vp, dir_t dir)
6419304Speter{
65254225Speter	static EXCMDLIST fake = { L("search") };
6619304Speter	EXCMD *cmdp;
6719304Speter	GS *gp;
6819304Speter	TEXT *tp;
6919304Speter	recno_t s_lno;
7019304Speter	size_t len, s_cno, tlen;
7119304Speter	int err, nb, type;
72254225Speter	char buf[20];
73254225Speter	CHAR_T *cmd, *t;
74254225Speter	CHAR_T *w;
75254225Speter	size_t wlen;
7619304Speter
7719304Speter	/*
7819304Speter	 * !!!
7919304Speter	 * If using the search command as a motion, any addressing components
8019304Speter	 * are lost, i.e. y/ptrn/+2, when repeated, is the same as y/ptrn/.
8119304Speter	 */
8219304Speter	if (F_ISSET(vp, VC_ISDOT))
8319304Speter		return (v_search(sp, vp,
8419304Speter		    NULL, 0, SEARCH_PARSE | SEARCH_MSG | SEARCH_SET, dir));
8519304Speter
8619304Speter	/* Get the search pattern. */
8719304Speter	if (v_tcmd(sp, vp, dir == BACKWARD ? CH_BSEARCH : CH_FSEARCH,
8819304Speter	    TXT_BS | TXT_CR | TXT_ESCAPE | TXT_PROMPT |
8919304Speter	    (O_ISSET(sp, O_SEARCHINCR) ? TXT_SEARCHINCR : 0)))
9019304Speter		return (1);
9119304Speter
92254225Speter	tp = TAILQ_FIRST(sp->tiq);
9319304Speter
9419304Speter	/* If the user backspaced over the prompt, do nothing. */
9519304Speter	if (tp->term == TERM_BS)
9619304Speter		return (1);
9719304Speter
9819304Speter	/*
9919304Speter	 * If the user was doing an incremental search, then we've already
10019304Speter	 * updated the cursor and moved to the right location.  Return the
10119304Speter	 * correct values, we're done.
10219304Speter	 */
10319304Speter	if (tp->term == TERM_SEARCH) {
10419304Speter		vp->m_stop.lno = sp->lno;
10519304Speter		vp->m_stop.cno = sp->cno;
10619304Speter		if (ISMOTION(vp))
10719304Speter			return (v_correct(sp, vp, 0));
10819304Speter		vp->m_final = vp->m_stop;
10919304Speter		return (0);
11019304Speter	}
11119304Speter
11219304Speter	/*
11319304Speter	 * If the user entered <escape> or <carriage-return>, the length is
11419304Speter	 * 1 and the right thing will happen, i.e. the prompt will be used
11519304Speter	 * as a command character.
11619304Speter	 *
11719304Speter	 * Build a fake ex command structure.
11819304Speter	 */
11919304Speter	gp = sp->gp;
12019304Speter	gp->excmd.cp = tp->lb;
12119304Speter	gp->excmd.clen = tp->len;
12219304Speter	F_INIT(&gp->excmd, E_VISEARCH);
12319304Speter
12419304Speter	/*
12519304Speter	 * XXX
12619304Speter	 * Warn if the search wraps.  This is a pretty special case, but it's
12719304Speter	 * nice feature that wasn't in the original implementations of ex/vi.
12819304Speter	 * (It was added at some point to System V's version.)  This message
12919304Speter	 * is only displayed if there are no keys in the queue. The problem is
13019304Speter	 * the command is going to succeed, and the message is informational,
13119304Speter	 * not an error.  If a macro displays it repeatedly, e.g., the pattern
13219304Speter	 * only occurs once in the file and wrapscan is set, you lose big.  For
13319304Speter	 * example, if the macro does something like:
13419304Speter	 *
13519304Speter	 *	:map K /pattern/^MjK
13619304Speter	 *
13719304Speter	 * Each search will display the message, but the following "/pattern/"
13819304Speter	 * will immediately overwrite it, with strange results.  The System V
13919304Speter	 * vi displays the "wrapped" message multiple times, but because it's
14019304Speter	 * overwritten each time, it's not as noticeable.  As we don't discard
14119304Speter	 * messages, it's a real problem for us.
14219304Speter	 */
14319304Speter	if (!KEYS_WAITING(sp))
14419304Speter		F_SET(&gp->excmd, E_SEARCH_WMSG);
14519304Speter
14619304Speter	/* Save the current line/column. */
14719304Speter	s_lno = sp->lno;
14819304Speter	s_cno = sp->cno;
14919304Speter
15019304Speter	/*
15119304Speter	 * !!!
15219304Speter	 * Historically, vi / and ? commands were full-blown ex addresses,
15319304Speter	 * including ';' delimiters, trailing <blank>'s, multiple search
15419304Speter	 * strings (separated by semi-colons) and, finally, full-blown z
15519304Speter	 * commands after the / and ? search strings.  (If the search was
15619304Speter	 * being used as a motion, the trailing z command was ignored.
15719304Speter	 * Also, we do some argument checking on the z command, to be sure
15819304Speter	 * that it's not some other random command.) For multiple search
15919304Speter	 * strings, leading <blank>'s at the second and subsequent strings
16019304Speter	 * were eaten as well.  This has some (unintended?) side-effects:
16119304Speter	 * the command /ptrn/;3 is legal and results in moving to line 3.
16219304Speter	 * I suppose you could use it to optionally move to line 3...
16319304Speter	 *
16419304Speter	 * !!!
16519304Speter	 * Historically, if any part of the search command failed, the cursor
16619304Speter	 * remained unmodified (even if ; was used).  We have to play games
16719304Speter	 * because the underlying ex parser thinks we're modifying the cursor
16819304Speter	 * as we go, but I think we're compatible with historic practice.
16919304Speter	 *
17019304Speter	 * !!!
17119304Speter	 * Historically, the command "/STRING/;   " failed, apparently it
17219304Speter	 * confused the parser.  We're not that compatible.
17319304Speter	 */
17419304Speter	cmdp = &gp->excmd;
17519304Speter	if (ex_range(sp, cmdp, &err))
17619304Speter		return (1);
17719304Speter
17819304Speter	/*
17919304Speter	 * Remember where any remaining command information is, and clean
18019304Speter	 * up the fake ex command.
18119304Speter	 */
18219304Speter	cmd = cmdp->cp;
18319304Speter	len = cmdp->clen;
18419304Speter	gp->excmd.clen = 0;
18519304Speter
18619304Speter	if (err)
18719304Speter		goto err2;
18819304Speter
18919304Speter	/* Copy out the new cursor position and make sure it's okay. */
19019304Speter	switch (cmdp->addrcnt) {
19119304Speter	case 1:
19219304Speter		vp->m_stop = cmdp->addr1;
19319304Speter		break;
19419304Speter	case 2:
19519304Speter		vp->m_stop = cmdp->addr2;
19619304Speter		break;
19719304Speter	}
19819304Speter	if (!db_exist(sp, vp->m_stop.lno)) {
19919304Speter		ex_badaddr(sp, &fake,
20019304Speter		    vp->m_stop.lno == 0 ? A_ZERO : A_EOF, NUM_OK);
20119304Speter		goto err2;
20219304Speter	}
20319304Speter
20419304Speter	/*
20519304Speter	 * !!!
20619304Speter	 * Historic practice is that a trailing 'z' was ignored if it was a
20719304Speter	 * motion command.  Should probably be an error, but not worth the
20819304Speter	 * effort.
20919304Speter	 */
21019304Speter	if (ISMOTION(vp))
21119304Speter		return (v_correct(sp, vp, F_ISSET(cmdp, E_DELTA)));
21219304Speter
21319304Speter	/*
21419304Speter	 * !!!
21519304Speter	 * Historically, if it wasn't a motion command, a delta in the search
21619304Speter	 * pattern turns it into a first nonblank movement.
21719304Speter	 */
21819304Speter	nb = F_ISSET(cmdp, E_DELTA);
21919304Speter
22019304Speter	/* Check for the 'z' command. */
22119304Speter	if (len != 0) {
22219304Speter		if (*cmd != 'z')
22319304Speter			goto err1;
22419304Speter
22519304Speter		/* No blanks, just like the z command. */
22619304Speter		for (t = cmd + 1, tlen = len - 1; tlen > 0; ++t, --tlen)
22719304Speter			if (!isdigit(*t))
22819304Speter				break;
22919304Speter		if (tlen &&
23019304Speter		    (*t == '-' || *t == '.' || *t == '+' || *t == '^')) {
23119304Speter			++t;
23219304Speter			--tlen;
23319304Speter			type = 1;
23419304Speter		} else
23519304Speter			type = 0;
23619304Speter		if (tlen)
23719304Speter			goto err1;
23819304Speter
23919304Speter		/* The z command will do the nonblank for us. */
24019304Speter		nb = 0;
24119304Speter
24219304Speter		/* Default to z+. */
24319304Speter		if (!type &&
244254225Speter		    v_event_push(sp, NULL, L("+"), 1, CH_NOMAP | CH_QUOTED))
24519304Speter			return (1);
24619304Speter
24719304Speter		/* Push the user's command. */
24819304Speter		if (v_event_push(sp, NULL, cmd, len, CH_NOMAP | CH_QUOTED))
24919304Speter			return (1);
25019304Speter
25119304Speter		/* Push line number so get correct z display. */
25219304Speter		tlen = snprintf(buf,
25319304Speter		    sizeof(buf), "%lu", (u_long)vp->m_stop.lno);
254254225Speter		CHAR2INT(sp, buf, tlen, w, wlen);
255254225Speter		if (v_event_push(sp, NULL, w, wlen, CH_NOMAP | CH_QUOTED))
25619304Speter			return (1);
25719304Speter
25819304Speter		/* Don't refresh until after 'z' happens. */
25919304Speter		F_SET(VIP(sp), VIP_S_REFRESH);
26019304Speter	}
26119304Speter
26219304Speter	/* Non-motion commands move to the end of the range. */
26319304Speter	vp->m_final = vp->m_stop;
26419304Speter	if (nb) {
26519304Speter		F_CLR(vp, VM_RCM_MASK);
26619304Speter		F_SET(vp, VM_RCM_SETFNB);
26719304Speter	}
26819304Speter	return (0);
26919304Speter
27019304Spetererr1:	msgq(sp, M_ERR,
27119304Speter	    "188|Characters after search string, line offset and/or z command");
27219304Spetererr2:	vp->m_final.lno = s_lno;
27319304Speter	vp->m_final.cno = s_cno;
27419304Speter	return (1);
27519304Speter}
27619304Speter
27719304Speter/*
27819304Speter * v_searchN -- N
27919304Speter *	Reverse last search.
28019304Speter *
28119304Speter * PUBLIC: int v_searchN __P((SCR *, VICMD *));
28219304Speter */
28319304Speterint
284254225Speterv_searchN(SCR *sp, VICMD *vp)
28519304Speter{
28619304Speter	dir_t dir;
28719304Speter
28819304Speter	switch (sp->searchdir) {
28919304Speter	case BACKWARD:
29019304Speter		dir = FORWARD;
29119304Speter		break;
29219304Speter	case FORWARD:
29319304Speter		dir = BACKWARD;
29419304Speter		break;
29519304Speter	default:
29619304Speter		dir = sp->searchdir;
29719304Speter		break;
29819304Speter	}
29919304Speter	return (v_search(sp, vp, NULL, 0, SEARCH_PARSE, dir));
30019304Speter}
30119304Speter
30219304Speter/*
30319304Speter * v_searchn -- n
30419304Speter *	Repeat last search.
30519304Speter *
30619304Speter * PUBLIC: int v_searchn __P((SCR *, VICMD *));
30719304Speter */
30819304Speterint
309254225Speterv_searchn(SCR *sp, VICMD *vp)
31019304Speter{
31119304Speter	return (v_search(sp, vp, NULL, 0, SEARCH_PARSE, sp->searchdir));
31219304Speter}
31319304Speter
31419304Speter/*
315254225Speter * is_special --
316254225Speter *	Test if the character is special in a basic RE.
317254225Speter */
318254225Speterstatic int
319254225Speteris_special(CHAR_T c)
320254225Speter{
321254225Speter	/*
322254225Speter	 * !!!
323254225Speter	 * `*' and `$' are ordinary when appear at the beginning of a RE,
324254225Speter	 * but it's safe to distinguish them from the ordinary characters.
325254225Speter	 * The tilde is vi-specific, of course.
326254225Speter	 */
327254225Speter	return (STRCHR(L(".[*\\^$~"), c) && c);
328254225Speter}
329254225Speter
330254225Speter/*
331254225Speter * Rear delimiter for word search when the keyword ends in
332254225Speter * (i.e., consists of) a non-word character.  See v_searchw below.
333254225Speter */
334254225Speter#define RE_NWSTOP	L("([^[:alnum:]_]|$)")
335254225Speter#define RE_NWSTOP_LEN	(SIZE(RE_NWSTOP) - 1)
336254225Speter
337254225Speter/*
33819304Speter * v_searchw -- [count]^A
33919304Speter *	Search for the word under the cursor.
34019304Speter *
34119304Speter * PUBLIC: int v_searchw __P((SCR *, VICMD *));
34219304Speter */
34319304Speterint
344254225Speterv_searchw(SCR *sp, VICMD *vp)
34519304Speter{
34619304Speter	size_t blen, len;
34719304Speter	int rval;
348254225Speter	CHAR_T *bp, *p;
34919304Speter
350254225Speter	/* An upper bound for the SIZE of the RE under construction. */
351254225Speter	len = VIP(sp)->klen + MAX(RE_WSTART_LEN, 1)
352254225Speter	    + MAX(RE_WSTOP_LEN, RE_NWSTOP_LEN);
353254225Speter	GET_SPACE_RETW(sp, bp, blen, len);
354254225Speter	p = bp;
35519304Speter
356254225Speter	/* Only the first character can be non-word, see v_curword. */
357254225Speter	if (inword(VIP(sp)->keyw[0])) {
358254225Speter		MEMCPY(p, RE_WSTART, RE_WSTART_LEN);
359254225Speter		p += RE_WSTART_LEN;
360254225Speter	} else if (is_special(VIP(sp)->keyw[0])) {
361254225Speter		MEMCPY(p, L("\\"), 1);
362254225Speter		p += 1;
363254225Speter	}
364254225Speter
365254225Speter	MEMCPY(p, VIP(sp)->keyw, VIP(sp)->klen);
366254225Speter	p += VIP(sp)->klen;
367254225Speter
368254225Speter	if (inword(p[-1])) {
369254225Speter		MEMCPY(p, RE_WSTOP, RE_WSTOP_LEN);
370254225Speter		p += RE_WSTOP_LEN;
371254225Speter	} else {
372254225Speter		/*
373254225Speter		 * The keyword is a single non-word character.
374254225Speter		 * We want it to stay the same when typing ^A several times
375254225Speter		 * in a row, just the way the other cases behave.
376254225Speter		 */
377254225Speter		MEMCPY(p, RE_NWSTOP, RE_NWSTOP_LEN);
378254225Speter		p += RE_NWSTOP_LEN;
379254225Speter	}
380254225Speter
381254225Speter	len = p - bp;
38219304Speter	rval = v_search(sp, vp, bp, len, SEARCH_SET, FORWARD);
38319304Speter
384254225Speter	FREE_SPACEW(sp, bp, blen);
38519304Speter	return (rval);
38619304Speter}
38719304Speter
38819304Speter/*
38919304Speter * v_search --
39019304Speter *	The search commands.
39119304Speter */
39219304Speterstatic int
393254225Speterv_search(SCR *sp, VICMD *vp, CHAR_T *ptrn, size_t plen, u_int flags, dir_t dir)
39419304Speter{
39519304Speter	/* Display messages. */
39619304Speter	LF_SET(SEARCH_MSG);
39719304Speter
39819304Speter	/* If it's a motion search, offset past end-of-line is okay. */
39919304Speter	if (ISMOTION(vp))
40019304Speter		LF_SET(SEARCH_EOL);
40119304Speter
40219304Speter	/*
40319304Speter	 * XXX
40419304Speter	 * Warn if the search wraps.  See the comment above, in v_exaddr().
40519304Speter	 */
40619304Speter	if (!KEYS_WAITING(sp))
40719304Speter		LF_SET(SEARCH_WMSG);
40819304Speter
40919304Speter	switch (dir) {
41019304Speter	case BACKWARD:
41119304Speter		if (b_search(sp,
41219304Speter		    &vp->m_start, &vp->m_stop, ptrn, plen, NULL, flags))
41319304Speter			return (1);
41419304Speter		break;
41519304Speter	case FORWARD:
41619304Speter		if (f_search(sp,
41719304Speter		    &vp->m_start, &vp->m_stop, ptrn, plen, NULL, flags))
41819304Speter			return (1);
41919304Speter		break;
42019304Speter	case NOTSET:
42119304Speter		msgq(sp, M_ERR, "189|No previous search pattern");
42219304Speter		return (1);
42319304Speter	default:
42419304Speter		abort();
42519304Speter	}
42619304Speter
42719304Speter	/* Correct motion commands, otherwise, simply move to the location. */
42819304Speter	if (ISMOTION(vp)) {
42919304Speter		if (v_correct(sp, vp, 0))
43019304Speter			return(1);
43119304Speter	} else
43219304Speter		vp->m_final = vp->m_stop;
43319304Speter	return (0);
43419304Speter}
43519304Speter
43619304Speter/*
43719304Speter * v_correct --
43819304Speter *	Handle command with a search as the motion.
43919304Speter *
44019304Speter * !!!
44119304Speter * Historically, commands didn't affect the line searched to/from if the
44219304Speter * motion command was a search and the final position was the start/end
44319304Speter * of the line.  There were some special cases and vi was not consistent;
44419304Speter * it was fairly easy to confuse it.  For example, given the two lines:
44519304Speter *
44619304Speter *	abcdefghi
44719304Speter *	ABCDEFGHI
44819304Speter *
44919304Speter * placing the cursor on the 'A' and doing y?$ would so confuse it that 'h'
45019304Speter * 'k' and put would no longer work correctly.  In any case, we try to do
45119304Speter * the right thing, but it's not going to exactly match historic practice.
45219304Speter *
45319304Speter * PUBLIC: int v_correct __P((SCR *, VICMD *, int));
45419304Speter */
45519304Speterint
456254225Speterv_correct(SCR *sp, VICMD *vp, int isdelta)
45719304Speter{
45819304Speter	dir_t dir;
45919304Speter	MARK m;
46019304Speter	size_t len;
46119304Speter
46219304Speter	/*
46319304Speter	 * !!!
46419304Speter	 * We may have wrapped if wrapscan was set, and we may have returned
46519304Speter	 * to the position where the cursor started.  Historic vi didn't cope
46619304Speter	 * with this well.  Yank wouldn't beep, but the first put after the
46719304Speter	 * yank would move the cursor right one column (without adding any
46819304Speter	 * text) and the second would put a copy of the current line.  The
46919304Speter	 * change and delete commands would beep, but would leave the cursor
47019304Speter	 * on the colon command line.  I believe that there are macros that
47119304Speter	 * depend on delete, at least, failing.  For now, commands that use
47219304Speter	 * search as a motion component fail when the search returns to the
47319304Speter	 * original cursor position.
47419304Speter	 */
47519304Speter	if (vp->m_start.lno == vp->m_stop.lno &&
47619304Speter	    vp->m_start.cno == vp->m_stop.cno) {
47719304Speter		msgq(sp, M_BERR, "190|Search wrapped to original position");
47819304Speter		return (1);
47919304Speter	}
48019304Speter
48119304Speter	/*
48219304Speter	 * !!!
48319304Speter	 * Searches become line mode operations if there was a delta specified
48419304Speter	 * to the search pattern.
48519304Speter	 */
48619304Speter	if (isdelta)
48719304Speter		F_SET(vp, VM_LMODE);
48819304Speter
48919304Speter	/*
49019304Speter	 * If the motion is in the reverse direction, switch the start and
49119304Speter	 * stop MARK's so that it's in a forward direction.  (There's no
49219304Speter	 * reason for this other than to make the tests below easier.  The
49319304Speter	 * code in vi.c:vi() would have done the switch.)  Both forward
49419304Speter	 * and backward motions can happen for any kind of search command
49519304Speter	 * because of the wrapscan option.
49619304Speter	 */
49719304Speter	if (vp->m_start.lno > vp->m_stop.lno ||
498254225Speter	    (vp->m_start.lno == vp->m_stop.lno &&
499254225Speter	    vp->m_start.cno > vp->m_stop.cno)) {
50019304Speter		m = vp->m_start;
50119304Speter		vp->m_start = vp->m_stop;
50219304Speter		vp->m_stop = m;
50319304Speter		dir = BACKWARD;
50419304Speter	} else
50519304Speter		dir = FORWARD;
50619304Speter
50719304Speter	/*
50819304Speter	 * BACKWARD:
50919304Speter	 *	Delete and yank commands move to the end of the range.
51019304Speter	 *	Ignore others.
51119304Speter	 *
51219304Speter	 * FORWARD:
51319304Speter	 *	Delete and yank commands don't move.  Ignore others.
51419304Speter	 */
51519304Speter	vp->m_final = vp->m_start;
51619304Speter
51719304Speter	/*
51819304Speter	 * !!!
51919304Speter	 * Delta'd searches don't correct based on column positions.
52019304Speter	 */
52119304Speter	if (isdelta)
52219304Speter		return (0);
52319304Speter
52419304Speter	/*
52519304Speter	 * !!!
52619304Speter	 * Backward searches starting at column 0, and forward searches ending
52719304Speter	 * at column 0 are corrected to the last column of the previous line.
52819304Speter	 * Otherwise, adjust the starting/ending point to the character before
52919304Speter	 * the current one (this is safe because we know the search had to move
53019304Speter	 * to succeed).
53119304Speter	 *
53219304Speter	 * Searches become line mode operations if they start at the first
53319304Speter	 * nonblank and end at column 0 of another line.
53419304Speter	 */
53519304Speter	if (vp->m_start.lno < vp->m_stop.lno && vp->m_stop.cno == 0) {
53619304Speter		if (db_get(sp, --vp->m_stop.lno, DBG_FATAL, NULL, &len))
53719304Speter			return (1);
53819304Speter		vp->m_stop.cno = len ? len - 1 : 0;
53919304Speter		len = 0;
54019304Speter		if (nonblank(sp, vp->m_start.lno, &len))
54119304Speter			return (1);
54219304Speter		if (vp->m_start.cno <= len)
54319304Speter			F_SET(vp, VM_LMODE);
54419304Speter	} else
54519304Speter		--vp->m_stop.cno;
54619304Speter
54719304Speter	return (0);
54819304Speter}
549