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