1/*	$NetBSD: v_left.c,v 1.2 2013/11/22 15:52:06 christos Exp $	*/
2/*-
3 * Copyright (c) 1992, 1993, 1994
4 *	The Regents of the University of California.  All rights reserved.
5 * Copyright (c) 1992, 1993, 1994, 1995, 1996
6 *	Keith Bostic.  All rights reserved.
7 *
8 * See the LICENSE file for redistribution information.
9 */
10
11#include "config.h"
12
13#include <sys/cdefs.h>
14#if 0
15#ifndef lint
16static const char sccsid[] = "Id: v_left.c,v 10.9 2001/06/25 15:19:32 skimo Exp  (Berkeley) Date: 2001/06/25 15:19:32 ";
17#endif /* not lint */
18#else
19__RCSID("$NetBSD$");
20#endif
21
22#include <sys/types.h>
23#include <sys/queue.h>
24#include <sys/time.h>
25
26#include <bitstring.h>
27#include <limits.h>
28#include <stdio.h>
29
30#include "../common/common.h"
31#include "vi.h"
32
33/*
34 * v_left -- [count]^H, [count]h
35 *	Move left by columns.
36 *
37 * PUBLIC: int v_left __P((SCR *, VICMD *));
38 */
39int
40v_left(SCR *sp, VICMD *vp)
41{
42	db_recno_t cnt;
43
44	/*
45	 * !!!
46	 * The ^H and h commands always failed in the first column.
47	 */
48	if (vp->m_start.cno == 0) {
49		v_sol(sp);
50		return (1);
51	}
52
53	/* Find the end of the range. */
54	cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
55	if (vp->m_start.cno > cnt)
56		vp->m_stop.cno = vp->m_start.cno - cnt;
57	else
58		vp->m_stop.cno = 0;
59
60	/*
61	 * All commands move to the end of the range.  Motion commands
62	 * adjust the starting point to the character before the current
63	 * one.
64	 */
65	if (ISMOTION(vp))
66		--vp->m_start.cno;
67	vp->m_final = vp->m_stop;
68	return (0);
69}
70
71/*
72 * v_cfirst -- [count]_
73 *	Move to the first non-blank character in a line.
74 *
75 * PUBLIC: int v_cfirst __P((SCR *, VICMD *));
76 */
77int
78v_cfirst(SCR *sp, VICMD *vp)
79{
80	db_recno_t cnt, lno;
81
82	/*
83	 * !!!
84	 * If the _ is a motion component, it makes the command a line motion
85	 * e.g. "d_" deletes the line.  It also means that the cursor doesn't
86	 * move.
87	 *
88	 * The _ command never failed in the first column.
89	 */
90	if (ISMOTION(vp))
91		F_SET(vp, VM_LMODE);
92	/*
93	 * !!!
94	 * Historically a specified count makes _ move down count - 1
95	 * rows, so, "3_" is the same as "2j_".
96	 */
97	cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
98	if (cnt != 1) {
99		--vp->count;
100		return (v_down(sp, vp));
101	}
102
103	/*
104	 * Move to the first non-blank.
105	 *
106	 * Can't just use RCM_SET_FNB, in case _ is used as the motion
107	 * component of another command.
108	 */
109	vp->m_stop.cno = 0;
110	if (nonblank(sp, vp->m_stop.lno, &vp->m_stop.cno))
111		return (1);
112
113	/*
114	 * !!!
115	 * The _ command has to fail if the file is empty and we're doing
116	 * a delete.  If deleting line 1, and 0 is the first nonblank,
117	 * make the check.
118	 */
119	if (vp->m_stop.lno == 1 &&
120	    vp->m_stop.cno == 0 && ISCMD(vp->rkp, 'd')) {
121		if (db_last(sp, &lno))
122			return (1);
123		if (lno == 0) {
124			v_sol(sp);
125			return (1);
126		}
127	}
128
129	/*
130	 * Delete and non-motion commands move to the end of the range,
131	 * yank stays at the start.  Ignore others.
132	 */
133	vp->m_final =
134	    ISMOTION(vp) && ISCMD(vp->rkp, 'y') ? vp->m_start : vp->m_stop;
135	return (0);
136}
137
138/*
139 * v_first -- ^
140 *	Move to the first non-blank character in this line.
141 *
142 * PUBLIC: int v_first __P((SCR *, VICMD *));
143 */
144int
145v_first(SCR *sp, 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 */
206int
207v_ncol(SCR *sp, VICMD *vp)
208{
209	if (F_ISSET(vp, VC_C1SET) && vp->count > 1) {
210		--vp->count;
211		vp->m_stop.cno =
212		    vs_colpos(sp, vp->m_start.lno, (size_t)vp->count);
213		/*
214		 * !!!
215		 * The | command succeeded if used as a command and the cursor
216		 * didn't move, but failed if used as a motion component in the
217		 * same situation.
218		 */
219		if (ISMOTION(vp) && vp->m_stop.cno == vp->m_start.cno) {
220			v_nomove(sp);
221			return (1);
222		}
223	} else {
224		/*
225		 * !!!
226		 * The | command succeeded if used as a command in column 0
227		 * without a count, but failed if used as a motion component
228		 * in the same situation.
229		 */
230		if (ISMOTION(vp) && vp->m_start.cno == 0) {
231			v_sol(sp);
232			return (1);
233		}
234		vp->m_stop.cno = 0;
235	}
236
237	/*
238	 * If moving right, non-motion commands move to the end of the range.
239	 * Delete and yank stay at the start.  Motion commands adjust the
240	 * ending point to the character before the current ending charcter.
241	 *
242	 * If moving left, all commands move to the end of the range.  Motion
243	 * commands adjust the starting point to the character before the
244	 * current starting character.
245	 */
246	if (vp->m_start.cno < vp->m_stop.cno)
247		if (ISMOTION(vp)) {
248			--vp->m_stop.cno;
249			vp->m_final = vp->m_start;
250		} else
251			vp->m_final = vp->m_stop;
252	else {
253		if (ISMOTION(vp))
254			--vp->m_start.cno;
255		vp->m_final = vp->m_stop;
256	}
257	return (0);
258}
259
260/*
261 * v_zero -- 0
262 *	Move to the first column on this line.
263 *
264 * PUBLIC: int v_zero __P((SCR *, VICMD *));
265 */
266int
267v_zero(SCR *sp, VICMD *vp)
268{
269	/*
270	 * !!!
271	 * The 0 command succeeded if used as a command in the first column
272	 * but failed if used as a motion component in the same situation.
273	 */
274	if (ISMOTION(vp) && vp->m_start.cno == 0) {
275		v_sol(sp);
276		return (1);
277	}
278
279	/*
280	 * All commands move to the end of the range.  Motion commands
281	 * adjust the starting point to the character before the current
282	 * one.
283	 */
284	vp->m_stop.cno = 0;
285	if (ISMOTION(vp))
286		--vp->m_start.cno;
287	vp->m_final = vp->m_stop;
288	return (0);
289}
290