GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/mg/yank.c Lines: 0 103 0.0 %
Date: 2017-11-07 Branches: 0 82 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: yank.c,v 1.14 2015/12/11 20:21:23 mmcc Exp $	*/
2
3
/* This file is in the public domain. */
4
5
/*
6
 *	kill ring functions
7
 */
8
9
#include <sys/queue.h>
10
#include <signal.h>
11
#include <stdio.h>
12
#include <stdlib.h>
13
#include <string.h>
14
15
#include "def.h"
16
17
#define KBLOCK	 8192		/* Kill grow.                    */
18
19
static char	*kbufp = NULL;	/* Kill buffer data.		 */
20
static RSIZE	 kused = 0;	/* # of bytes used in KB.	 */
21
static RSIZE	 ksize = 0;	/* # of bytes allocated in KB.	 */
22
static RSIZE	 kstart = 0;	/* # of first used byte in KB.	 */
23
24
static int	 kgrow(int);
25
26
/*
27
 * Delete all of the text saved in the kill buffer.  Called by commands when
28
 * a new kill context is created. The kill buffer array is released, just in
29
 * case the buffer has grown to an immense size.  No errors.
30
 */
31
void
32
kdelete(void)
33
{
34
	if (kbufp != NULL) {
35
		free(kbufp);
36
		kbufp = NULL;
37
		kstart = kused = ksize = 0;
38
	}
39
}
40
41
/*
42
 * Insert a character to the kill buffer, enlarging the buffer if there
43
 * isn't any room. Always grow the buffer in chunks, on the assumption
44
 * that if you put something in the kill buffer you are going to put more
45
 * stuff there too later. Return TRUE if all is well, and FALSE on errors.
46
 * Print a message on errors.  Dir says whether to put it at back or front.
47
 * This call is ignored if  KNONE is set.
48
 */
49
int
50
kinsert(int c, int dir)
51
{
52
	if (dir == KNONE)
53
		return (TRUE);
54
	if (kused == ksize && dir == KFORW && kgrow(dir) == FALSE)
55
		return (FALSE);
56
	if (kstart == 0 && dir == KBACK && kgrow(dir) == FALSE)
57
		return (FALSE);
58
	if (dir == KFORW)
59
		kbufp[kused++] = c;
60
	else if (dir == KBACK)
61
		kbufp[--kstart] = c;
62
	else
63
		panic("broken kinsert call");	/* Oh shit! */
64
	return (TRUE);
65
}
66
67
/*
68
 * kgrow - just get more kill buffer for the callee. If dir = KBACK
69
 * we are trying to get space at the beginning of the kill buffer.
70
 */
71
static int
72
kgrow(int dir)
73
{
74
	int	 nstart;
75
	char	*nbufp;
76
77
	if ((unsigned)(ksize + KBLOCK) <= (unsigned)ksize) {
78
		/* probably 16 bit unsigned */
79
		dobeep();
80
		ewprintf("Kill buffer size at maximum");
81
		return (FALSE);
82
	}
83
	if ((nbufp = malloc((unsigned)(ksize + KBLOCK))) == NULL) {
84
		dobeep();
85
		ewprintf("Can't get %ld bytes", (long)(ksize + KBLOCK));
86
		return (FALSE);
87
	}
88
	nstart = (dir == KBACK) ? (kstart + KBLOCK) : (KBLOCK / 4);
89
	bcopy(&(kbufp[kstart]), &(nbufp[nstart]), (int)(kused - kstart));
90
	free(kbufp);
91
	kbufp = nbufp;
92
	ksize += KBLOCK;
93
	kused = kused - kstart + nstart;
94
	kstart = nstart;
95
	return (TRUE);
96
}
97
98
/*
99
 * This function gets characters from the kill buffer. If the character
100
 * index "n" is off the end, it returns "-1". This lets the caller just
101
 * scan along until it gets a "-1" back.
102
 */
103
int
104
kremove(int n)
105
{
106
	if (n < 0 || n + kstart >= kused)
107
		return (-1);
108
	return (CHARMASK(kbufp[n + kstart]));
109
}
110
111
/*
112
 * Copy a string into the kill buffer. kflag gives direction.
113
 * if KNONE, do nothing.
114
 */
115
int
116
kchunk(char *cp1, RSIZE chunk, int kflag)
117
{
118
	/*
119
	 * HACK - doesn't matter, and fixes back-over-nl bug for empty
120
	 *	kill buffers.
121
	 */
122
	if (kused == kstart)
123
		kflag = KFORW;
124
125
	if (kflag & KFORW) {
126
		while (ksize - kused < chunk)
127
			if (kgrow(kflag) == FALSE)
128
				return (FALSE);
129
		bcopy(cp1, &(kbufp[kused]), (int)chunk);
130
		kused += chunk;
131
	} else if (kflag & KBACK) {
132
		while (kstart < chunk)
133
			if (kgrow(kflag) == FALSE)
134
				return (FALSE);
135
		bcopy(cp1, &(kbufp[kstart - chunk]), (int)chunk);
136
		kstart -= chunk;
137
	}
138
139
	return (TRUE);
140
}
141
142
/*
143
 * Kill line.  If called without an argument, it kills from dot to the end
144
 * of the line, unless it is at the end of the line, when it kills the
145
 * newline.  If called with an argument of 0, it kills from the start of the
146
 * line to dot.  If called with a positive argument, it kills from dot
147
 * forward over that number of newlines.  If called with a negative argument
148
 * it kills any text before dot on the current line, then it kills back
149
 * abs(arg) lines.
150
 */
151
/* ARGSUSED */
152
int
153
killline(int f, int n)
154
{
155
	struct line	*nextp;
156
	RSIZE	 chunk;
157
	int	 i, c;
158
159
	/* clear kill buffer if last wasn't a kill */
160
	if ((lastflag & CFKILL) == 0)
161
		kdelete();
162
	thisflag |= CFKILL;
163
	if (!(f & FFARG)) {
164
		for (i = curwp->w_doto; i < llength(curwp->w_dotp); ++i)
165
			if ((c = lgetc(curwp->w_dotp, i)) != ' ' && c != '\t')
166
				break;
167
		if (i == llength(curwp->w_dotp))
168
			chunk = llength(curwp->w_dotp) - curwp->w_doto + 1;
169
		else {
170
			chunk = llength(curwp->w_dotp) - curwp->w_doto;
171
			if (chunk == 0)
172
				chunk = 1;
173
		}
174
	} else if (n > 0) {
175
		chunk = llength(curwp->w_dotp) - curwp->w_doto;
176
		nextp = lforw(curwp->w_dotp);
177
		if (nextp != curbp->b_headp)
178
			chunk++;		/* newline */
179
		if (nextp == curbp->b_headp)
180
			goto done;		/* EOL */
181
		i = n;
182
		while (--i) {
183
			chunk += llength(nextp);
184
			nextp = lforw(nextp);
185
			if (nextp != curbp->b_headp)
186
				chunk++;	/* newline */
187
			if (nextp == curbp->b_headp)
188
				break;		/* EOL */
189
		}
190
	} else {
191
		/* n <= 0 */
192
		chunk = curwp->w_doto;
193
		curwp->w_doto = 0;
194
		i = n;
195
		while (i++) {
196
			if (lforw(curwp->w_dotp))
197
				chunk++;
198
			curwp->w_dotp = lback(curwp->w_dotp);
199
			curwp->w_rflag |= WFMOVE;
200
			chunk += llength(curwp->w_dotp);
201
		}
202
	}
203
	/*
204
	 * KFORW here is a bug.  Should be KBACK/KFORW, but we need to
205
	 * rewrite the ldelete code (later)?
206
	 */
207
done:
208
	if (chunk)
209
		return (ldelete(chunk, KFORW));
210
	return (TRUE);
211
}
212
213
/*
214
 * Yank text back from the kill buffer.  This is really easy.  All of the work
215
 * is done by the standard insert routines.  All you do is run the loop, and
216
 * check for errors.  The blank lines are inserted with a call to "newline"
217
 * instead of a call to "lnewline" so that the magic stuff that happens when
218
 * you type a carriage return also happens when a carriage return is yanked
219
 * back from the kill buffer.  An attempt has been made to fix the cosmetic
220
 * bug associated with a yank when dot is on the top line of the window
221
 * (nothing moves, because all of the new text landed off screen).
222
 */
223
/* ARGSUSED */
224
int
225
yank(int f, int n)
226
{
227
	struct line	*lp;
228
	int	 c, i, nline;
229
230
	if (n < 0)
231
		return (FALSE);
232
233
	/* newline counting */
234
	nline = 0;
235
236
	undo_boundary_enable(FFRAND, 0);
237
	while (n--) {
238
		/* mark around last yank */
239
		isetmark();
240
		i = 0;
241
		while ((c = kremove(i)) >= 0) {
242
			if (c == '\n') {
243
				if (enewline(FFRAND, 1) == FALSE)
244
					return (FALSE);
245
				++nline;
246
			} else {
247
				if (linsert(1, c) == FALSE)
248
					return (FALSE);
249
			}
250
			++i;
251
		}
252
	}
253
	/* cosmetic adjustment */
254
	lp = curwp->w_linep;
255
256
	/* if offscreen insert */
257
	if (curwp->w_dotp == lp) {
258
		while (nline-- && lback(lp) != curbp->b_headp)
259
			lp = lback(lp);
260
		/* adjust framing */
261
		curwp->w_linep = lp;
262
		curwp->w_rflag |= WFFULL;
263
	}
264
	undo_boundary_enable(FFRAND, 1);
265
	return (TRUE);
266
}
267