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