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[] = "$Id: v_mark.c,v 10.12 2001/06/25 15:19:32 skimo Exp $";
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 <stdlib.h>
23#include <stdio.h>
24
25#include "../common/common.h"
26#include "vi.h"
27
28enum which {BQMARK, FQMARK};
29static int mark __P((SCR *, VICMD *, int, enum which));
30
31/*
32 * v_mark -- m[a-z]
33 *	Set a mark.
34 *
35 * PUBLIC: int v_mark __P((SCR *, VICMD *));
36 */
37int
38v_mark(SCR *sp, VICMD *vp)
39{
40	return (mark_set(sp, vp->character, &vp->m_start, 1));
41}
42
43/*
44 * v_bmark -- `['`a-z]
45 *	Move to a mark.
46 *
47 * Moves to a mark, setting both row and column.
48 *
49 * !!!
50 * Although not commonly known, the "'`" and "'`" forms are historically
51 * valid.  The behavior is determined by the first character, so "`'" is
52 * the same as "``".  Remember this fact -- you'll be amazed at how many
53 * people don't know it and will be delighted that you are able to tell
54 * them.
55 *
56 * PUBLIC: int v_bmark __P((SCR *, VICMD *));
57 */
58int
59v_bmark(SCR *sp, VICMD *vp)
60{
61	return (mark(sp, vp, 1, BQMARK));
62}
63
64/*
65 * v_fmark -- '['`a-z]
66 *	Move to a mark.
67 *
68 * Move to the first nonblank character of the line containing the mark.
69 *
70 * PUBLIC: int v_fmark __P((SCR *, VICMD *));
71 */
72int
73v_fmark(SCR *sp, VICMD *vp)
74{
75	return (mark(sp, vp, 1, FQMARK));
76}
77
78/*
79 * v_emark -- <mouse click>
80 *	Mouse mark.
81 *
82 * PUBLIC: int v_emark __P((SCR *, VICMD *));
83 */
84int
85v_emark(SCR *sp, VICMD *vp)
86{
87	SMAP *smp;
88
89	smp = HMAP + vp->ev.e_lno;
90	if (smp > TMAP) {
91		msgq(sp, M_BERR, "320|Unknown cursor position.");
92		return (1);
93	}
94	vp->m_stop.lno = smp->lno;
95	vp->m_stop.cno =
96	    vs_colpos(sp, smp->lno, vp->ev.e_cno + (smp->soff - 1) * sp->cols);
97	return (mark(sp, vp, 0, BQMARK));
98}
99
100/*
101 * mark --
102 *	Mark commands.
103 */
104static int
105mark(SCR *sp, VICMD *vp, int getmark, enum which cmd)
106{
107	dir_t dir;
108	MARK m;
109	size_t len;
110
111	if (getmark && mark_get(sp, vp->character, &vp->m_stop, M_BERR))
112		return (1);
113
114	/*
115	 * !!!
116	 * Historically, BQMARKS for character positions that no longer
117	 * existed acted as FQMARKS.
118	 *
119	 * FQMARKS move to the first non-blank.
120	 */
121	switch (cmd) {
122	case BQMARK:
123		if (db_get(sp, vp->m_stop.lno, DBG_FATAL, NULL, &len))
124			return (1);
125		if (vp->m_stop.cno < len ||
126		    (vp->m_stop.cno == len && len == 0))
127			break;
128
129		if (ISMOTION(vp))
130			F_SET(vp, VM_LMODE);
131		cmd = FQMARK;
132		/* FALLTHROUGH */
133	case FQMARK:
134		vp->m_stop.cno = 0;
135		if (nonblank(sp, vp->m_stop.lno, &vp->m_stop.cno))
136			return (1);
137		break;
138	default:
139		abort();
140	}
141
142	/* Non-motion commands move to the end of the range. */
143	if (!ISMOTION(vp)) {
144		vp->m_final = vp->m_stop;
145		return (0);
146	}
147
148	/*
149	 * !!!
150	 * If a motion component to a BQMARK, the cursor has to move.
151	 */
152	if (cmd == BQMARK &&
153	    vp->m_stop.lno == vp->m_start.lno &&
154	    vp->m_stop.cno == vp->m_start.cno) {
155		v_nomove(sp);
156		return (1);
157	}
158
159	/*
160	 * If the motion is in the reverse direction, switch the start and
161	 * stop MARK's so that it's in a forward direction.  (There's no
162	 * reason for this other than to make the tests below easier.  The
163	 * code in vi.c:vi() would have done the switch.)  Both forward
164	 * and backward motions can happen for any kind of search command.
165	 */
166	if (vp->m_start.lno > vp->m_stop.lno ||
167	    (vp->m_start.lno == vp->m_stop.lno &&
168	    vp->m_start.cno > vp->m_stop.cno)) {
169		m = vp->m_start;
170		vp->m_start = vp->m_stop;
171		vp->m_stop = m;
172		dir = BACKWARD;
173	} else
174		dir = FORWARD;
175
176	/*
177	 * Yank cursor motion, when associated with marks as motion commands,
178	 * historically behaved as follows:
179	 *
180	 * ` motion			' motion
181	 *		Line change?		Line change?
182	 *		Y	N		Y	N
183	 *	      --------------	      ---------------
184	 * FORWARD:  |	NM	NM	      | NM	NM
185	 *	     |			      |
186	 * BACKWARD: |	M	M	      | M	NM(1)
187	 *
188	 * where NM means the cursor didn't move, and M means the cursor
189	 * moved to the mark.
190	 *
191	 * As the cursor was usually moved for yank commands associated
192	 * with backward motions, this implementation regularizes it by
193	 * changing the NM at position (1) to be an M.  This makes mark
194	 * motions match search motions, which is probably A Good Thing.
195	 *
196	 * Delete cursor motion was always to the start of the text region,
197	 * regardless.  Ignore other motion commands.
198	 */
199#ifdef HISTORICAL_PRACTICE
200	if (ISCMD(vp->rkp, 'y')) {
201		if ((cmd == BQMARK ||
202		    (cmd == FQMARK && vp->m_start.lno != vp->m_stop.lno)) &&
203		    (vp->m_start.lno > vp->m_stop.lno ||
204		    (vp->m_start.lno == vp->m_stop.lno &&
205		    vp->m_start.cno > vp->m_stop.cno)))
206			vp->m_final = vp->m_stop;
207	} else if (ISCMD(vp->rkp, 'd'))
208		if (vp->m_start.lno > vp->m_stop.lno ||
209		    (vp->m_start.lno == vp->m_stop.lno &&
210		    vp->m_start.cno > vp->m_stop.cno))
211			vp->m_final = vp->m_stop;
212#else
213	vp->m_final = vp->m_start;
214#endif
215
216	/*
217	 * Forward marks are always line oriented, and it's set in the
218	 * vcmd.c table.
219	 */
220	if (cmd == FQMARK)
221		return (0);
222
223	/*
224	 * BQMARK'S moving backward and starting at column 0, and ones moving
225	 * forward and ending at column 0 are corrected to the last column of
226	 * the previous line.  Otherwise, adjust the starting/ending point to
227	 * the character before the current one (this is safe because we know
228	 * the search had to move to succeed).
229	 *
230	 * Mark motions become line mode opertions if they start at the first
231	 * nonblank and end at column 0 of another line.
232	 */
233	if (vp->m_start.lno < vp->m_stop.lno && vp->m_stop.cno == 0) {
234		if (db_get(sp, --vp->m_stop.lno, DBG_FATAL, NULL, &len))
235			return (1);
236		vp->m_stop.cno = len ? len - 1 : 0;
237		len = 0;
238		if (nonblank(sp, vp->m_start.lno, &len))
239			return (1);
240		if (vp->m_start.cno <= len)
241			F_SET(vp, VM_LMODE);
242	} else
243		--vp->m_stop.cno;
244
245	return (0);
246}
247