119304Speter/*-
219304Speter * Copyright (c) 1992, 1993, 1994
319304Speter *	The Regents of the University of California.  All rights reserved.
419304Speter * Copyright (c) 1992, 1993, 1994, 1995, 1996
519304Speter *	Keith Bostic.  All rights reserved.
619304Speter *
719304Speter * See the LICENSE file for redistribution information.
819304Speter */
919304Speter
1019304Speter#include "config.h"
1119304Speter
1219304Speter#ifndef lint
13254225Speterstatic const char sccsid[] = "$Id: v_match.c,v 10.11 2012/02/11 00:33:46 zy Exp $";
1419304Speter#endif /* not lint */
1519304Speter
1619304Speter#include <sys/types.h>
1719304Speter#include <sys/queue.h>
1819304Speter#include <sys/time.h>
1919304Speter
2019304Speter#include <bitstring.h>
21254225Speter#include <ctype.h>
2219304Speter#include <limits.h>
2319304Speter#include <stdio.h>
24254225Speter#include <stdlib.h>
2519304Speter#include <string.h>
2619304Speter
2719304Speter#include "../common/common.h"
2819304Speter#include "vi.h"
2919304Speter
3019304Speter/*
3119304Speter * v_match -- %
3219304Speter *	Search to matching character.
3319304Speter *
34281373Sbapt * PUBLIC: int v_match(SCR *, VICMD *);
3519304Speter */
3619304Speterint
37254225Speterv_match(SCR *sp, VICMD *vp)
3819304Speter{
3919304Speter	VCS cs;
4019304Speter	MARK *mp;
4119304Speter	size_t cno, len, off;
42281373Sbapt	int cnt, isempty, matchc, startc, (*gc)(SCR *, VCS *);
43254225Speter	CHAR_T *p;
44254225Speter	CHAR_T *cp;
45254225Speter	const CHAR_T *match_chars;
4619304Speter
4719304Speter	/*
48254225Speter	 * Historically vi would match (), {} and [] however
49254225Speter	 * an update included <>.  This is ok for editing HTML
50254225Speter	 * but a pain in the butt for C source.
51254225Speter	 * Making it an option lets the user decide what is 'right'.
52254225Speter	 */
53254225Speter	match_chars = VIP(sp)->mcs;
54254225Speter
55254225Speter	/*
5619304Speter	 * !!!
5719304Speter	 * Historic practice; ignore the count.
5819304Speter	 *
5919304Speter	 * !!!
6019304Speter	 * Historical practice was to search for the initial character in the
6119304Speter	 * forward direction only.
6219304Speter	 */
6319304Speter	if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
6419304Speter		if (isempty)
6519304Speter			goto nomatch;
6619304Speter		return (1);
6719304Speter	}
6819304Speter	for (off = vp->m_start.cno;; ++off) {
6919304Speter		if (off >= len) {
7019304Speternomatch:		msgq(sp, M_BERR, "184|No match character on this line");
7119304Speter			return (1);
7219304Speter		}
73254225Speter		startc = p[off];
74254225Speter		cp = STRCHR(match_chars, startc);
75254225Speter		if (cp != NULL) {
76254225Speter			cnt = cp - match_chars;
77254225Speter			matchc = match_chars[cnt ^ 1];
78254225Speter			gc = cnt & 1 ? cs_prev : cs_next;
7919304Speter			break;
8019304Speter		}
8119304Speter	}
8219304Speter
8319304Speter	cs.cs_lno = vp->m_start.lno;
8419304Speter	cs.cs_cno = off;
8519304Speter	if (cs_init(sp, &cs))
8619304Speter		return (1);
8719304Speter	for (cnt = 1;;) {
8819304Speter		if (gc(sp, &cs))
8919304Speter			return (1);
9019304Speter		if (cs.cs_flags != 0) {
9119304Speter			if (cs.cs_flags == CS_EOF || cs.cs_flags == CS_SOF)
9219304Speter				break;
9319304Speter			continue;
9419304Speter		}
9519304Speter		if (cs.cs_ch == startc)
9619304Speter			++cnt;
9719304Speter		else if (cs.cs_ch == matchc && --cnt == 0)
9819304Speter			break;
9919304Speter	}
10019304Speter	if (cnt) {
10119304Speter		msgq(sp, M_BERR, "185|Matching character not found");
10219304Speter		return (1);
10319304Speter	}
10419304Speter
10519304Speter	vp->m_stop.lno = cs.cs_lno;
10619304Speter	vp->m_stop.cno = cs.cs_cno;
10719304Speter
10819304Speter	/*
10919304Speter	 * If moving right, non-motion commands move to the end of the range.
11019304Speter	 * Delete and yank stay at the start.
11119304Speter	 *
11219304Speter	 * If moving left, all commands move to the end of the range.
11319304Speter	 *
11419304Speter	 * !!!
11519304Speter	 * Don't correct for leftward movement -- historic vi deleted the
11619304Speter	 * starting cursor position when deleting to a match.
11719304Speter	 */
11819304Speter	if (vp->m_start.lno < vp->m_stop.lno ||
119254225Speter	    (vp->m_start.lno == vp->m_stop.lno &&
120254225Speter	    vp->m_start.cno < vp->m_stop.cno))
12119304Speter		vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
12219304Speter	else
12319304Speter		vp->m_final = vp->m_stop;
12419304Speter
12519304Speter	/*
12619304Speter	 * !!!
12719304Speter	 * If the motion is across lines, and the earliest cursor position
12819304Speter	 * is at or before any non-blank characters in the line, i.e. the
12919304Speter	 * movement is cutting all of the line's text, and the later cursor
13019304Speter	 * position has nothing other than whitespace characters between it
13119304Speter	 * and the end of its line, the buffer is in line mode.
13219304Speter	 */
13319304Speter	if (!ISMOTION(vp) || vp->m_start.lno == vp->m_stop.lno)
13419304Speter		return (0);
13519304Speter	mp = vp->m_start.lno < vp->m_stop.lno ? &vp->m_start : &vp->m_stop;
13619304Speter	if (mp->cno != 0) {
13719304Speter		cno = 0;
13819304Speter		if (nonblank(sp, mp->lno, &cno))
13919304Speter			return (1);
14019304Speter		if (cno < mp->cno)
14119304Speter			return (0);
14219304Speter	}
14319304Speter	mp = vp->m_start.lno < vp->m_stop.lno ? &vp->m_stop : &vp->m_start;
14419304Speter	if (db_get(sp, mp->lno, DBG_FATAL, &p, &len))
14519304Speter		return (1);
14619304Speter	for (p += mp->cno + 1, len -= mp->cno; --len; ++p)
14719304Speter		if (!isblank(*p))
14819304Speter			return (0);
14919304Speter	F_SET(vp, VM_LMODE);
15019304Speter	return (0);
15119304Speter}
152254225Speter
153254225Speter/*
154254225Speter * v_buildmcs --
155254225Speter *	Build the match character list.
156254225Speter *
157281373Sbapt * PUBLIC: int v_buildmcs(SCR *, char *);
158254225Speter */
159254225Speterint
160254225Speterv_buildmcs(SCR *sp, char *str)
161254225Speter{
162254225Speter	CHAR_T **mp = &VIP(sp)->mcs;
163254225Speter	size_t len = strlen(str) + 1;
164254225Speter
165254225Speter	if (*mp != NULL)
166254225Speter		free(*mp);
167254225Speter	MALLOC(sp, *mp, CHAR_T *, len * sizeof(CHAR_T));
168254225Speter	if (*mp == NULL)
169254225Speter		return (1);
170254225Speter#ifdef USE_WIDECHAR
171254225Speter	if (mbstowcs(*mp, str, len) == (size_t)-1)
172254225Speter		return (1);
173254225Speter#else
174254225Speter	memcpy(*mp, str, len);
175254225Speter#endif
176254225Speter	return (0);
177254225Speter}
178