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