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