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