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