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