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
1319304Speterstatic const char sccsid[] = "@(#)v_ch.c	10.8 (Berkeley) 3/6/96";
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 <limits.h>
2219304Speter#include <stdio.h>
2319304Speter#include <stdlib.h>
2419304Speter
2519304Speter#include "../common/common.h"
2619304Speter#include "vi.h"
2719304Speter
2819304Speterstatic void notfound __P((SCR *, ARG_CHAR_T));
2919304Speterstatic void noprev __P((SCR *));
3019304Speter
3119304Speter/*
3219304Speter * v_chrepeat -- [count];
3319304Speter *	Repeat the last F, f, T or t search.
3419304Speter *
3519304Speter * PUBLIC: int v_chrepeat __P((SCR *, VICMD *));
3619304Speter */
3719304Speterint
3819304Speterv_chrepeat(sp, vp)
3919304Speter	SCR *sp;
4019304Speter	VICMD *vp;
4119304Speter{
4219304Speter	vp->character = VIP(sp)->lastckey;
4319304Speter
4419304Speter	switch (VIP(sp)->csearchdir) {
4519304Speter	case CNOTSET:
4619304Speter		noprev(sp);
4719304Speter		return (1);
4819304Speter	case FSEARCH:
4919304Speter		return (v_chF(sp, vp));
5019304Speter	case fSEARCH:
5119304Speter		return (v_chf(sp, vp));
5219304Speter	case TSEARCH:
5319304Speter		return (v_chT(sp, vp));
5419304Speter	case tSEARCH:
5519304Speter		return (v_cht(sp, vp));
5619304Speter	default:
5719304Speter		abort();
5819304Speter	}
5919304Speter	/* NOTREACHED */
6019304Speter}
6119304Speter
6219304Speter/*
6319304Speter * v_chrrepeat -- [count],
6419304Speter *	Repeat the last F, f, T or t search in the reverse direction.
6519304Speter *
6619304Speter * PUBLIC: int v_chrrepeat __P((SCR *, VICMD *));
6719304Speter */
6819304Speterint
6919304Speterv_chrrepeat(sp, vp)
7019304Speter	SCR *sp;
7119304Speter	VICMD *vp;
7219304Speter{
7319304Speter	cdir_t savedir;
7419304Speter	int rval;
7519304Speter
7619304Speter	vp->character = VIP(sp)->lastckey;
7719304Speter	savedir = VIP(sp)->csearchdir;
7819304Speter
7919304Speter	switch (VIP(sp)->csearchdir) {
8019304Speter	case CNOTSET:
8119304Speter		noprev(sp);
8219304Speter		return (1);
8319304Speter	case FSEARCH:
8419304Speter		rval = v_chf(sp, vp);
8519304Speter		break;
8619304Speter	case fSEARCH:
8719304Speter		rval = v_chF(sp, vp);
8819304Speter		break;
8919304Speter	case TSEARCH:
9019304Speter		rval = v_cht(sp, vp);
9119304Speter		break;
9219304Speter	case tSEARCH:
9319304Speter		rval = v_chT(sp, vp);
9419304Speter		break;
9519304Speter	default:
9619304Speter		abort();
9719304Speter	}
9819304Speter	VIP(sp)->csearchdir = savedir;
9919304Speter	return (rval);
10019304Speter}
10119304Speter
10219304Speter/*
10319304Speter * v_cht -- [count]tc
10419304Speter *	Search forward in the line for the character before the next
10519304Speter *	occurrence of the specified character.
10619304Speter *
10719304Speter * PUBLIC: int v_cht __P((SCR *, VICMD *));
10819304Speter */
10919304Speterint
11019304Speterv_cht(sp, vp)
11119304Speter	SCR *sp;
11219304Speter	VICMD *vp;
11319304Speter{
11419304Speter	if (v_chf(sp, vp))
11519304Speter		return (1);
11619304Speter
11719304Speter	/*
11819304Speter	 * v_chf places the cursor on the character, where the 't'
11919304Speter	 * command wants it to its left.  We know this is safe since
12019304Speter	 * we had to move right for v_chf() to have succeeded.
12119304Speter	 */
12219304Speter	--vp->m_stop.cno;
12319304Speter
12419304Speter	/*
12519304Speter	 * Make any necessary correction to the motion decision made
12619304Speter	 * by the v_chf routine.
12719304Speter	 */
12819304Speter	if (!ISMOTION(vp))
12919304Speter		vp->m_final = vp->m_stop;
13019304Speter
13119304Speter	VIP(sp)->csearchdir = tSEARCH;
13219304Speter	return (0);
13319304Speter}
13419304Speter
13519304Speter/*
13619304Speter * v_chf -- [count]fc
13719304Speter *	Search forward in the line for the next occurrence of the
13819304Speter *	specified character.
13919304Speter *
14019304Speter * PUBLIC: int v_chf __P((SCR *, VICMD *));
14119304Speter */
14219304Speterint
14319304Speterv_chf(sp, vp)
14419304Speter	SCR *sp;
14519304Speter	VICMD *vp;
14619304Speter{
14719304Speter	size_t len;
14819304Speter	u_long cnt;
14919304Speter	int isempty, key;
15019304Speter	char *endp, *p, *startp;
15119304Speter
15219304Speter	/*
15319304Speter	 * !!!
15419304Speter	 * If it's a dot command, it doesn't reset the key for which we're
15519304Speter	 * searching, e.g. in "df1|f2|.|;", the ';' searches for a '2'.
15619304Speter	 */
15719304Speter	key = vp->character;
15819304Speter	if (!F_ISSET(vp, VC_ISDOT))
15919304Speter		VIP(sp)->lastckey = key;
16019304Speter	VIP(sp)->csearchdir = fSEARCH;
16119304Speter
16219304Speter	if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
16319304Speter		if (isempty)
16419304Speter			goto empty;
16519304Speter		return (1);
16619304Speter	}
16719304Speter
16819304Speter	if (len == 0) {
16919304Speterempty:		notfound(sp, key);
17019304Speter		return (1);
17119304Speter	}
17219304Speter
17319304Speter	endp = (startp = p) + len;
17419304Speter	p += vp->m_start.cno;
17519304Speter	for (cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
17619304Speter		while (++p < endp && *p != key);
17719304Speter		if (p == endp) {
17819304Speter			notfound(sp, key);
17919304Speter			return (1);
18019304Speter		}
18119304Speter	}
18219304Speter
18319304Speter	vp->m_stop.cno = p - startp;
18419304Speter
18519304Speter	/*
18619304Speter	 * Non-motion commands move to the end of the range.
18719304Speter	 * Delete and yank stay at the start, ignore others.
18819304Speter	 */
18919304Speter	vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
19019304Speter	return (0);
19119304Speter}
19219304Speter
19319304Speter/*
19419304Speter * v_chT -- [count]Tc
19519304Speter *	Search backward in the line for the character after the next
19619304Speter *	occurrence of the specified character.
19719304Speter *
19819304Speter * PUBLIC: int v_chT __P((SCR *, VICMD *));
19919304Speter */
20019304Speterint
20119304Speterv_chT(sp, vp)
20219304Speter	SCR *sp;
20319304Speter	VICMD *vp;
20419304Speter{
20519304Speter	if (v_chF(sp, vp))
20619304Speter		return (1);
20719304Speter
20819304Speter	/*
20919304Speter	 * v_chF places the cursor on the character, where the 'T'
21019304Speter	 * command wants it to its right.  We know this is safe since
21119304Speter	 * we had to move left for v_chF() to have succeeded.
21219304Speter	 */
21319304Speter	++vp->m_stop.cno;
21419304Speter	vp->m_final = vp->m_stop;
21519304Speter
21619304Speter	VIP(sp)->csearchdir = TSEARCH;
21719304Speter	return (0);
21819304Speter}
21919304Speter
22019304Speter/*
22119304Speter * v_chF -- [count]Fc
22219304Speter *	Search backward in the line for the next occurrence of the
22319304Speter *	specified character.
22419304Speter *
22519304Speter * PUBLIC: int v_chF __P((SCR *, VICMD *));
22619304Speter */
22719304Speterint
22819304Speterv_chF(sp, vp)
22919304Speter	SCR *sp;
23019304Speter	VICMD *vp;
23119304Speter{
23219304Speter	size_t len;
23319304Speter	u_long cnt;
23419304Speter	int isempty, key;
23519304Speter	char *endp, *p;
23619304Speter
23719304Speter	/*
23819304Speter	 * !!!
23919304Speter	 * If it's a dot command, it doesn't reset the key for which
24019304Speter	 * we're searching, e.g. in "df1|f2|.|;", the ';' searches
24119304Speter	 * for a '2'.
24219304Speter	 */
24319304Speter	key = vp->character;
24419304Speter	if (!F_ISSET(vp, VC_ISDOT))
24519304Speter		VIP(sp)->lastckey = key;
24619304Speter	VIP(sp)->csearchdir = FSEARCH;
24719304Speter
24819304Speter	if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
24919304Speter		if (isempty)
25019304Speter			goto empty;
25119304Speter		return (1);
25219304Speter	}
25319304Speter
25419304Speter	if (len == 0) {
25519304Speterempty:		notfound(sp, key);
25619304Speter		return (1);
25719304Speter	}
25819304Speter
25919304Speter	endp = p - 1;
26019304Speter	p += vp->m_start.cno;
26119304Speter	for (cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
26219304Speter		while (--p > endp && *p != key);
26319304Speter		if (p == endp) {
26419304Speter			notfound(sp, key);
26519304Speter			return (1);
26619304Speter		}
26719304Speter	}
26819304Speter
26919304Speter	vp->m_stop.cno = (p - endp) - 1;
27019304Speter
27119304Speter	/*
27219304Speter	 * All commands move to the end of the range.  Motion commands
27319304Speter	 * adjust the starting point to the character before the current
27419304Speter	 * one.
27519304Speter	 */
27619304Speter	vp->m_final = vp->m_stop;
27719304Speter	if (ISMOTION(vp))
27819304Speter		--vp->m_start.cno;
27919304Speter	return (0);
28019304Speter}
28119304Speter
28219304Speterstatic void
28319304Speternoprev(sp)
28419304Speter	SCR *sp;
28519304Speter{
28619304Speter	msgq(sp, M_BERR, "178|No previous F, f, T or t search");
28719304Speter}
28819304Speter
28919304Speterstatic void
29019304Speternotfound(sp, ch)
29119304Speter	SCR *sp;
29219304Speter	ARG_CHAR_T ch;
29319304Speter{
29419304Speter	msgq(sp, M_BERR, "179|%s not found", KEY_NAME(sp, ch));
29519304Speter}
296