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