bug, wrong value to getcount for 'z' command
[nvi.git] / vi / v_match.c
blob9afd77e4d91d359d9a44d288734e9ef28b8b4416
1 /*-
2 * Copyright (c) 1992, 1993
3 * The Regents of the University of California. All rights reserved.
5 * %sccs.include.redist.c%
6 */
8 #ifndef lint
9 static char sccsid[] = "$Id: v_match.c,v 8.6 1993/11/07 15:19:32 bostic Exp $ (Berkeley) $Date: 1993/11/07 15:19:32 $";
10 #endif /* not lint */
12 #include <sys/types.h>
14 #include <string.h>
16 #include "vi.h"
17 #include "vcmd.h"
19 static int findmatchc __P((MARK *, char *, size_t, MARK *));
22 * v_match -- %
23 * Search to matching character.
25 int
26 v_match(sp, ep, vp, fm, tm, rp)
27 SCR *sp;
28 EXF *ep;
29 VICMDARG *vp;
30 MARK *fm, *tm, *rp;
32 register int cnt, matchc, startc;
33 VCS cs;
34 recno_t lno;
35 size_t len;
36 int (*gc)__P((SCR *, EXF *, VCS *));
37 char *p;
39 if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) {
40 if (file_lline(sp, ep, &lno))
41 return (1);
42 if (lno == 0)
43 goto nomatch;
44 GETLINE_ERR(sp, fm->lno);
45 return (1);
48 if (len == 0)
49 goto nomatch;
51 retry: switch (startc = p[fm->cno]) {
52 case '(':
53 matchc = ')';
54 gc = cs_next;
55 break;
56 case ')':
57 matchc = '(';
58 gc = cs_prev;
59 break;
60 case '[':
61 matchc = ']';
62 gc = cs_next;
63 break;
64 case ']':
65 matchc = '[';
66 gc = cs_prev;
67 break;
68 case '{':
69 matchc = '}';
70 gc = cs_next;
71 break;
72 case '}':
73 matchc = '{';
74 gc = cs_prev;
75 break;
76 default:
77 if (F_ISSET(vp, VC_C | VC_D | VC_Y)) {
78 msgq(sp, M_BERR,
79 "No proximity match for motion commands.");
80 return (1);
82 if (len == 0 || findmatchc(fm, p, len, rp)) {
83 nomatch: msgq(sp, M_BERR, "No match character on this line.");
84 return (1);
86 fm->cno = rp->cno;
87 goto retry;
90 cs.cs_lno = fm->lno;
91 cs.cs_cno = fm->cno;
92 if (cs_init(sp, ep, &cs))
93 return (1);
94 for (cnt = 1;;) {
95 if (gc(sp, ep, &cs))
96 return (1);
97 if (cs.cs_flags != 0) {
98 if (cs.cs_flags == CS_EOF || cs.cs_flags == CS_SOF)
99 break;
100 continue;
102 if (cs.cs_ch == startc)
103 ++cnt;
104 else if (cs.cs_ch == matchc && --cnt == 0)
105 break;
107 if (cnt) {
108 msgq(sp, M_BERR, "Matching character not found.");
109 return (1);
111 rp->lno = cs.cs_lno;
112 rp->cno = cs.cs_cno;
115 * Movement commands go one space further. Increment the return
116 * MARK or from MARK depending on the direction of the search.
118 if (F_ISSET(vp, VC_C | VC_D | VC_Y)) {
119 if (file_gline(sp, ep, rp->lno, &len) == NULL) {
120 GETLINE_ERR(sp, rp->lno);
121 return (1);
123 if (len)
124 if (gc == cs_next)
125 ++rp->cno;
126 else
127 ++fm->cno;
129 return (0);
133 * findmatchc --
134 * If we're not on a character we know how to match, try and find the
135 * closest character from the set "{}[]()". The historic vi did this
136 * as well, but it only searched forward from the cursor. This seems
137 * wrong, so we search both forward and backward on the line, going
138 * to the closest hit. Ties go left because differently handed people
139 * have been traditionally discriminated against by our society.
141 static int
142 findmatchc(fm, p, len, rp)
143 MARK *fm, *rp;
144 char *p;
145 size_t len;
147 register size_t off;
148 size_t left, right; /* Can't be uninitialized. */
149 int leftfound, rightfound;
150 char *t;
152 leftfound = rightfound = 0;
153 for (off = 0, t = &p[off]; off++ < fm->cno;)
154 if (strchr("{}[]()", *t++)) {
155 left = off - 1;
156 leftfound = 1;
157 break;
160 for (off = fm->cno + 1, t = &p[off]; off++ < len;)
161 if (strchr("{}[]()", *t++)) {
162 right = off - 1;
163 rightfound = 1;
164 break;
167 rp->lno = fm->lno;
168 if (leftfound)
169 if (rightfound)
170 if (fm->cno - left > right - fm->cno)
171 rp->cno = right;
172 else
173 rp->cno = left;
174 else
175 rp->cno = left;
176 else if (rightfound)
177 rp->cno = right;
178 else
179 return (1);
180 return (0);