xref: /openbsd/usr.bin/vi/vi/v_section.c (revision 78b63d65)
1 /*	$OpenBSD: v_section.c,v 1.3 2001/01/29 01:58:52 niklas Exp $	*/
2 
3 /*-
4  * Copyright (c) 1992, 1993, 1994
5  *	The Regents of the University of California.  All rights reserved.
6  * Copyright (c) 1992, 1993, 1994, 1995, 1996
7  *	Keith Bostic.  All rights reserved.
8  *
9  * See the LICENSE file for redistribution information.
10  */
11 
12 #include "config.h"
13 
14 #ifndef lint
15 static const char sccsid[] = "@(#)v_section.c	10.7 (Berkeley) 3/6/96";
16 #endif /* not lint */
17 
18 #include <sys/types.h>
19 #include <sys/queue.h>
20 #include <sys/time.h>
21 
22 #include <bitstring.h>
23 #include <limits.h>
24 #include <stdio.h>
25 #include <string.h>
26 
27 #include "../common/common.h"
28 #include "vi.h"
29 
30 /*
31  * !!!
32  * In historic vi, the section commands ignored empty lines, unlike the
33  * paragraph commands, which was probably okay.  However, they also moved
34  * to the start of the last line when there where no more sections instead
35  * of the end of the last line like the paragraph commands.  I've changed
36  * the latter behavior to match the paragraph commands.
37  *
38  * In historic vi, a section was defined as the first character(s) of the
39  * line matching, which could be followed by anything.  This implementation
40  * follows that historic practice.
41  *
42  * !!!
43  * The historic vi documentation (USD:15-10) claimed:
44  *	The section commands interpret a preceding count as a different
45  *	window size in which to redraw the screen at the new location,
46  *	and this window size is the base size for newly drawn windows
47  *	until another size is specified.  This is very useful if you are
48  *	on a slow terminal ...
49  *
50  * I can't get the 4BSD vi to do this, it just beeps at me.  For now, a
51  * count to the section commands simply repeats the command.
52  */
53 
54 /*
55  * v_sectionf -- [count]]]
56  *	Move forward count sections/functions.
57  *
58  * !!!
59  * Using ]] as a motion command was a bit special, historically.  It could
60  * match } as well as the usual { and section values.  If it matched a { or
61  * a section, it did NOT include the matched line.  If it matched a }, it
62  * did include the line.  No clue why.
63  *
64  * PUBLIC: int v_sectionf __P((SCR *, VICMD *));
65  */
66 int
67 v_sectionf(sp, vp)
68 	SCR *sp;
69 	VICMD *vp;
70 {
71 	recno_t cnt, lno;
72 	size_t len;
73 	char *p, *list, *lp;
74 
75 	/* Get the macro list. */
76 	if ((list = O_STR(sp, O_SECTIONS)) == NULL)
77 		return (1);
78 
79 	/*
80 	 * !!!
81 	 * If the starting cursor position is at or before any non-blank
82 	 * characters in the line, i.e. the movement is cutting all of the
83 	 * line's text, the buffer is in line mode.  It's a lot easier to
84 	 * check here, because we know that the end is going to be the start
85 	 * or end of a line.
86 	 */
87 	if (ISMOTION(vp))
88 		if (vp->m_start.cno == 0)
89 			F_SET(vp, VM_LMODE);
90 		else {
91 			vp->m_stop = vp->m_start;
92 			vp->m_stop.cno = 0;
93 			if (nonblank(sp, vp->m_stop.lno, &vp->m_stop.cno))
94 				return (1);
95 			if (vp->m_start.cno <= vp->m_stop.cno)
96 				F_SET(vp, VM_LMODE);
97 		}
98 
99 	cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
100 	for (lno = vp->m_start.lno; !db_get(sp, ++lno, 0, &p, &len);) {
101 		if (len == 0)
102 			continue;
103 		if (p[0] == '{' || ISMOTION(vp) && p[0] == '}') {
104 			if (!--cnt) {
105 				if (p[0] == '{')
106 					goto adjust1;
107 				goto adjust2;
108 			}
109 			continue;
110 		}
111 		/*
112 		 * !!!
113 		 * Historic documentation (USD:15-11, 4.2) said that formfeed
114 		 * characters (^L) in the first column delimited sections.
115 		 * The historic code mentions formfeed characters, but never
116 		 * implements them.  Seems reasonable, do it.
117 		 */
118 		if (p[0] == '\014') {
119 			if (!--cnt)
120 				goto adjust1;
121 			continue;
122 		}
123 		if (p[0] != '.' || len < 2)
124 			continue;
125 		for (lp = list; *lp != '\0'; lp += 2 * sizeof(*lp))
126 			if (lp[0] == p[1] &&
127 			    (lp[1] == ' ' && len == 2 || lp[1] == p[2]) &&
128 			    !--cnt) {
129 				/*
130 				 * !!!
131 				 * If not cutting this line, adjust to the end
132 				 * of the previous one.  Otherwise, position to
133 				 * column 0.
134 				 */
135 adjust1:			if (ISMOTION(vp))
136 					goto ret1;
137 
138 adjust2:			vp->m_stop.lno = lno;
139 				vp->m_stop.cno = 0;
140 				goto ret2;
141 			}
142 	}
143 
144 	/* If moving forward, reached EOF, check to see if we started there. */
145 	if (vp->m_start.lno == lno - 1) {
146 		v_eof(sp, NULL);
147 		return (1);
148 	}
149 
150 ret1:	if (db_get(sp, --lno, DBG_FATAL, NULL, &len))
151 		return (1);
152 	vp->m_stop.lno = lno;
153 	vp->m_stop.cno = len ? len - 1 : 0;
154 
155 	/*
156 	 * Non-motion commands go to the end of the range.  Delete and
157 	 * yank stay at the start of the range.  Ignore others.
158 	 */
159 ret2:	if (ISMOTION(vp)) {
160 		vp->m_final = vp->m_start;
161 		if (F_ISSET(vp, VM_LMODE))
162 			vp->m_final.cno = 0;
163 	} else
164 		vp->m_final = vp->m_stop;
165 	return (0);
166 }
167 
168 /*
169  * v_sectionb -- [count][[
170  *	Move backward count sections/functions.
171  *
172  * PUBLIC: int v_sectionb __P((SCR *, VICMD *));
173  */
174 int
175 v_sectionb(sp, vp)
176 	SCR *sp;
177 	VICMD *vp;
178 {
179 	size_t len;
180 	recno_t cnt, lno;
181 	char *p, *list, *lp;
182 
183 	/* An empty file or starting from line 1 is always illegal. */
184 	if (vp->m_start.lno <= 1) {
185 		v_sof(sp, NULL);
186 		return (1);
187 	}
188 
189 	/* Get the macro list. */
190 	if ((list = O_STR(sp, O_SECTIONS)) == NULL)
191 		return (1);
192 
193 	cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
194 	for (lno = vp->m_start.lno; !db_get(sp, --lno, 0, &p, &len);) {
195 		if (len == 0)
196 			continue;
197 		if (p[0] == '{') {
198 			if (!--cnt)
199 				goto adjust1;
200 			continue;
201 		}
202 		/*
203 		 * !!!
204 		 * Historic documentation (USD:15-11, 4.2) said that formfeed
205 		 * characters (^L) in the first column delimited sections.
206 		 * The historic code mentions formfeed characters, but never
207 		 * implements them.  Seems reasonable, do it.
208 		 */
209 		if (p[0] == '\014') {
210 			if (!--cnt)
211 				goto adjust1;
212 			continue;
213 		}
214 		if (p[0] != '.' || len < 2)
215 			continue;
216 		for (lp = list; *lp != '\0'; lp += 2 * sizeof(*lp))
217 			if (lp[0] == p[1] &&
218 			    (lp[1] == ' ' && len == 2 || lp[1] == p[2]) &&
219 			    !--cnt) {
220 adjust1:			vp->m_stop.lno = lno;
221 				vp->m_stop.cno = 0;
222 				goto ret1;
223 			}
224 	}
225 
226 	/*
227 	 * If moving backward, reached SOF, which is a movement sink.
228 	 * We already checked for starting there.
229 	 */
230 	vp->m_stop.lno = 1;
231 	vp->m_stop.cno = 0;
232 
233 	/*
234 	 * All commands move to the end of the range.
235 	 *
236 	 * !!!
237 	 * Historic practice is the section cut was in line mode if it started
238 	 * from column 0 and was in the backward direction.  Otherwise, left
239 	 * motion commands adjust the starting point to the character before
240 	 * the current one.  What makes this worse is that if it cut to line
241 	 * mode it also went to the first non-<blank>.
242 	 */
243 ret1:	if (vp->m_start.cno == 0) {
244 		F_CLR(vp, VM_RCM_MASK);
245 		F_SET(vp, VM_RCM_SETFNB);
246 
247 		--vp->m_start.lno;
248 		F_SET(vp, VM_LMODE);
249 	} else
250 		--vp->m_start.cno;
251 
252 	vp->m_final = vp->m_stop;
253 	return (0);
254 }
255