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