xref: /freebsd/contrib/nvi/vi/v_mark.c (revision 9768746ba83efa02837c5b9c66348db6e900208f)
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 
24 enum which {BQMARK, FQMARK};
25 static 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  */
33 int
34 v_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  */
54 int
55 v_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  */
68 int
69 v_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  */
80 int
81 v_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  */
100 static int
101 mark(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