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