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