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