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