xref: /minix/external/bsd/nvi/dist/vi/v_mark.c (revision 0a6a1f1d)
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
16 static 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 
34 enum which {BQMARK, FQMARK};
35 static 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  */
43 int
v_mark(SCR * sp,VICMD * vp)44 v_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  */
64 int
v_bmark(SCR * sp,VICMD * vp)65 v_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  */
78 int
v_fmark(SCR * sp,VICMD * vp)79 v_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  */
90 int
v_emark(SCR * sp,VICMD * vp)91 v_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  */
110 static int
mark(SCR * sp,VICMD * vp,int getmark,enum which cmd)111 mark(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