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