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_mark.c,v 10.12 2001/06/25 15:19:32 skimo 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>
2119304Speter#include <limits.h>
2219304Speter#include <stdlib.h>
2319304Speter#include <stdio.h>
2419304Speter
2519304Speter#include "../common/common.h"
2619304Speter#include "vi.h"
2719304Speter
28254225Speterenum which {BQMARK, FQMARK};
29254225Speterstatic int mark __P((SCR *, VICMD *, int, enum which));
30254225Speter
3119304Speter/*
3219304Speter * v_mark -- m[a-z]
3319304Speter *	Set a mark.
3419304Speter *
3519304Speter * PUBLIC: int v_mark __P((SCR *, VICMD *));
3619304Speter */
3719304Speterint
38254225Speterv_mark(SCR *sp, VICMD *vp)
3919304Speter{
4019304Speter	return (mark_set(sp, vp->character, &vp->m_start, 1));
4119304Speter}
4219304Speter
4319304Speter/*
4419304Speter * v_bmark -- `['`a-z]
4519304Speter *	Move to a mark.
4619304Speter *
4719304Speter * Moves to a mark, setting both row and column.
4819304Speter *
4919304Speter * !!!
5019304Speter * Although not commonly known, the "'`" and "'`" forms are historically
5119304Speter * valid.  The behavior is determined by the first character, so "`'" is
5219304Speter * the same as "``".  Remember this fact -- you'll be amazed at how many
5319304Speter * people don't know it and will be delighted that you are able to tell
5419304Speter * them.
5519304Speter *
5619304Speter * PUBLIC: int v_bmark __P((SCR *, VICMD *));
5719304Speter */
5819304Speterint
59254225Speterv_bmark(SCR *sp, VICMD *vp)
6019304Speter{
61254225Speter	return (mark(sp, vp, 1, BQMARK));
6219304Speter}
6319304Speter
6419304Speter/*
6519304Speter * v_fmark -- '['`a-z]
6619304Speter *	Move to a mark.
6719304Speter *
6819304Speter * Move to the first nonblank character of the line containing the mark.
6919304Speter *
7019304Speter * PUBLIC: int v_fmark __P((SCR *, VICMD *));
7119304Speter */
7219304Speterint
73254225Speterv_fmark(SCR *sp, VICMD *vp)
7419304Speter{
75254225Speter	return (mark(sp, vp, 1, FQMARK));
7619304Speter}
7719304Speter
7819304Speter/*
79254225Speter * v_emark -- <mouse click>
80254225Speter *	Mouse mark.
81254225Speter *
82254225Speter * PUBLIC: int v_emark __P((SCR *, VICMD *));
83254225Speter */
84254225Speterint
85254225Speterv_emark(SCR *sp, VICMD *vp)
86254225Speter{
87254225Speter	SMAP *smp;
88254225Speter
89254225Speter	smp = HMAP + vp->ev.e_lno;
90254225Speter	if (smp > TMAP) {
91254225Speter		msgq(sp, M_BERR, "320|Unknown cursor position.");
92254225Speter		return (1);
93254225Speter	}
94254225Speter	vp->m_stop.lno = smp->lno;
95254225Speter	vp->m_stop.cno =
96254225Speter	    vs_colpos(sp, smp->lno, vp->ev.e_cno + (smp->soff - 1) * sp->cols);
97254225Speter	return (mark(sp, vp, 0, BQMARK));
98254225Speter}
99254225Speter
100254225Speter/*
10119304Speter * mark --
10219304Speter *	Mark commands.
10319304Speter */
10419304Speterstatic int
105254225Spetermark(SCR *sp, VICMD *vp, int getmark, enum which cmd)
10619304Speter{
10719304Speter	dir_t dir;
10819304Speter	MARK m;
10919304Speter	size_t len;
11019304Speter
111254225Speter	if (getmark && mark_get(sp, vp->character, &vp->m_stop, M_BERR))
11219304Speter		return (1);
11319304Speter
11419304Speter	/*
11519304Speter	 * !!!
11619304Speter	 * Historically, BQMARKS for character positions that no longer
11719304Speter	 * existed acted as FQMARKS.
11819304Speter	 *
11919304Speter	 * FQMARKS move to the first non-blank.
12019304Speter	 */
12119304Speter	switch (cmd) {
12219304Speter	case BQMARK:
12319304Speter		if (db_get(sp, vp->m_stop.lno, DBG_FATAL, NULL, &len))
12419304Speter			return (1);
12519304Speter		if (vp->m_stop.cno < len ||
126254225Speter		    (vp->m_stop.cno == len && len == 0))
12719304Speter			break;
12819304Speter
12919304Speter		if (ISMOTION(vp))
13019304Speter			F_SET(vp, VM_LMODE);
13119304Speter		cmd = FQMARK;
13219304Speter		/* FALLTHROUGH */
13319304Speter	case FQMARK:
13419304Speter		vp->m_stop.cno = 0;
13519304Speter		if (nonblank(sp, vp->m_stop.lno, &vp->m_stop.cno))
13619304Speter			return (1);
13719304Speter		break;
13819304Speter	default:
13919304Speter		abort();
14019304Speter	}
14119304Speter
14219304Speter	/* Non-motion commands move to the end of the range. */
14319304Speter	if (!ISMOTION(vp)) {
14419304Speter		vp->m_final = vp->m_stop;
14519304Speter		return (0);
14619304Speter	}
14719304Speter
14819304Speter	/*
14919304Speter	 * !!!
15019304Speter	 * If a motion component to a BQMARK, the cursor has to move.
15119304Speter	 */
15219304Speter	if (cmd == BQMARK &&
15319304Speter	    vp->m_stop.lno == vp->m_start.lno &&
15419304Speter	    vp->m_stop.cno == vp->m_start.cno) {
15519304Speter		v_nomove(sp);
15619304Speter		return (1);
15719304Speter	}
15819304Speter
15919304Speter	/*
16019304Speter	 * If the motion is in the reverse direction, switch the start and
16119304Speter	 * stop MARK's so that it's in a forward direction.  (There's no
16219304Speter	 * reason for this other than to make the tests below easier.  The
16319304Speter	 * code in vi.c:vi() would have done the switch.)  Both forward
16419304Speter	 * and backward motions can happen for any kind of search command.
16519304Speter	 */
16619304Speter	if (vp->m_start.lno > vp->m_stop.lno ||
167254225Speter	    (vp->m_start.lno == vp->m_stop.lno &&
168254225Speter	    vp->m_start.cno > vp->m_stop.cno)) {
16919304Speter		m = vp->m_start;
17019304Speter		vp->m_start = vp->m_stop;
17119304Speter		vp->m_stop = m;
17219304Speter		dir = BACKWARD;
17319304Speter	} else
17419304Speter		dir = FORWARD;
17519304Speter
17619304Speter	/*
17719304Speter	 * Yank cursor motion, when associated with marks as motion commands,
17819304Speter	 * historically behaved as follows:
17919304Speter	 *
18019304Speter	 * ` motion			' motion
18119304Speter	 *		Line change?		Line change?
18219304Speter	 *		Y	N		Y	N
18319304Speter	 *	      --------------	      ---------------
18419304Speter	 * FORWARD:  |	NM	NM	      | NM	NM
18519304Speter	 *	     |			      |
18619304Speter	 * BACKWARD: |	M	M	      | M	NM(1)
18719304Speter	 *
18819304Speter	 * where NM means the cursor didn't move, and M means the cursor
18919304Speter	 * moved to the mark.
19019304Speter	 *
19119304Speter	 * As the cursor was usually moved for yank commands associated
19219304Speter	 * with backward motions, this implementation regularizes it by
19319304Speter	 * changing the NM at position (1) to be an M.  This makes mark
19419304Speter	 * motions match search motions, which is probably A Good Thing.
19519304Speter	 *
19619304Speter	 * Delete cursor motion was always to the start of the text region,
19719304Speter	 * regardless.  Ignore other motion commands.
19819304Speter	 */
19919304Speter#ifdef HISTORICAL_PRACTICE
20019304Speter	if (ISCMD(vp->rkp, 'y')) {
20119304Speter		if ((cmd == BQMARK ||
202254225Speter		    (cmd == FQMARK && vp->m_start.lno != vp->m_stop.lno)) &&
20319304Speter		    (vp->m_start.lno > vp->m_stop.lno ||
204254225Speter		    (vp->m_start.lno == vp->m_stop.lno &&
205254225Speter		    vp->m_start.cno > vp->m_stop.cno)))
20619304Speter			vp->m_final = vp->m_stop;
20719304Speter	} else if (ISCMD(vp->rkp, 'd'))
20819304Speter		if (vp->m_start.lno > vp->m_stop.lno ||
209254225Speter		    (vp->m_start.lno == vp->m_stop.lno &&
210254225Speter		    vp->m_start.cno > vp->m_stop.cno))
21119304Speter			vp->m_final = vp->m_stop;
21219304Speter#else
21319304Speter	vp->m_final = vp->m_start;
21419304Speter#endif
21519304Speter
21619304Speter	/*
21719304Speter	 * Forward marks are always line oriented, and it's set in the
21819304Speter	 * vcmd.c table.
21919304Speter	 */
22019304Speter	if (cmd == FQMARK)
22119304Speter		return (0);
22219304Speter
22319304Speter	/*
22419304Speter	 * BQMARK'S moving backward and starting at column 0, and ones moving
22519304Speter	 * forward and ending at column 0 are corrected to the last column of
22619304Speter	 * the previous line.  Otherwise, adjust the starting/ending point to
22719304Speter	 * the character before the current one (this is safe because we know
22819304Speter	 * the search had to move to succeed).
22919304Speter	 *
23019304Speter	 * Mark motions become line mode opertions if they start at the first
23119304Speter	 * nonblank and end at column 0 of another line.
23219304Speter	 */
23319304Speter	if (vp->m_start.lno < vp->m_stop.lno && vp->m_stop.cno == 0) {
23419304Speter		if (db_get(sp, --vp->m_stop.lno, DBG_FATAL, NULL, &len))
23519304Speter			return (1);
23619304Speter		vp->m_stop.cno = len ? len - 1 : 0;
23719304Speter		len = 0;
23819304Speter		if (nonblank(sp, vp->m_start.lno, &len))
23919304Speter			return (1);
24019304Speter		if (vp->m_start.cno <= len)
24119304Speter			F_SET(vp, VM_LMODE);
24219304Speter	} else
24319304Speter		--vp->m_stop.cno;
24419304Speter
24519304Speter	return (0);
24619304Speter}
247