v_match.c revision 19304
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#ifndef lint
13static const char sccsid[] = "@(#)v_match.c	10.8 (Berkeley) 3/6/96";
14#endif /* not lint */
15
16#include <sys/types.h>
17#include <sys/queue.h>
18#include <sys/time.h>
19
20#include <bitstring.h>
21#include <limits.h>
22#include <stdio.h>
23#include <string.h>
24
25#include "../common/common.h"
26#include "vi.h"
27
28/*
29 * v_match -- %
30 *	Search to matching character.
31 *
32 * PUBLIC: int v_match __P((SCR *, VICMD *));
33 */
34int
35v_match(sp, vp)
36	SCR *sp;
37	VICMD *vp;
38{
39	VCS cs;
40	MARK *mp;
41	size_t cno, len, off;
42	int cnt, isempty, matchc, startc, (*gc)__P((SCR *, VCS *));
43	char *p;
44
45	/*
46	 * !!!
47	 * Historic practice; ignore the count.
48	 *
49	 * !!!
50	 * Historical practice was to search for the initial character in the
51	 * forward direction only.
52	 */
53	if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
54		if (isempty)
55			goto nomatch;
56		return (1);
57	}
58	for (off = vp->m_start.cno;; ++off) {
59		if (off >= len) {
60nomatch:		msgq(sp, M_BERR, "184|No match character on this line");
61			return (1);
62		}
63		switch (startc = p[off]) {
64		case '(':
65			matchc = ')';
66			gc = cs_next;
67			break;
68		case ')':
69			matchc = '(';
70			gc = cs_prev;
71			break;
72		case '[':
73			matchc = ']';
74			gc = cs_next;
75			break;
76		case ']':
77			matchc = '[';
78			gc = cs_prev;
79			break;
80		case '{':
81			matchc = '}';
82			gc = cs_next;
83			break;
84		case '}':
85			matchc = '{';
86			gc = cs_prev;
87			break;
88		case '<':
89			matchc = '>';
90			gc = cs_next;
91			break;
92		case '>':
93			matchc = '<';
94			gc = cs_prev;
95			break;
96		default:
97			continue;
98		}
99		break;
100	}
101
102	cs.cs_lno = vp->m_start.lno;
103	cs.cs_cno = off;
104	if (cs_init(sp, &cs))
105		return (1);
106	for (cnt = 1;;) {
107		if (gc(sp, &cs))
108			return (1);
109		if (cs.cs_flags != 0) {
110			if (cs.cs_flags == CS_EOF || cs.cs_flags == CS_SOF)
111				break;
112			continue;
113		}
114		if (cs.cs_ch == startc)
115			++cnt;
116		else if (cs.cs_ch == matchc && --cnt == 0)
117			break;
118	}
119	if (cnt) {
120		msgq(sp, M_BERR, "185|Matching character not found");
121		return (1);
122	}
123
124	vp->m_stop.lno = cs.cs_lno;
125	vp->m_stop.cno = cs.cs_cno;
126
127	/*
128	 * If moving right, non-motion commands move to the end of the range.
129	 * Delete and yank stay at the start.
130	 *
131	 * If moving left, all commands move to the end of the range.
132	 *
133	 * !!!
134	 * Don't correct for leftward movement -- historic vi deleted the
135	 * starting cursor position when deleting to a match.
136	 */
137	if (vp->m_start.lno < vp->m_stop.lno ||
138	    vp->m_start.lno == vp->m_stop.lno &&
139	    vp->m_start.cno < vp->m_stop.cno)
140		vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
141	else
142		vp->m_final = vp->m_stop;
143
144	/*
145	 * !!!
146	 * If the motion is across lines, and the earliest cursor position
147	 * is at or before any non-blank characters in the line, i.e. the
148	 * movement is cutting all of the line's text, and the later cursor
149	 * position has nothing other than whitespace characters between it
150	 * and the end of its line, the buffer is in line mode.
151	 */
152	if (!ISMOTION(vp) || vp->m_start.lno == vp->m_stop.lno)
153		return (0);
154	mp = vp->m_start.lno < vp->m_stop.lno ? &vp->m_start : &vp->m_stop;
155	if (mp->cno != 0) {
156		cno = 0;
157		if (nonblank(sp, mp->lno, &cno))
158			return (1);
159		if (cno < mp->cno)
160			return (0);
161	}
162	mp = vp->m_start.lno < vp->m_stop.lno ? &vp->m_stop : &vp->m_start;
163	if (db_get(sp, mp->lno, DBG_FATAL, &p, &len))
164		return (1);
165	for (p += mp->cno + 1, len -= mp->cno; --len; ++p)
166		if (!isblank(*p))
167			return (0);
168	F_SET(vp, VM_LMODE);
169	return (0);
170}
171