xref: /freebsd/contrib/nvi/vi/v_left.c (revision aa0a1e58)
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 #ifndef lint
13 static const char sccsid[] = "@(#)v_left.c	10.7 (Berkeley) 3/6/96";
14 #endif /* not lint */
15 
16 #include <sys/types.h>
17 #include <sys/queue.h>
18 #include <sys/time.h>
19 
20 #include <bitstring.h>
21 #include <limits.h>
22 #include <stdio.h>
23 
24 #include "../common/common.h"
25 #include "vi.h"
26 
27 /*
28  * v_left -- [count]^H, [count]h
29  *	Move left by columns.
30  *
31  * PUBLIC: int v_left __P((SCR *, VICMD *));
32  */
33 int
34 v_left(sp, vp)
35 	SCR *sp;
36 	VICMD *vp;
37 {
38 	recno_t cnt;
39 
40 	/*
41 	 * !!!
42 	 * The ^H and h commands always failed in the first column.
43 	 */
44 	if (vp->m_start.cno == 0) {
45 		v_sol(sp);
46 		return (1);
47 	}
48 
49 	/* Find the end of the range. */
50 	cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
51 	if (vp->m_start.cno > cnt)
52 		vp->m_stop.cno = vp->m_start.cno - cnt;
53 	else
54 		vp->m_stop.cno = 0;
55 
56 	/*
57 	 * All commands move to the end of the range.  Motion commands
58 	 * adjust the starting point to the character before the current
59 	 * one.
60 	 */
61 	if (ISMOTION(vp))
62 		--vp->m_start.cno;
63 	vp->m_final = vp->m_stop;
64 	return (0);
65 }
66 
67 /*
68  * v_cfirst -- [count]_
69  *	Move to the first non-blank character in a line.
70  *
71  * PUBLIC: int v_cfirst __P((SCR *, VICMD *));
72  */
73 int
74 v_cfirst(sp, vp)
75 	SCR *sp;
76 	VICMD *vp;
77 {
78 	recno_t cnt, lno;
79 
80 	/*
81 	 * !!!
82 	 * If the _ is a motion component, it makes the command a line motion
83 	 * e.g. "d_" deletes the line.  It also means that the cursor doesn't
84 	 * move.
85 	 *
86 	 * The _ command never failed in the first column.
87 	 */
88 	if (ISMOTION(vp))
89 		F_SET(vp, VM_LMODE);
90 	/*
91 	 * !!!
92 	 * Historically a specified count makes _ move down count - 1
93 	 * rows, so, "3_" is the same as "2j_".
94 	 */
95 	cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
96 	if (cnt != 1) {
97 		--vp->count;
98 		return (v_down(sp, vp));
99 	}
100 
101 	/*
102 	 * Move to the first non-blank.
103 	 *
104 	 * Can't just use RCM_SET_FNB, in case _ is used as the motion
105 	 * component of another command.
106 	 */
107 	vp->m_stop.cno = 0;
108 	if (nonblank(sp, vp->m_stop.lno, &vp->m_stop.cno))
109 		return (1);
110 
111 	/*
112 	 * !!!
113 	 * The _ command has to fail if the file is empty and we're doing
114 	 * a delete.  If deleting line 1, and 0 is the first nonblank,
115 	 * make the check.
116 	 */
117 	if (vp->m_stop.lno == 1 &&
118 	    vp->m_stop.cno == 0 && ISCMD(vp->rkp, 'd')) {
119 		if (db_last(sp, &lno))
120 			return (1);
121 		if (lno == 0) {
122 			v_sol(sp);
123 			return (1);
124 		}
125 	}
126 
127 	/*
128 	 * Delete and non-motion commands move to the end of the range,
129 	 * yank stays at the start.  Ignore others.
130 	 */
131 	vp->m_final =
132 	    ISMOTION(vp) && ISCMD(vp->rkp, 'y') ? vp->m_start : vp->m_stop;
133 	return (0);
134 }
135 
136 /*
137  * v_first -- ^
138  *	Move to the first non-blank character in this line.
139  *
140  * PUBLIC: int v_first __P((SCR *, VICMD *));
141  */
142 int
143 v_first(sp, vp)
144 	SCR *sp;
145 	VICMD *vp;
146 {
147 	/*
148 	 * !!!
149 	 * Yielding to none in our quest for compatibility with every
150 	 * historical blemish of vi, no matter how strange it might be,
151 	 * we permit the user to enter a count and then ignore it.
152 	 */
153 
154 	/*
155 	 * Move to the first non-blank.
156 	 *
157 	 * Can't just use RCM_SET_FNB, in case ^ is used as the motion
158 	 * component of another command.
159 	 */
160 	vp->m_stop.cno = 0;
161 	if (nonblank(sp, vp->m_stop.lno, &vp->m_stop.cno))
162 		return (1);
163 
164 	/*
165 	 * !!!
166 	 * The ^ command succeeded if used as a command when the cursor was
167 	 * on the first non-blank in the line, but failed if used as a motion
168 	 * component in the same situation.
169 	 */
170 	if (ISMOTION(vp) && vp->m_start.cno == vp->m_stop.cno) {
171 		v_sol(sp);
172 		return (1);
173 	}
174 
175 	/*
176 	 * If moving right, non-motion commands move to the end of the range.
177 	 * Delete and yank stay at the start.  Motion commands adjust the
178 	 * ending point to the character before the current ending charcter.
179 	 *
180 	 * If moving left, all commands move to the end of the range.  Motion
181 	 * commands adjust the starting point to the character before the
182 	 * current starting character.
183 	 */
184 	if (vp->m_start.cno < vp->m_stop.cno)
185 		if (ISMOTION(vp)) {
186 			--vp->m_stop.cno;
187 			vp->m_final = vp->m_start;
188 		} else
189 			vp->m_final = vp->m_stop;
190 	else {
191 		if (ISMOTION(vp))
192 			--vp->m_start.cno;
193 		vp->m_final = vp->m_stop;
194 	}
195 	return (0);
196 }
197 
198 /*
199  * v_ncol -- [count]|
200  *	Move to column count or the first column on this line.  If the
201  *	requested column is past EOL, move to EOL.  The nasty part is
202  *	that we have to know character column widths to make this work.
203  *
204  * PUBLIC: int v_ncol __P((SCR *, VICMD *));
205  */
206 int
207 v_ncol(sp, vp)
208 	SCR *sp;
209 	VICMD *vp;
210 {
211 	if (F_ISSET(vp, VC_C1SET) && vp->count > 1) {
212 		--vp->count;
213 		vp->m_stop.cno =
214 		    vs_colpos(sp, vp->m_start.lno, (size_t)vp->count);
215 		/*
216 		 * !!!
217 		 * The | command succeeded if used as a command and the cursor
218 		 * didn't move, but failed if used as a motion component in the
219 		 * same situation.
220 		 */
221 		if (ISMOTION(vp) && vp->m_stop.cno == vp->m_start.cno) {
222 			v_nomove(sp);
223 			return (1);
224 		}
225 	} else {
226 		/*
227 		 * !!!
228 		 * The | command succeeded if used as a command in column 0
229 		 * without a count, but failed if used as a motion component
230 		 * in the same situation.
231 		 */
232 		if (ISMOTION(vp) && vp->m_start.cno == 0) {
233 			v_sol(sp);
234 			return (1);
235 		}
236 		vp->m_stop.cno = 0;
237 	}
238 
239 	/*
240 	 * If moving right, non-motion commands move to the end of the range.
241 	 * Delete and yank stay at the start.  Motion commands adjust the
242 	 * ending point to the character before the current ending charcter.
243 	 *
244 	 * If moving left, all commands move to the end of the range.  Motion
245 	 * commands adjust the starting point to the character before the
246 	 * current starting character.
247 	 */
248 	if (vp->m_start.cno < vp->m_stop.cno)
249 		if (ISMOTION(vp)) {
250 			--vp->m_stop.cno;
251 			vp->m_final = vp->m_start;
252 		} else
253 			vp->m_final = vp->m_stop;
254 	else {
255 		if (ISMOTION(vp))
256 			--vp->m_start.cno;
257 		vp->m_final = vp->m_stop;
258 	}
259 	return (0);
260 }
261 
262 /*
263  * v_zero -- 0
264  *	Move to the first column on this line.
265  *
266  * PUBLIC: int v_zero __P((SCR *, VICMD *));
267  */
268 int
269 v_zero(sp, vp)
270 	SCR *sp;
271 	VICMD *vp;
272 {
273 	/*
274 	 * !!!
275 	 * The 0 command succeeded if used as a command in the first column
276 	 * but failed if used as a motion component in the same situation.
277 	 */
278 	if (ISMOTION(vp) && vp->m_start.cno == 0) {
279 		v_sol(sp);
280 		return (1);
281 	}
282 
283 	/*
284 	 * All commands move to the end of the range.  Motion commands
285 	 * adjust the starting point to the character before the current
286 	 * one.
287 	 */
288 	vp->m_stop.cno = 0;
289 	if (ISMOTION(vp))
290 		--vp->m_start.cno;
291 	vp->m_final = vp->m_stop;
292 	return (0);
293 }
294