GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/vi/build/../vi/v_paragraph.c Lines: 0 99 0.0 %
Date: 2017-11-13 Branches: 0 140 0.0 %

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