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