if (tlen >= sizeof(buf)) tlen = sizeof(buf) - 1;
v_exaddr(sp, vp, dir)	SCR *sp;	VICMD *vp;	dir_t dir;{	static EXCMDLIST fake = { "search" };	EXCMD *cmdp;	GS *gp;	TEXT *tp;	recno_t s_lno;	size_t len, s_cno, tlen;	int err, nb, type;	char *cmd, *t, buf[20];	/*	 * !!!	 * If using the search command as a motion, any addressing components	 * are lost, i.e. y/ptrn/+2, when repeated, is the same as y/ptrn/.	 */	if (F_ISSET(vp, VC_ISDOT))		return (v_search(sp, vp,		    NULL, 0, SEARCH_PARSE | SEARCH_MSG | SEARCH_SET, dir));	/* Get the search pattern. */	if (v_tcmd(sp, vp, dir == BACKWARD ? CH_BSEARCH : CH_FSEARCH,	    TXT_BS | TXT_CR | TXT_ESCAPE | TXT_PROMPT |	    (O_ISSET(sp, O_SEARCHINCR) ? TXT_SEARCHINCR : 0)))		return (1);	tp = sp->tiq.cqh_first;	/* If the user backspaced over the prompt, do nothing. */	if (tp->term == TERM_BS)		return (1);	/*	 * If the user was doing an incremental search, then we've already	 * updated the cursor and moved to the right location.  Return the	 * correct values, we're done.	 */	if (tp->term == TERM_SEARCH) {		vp->m_stop.lno = sp->lno;		vp->m_stop.cno = sp->cno;		if (ISMOTION(vp))			return (v_correct(sp, vp, 0));		vp->m_final = vp->m_stop;		return (0);	}	/*	 * If the user entered <escape> or <carriage-return>, the length is	 * 1 and the right thing will happen, i.e. the prompt will be used	 * as a command character.	 *	 * Build a fake ex command structure.	 */	gp = sp->gp;	gp->excmd.cp = tp->lb;	gp->excmd.clen = tp->len;	F_INIT(&gp->excmd, E_VISEARCH);	/*	 * XXX	 * Warn if the search wraps.  This is a pretty special case, but it's	 * nice feature that wasn't in the original implementations of ex/vi.	 * (It was added at some point to System V's version.)  This message	 * is only displayed if there are no keys in the queue. The problem is	 * the command is going to succeed, and the message is informational,	 * not an error.  If a macro displays it repeatedly, e.g., the pattern	 * only occurs once in the file and wrapscan is set, you lose big.  For	 * example, if the macro does something like:	 *	 *	:map K /pattern/^MjK	 *	 * Each search will display the message, but the following "/pattern/"	 * will immediately overwrite it, with strange results.  The System V	 * vi displays the "wrapped" message multiple times, but because it's	 * overwritten each time, it's not as noticeable.  As we don't discard	 * messages, it's a real problem for us.	 */	if (!KEYS_WAITING(sp))		F_SET(&gp->excmd, E_SEARCH_WMSG);			/* Save the current line/column. */	s_lno = sp->lno;	s_cno = sp->cno;	/*	 * !!!	 * Historically, vi / and ? commands were full-blown ex addresses,	 * including ';' delimiters, trailing <blank>'s, multiple search	 * strings (separated by semi-colons) and, finally, full-blown z	 * commands after the / and ? search strings.  (If the search was	 * being used as a motion, the trailing z command was ignored.	 * Also, we do some argument checking on the z command, to be sure	 * that it's not some other random command.) For multiple search	 * strings, leading <blank>'s at the second and subsequent strings	 * were eaten as well.  This has some (unintended?) side-effects:	 * the command /ptrn/;3 is legal and results in moving to line 3.	 * I suppose you could use it to optionally move to line 3...	 *	 * !!!	 * Historically, if any part of the search command failed, the cursor	 * remained unmodified (even if ; was used).  We have to play games	 * because the underlying ex parser thinks we're modifying the cursor	 * as we go, but I think we're compatible with historic practice.	 *	 * !!!	 * Historically, the command "/STRING/;   " failed, apparently it	 * confused the parser.  We're not that compatible.	 */	cmdp = &gp->excmd;	if (ex_range(sp, cmdp, &err))		return (1);		/*	 * Remember where any remaining command information is, and clean	 * up the fake ex command.	 */	cmd = cmdp->cp;	len = cmdp->clen;	gp->excmd.clen = 0;	if (err)		goto err2;	/* Copy out the new cursor position and make sure it's okay. */	switch (cmdp->addrcnt) {	case 1:		vp->m_stop = cmdp->addr1;		break;	case 2:		vp->m_stop = cmdp->addr2;		break;	}	if (!db_exist(sp, vp->m_stop.lno)) {		ex_badaddr(sp, &fake,		    vp->m_stop.lno == 0 ? A_ZERO : A_EOF, NUM_OK);		goto err2;	}	/*	 * !!!	 * Historic practice is that a trailing 'z' was ignored if it was a	 * motion command.  Should probably be an error, but not worth the	 * effort.	 */	if (ISMOTION(vp))		return (v_correct(sp, vp, F_ISSET(cmdp, E_DELTA)));			/*	 * !!!	 * Historically, if it wasn't a motion command, a delta in the search	 * pattern turns it into a first nonblank movement.	 */	nb = F_ISSET(cmdp, E_DELTA);	/* Check for the 'z' command. */	if (len != 0) {		if (*cmd != 'z')			goto err1;		/* No blanks, just like the z command. */		for (t = cmd + 1, tlen = len - 1; tlen > 0; ++t, --tlen)			if (!isdigit(*t))				break;		if (tlen &&		    (*t == '-' || *t == '.' || *t == '+' || *t == '^')) {			++t;			--tlen;			type = 1;		} else			type = 0;		if (tlen)			goto err1;		/* The z command will do the nonblank for us. */		nb = 0;		/* Default to z+. */		if (!type &&		    v_event_push(sp, NULL, "+", 1, CH_NOMAP | CH_QUOTED))			return (1);		/* Push the user's command. */		if (v_event_push(sp, NULL, cmd, len, CH_NOMAP | CH_QUOTED))			return (1);		/* Push line number so get correct z display. */		tlen = snprintf(buf,		    sizeof(buf), "%lu", (u_long)vp->m_stop.lno);		if (v_event_push(sp, NULL, buf, tlen, CH_NOMAP | CH_QUOTED))			return (1);		 		/* Don't refresh until after 'z' happens. */		F_SET(VIP(sp), VIP_S_REFRESH);	}	/* Non-motion commands move to the end of the range. */	vp->m_final = vp->m_stop;	if (nb) {		F_CLR(vp, VM_RCM_MASK);		F_SET(vp, VM_RCM_SETFNB);	}	return (0);err1:	msgq(sp, M_ERR,	    "188|Characters after search string, line offset and/or z command");err2:	vp->m_final.lno = s_lno;	vp->m_final.cno = s_cno;	return (1);}