xref: /freebsd/contrib/nvi/vi/v_mark.c (revision 0b3105a37d7adcadcb720112fed4dc4e8040be99)
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
13 static 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 
28 enum which {BQMARK, FQMARK};
29 static int mark(SCR *, VICMD *, int, enum which);
30 
31 /*
32  * v_mark -- m[a-z]
33  *	Set a mark.
34  *
35  * PUBLIC: int v_mark(SCR *, VICMD *);
36  */
37 int
38 v_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(SCR *, VICMD *);
57  */
58 int
59 v_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(SCR *, VICMD *);
71  */
72 int
73 v_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(SCR *, VICMD *);
83  */
84 int
85 v_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  */
104 static int
105 mark(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 	vp->m_final = vp->m_start;
200 
201 	/*
202 	 * Forward marks are always line oriented, and it's set in the
203 	 * vcmd.c table.
204 	 */
205 	if (cmd == FQMARK)
206 		return (0);
207 
208 	/*
209 	 * BQMARK'S moving backward and starting at column 0, and ones moving
210 	 * forward and ending at column 0 are corrected to the last column of
211 	 * the previous line.  Otherwise, adjust the starting/ending point to
212 	 * the character before the current one (this is safe because we know
213 	 * the search had to move to succeed).
214 	 *
215 	 * Mark motions become line mode opertions if they start at the first
216 	 * nonblank and end at column 0 of another line.
217 	 */
218 	if (vp->m_start.lno < vp->m_stop.lno && vp->m_stop.cno == 0) {
219 		if (db_get(sp, --vp->m_stop.lno, DBG_FATAL, NULL, &len))
220 			return (1);
221 		vp->m_stop.cno = len ? len - 1 : 0;
222 		len = 0;
223 		if (nonblank(sp, vp->m_start.lno, &len))
224 			return (1);
225 		if (vp->m_start.cno <= len)
226 			F_SET(vp, VM_LMODE);
227 	} else
228 		--vp->m_stop.cno;
229 
230 	return (0);
231 }
232