xref: /dflybsd-src/contrib/nvi2/vi/v_paragraph.c (revision 07bc39c2f4bbca56f12568e06d89da17f2eeb965)
1e0b8e63eSJohn Marino /*-
2e0b8e63eSJohn Marino  * Copyright (c) 1992, 1993, 1994
3e0b8e63eSJohn Marino  *	The Regents of the University of California.  All rights reserved.
4e0b8e63eSJohn Marino  * Copyright (c) 1992, 1993, 1994, 1995, 1996
5e0b8e63eSJohn Marino  *	Keith Bostic.  All rights reserved.
6e0b8e63eSJohn Marino  *
7e0b8e63eSJohn Marino  * See the LICENSE file for redistribution information.
8e0b8e63eSJohn Marino  */
9e0b8e63eSJohn Marino 
10e0b8e63eSJohn Marino #include "config.h"
11e0b8e63eSJohn Marino 
12e0b8e63eSJohn Marino #include <sys/types.h>
13e0b8e63eSJohn Marino #include <sys/queue.h>
14e0b8e63eSJohn Marino #include <sys/time.h>
15e0b8e63eSJohn Marino 
16e0b8e63eSJohn Marino #include <bitstring.h>
17e0b8e63eSJohn Marino #include <errno.h>
18e0b8e63eSJohn Marino #include <limits.h>
19e0b8e63eSJohn Marino #include <stdio.h>
20e0b8e63eSJohn Marino #include <stdlib.h>
21e0b8e63eSJohn Marino #include <string.h>
22e0b8e63eSJohn Marino 
23e0b8e63eSJohn Marino #include "../common/common.h"
24e0b8e63eSJohn Marino #include "vi.h"
25e0b8e63eSJohn Marino 
26e0b8e63eSJohn Marino #define	INTEXT_CHECK {							\
27e0b8e63eSJohn Marino 	if (len == 0 || v_isempty(p, len)) {				\
28e0b8e63eSJohn Marino 		if (!--cnt)						\
29e0b8e63eSJohn Marino 			goto found;					\
30e0b8e63eSJohn Marino 		pstate = P_INBLANK;					\
31e0b8e63eSJohn Marino 	}								\
32e0b8e63eSJohn Marino 	/*								\
33e0b8e63eSJohn Marino 	 * !!!								\
34e0b8e63eSJohn Marino 	 * Historic documentation (USD:15-11, 4.2) said that formfeed	\
35e0b8e63eSJohn Marino 	 * characters (^L) in the first column delimited paragraphs.	\
36e0b8e63eSJohn Marino 	 * The historic vi code mentions formfeed characters, but never	\
37e0b8e63eSJohn Marino 	 * implements them.  It seems reasonable, do it.		\
38e0b8e63eSJohn Marino 	 */								\
39e0b8e63eSJohn Marino 	if (p[0] == '\014') {						\
40e0b8e63eSJohn Marino 		if (!--cnt)						\
41e0b8e63eSJohn Marino 			goto found;					\
42e0b8e63eSJohn Marino 		continue;						\
43e0b8e63eSJohn Marino 	}								\
44e0b8e63eSJohn Marino 	if (p[0] != '.' || len < 2)					\
45e0b8e63eSJohn Marino 		continue;						\
46e0b8e63eSJohn Marino 	for (lp = VIP(sp)->ps; *lp != '\0'; lp += 2)			\
47e0b8e63eSJohn Marino 		if (lp[0] == p[1] &&					\
48e0b8e63eSJohn Marino 		    (lp[1] == ' ' && len == 2 || lp[1] == p[2]) &&	\
49e0b8e63eSJohn Marino 		    !--cnt)						\
50e0b8e63eSJohn Marino 			goto found;					\
51e0b8e63eSJohn Marino }
52e0b8e63eSJohn Marino 
53e0b8e63eSJohn Marino /*
54e0b8e63eSJohn Marino  * v_paragraphf -- [count]}
55e0b8e63eSJohn Marino  *	Move forward count paragraphs.
56e0b8e63eSJohn Marino  *
57e0b8e63eSJohn Marino  * Paragraphs are empty lines after text, formfeed characters, or values
58e0b8e63eSJohn Marino  * from the paragraph or section options.
59e0b8e63eSJohn Marino  *
60e0b8e63eSJohn Marino  * PUBLIC: int v_paragraphf(SCR *, VICMD *);
61e0b8e63eSJohn Marino  */
62e0b8e63eSJohn Marino int
v_paragraphf(SCR * sp,VICMD * vp)63e0b8e63eSJohn Marino v_paragraphf(SCR *sp, VICMD *vp)
64e0b8e63eSJohn Marino {
65e0b8e63eSJohn Marino 	enum { P_INTEXT, P_INBLANK } pstate;
66e0b8e63eSJohn Marino 	size_t lastlen, len;
67e0b8e63eSJohn Marino 	recno_t cnt, lastlno, lno;
68e0b8e63eSJohn Marino 	int isempty;
69e0b8e63eSJohn Marino 	CHAR_T *p;
70e0b8e63eSJohn Marino 	char *lp;
71e0b8e63eSJohn Marino 
72e0b8e63eSJohn Marino 	/*
73e0b8e63eSJohn Marino 	 * !!!
74e0b8e63eSJohn Marino 	 * If the starting cursor position is at or before any non-blank
75e0b8e63eSJohn Marino 	 * characters in the line, i.e. the movement is cutting all of the
76e0b8e63eSJohn Marino 	 * line's text, the buffer is in line mode.  It's a lot easier to
77e0b8e63eSJohn Marino 	 * check here, because we know that the end is going to be the start
78e0b8e63eSJohn Marino 	 * or end of a line.
79e0b8e63eSJohn Marino 	 *
80e0b8e63eSJohn Marino 	 * This was historical practice in vi, with a single exception.  If
81e0b8e63eSJohn Marino 	 * the paragraph movement was from the start of the last line to EOF,
82e0b8e63eSJohn Marino 	 * then all the characters were deleted from the last line, but the
83e0b8e63eSJohn Marino 	 * line itself remained.  If somebody complains, don't pause, don't
84e0b8e63eSJohn Marino 	 * hesitate, just hit them.
85e0b8e63eSJohn Marino 	 */
86e0b8e63eSJohn Marino 	if (ISMOTION(vp))
87e0b8e63eSJohn Marino 		if (vp->m_start.cno == 0)
88e0b8e63eSJohn Marino 			F_SET(vp, VM_LMODE);
89e0b8e63eSJohn Marino 		else {
90e0b8e63eSJohn Marino 			vp->m_stop = vp->m_start;
91e0b8e63eSJohn Marino 			vp->m_stop.cno = 0;
92e0b8e63eSJohn Marino 			if (nonblank(sp, vp->m_stop.lno, &vp->m_stop.cno))
93e0b8e63eSJohn Marino 				return (1);
94e0b8e63eSJohn Marino 			if (vp->m_start.cno <= vp->m_stop.cno)
95e0b8e63eSJohn Marino 				F_SET(vp, VM_LMODE);
96e0b8e63eSJohn Marino 		}
97e0b8e63eSJohn Marino 
98e0b8e63eSJohn Marino 	/* Figure out what state we're currently in. */
99e0b8e63eSJohn Marino 	lno = vp->m_start.lno;
100e0b8e63eSJohn Marino 	if (db_get(sp, lno, 0, &p, &len))
101e0b8e63eSJohn Marino 		goto eof;
102e0b8e63eSJohn Marino 
103e0b8e63eSJohn Marino 	/*
104e0b8e63eSJohn Marino 	 * If we start in text, we want to switch states
105e0b8e63eSJohn Marino 	 * (2 * N - 1) times, in non-text, (2 * N) times.
106e0b8e63eSJohn Marino 	 */
107e0b8e63eSJohn Marino 	cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
108e0b8e63eSJohn Marino 	cnt *= 2;
109e0b8e63eSJohn Marino 	if (len == 0 || v_isempty(p, len))
110e0b8e63eSJohn Marino 		pstate = P_INBLANK;
111e0b8e63eSJohn Marino 	else {
112e0b8e63eSJohn Marino 		--cnt;
113e0b8e63eSJohn Marino 		pstate = P_INTEXT;
114e0b8e63eSJohn Marino 	}
115e0b8e63eSJohn Marino 
116e0b8e63eSJohn Marino 	for (;;) {
117e0b8e63eSJohn Marino 		lastlno = lno;
118e0b8e63eSJohn Marino 		lastlen = len;
119e0b8e63eSJohn Marino 		if (db_get(sp, ++lno, 0, &p, &len))
120e0b8e63eSJohn Marino 			goto eof;
121e0b8e63eSJohn Marino 		switch (pstate) {
122e0b8e63eSJohn Marino 		case P_INTEXT:
123e0b8e63eSJohn Marino 			INTEXT_CHECK;
124e0b8e63eSJohn Marino 			break;
125e0b8e63eSJohn Marino 		case P_INBLANK:
126e0b8e63eSJohn Marino 			if (len == 0 || v_isempty(p, len))
127e0b8e63eSJohn Marino 				break;
128e0b8e63eSJohn Marino 			if (--cnt) {
129e0b8e63eSJohn Marino 				pstate = P_INTEXT;
130e0b8e63eSJohn Marino 				break;
131e0b8e63eSJohn Marino 			}
132e0b8e63eSJohn Marino 			/*
133e0b8e63eSJohn Marino 			 * !!!
134e0b8e63eSJohn Marino 			 * Non-motion commands move to the end of the range,
135e0b8e63eSJohn Marino 			 * delete and yank stay at the start.  Ignore others.
136e0b8e63eSJohn Marino 			 * Adjust the end of the range for motion commands;
137e0b8e63eSJohn Marino 			 * historically, a motion component was to the end of
138e0b8e63eSJohn Marino 			 * the previous line, whereas the movement command was
139e0b8e63eSJohn Marino 			 * to the start of the new "paragraph".
140e0b8e63eSJohn Marino 			 */
141e0b8e63eSJohn Marino found:			if (ISMOTION(vp)) {
142e0b8e63eSJohn Marino 				vp->m_stop.lno = lastlno;
143e0b8e63eSJohn Marino 				vp->m_stop.cno = lastlen ? lastlen - 1 : 0;
144e0b8e63eSJohn Marino 				vp->m_final = vp->m_start;
145e0b8e63eSJohn Marino 			} else {
146e0b8e63eSJohn Marino 				vp->m_stop.lno = lno;
147e0b8e63eSJohn Marino 				vp->m_stop.cno = 0;
148e0b8e63eSJohn Marino 				vp->m_final = vp->m_stop;
149e0b8e63eSJohn Marino 			}
150e0b8e63eSJohn Marino 			return (0);
151e0b8e63eSJohn Marino 		default:
152e0b8e63eSJohn Marino 			abort();
153e0b8e63eSJohn Marino 		}
154e0b8e63eSJohn Marino 	}
155e0b8e63eSJohn Marino 
156e0b8e63eSJohn Marino 	/*
157e0b8e63eSJohn Marino 	 * !!!
158e0b8e63eSJohn Marino 	 * Adjust end of the range for motion commands; EOF is a movement
159e0b8e63eSJohn Marino 	 * sink.  The } command historically moved to the end of the last
160e0b8e63eSJohn Marino 	 * line, not the beginning, from any position before the end of the
161e0b8e63eSJohn Marino 	 * last line.  It also historically worked on empty files, so we
162e0b8e63eSJohn Marino 	 * have to make it okay.
163e0b8e63eSJohn Marino 	 */
164e0b8e63eSJohn Marino eof:	if (vp->m_start.lno == lno || vp->m_start.lno == lno - 1) {
165e0b8e63eSJohn Marino 		if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
166e0b8e63eSJohn Marino 			if (!isempty)
167e0b8e63eSJohn Marino 				return (1);
168e0b8e63eSJohn Marino 			vp->m_start.cno = 0;
169e0b8e63eSJohn Marino 			return (0);
170e0b8e63eSJohn Marino 		}
171e0b8e63eSJohn Marino 		if (vp->m_start.cno == (len ? len - 1 : 0)) {
172e0b8e63eSJohn Marino 			v_eof(sp, NULL);
173e0b8e63eSJohn Marino 			return (1);
174e0b8e63eSJohn Marino 		}
175e0b8e63eSJohn Marino 	}
176e0b8e63eSJohn Marino 	/*
177e0b8e63eSJohn Marino 	 * !!!
178e0b8e63eSJohn Marino 	 * Non-motion commands move to the end of the range, delete
179e0b8e63eSJohn Marino 	 * and yank stay at the start.  Ignore others.
180e0b8e63eSJohn Marino 	 *
181e0b8e63eSJohn Marino 	 * If deleting the line (which happens if deleting to EOF), then
182e0b8e63eSJohn Marino 	 * cursor movement is to the first nonblank.
183e0b8e63eSJohn Marino 	 */
184e0b8e63eSJohn Marino 	if (ISMOTION(vp) && ISCMD(vp->rkp, 'd')) {
185e0b8e63eSJohn Marino 		F_CLR(vp, VM_RCM_MASK);
186e0b8e63eSJohn Marino 		F_SET(vp, VM_RCM_SETFNB);
187e0b8e63eSJohn Marino 	}
188e0b8e63eSJohn Marino 	vp->m_stop.lno = lno - 1;
189e0b8e63eSJohn Marino 	vp->m_stop.cno = len ? len - 1 : 0;
190e0b8e63eSJohn Marino 	vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
191e0b8e63eSJohn Marino 	return (0);
192e0b8e63eSJohn Marino }
193e0b8e63eSJohn Marino 
194e0b8e63eSJohn Marino /*
195e0b8e63eSJohn Marino  * v_paragraphb -- [count]{
196e0b8e63eSJohn Marino  *	Move backward count paragraphs.
197e0b8e63eSJohn Marino  *
198e0b8e63eSJohn Marino  * PUBLIC: int v_paragraphb(SCR *, VICMD *);
199e0b8e63eSJohn Marino  */
200e0b8e63eSJohn Marino int
v_paragraphb(SCR * sp,VICMD * vp)201e0b8e63eSJohn Marino v_paragraphb(SCR *sp, VICMD *vp)
202e0b8e63eSJohn Marino {
203e0b8e63eSJohn Marino 	enum { P_INTEXT, P_INBLANK } pstate;
204e0b8e63eSJohn Marino 	size_t len;
205e0b8e63eSJohn Marino 	recno_t cnt, lno;
206e0b8e63eSJohn Marino 	CHAR_T *p;
207e0b8e63eSJohn Marino 	char *lp;
208e0b8e63eSJohn Marino 
209e0b8e63eSJohn Marino 	/*
210e0b8e63eSJohn Marino 	 * !!!
211e0b8e63eSJohn Marino 	 * Check for SOF.  The historic vi didn't complain if users hit SOF
212e0b8e63eSJohn Marino 	 * repeatedly, unless it was part of a motion command.  There is no
213e0b8e63eSJohn Marino 	 * question but that Emerson's editor of choice was vi.
214e0b8e63eSJohn Marino 	 *
215e0b8e63eSJohn Marino 	 * The { command historically moved to the beginning of the first
216e0b8e63eSJohn Marino 	 * line if invoked on the first line.
217e0b8e63eSJohn Marino 	 *
218e0b8e63eSJohn Marino 	 * !!!
219e0b8e63eSJohn Marino 	 * If the starting cursor position is in the first column (backward
220e0b8e63eSJohn Marino 	 * paragraph movements did NOT historically pay attention to non-blank
221e0b8e63eSJohn Marino 	 * characters) i.e. the movement is cutting the entire line, the buffer
222e0b8e63eSJohn Marino 	 * is in line mode.  Cuts from the beginning of the line also did not
223e0b8e63eSJohn Marino 	 * cut the current line, but started at the previous EOL.
224e0b8e63eSJohn Marino 	 *
225e0b8e63eSJohn Marino 	 * Correct for a left motion component while we're thinking about it.
226e0b8e63eSJohn Marino 	 */
227e0b8e63eSJohn Marino 	lno = vp->m_start.lno;
228e0b8e63eSJohn Marino 
229e0b8e63eSJohn Marino 	if (ISMOTION(vp))
230e0b8e63eSJohn Marino 		if (vp->m_start.cno == 0) {
231e0b8e63eSJohn Marino 			if (vp->m_start.lno == 1) {
232e0b8e63eSJohn Marino 				v_sof(sp, &vp->m_start);
233e0b8e63eSJohn Marino 				return (1);
234e0b8e63eSJohn Marino 			} else
235e0b8e63eSJohn Marino 				--vp->m_start.lno;
236e0b8e63eSJohn Marino 			F_SET(vp, VM_LMODE);
237e0b8e63eSJohn Marino 		} else
238e0b8e63eSJohn Marino 			--vp->m_start.cno;
239e0b8e63eSJohn Marino 
240e0b8e63eSJohn Marino 	if (vp->m_start.lno <= 1)
241e0b8e63eSJohn Marino 		goto sof;
242e0b8e63eSJohn Marino 
243e0b8e63eSJohn Marino 	/* Figure out what state we're currently in. */
244e0b8e63eSJohn Marino 	if (db_get(sp, lno, 0, &p, &len))
245e0b8e63eSJohn Marino 		goto sof;
246e0b8e63eSJohn Marino 
247e0b8e63eSJohn Marino 	/*
248e0b8e63eSJohn Marino 	 * If we start in text, we want to switch states
249e0b8e63eSJohn Marino 	 * (2 * N - 1) times, in non-text, (2 * N) times.
250e0b8e63eSJohn Marino 	 */
251e0b8e63eSJohn Marino 	cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
252e0b8e63eSJohn Marino 	cnt *= 2;
253e0b8e63eSJohn Marino 	if (len == 0 || v_isempty(p, len))
254e0b8e63eSJohn Marino 		pstate = P_INBLANK;
255e0b8e63eSJohn Marino 	else {
256e0b8e63eSJohn Marino 		--cnt;
257e0b8e63eSJohn Marino 		pstate = P_INTEXT;
258e0b8e63eSJohn Marino 
259e0b8e63eSJohn Marino 		/*
260e0b8e63eSJohn Marino 		 * !!!
261e0b8e63eSJohn Marino 		 * If the starting cursor is past the first column,
262e0b8e63eSJohn Marino 		 * the current line is checked for a paragraph.
263e0b8e63eSJohn Marino 		 */
264e0b8e63eSJohn Marino 		if (vp->m_start.cno > 0)
265e0b8e63eSJohn Marino 			++lno;
266e0b8e63eSJohn Marino 	}
267e0b8e63eSJohn Marino 
268e0b8e63eSJohn Marino 	for (;;) {
269e0b8e63eSJohn Marino 		if (db_get(sp, --lno, 0, &p, &len))
270e0b8e63eSJohn Marino 			goto sof;
271e0b8e63eSJohn Marino 		switch (pstate) {
272e0b8e63eSJohn Marino 		case P_INTEXT:
273e0b8e63eSJohn Marino 			INTEXT_CHECK;
274e0b8e63eSJohn Marino 			break;
275e0b8e63eSJohn Marino 		case P_INBLANK:
276e0b8e63eSJohn Marino 			if (len != 0 && !v_isempty(p, len)) {
277e0b8e63eSJohn Marino 				if (!--cnt)
278e0b8e63eSJohn Marino 					goto found;
279e0b8e63eSJohn Marino 				pstate = P_INTEXT;
280e0b8e63eSJohn Marino 			}
281e0b8e63eSJohn Marino 			break;
282e0b8e63eSJohn Marino 		default:
283e0b8e63eSJohn Marino 			abort();
284e0b8e63eSJohn Marino 		}
285e0b8e63eSJohn Marino 	}
286e0b8e63eSJohn Marino 
287e0b8e63eSJohn Marino 	/* SOF is a movement sink. */
288e0b8e63eSJohn Marino sof:	lno = 1;
289e0b8e63eSJohn Marino 
290e0b8e63eSJohn Marino found:	vp->m_stop.lno = lno;
291e0b8e63eSJohn Marino 	vp->m_stop.cno = 0;
292e0b8e63eSJohn Marino 
293e0b8e63eSJohn Marino 	/*
294e0b8e63eSJohn Marino 	 * All commands move to the end of the range.  (We already
295e0b8e63eSJohn Marino 	 * adjusted the start of the range for motion commands).
296e0b8e63eSJohn Marino 	 */
297e0b8e63eSJohn Marino 	vp->m_final = vp->m_stop;
298e0b8e63eSJohn Marino 	return (0);
299e0b8e63eSJohn Marino }
300e0b8e63eSJohn Marino 
301e0b8e63eSJohn Marino /*
302e0b8e63eSJohn Marino  * v_buildps --
303e0b8e63eSJohn Marino  *	Build the paragraph command search pattern.
304e0b8e63eSJohn Marino  *
305e0b8e63eSJohn Marino  * PUBLIC: int v_buildps(SCR *, char *, char *);
306e0b8e63eSJohn Marino  */
307e0b8e63eSJohn Marino int
v_buildps(SCR * sp,char * p_p,char * s_p)308e0b8e63eSJohn Marino v_buildps(SCR *sp, char *p_p, char *s_p)
309e0b8e63eSJohn Marino {
310e0b8e63eSJohn Marino 	VI_PRIVATE *vip;
311e0b8e63eSJohn Marino 	size_t p_len, s_len;
312e0b8e63eSJohn Marino 	char *p;
313e0b8e63eSJohn Marino 
314e0b8e63eSJohn Marino 	/*
315e0b8e63eSJohn Marino 	 * The vi paragraph command searches for either a paragraph or
316e0b8e63eSJohn Marino 	 * section option macro.
317e0b8e63eSJohn Marino 	 */
318e0b8e63eSJohn Marino 	p_len = p_p == NULL ? 0 : strlen(p_p);
319e0b8e63eSJohn Marino 	s_len = s_p == NULL ? 0 : strlen(s_p);
320e0b8e63eSJohn Marino 
321e0b8e63eSJohn Marino 	if (p_len == 0 && s_len == 0)
322e0b8e63eSJohn Marino 		return (0);
323e0b8e63eSJohn Marino 
324*b1ac2ebbSDaniel Fojt 	MALLOC_RET(sp, p, p_len + s_len + 1);
325e0b8e63eSJohn Marino 
326e0b8e63eSJohn Marino 	vip = VIP(sp);
327e0b8e63eSJohn Marino 	free(vip->ps);
328e0b8e63eSJohn Marino 
329e0b8e63eSJohn Marino 	if (p_p != NULL)
330e0b8e63eSJohn Marino 		memmove(p, p_p, p_len + 1);
331e0b8e63eSJohn Marino 	if (s_p != NULL)
332e0b8e63eSJohn Marino 		memmove(p + p_len, s_p, s_len + 1);
333e0b8e63eSJohn Marino 	vip->ps = p;
334e0b8e63eSJohn Marino 	return (0);
335e0b8e63eSJohn Marino }
336