xref: /netbsd/external/bsd/nvi/dist/vi/v_search.c (revision cc73507a)
1*cc73507aSchristos /*	$NetBSD: v_search.c,v 1.6 2014/01/26 21:43:45 christos Exp $ */
23a571abcSchristos /*-
33a571abcSchristos  * Copyright (c) 1992, 1993, 1994
43a571abcSchristos  *	The Regents of the University of California.  All rights reserved.
53a571abcSchristos  * Copyright (c) 1992, 1993, 1994, 1995, 1996
63a571abcSchristos  *	Keith Bostic.  All rights reserved.
73a571abcSchristos  *
83a571abcSchristos  * See the LICENSE file for redistribution information.
93a571abcSchristos  */
103a571abcSchristos 
113a571abcSchristos #include "config.h"
123a571abcSchristos 
13*cc73507aSchristos #include <sys/cdefs.h>
14*cc73507aSchristos #if 0
153a571abcSchristos #ifndef lint
163a571abcSchristos static const char sccsid[] = "Id: v_search.c,v 10.30 2001/09/11 20:52:46 skimo Exp  (Berkeley) Date: 2001/09/11 20:52:46 ";
173a571abcSchristos #endif /* not lint */
18*cc73507aSchristos #else
19*cc73507aSchristos __RCSID("$NetBSD: v_search.c,v 1.6 2014/01/26 21:43:45 christos Exp $");
20*cc73507aSchristos #endif
213a571abcSchristos 
223a571abcSchristos #include <sys/types.h>
233a571abcSchristos #include <sys/queue.h>
243a571abcSchristos #include <sys/time.h>
253a571abcSchristos 
263a571abcSchristos #include <bitstring.h>
273a571abcSchristos #include <ctype.h>
283a571abcSchristos #include <errno.h>
293a571abcSchristos #include <limits.h>
303a571abcSchristos #include <stdio.h>
313a571abcSchristos #include <stdlib.h>
323a571abcSchristos #include <string.h>
333a571abcSchristos 
343a571abcSchristos #include "../common/common.h"
353a571abcSchristos #include "vi.h"
363a571abcSchristos #include "../ipc/ip.h"
373a571abcSchristos 
383a571abcSchristos static int v_exaddr __P((SCR *, VICMD *, dir_t));
393a571abcSchristos static int v_search __P((SCR *, VICMD *, CHAR_T *, size_t, u_int, dir_t));
403a571abcSchristos 
413a571abcSchristos /*
423a571abcSchristos  * v_srch -- [count]?RE[? offset]
433a571abcSchristos  *	Ex address search backward.
443a571abcSchristos  *
453a571abcSchristos  * PUBLIC: int v_searchb __P((SCR *, VICMD *));
463a571abcSchristos  */
473a571abcSchristos int
v_searchb(SCR * sp,VICMD * vp)483a571abcSchristos v_searchb(SCR *sp, VICMD *vp)
493a571abcSchristos {
503a571abcSchristos 	return (v_exaddr(sp, vp, BACKWARD));
513a571abcSchristos }
523a571abcSchristos 
533a571abcSchristos /*
543a571abcSchristos  * v_searchf -- [count]/RE[/ offset]
553a571abcSchristos  *	Ex address search forward.
563a571abcSchristos  *
573a571abcSchristos  * PUBLIC: int v_searchf __P((SCR *, VICMD *));
583a571abcSchristos  */
593a571abcSchristos int
v_searchf(SCR * sp,VICMD * vp)603a571abcSchristos v_searchf(SCR *sp, VICMD *vp)
613a571abcSchristos {
623a571abcSchristos 	return (v_exaddr(sp, vp, FORWARD));
633a571abcSchristos }
643a571abcSchristos 
653a571abcSchristos /*
663a571abcSchristos  * v_exaddr --
673a571abcSchristos  *	Do a vi search (which is really an ex address).
683a571abcSchristos  */
693a571abcSchristos static int
v_exaddr(SCR * sp,VICMD * vp,dir_t dir)703a571abcSchristos v_exaddr(SCR *sp, VICMD *vp, dir_t dir)
713a571abcSchristos {
7208d478e3Schristos 	static EXCMDLIST fake = { .name = L("search") };
733a571abcSchristos 	EXCMD *cmdp;
743a571abcSchristos 	WIN *wp;
753a571abcSchristos 	TEXT *tp;
763a571abcSchristos 	db_recno_t s_lno;
773a571abcSchristos 	size_t len, s_cno, tlen;
78defa1437Schristos 	int error, nb, type;
793a571abcSchristos 	char buf[20];
803a571abcSchristos 	CHAR_T *cmd, *t;
8108d478e3Schristos 	const CHAR_T *w;
823a571abcSchristos 	size_t wlen;
833a571abcSchristos 
843a571abcSchristos 	/*
853a571abcSchristos 	 * !!!
863a571abcSchristos 	 * If using the search command as a motion, any addressing components
873a571abcSchristos 	 * are lost, i.e. y/ptrn/+2, when repeated, is the same as y/ptrn/.
883a571abcSchristos 	 */
893a571abcSchristos 	if (F_ISSET(vp, VC_ISDOT))
903a571abcSchristos 		return (v_search(sp, vp,
913a571abcSchristos 		    NULL, 0, SEARCH_PARSE | SEARCH_MSG | SEARCH_SET, dir));
923a571abcSchristos 
933a571abcSchristos 	/* Get the search pattern. */
943a571abcSchristos 	if (v_tcmd(sp, vp, dir == BACKWARD ? CH_BSEARCH : CH_FSEARCH,
953a571abcSchristos 	    TXT_BS | TXT_CR | TXT_ESCAPE | TXT_PROMPT |
963a571abcSchristos 	    (O_ISSET(sp, O_SEARCHINCR) ? TXT_SEARCHINCR : 0)))
973a571abcSchristos 		return (1);
983a571abcSchristos 
990b5c88f5Schristos 	tp = TAILQ_FIRST(&sp->tiq);
1003a571abcSchristos 
1013a571abcSchristos 	/* If the user backspaced over the prompt, do nothing. */
1023a571abcSchristos 	if (tp->term == TERM_BS)
1033a571abcSchristos 		return (1);
1043a571abcSchristos 
1053a571abcSchristos 	/*
1063a571abcSchristos 	 * If the user was doing an incremental search, then we've already
1073a571abcSchristos 	 * updated the cursor and moved to the right location.  Return the
1083a571abcSchristos 	 * correct values, we're done.
1093a571abcSchristos 	 */
1103a571abcSchristos 	if (tp->term == TERM_SEARCH) {
1113a571abcSchristos 		vp->m_stop.lno = sp->lno;
1123a571abcSchristos 		vp->m_stop.cno = sp->cno;
1133a571abcSchristos 		if (ISMOTION(vp))
1143a571abcSchristos 			return (v_correct(sp, vp, 0));
1153a571abcSchristos 		vp->m_final = vp->m_stop;
1163a571abcSchristos 		return (0);
1173a571abcSchristos 	}
1183a571abcSchristos 
1193a571abcSchristos 	/*
1203a571abcSchristos 	 * If the user entered <escape> or <carriage-return>, the length is
1213a571abcSchristos 	 * 1 and the right thing will happen, i.e. the prompt will be used
1223a571abcSchristos 	 * as a command character.
1233a571abcSchristos 	 *
1243a571abcSchristos 	 * Build a fake ex command structure.
1253a571abcSchristos 	 */
1263a571abcSchristos 	wp = sp->wp;
1273a571abcSchristos 	wp->excmd.cp = tp->lb;
1283a571abcSchristos 	wp->excmd.clen = tp->len;
1293a571abcSchristos 	F_INIT(&wp->excmd, E_VISEARCH);
1303a571abcSchristos 
1313a571abcSchristos 	/*
1323a571abcSchristos 	 * XXX
1333a571abcSchristos 	 * Warn if the search wraps.  This is a pretty special case, but it's
1343a571abcSchristos 	 * nice feature that wasn't in the original implementations of ex/vi.
1353a571abcSchristos 	 * (It was added at some point to System V's version.)  This message
1363a571abcSchristos 	 * is only displayed if there are no keys in the queue. The problem is
1373a571abcSchristos 	 * the command is going to succeed, and the message is informational,
1383a571abcSchristos 	 * not an error.  If a macro displays it repeatedly, e.g., the pattern
1393a571abcSchristos 	 * only occurs once in the file and wrapscan is set, you lose big.  For
1403a571abcSchristos 	 * example, if the macro does something like:
1413a571abcSchristos 	 *
1423a571abcSchristos 	 *	:map K /pattern/^MjK
1433a571abcSchristos 	 *
1443a571abcSchristos 	 * Each search will display the message, but the following "/pattern/"
1453a571abcSchristos 	 * will immediately overwrite it, with strange results.  The System V
1463a571abcSchristos 	 * vi displays the "wrapped" message multiple times, but because it's
1473a571abcSchristos 	 * overwritten each time, it's not as noticeable.  As we don't discard
1483a571abcSchristos 	 * messages, it's a real problem for us.
1493a571abcSchristos 	 */
1503a571abcSchristos 	if (!KEYS_WAITING(sp))
1513a571abcSchristos 		F_SET(&wp->excmd, E_SEARCH_WMSG);
1523a571abcSchristos 
1533a571abcSchristos 	/* Save the current line/column. */
1543a571abcSchristos 	s_lno = sp->lno;
1553a571abcSchristos 	s_cno = sp->cno;
1563a571abcSchristos 
1573a571abcSchristos 	/*
1583a571abcSchristos 	 * !!!
1593a571abcSchristos 	 * Historically, vi / and ? commands were full-blown ex addresses,
1603a571abcSchristos 	 * including ';' delimiters, trailing <blank>'s, multiple search
1613a571abcSchristos 	 * strings (separated by semi-colons) and, finally, full-blown z
1623a571abcSchristos 	 * commands after the / and ? search strings.  (If the search was
1633a571abcSchristos 	 * being used as a motion, the trailing z command was ignored.
1643a571abcSchristos 	 * Also, we do some argument checking on the z command, to be sure
1653a571abcSchristos 	 * that it's not some other random command.) For multiple search
1663a571abcSchristos 	 * strings, leading <blank>'s at the second and subsequent strings
1673a571abcSchristos 	 * were eaten as well.  This has some (unintended?) side-effects:
1683a571abcSchristos 	 * the command /ptrn/;3 is legal and results in moving to line 3.
1693a571abcSchristos 	 * I suppose you could use it to optionally move to line 3...
1703a571abcSchristos 	 *
1713a571abcSchristos 	 * !!!
1723a571abcSchristos 	 * Historically, if any part of the search command failed, the cursor
1733a571abcSchristos 	 * remained unmodified (even if ; was used).  We have to play games
1743a571abcSchristos 	 * because the underlying ex parser thinks we're modifying the cursor
1753a571abcSchristos 	 * as we go, but I think we're compatible with historic practice.
1763a571abcSchristos 	 *
1773a571abcSchristos 	 * !!!
1783a571abcSchristos 	 * Historically, the command "/STRING/;   " failed, apparently it
1793a571abcSchristos 	 * confused the parser.  We're not that compatible.
1803a571abcSchristos 	 */
1813a571abcSchristos 	cmdp = &wp->excmd;
182defa1437Schristos 	if (ex_range(sp, cmdp, &error))
1833a571abcSchristos 		return (1);
1843a571abcSchristos 
1853a571abcSchristos 	/*
1863a571abcSchristos 	 * Remember where any remaining command information is, and clean
1873a571abcSchristos 	 * up the fake ex command.
1883a571abcSchristos 	 */
1893a571abcSchristos 	cmd = cmdp->cp;
1903a571abcSchristos 	len = cmdp->clen;
1913a571abcSchristos 	wp->excmd.clen = 0;
1923a571abcSchristos 
193defa1437Schristos 	if (error)
1943a571abcSchristos 		goto err2;
1953a571abcSchristos 
1963a571abcSchristos 	/* Copy out the new cursor position and make sure it's okay. */
1973a571abcSchristos 	switch (cmdp->addrcnt) {
1983a571abcSchristos 	case 1:
1993a571abcSchristos 		vp->m_stop = cmdp->addr1;
2003a571abcSchristos 		break;
2013a571abcSchristos 	case 2:
2023a571abcSchristos 		vp->m_stop = cmdp->addr2;
2033a571abcSchristos 		break;
2043a571abcSchristos 	}
2053a571abcSchristos 	if (!db_exist(sp, vp->m_stop.lno)) {
2063a571abcSchristos 		ex_badaddr(sp, &fake,
2073a571abcSchristos 		    vp->m_stop.lno == 0 ? A_ZERO : A_EOF, NUM_OK);
2083a571abcSchristos 		goto err2;
2093a571abcSchristos 	}
2103a571abcSchristos 
2113a571abcSchristos 	/*
2123a571abcSchristos 	 * !!!
2133a571abcSchristos 	 * Historic practice is that a trailing 'z' was ignored if it was a
2143a571abcSchristos 	 * motion command.  Should probably be an error, but not worth the
2153a571abcSchristos 	 * effort.
2163a571abcSchristos 	 */
2173a571abcSchristos 	if (ISMOTION(vp))
2183a571abcSchristos 		return (v_correct(sp, vp, F_ISSET(cmdp, E_DELTA)));
2193a571abcSchristos 
2203a571abcSchristos 	/*
2213a571abcSchristos 	 * !!!
2223a571abcSchristos 	 * Historically, if it wasn't a motion command, a delta in the search
2233a571abcSchristos 	 * pattern turns it into a first nonblank movement.
2243a571abcSchristos 	 */
2253a571abcSchristos 	nb = F_ISSET(cmdp, E_DELTA);
2263a571abcSchristos 
2273a571abcSchristos 	/* Check for the 'z' command. */
2283a571abcSchristos 	if (len != 0) {
2293a571abcSchristos 		if (*cmd != 'z')
2303a571abcSchristos 			goto err1;
2313a571abcSchristos 
2323a571abcSchristos 		/* No blanks, just like the z command. */
2333a571abcSchristos 		for (t = cmd + 1, tlen = len - 1; tlen > 0; ++t, --tlen)
23408d478e3Schristos 			if (!ISDIGIT((UCHAR_T)*t))
2353a571abcSchristos 				break;
2363a571abcSchristos 		if (tlen &&
2373a571abcSchristos 		    (*t == '-' || *t == '.' || *t == '+' || *t == '^')) {
2383a571abcSchristos 			++t;
2393a571abcSchristos 			--tlen;
2403a571abcSchristos 			type = 1;
2413a571abcSchristos 		} else
2423a571abcSchristos 			type = 0;
2433a571abcSchristos 		if (tlen)
2443a571abcSchristos 			goto err1;
2453a571abcSchristos 
2463a571abcSchristos 		/* The z command will do the nonblank for us. */
2473a571abcSchristos 		nb = 0;
2483a571abcSchristos 
2493a571abcSchristos 		/* Default to z+. */
2503a571abcSchristos 		if (!type &&
2513a571abcSchristos 		    v_event_push(sp, NULL, L("+"), 1, CH_NOMAP | CH_QUOTED))
2523a571abcSchristos 			return (1);
2533a571abcSchristos 
2543a571abcSchristos 		/* Push the user's command. */
2553a571abcSchristos 		if (v_event_push(sp, NULL, cmd, len, CH_NOMAP | CH_QUOTED))
2563a571abcSchristos 			return (1);
2573a571abcSchristos 
2583a571abcSchristos 		/* Push line number so get correct z display. */
2593a571abcSchristos 		tlen = snprintf(buf,
2603a571abcSchristos 		    sizeof(buf), "%lu", (u_long)vp->m_stop.lno);
2613a571abcSchristos 		CHAR2INT(sp, buf, tlen, w, wlen);
2623a571abcSchristos 		if (v_event_push(sp, NULL, w, wlen, CH_NOMAP | CH_QUOTED))
2633a571abcSchristos 			return (1);
2643a571abcSchristos 
2653a571abcSchristos 		/* Don't refresh until after 'z' happens. */
2663a571abcSchristos 		F_SET(VIP(sp), VIP_S_REFRESH);
2673a571abcSchristos 	}
2683a571abcSchristos 
2693a571abcSchristos 	/* Non-motion commands move to the end of the range. */
2703a571abcSchristos 	vp->m_final = vp->m_stop;
2713a571abcSchristos 	if (nb) {
2723a571abcSchristos 		F_CLR(vp, VM_RCM_MASK);
2733a571abcSchristos 		F_SET(vp, VM_RCM_SETFNB);
2743a571abcSchristos 	}
2753a571abcSchristos 	return (0);
2763a571abcSchristos 
2773a571abcSchristos err1:	msgq(sp, M_ERR,
2783a571abcSchristos 	    "188|Characters after search string, line offset and/or z command");
2793a571abcSchristos err2:	vp->m_final.lno = s_lno;
2803a571abcSchristos 	vp->m_final.cno = s_cno;
2813a571abcSchristos 	return (1);
2823a571abcSchristos }
2833a571abcSchristos 
2843a571abcSchristos /*
2853a571abcSchristos  * v_searchN -- N
2863a571abcSchristos  *	Reverse last search.
2873a571abcSchristos  *
2883a571abcSchristos  * PUBLIC: int v_searchN __P((SCR *, VICMD *));
2893a571abcSchristos  */
2903a571abcSchristos int
v_searchN(SCR * sp,VICMD * vp)2913a571abcSchristos v_searchN(SCR *sp, VICMD *vp)
2923a571abcSchristos {
2933a571abcSchristos 	dir_t dir;
2943a571abcSchristos 
2953a571abcSchristos 	switch (sp->searchdir) {
2963a571abcSchristos 	case BACKWARD:
2973a571abcSchristos 		dir = FORWARD;
2983a571abcSchristos 		break;
2993a571abcSchristos 	case FORWARD:
3003a571abcSchristos 		dir = BACKWARD;
3013a571abcSchristos 		break;
3023a571abcSchristos 	default:
3033a571abcSchristos 		dir = sp->searchdir;
3043a571abcSchristos 		break;
3053a571abcSchristos 	}
3063a571abcSchristos 	return (v_search(sp, vp, NULL, 0, SEARCH_PARSE, dir));
3073a571abcSchristos }
3083a571abcSchristos 
3093a571abcSchristos /*
3103a571abcSchristos  * v_searchn -- n
3113a571abcSchristos  *	Repeat last search.
3123a571abcSchristos  *
3133a571abcSchristos  * PUBLIC: int v_searchn __P((SCR *, VICMD *));
3143a571abcSchristos  */
3153a571abcSchristos int
v_searchn(SCR * sp,VICMD * vp)3163a571abcSchristos v_searchn(SCR *sp, VICMD *vp)
3173a571abcSchristos {
3183a571abcSchristos 	return (v_search(sp, vp, NULL, 0, SEARCH_PARSE, sp->searchdir));
3193a571abcSchristos }
3203a571abcSchristos 
3213a571abcSchristos /*
3223a571abcSchristos  * is_especial --
3233a571abcSchristos  *	Test if the character is special in an extended RE.
3243a571abcSchristos  */
3253a571abcSchristos static int
is_especial(CHAR_T c)3263a571abcSchristos is_especial(CHAR_T c)
3273a571abcSchristos {
3283a571abcSchristos 	/*
3293a571abcSchristos 	 * !!!
3303a571abcSchristos 	 * Right-brace is not an ERE special according to IEEE 1003.1-2001.
3313a571abcSchristos 	 * Right-parenthesis is a special character (so quoting doesn't hurt),
3323a571abcSchristos 	 * though it has no special meaning in this context, viz. at the
3333a571abcSchristos 	 * beginning of the string.  So we need not quote it.  Then again,
3343a571abcSchristos 	 * see the BUGS section in regex/re_format.7.
3353a571abcSchristos 	 * The tilde is vi-specific, of course.
3363a571abcSchristos 	 */
3373a571abcSchristos 	return (STRCHR(L(".[\\()*+?{|^$~"), c) && c);
3383a571abcSchristos }
3393a571abcSchristos 
3403a571abcSchristos /*
3413a571abcSchristos  * Rear delimiter for word search when the keyword ends in
3423a571abcSchristos  * (i.e., consists of) a non-word character.  See v_searchw below.
3433a571abcSchristos  */
3443a571abcSchristos #define RE_NWSTOP	L("([^[:alnum:]_]|$)")
3453a571abcSchristos #define RE_NWSTOP_LEN	(SIZE(RE_NWSTOP) - 1)
3463a571abcSchristos 
3473a571abcSchristos /*
3483a571abcSchristos  * v_searchw -- [count]^A
3493a571abcSchristos  *	Search for the word under the cursor.
3503a571abcSchristos  *
3513a571abcSchristos  * PUBLIC: int v_searchw __P((SCR *, VICMD *));
3523a571abcSchristos  */
3533a571abcSchristos int
v_searchw(SCR * sp,VICMD * vp)3543a571abcSchristos v_searchw(SCR *sp, VICMD *vp)
3553a571abcSchristos {
35608d478e3Schristos 	size_t blen, len;
3573a571abcSchristos 	int rval;
3583a571abcSchristos 	CHAR_T *bp, *p;
3593a571abcSchristos 
36008d478e3Schristos 	len = VIP(sp)->klen + MAX(RE_WSTART_LEN, 1)
36108d478e3Schristos 	    + MAX(RE_WSTOP_LEN, RE_NWSTOP_LEN);
36208d478e3Schristos 
3633a571abcSchristos 	GET_SPACE_RETW(sp, bp, blen, len);
3643a571abcSchristos 	p = bp;
3653a571abcSchristos 
3663a571abcSchristos 	/* Only the first character can be non-word, see v_curword. */
3673a571abcSchristos 	if (inword(VIP(sp)->keyw[0]))
3683a571abcSchristos 		p = MEMPCPY(p, RE_WSTART, RE_WSTART_LEN);
3693a571abcSchristos 	else if (is_especial(VIP(sp)->keyw[0]))
3703a571abcSchristos 		p = MEMPCPY(p, L("\\"), 1);
3713a571abcSchristos 
3723a571abcSchristos 	p = MEMPCPY(p, VIP(sp)->keyw, VIP(sp)->klen);
3733a571abcSchristos 
3743a571abcSchristos 	if (inword(p[-1]))
3753a571abcSchristos 		p = MEMPCPY(p, RE_WSTOP, RE_WSTOP_LEN);
3763a571abcSchristos 	else
3773a571abcSchristos 		/*
3783a571abcSchristos 		 * The keyword is a single non-word character.
3793a571abcSchristos 		 * We want it to stay the same when typing ^A several times
3803a571abcSchristos 		 * in a row, just the way the other cases behave.
3813a571abcSchristos 		 */
3823a571abcSchristos 		p = MEMPCPY(p, RE_NWSTOP, RE_NWSTOP_LEN);
3833a571abcSchristos 
3843a571abcSchristos 	len = p - bp;
3853a571abcSchristos 	rval = v_search(sp, vp, bp, len, SEARCH_SET | SEARCH_EXTEND, FORWARD);
3863a571abcSchristos 
3873a571abcSchristos 	FREE_SPACEW(sp, bp, blen);
3883a571abcSchristos 	return (rval);
3893a571abcSchristos }
3903a571abcSchristos 
3913a571abcSchristos /*
3923a571abcSchristos  * v_esearch -- <dialog box>
3933a571abcSchristos  *	Search command from the screen.
3943a571abcSchristos  *
3953a571abcSchristos  * PUBLIC: int v_esearch __P((SCR *, VICMD *));
3963a571abcSchristos  */
3973a571abcSchristos int
v_esearch(SCR * sp,VICMD * vp)3983a571abcSchristos v_esearch(SCR *sp, VICMD *vp)
3993a571abcSchristos {
4003a571abcSchristos 	int flags;
4013a571abcSchristos 
4023a571abcSchristos 	LF_INIT(SEARCH_NOOPT);
4033a571abcSchristos 	if (FL_ISSET(vp->ev.e_flags, VI_SEARCH_EXT))
4043a571abcSchristos 		LF_SET(SEARCH_EXTEND);
4053a571abcSchristos 	if (FL_ISSET(vp->ev.e_flags, VI_SEARCH_IC))
4063a571abcSchristos 		LF_SET(SEARCH_IC);
4073a571abcSchristos 	if (FL_ISSET(vp->ev.e_flags, VI_SEARCH_ICL))
4083a571abcSchristos 		LF_SET(SEARCH_ICL);
4093a571abcSchristos 	if (FL_ISSET(vp->ev.e_flags, VI_SEARCH_INCR))
4103a571abcSchristos 		LF_SET(SEARCH_INCR);
4113a571abcSchristos 	if (FL_ISSET(vp->ev.e_flags, VI_SEARCH_LIT))
4123a571abcSchristos 		LF_SET(SEARCH_LITERAL);
4133a571abcSchristos 	if (FL_ISSET(vp->ev.e_flags, VI_SEARCH_WR))
4143a571abcSchristos 		LF_SET(SEARCH_WRAP);
4153a571abcSchristos 	return (v_search(sp, vp, vp->ev.e_csp, vp->ev.e_len, flags,
4163a571abcSchristos 	    FL_ISSET(vp->ev.e_flags, VI_SEARCH_REV) ? BACKWARD : FORWARD));
4173a571abcSchristos }
4183a571abcSchristos 
4193a571abcSchristos /*
4203a571abcSchristos  * v_search --
4213a571abcSchristos  *	The search commands.
4223a571abcSchristos  */
4233a571abcSchristos static int
v_search(SCR * sp,VICMD * vp,CHAR_T * ptrn,size_t plen,u_int flags,dir_t dir)4243a571abcSchristos v_search(SCR *sp, VICMD *vp, CHAR_T *ptrn, size_t plen, u_int flags, dir_t dir)
4253a571abcSchristos {
4263a571abcSchristos 	/* Display messages. */
4273a571abcSchristos 	LF_SET(SEARCH_MSG);
4283a571abcSchristos 
4293a571abcSchristos 	/* If it's a motion search, offset past end-of-line is okay. */
4303a571abcSchristos 	if (ISMOTION(vp))
4313a571abcSchristos 		LF_SET(SEARCH_EOL);
4323a571abcSchristos 
4333a571abcSchristos 	/*
4343a571abcSchristos 	 * XXX
4353a571abcSchristos 	 * Warn if the search wraps.  See the comment above, in v_exaddr().
4363a571abcSchristos 	 */
4373a571abcSchristos 	if (!KEYS_WAITING(sp))
4383a571abcSchristos 		LF_SET(SEARCH_WMSG);
4393a571abcSchristos 
4403a571abcSchristos 	switch (dir) {
4413a571abcSchristos 	case BACKWARD:
4423a571abcSchristos 		if (b_search(sp,
4433a571abcSchristos 		    &vp->m_start, &vp->m_stop, ptrn, plen, NULL, flags))
4443a571abcSchristos 			return (1);
4453a571abcSchristos 		break;
4463a571abcSchristos 	case FORWARD:
4473a571abcSchristos 		if (f_search(sp,
4483a571abcSchristos 		    &vp->m_start, &vp->m_stop, ptrn, plen, NULL, flags))
4493a571abcSchristos 			return (1);
4503a571abcSchristos 		break;
4513a571abcSchristos 	case NOTSET:
4523a571abcSchristos 		msgq(sp, M_ERR, "189|No previous search pattern");
4533a571abcSchristos 		return (1);
4543a571abcSchristos 	default:
4553a571abcSchristos 		abort();
4563a571abcSchristos 	}
4573a571abcSchristos 
4583a571abcSchristos 	/* Correct motion commands, otherwise, simply move to the location. */
4593a571abcSchristos 	if (ISMOTION(vp)) {
4603a571abcSchristos 		if (v_correct(sp, vp, 0))
4613a571abcSchristos 			return(1);
4623a571abcSchristos 	} else
4633a571abcSchristos 		vp->m_final = vp->m_stop;
4643a571abcSchristos 	return (0);
4653a571abcSchristos }
4663a571abcSchristos 
4673a571abcSchristos /*
4683a571abcSchristos  * v_correct --
4693a571abcSchristos  *	Handle command with a search as the motion.
4703a571abcSchristos  *
4713a571abcSchristos  * !!!
4723a571abcSchristos  * Historically, commands didn't affect the line searched to/from if the
4733a571abcSchristos  * motion command was a search and the final position was the start/end
4743a571abcSchristos  * of the line.  There were some special cases and vi was not consistent;
4753a571abcSchristos  * it was fairly easy to confuse it.  For example, given the two lines:
4763a571abcSchristos  *
4773a571abcSchristos  *	abcdefghi
4783a571abcSchristos  *	ABCDEFGHI
4793a571abcSchristos  *
4803a571abcSchristos  * placing the cursor on the 'A' and doing y?$ would so confuse it that 'h'
4813a571abcSchristos  * 'k' and put would no longer work correctly.  In any case, we try to do
4823a571abcSchristos  * the right thing, but it's not going to exactly match historic practice.
4833a571abcSchristos  *
4843a571abcSchristos  * PUBLIC: int v_correct __P((SCR *, VICMD *, int));
4853a571abcSchristos  */
4863a571abcSchristos int
v_correct(SCR * sp,VICMD * vp,int isdelta)4873a571abcSchristos v_correct(SCR *sp, VICMD *vp, int isdelta)
4883a571abcSchristos {
4893a571abcSchristos 	MARK m;
4903a571abcSchristos 	size_t len;
4913a571abcSchristos 
4923a571abcSchristos 	/*
4933a571abcSchristos 	 * !!!
4943a571abcSchristos 	 * We may have wrapped if wrapscan was set, and we may have returned
4953a571abcSchristos 	 * to the position where the cursor started.  Historic vi didn't cope
4963a571abcSchristos 	 * with this well.  Yank wouldn't beep, but the first put after the
4973a571abcSchristos 	 * yank would move the cursor right one column (without adding any
4983a571abcSchristos 	 * text) and the second would put a copy of the current line.  The
4993a571abcSchristos 	 * change and delete commands would beep, but would leave the cursor
5003a571abcSchristos 	 * on the colon command line.  I believe that there are macros that
5013a571abcSchristos 	 * depend on delete, at least, failing.  For now, commands that use
5023a571abcSchristos 	 * search as a motion component fail when the search returns to the
5033a571abcSchristos 	 * original cursor position.
5043a571abcSchristos 	 */
5053a571abcSchristos 	if (vp->m_start.lno == vp->m_stop.lno &&
5063a571abcSchristos 	    vp->m_start.cno == vp->m_stop.cno) {
5073a571abcSchristos 		msgq(sp, M_BERR, "190|Search wrapped to original position");
5083a571abcSchristos 		return (1);
5093a571abcSchristos 	}
5103a571abcSchristos 
5113a571abcSchristos 	/*
5123a571abcSchristos 	 * !!!
5133a571abcSchristos 	 * Searches become line mode operations if there was a delta specified
5143a571abcSchristos 	 * to the search pattern.
5153a571abcSchristos 	 */
5163a571abcSchristos 	if (isdelta)
5173a571abcSchristos 		F_SET(vp, VM_LMODE);
5183a571abcSchristos 
5193a571abcSchristos 	/*
5203a571abcSchristos 	 * If the motion is in the reverse direction, switch the start and
5213a571abcSchristos 	 * stop MARK's so that it's in a forward direction.  (There's no
5223a571abcSchristos 	 * reason for this other than to make the tests below easier.  The
5233a571abcSchristos 	 * code in vi.c:vi() would have done the switch.)  Both forward
5243a571abcSchristos 	 * and backward motions can happen for any kind of search command
5253a571abcSchristos 	 * because of the wrapscan option.
5263a571abcSchristos 	 */
5273a571abcSchristos 	if (vp->m_start.lno > vp->m_stop.lno ||
52808d478e3Schristos 	    (vp->m_start.lno == vp->m_stop.lno &&
52908d478e3Schristos 	    vp->m_start.cno > vp->m_stop.cno)) {
5303a571abcSchristos 		m = vp->m_start;
5313a571abcSchristos 		vp->m_start = vp->m_stop;
5323a571abcSchristos 		vp->m_stop = m;
5334eee468dSchristos 	}
5343a571abcSchristos 
5353a571abcSchristos 	/*
5363a571abcSchristos 	 * BACKWARD:
5373a571abcSchristos 	 *	Delete and yank commands move to the end of the range.
5383a571abcSchristos 	 *	Ignore others.
5393a571abcSchristos 	 *
5403a571abcSchristos 	 * FORWARD:
5413a571abcSchristos 	 *	Delete and yank commands don't move.  Ignore others.
5423a571abcSchristos 	 */
5433a571abcSchristos 	vp->m_final = vp->m_start;
5443a571abcSchristos 
5453a571abcSchristos 	/*
5463a571abcSchristos 	 * !!!
5473a571abcSchristos 	 * Delta'd searches don't correct based on column positions.
5483a571abcSchristos 	 */
5493a571abcSchristos 	if (isdelta)
5503a571abcSchristos 		return (0);
5513a571abcSchristos 
5523a571abcSchristos 	/*
5533a571abcSchristos 	 * !!!
5543a571abcSchristos 	 * Backward searches starting at column 0, and forward searches ending
5553a571abcSchristos 	 * at column 0 are corrected to the last column of the previous line.
5563a571abcSchristos 	 * Otherwise, adjust the starting/ending point to the character before
5573a571abcSchristos 	 * the current one (this is safe because we know the search had to move
5583a571abcSchristos 	 * to succeed).
5593a571abcSchristos 	 *
5603a571abcSchristos 	 * Searches become line mode operations if they start at the first
5613a571abcSchristos 	 * nonblank and end at column 0 of another line.
5623a571abcSchristos 	 */
5633a571abcSchristos 	if (vp->m_start.lno < vp->m_stop.lno && vp->m_stop.cno == 0) {
5643a571abcSchristos 		if (db_get(sp, --vp->m_stop.lno, DBG_FATAL, NULL, &len))
5653a571abcSchristos 			return (1);
5663a571abcSchristos 		vp->m_stop.cno = len ? len - 1 : 0;
5673a571abcSchristos 		len = 0;
5683a571abcSchristos 		if (nonblank(sp, vp->m_start.lno, &len))
5693a571abcSchristos 			return (1);
5703a571abcSchristos 		if (vp->m_start.cno <= len)
5713a571abcSchristos 			F_SET(vp, VM_LMODE);
5723a571abcSchristos 	} else
5733a571abcSchristos 		--vp->m_stop.cno;
5743a571abcSchristos 
5753a571abcSchristos 	return (0);
5763a571abcSchristos }
577