1 /*	$OpenBSD: v_ch.c,v 1.5 2009/10/27 23:59:47 deraadt Exp $	*/
2 
3 /*-
4  * Copyright (c) 1992, 1993, 1994
5  *	The Regents of the University of California.  All rights reserved.
6  * Copyright (c) 1992, 1993, 1994, 1995, 1996
7  *	Keith Bostic.  All rights reserved.
8  *
9  * See the LICENSE file for redistribution information.
10  */
11 
12 #include "config.h"
13 
14 #include <sys/types.h>
15 #include <sys/queue.h>
16 #include <sys/time.h>
17 
18 #include <bitstring.h>
19 #include <limits.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 
23 #include "../common/common.h"
24 #include "vi.h"
25 
26 static void notfound(SCR *, ARG_CHAR_T);
27 static void noprev(SCR *);
28 
29 /*
30  * v_chrepeat -- [count];
31  *	Repeat the last F, f, T or t search.
32  *
33  * PUBLIC: int v_chrepeat(SCR *, VICMD *);
34  */
35 int
v_chrepeat(sp,vp)36 v_chrepeat(sp, vp)
37 	SCR *sp;
38 	VICMD *vp;
39 {
40 	vp->character = VIP(sp)->lastckey;
41 
42 	switch (VIP(sp)->csearchdir) {
43 	case CNOTSET:
44 		noprev(sp);
45 		return (1);
46 	case FSEARCH:
47 		return (v_chF(sp, vp));
48 	case fSEARCH:
49 		return (v_chf(sp, vp));
50 	case TSEARCH:
51 		return (v_chT(sp, vp));
52 	case tSEARCH:
53 		return (v_cht(sp, vp));
54 	default:
55 		abort();
56 	}
57 	/* NOTREACHED */
58 }
59 
60 /*
61  * v_chrrepeat -- [count],
62  *	Repeat the last F, f, T or t search in the reverse direction.
63  *
64  * PUBLIC: int v_chrrepeat(SCR *, VICMD *);
65  */
66 int
v_chrrepeat(sp,vp)67 v_chrrepeat(sp, vp)
68 	SCR *sp;
69 	VICMD *vp;
70 {
71 	cdir_t savedir;
72 	int rval;
73 
74 	vp->character = VIP(sp)->lastckey;
75 	savedir = VIP(sp)->csearchdir;
76 
77 	switch (VIP(sp)->csearchdir) {
78 	case CNOTSET:
79 		noprev(sp);
80 		return (1);
81 	case FSEARCH:
82 		rval = v_chf(sp, vp);
83 		break;
84 	case fSEARCH:
85 		rval = v_chF(sp, vp);
86 		break;
87 	case TSEARCH:
88 		rval = v_cht(sp, vp);
89 		break;
90 	case tSEARCH:
91 		rval = v_chT(sp, vp);
92 		break;
93 	default:
94 		abort();
95 	}
96 	VIP(sp)->csearchdir = savedir;
97 	return (rval);
98 }
99 
100 /*
101  * v_cht -- [count]tc
102  *	Search forward in the line for the character before the next
103  *	occurrence of the specified character.
104  *
105  * PUBLIC: int v_cht(SCR *, VICMD *);
106  */
107 int
v_cht(sp,vp)108 v_cht(sp, vp)
109 	SCR *sp;
110 	VICMD *vp;
111 {
112 	if (v_chf(sp, vp))
113 		return (1);
114 
115 	/*
116 	 * v_chf places the cursor on the character, where the 't'
117 	 * command wants it to its left.  We know this is safe since
118 	 * we had to move right for v_chf() to have succeeded.
119 	 */
120 	--vp->m_stop.cno;
121 
122 	/*
123 	 * Make any necessary correction to the motion decision made
124 	 * by the v_chf routine.
125 	 */
126 	if (!ISMOTION(vp))
127 		vp->m_final = vp->m_stop;
128 
129 	VIP(sp)->csearchdir = tSEARCH;
130 	return (0);
131 }
132 
133 /*
134  * v_chf -- [count]fc
135  *	Search forward in the line for the next occurrence of the
136  *	specified character.
137  *
138  * PUBLIC: int v_chf(SCR *, VICMD *);
139  */
140 int
v_chf(sp,vp)141 v_chf(sp, vp)
142 	SCR *sp;
143 	VICMD *vp;
144 {
145 	size_t len;
146 	u_long cnt;
147 	int isempty, key;
148 	char *endp, *p, *startp;
149 
150 	/*
151 	 * !!!
152 	 * If it's a dot command, it doesn't reset the key for which we're
153 	 * searching, e.g. in "df1|f2|.|;", the ';' searches for a '2'.
154 	 */
155 	key = vp->character;
156 	if (!F_ISSET(vp, VC_ISDOT))
157 		VIP(sp)->lastckey = key;
158 	VIP(sp)->csearchdir = fSEARCH;
159 
160 	if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
161 		if (isempty)
162 			goto empty;
163 		return (1);
164 	}
165 
166 	if (len == 0) {
167 empty:		notfound(sp, key);
168 		return (1);
169 	}
170 
171 	endp = (startp = p) + len;
172 	p += vp->m_start.cno;
173 	for (cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
174 		while (++p < endp && *p != key);
175 		if (p == endp) {
176 			notfound(sp, key);
177 			return (1);
178 		}
179 	}
180 
181 	vp->m_stop.cno = p - startp;
182 
183 	/*
184 	 * Non-motion commands move to the end of the range.
185 	 * Delete and yank stay at the start, ignore others.
186 	 */
187 	vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
188 	return (0);
189 }
190 
191 /*
192  * v_chT -- [count]Tc
193  *	Search backward in the line for the character after the next
194  *	occurrence of the specified character.
195  *
196  * PUBLIC: int v_chT(SCR *, VICMD *);
197  */
198 int
v_chT(sp,vp)199 v_chT(sp, vp)
200 	SCR *sp;
201 	VICMD *vp;
202 {
203 	if (v_chF(sp, vp))
204 		return (1);
205 
206 	/*
207 	 * v_chF places the cursor on the character, where the 'T'
208 	 * command wants it to its right.  We know this is safe since
209 	 * we had to move left for v_chF() to have succeeded.
210 	 */
211 	++vp->m_stop.cno;
212 	vp->m_final = vp->m_stop;
213 
214 	VIP(sp)->csearchdir = TSEARCH;
215 	return (0);
216 }
217 
218 /*
219  * v_chF -- [count]Fc
220  *	Search backward in the line for the next occurrence of the
221  *	specified character.
222  *
223  * PUBLIC: int v_chF(SCR *, VICMD *);
224  */
225 int
v_chF(sp,vp)226 v_chF(sp, vp)
227 	SCR *sp;
228 	VICMD *vp;
229 {
230 	size_t len;
231 	u_long cnt;
232 	int isempty, key;
233 	char *endp, *p;
234 
235 	/*
236 	 * !!!
237 	 * If it's a dot command, it doesn't reset the key for which
238 	 * we're searching, e.g. in "df1|f2|.|;", the ';' searches
239 	 * for a '2'.
240 	 */
241 	key = vp->character;
242 	if (!F_ISSET(vp, VC_ISDOT))
243 		VIP(sp)->lastckey = key;
244 	VIP(sp)->csearchdir = FSEARCH;
245 
246 	if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
247 		if (isempty)
248 			goto empty;
249 		return (1);
250 	}
251 
252 	if (len == 0) {
253 empty:		notfound(sp, key);
254 		return (1);
255 	}
256 
257 	endp = p - 1;
258 	p += vp->m_start.cno;
259 	for (cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
260 		while (--p > endp && *p != key);
261 		if (p == endp) {
262 			notfound(sp, key);
263 			return (1);
264 		}
265 	}
266 
267 	vp->m_stop.cno = (p - endp) - 1;
268 
269 	/*
270 	 * All commands move to the end of the range.  Motion commands
271 	 * adjust the starting point to the character before the current
272 	 * one.
273 	 */
274 	vp->m_final = vp->m_stop;
275 	if (ISMOTION(vp))
276 		--vp->m_start.cno;
277 	return (0);
278 }
279 
280 static void
noprev(sp)281 noprev(sp)
282 	SCR *sp;
283 {
284 	msgq(sp, M_BERR, "178|No previous F, f, T or t search");
285 }
286 
287 static void
notfound(sp,ch)288 notfound(sp, ch)
289 	SCR *sp;
290 	ARG_CHAR_T ch;
291 {
292 	msgq(sp, M_BERR, "179|%s not found", KEY_NAME(sp, ch));
293 }
294