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