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