yank.c revision 1.13
1/*	$OpenBSD: yank.c,v 1.13 2015/03/19 21:22:15 bcallah 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
19static char	*kbufp = NULL;	/* Kill buffer data.		 */
20static RSIZE	 kused = 0;	/* # of bytes used in KB.	 */
21static RSIZE	 ksize = 0;	/* # of bytes allocated in KB.	 */
22static RSIZE	 kstart = 0;	/* # of first used byte in KB.	 */
23
24static 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 */
31void
32kdelete(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 */
49int
50kinsert(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 */
71static int
72kgrow(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	if (kbufp != NULL)
91		free(kbufp);
92	kbufp = nbufp;
93	ksize += KBLOCK;
94	kused = kused - kstart + nstart;
95	kstart = nstart;
96	return (TRUE);
97}
98
99/*
100 * This function gets characters from the kill buffer. If the character
101 * index "n" is off the end, it returns "-1". This lets the caller just
102 * scan along until it gets a "-1" back.
103 */
104int
105kremove(int n)
106{
107	if (n < 0 || n + kstart >= kused)
108		return (-1);
109	return (CHARMASK(kbufp[n + kstart]));
110}
111
112/*
113 * Copy a string into the kill buffer. kflag gives direction.
114 * if KNONE, do nothing.
115 */
116int
117kchunk(char *cp1, RSIZE chunk, int kflag)
118{
119	/*
120	 * HACK - doesn't matter, and fixes back-over-nl bug for empty
121	 *	kill buffers.
122	 */
123	if (kused == kstart)
124		kflag = KFORW;
125
126	if (kflag & KFORW) {
127		while (ksize - kused < chunk)
128			if (kgrow(kflag) == FALSE)
129				return (FALSE);
130		bcopy(cp1, &(kbufp[kused]), (int)chunk);
131		kused += chunk;
132	} else if (kflag & KBACK) {
133		while (kstart < chunk)
134			if (kgrow(kflag) == FALSE)
135				return (FALSE);
136		bcopy(cp1, &(kbufp[kstart - chunk]), (int)chunk);
137		kstart -= chunk;
138	}
139
140	return (TRUE);
141}
142
143/*
144 * Kill line.  If called without an argument, it kills from dot to the end
145 * of the line, unless it is at the end of the line, when it kills the
146 * newline.  If called with an argument of 0, it kills from the start of the
147 * line to dot.  If called with a positive argument, it kills from dot
148 * forward over that number of newlines.  If called with a negative argument
149 * it kills any text before dot on the current line, then it kills back
150 * abs(arg) lines.
151 */
152/* ARGSUSED */
153int
154killline(int f, int n)
155{
156	struct line	*nextp;
157	RSIZE	 chunk;
158	int	 i, c;
159
160	/* clear kill buffer if last wasn't a kill */
161	if ((lastflag & CFKILL) == 0)
162		kdelete();
163	thisflag |= CFKILL;
164	if (!(f & FFARG)) {
165		for (i = curwp->w_doto; i < llength(curwp->w_dotp); ++i)
166			if ((c = lgetc(curwp->w_dotp, i)) != ' ' && c != '\t')
167				break;
168		if (i == llength(curwp->w_dotp))
169			chunk = llength(curwp->w_dotp) - curwp->w_doto + 1;
170		else {
171			chunk = llength(curwp->w_dotp) - curwp->w_doto;
172			if (chunk == 0)
173				chunk = 1;
174		}
175	} else if (n > 0) {
176		chunk = llength(curwp->w_dotp) - curwp->w_doto;
177		nextp = lforw(curwp->w_dotp);
178		if (nextp != curbp->b_headp)
179			chunk++;		/* newline */
180		if (nextp == curbp->b_headp)
181			goto done;		/* EOL */
182		i = n;
183		while (--i) {
184			chunk += llength(nextp);
185			nextp = lforw(nextp);
186			if (nextp != curbp->b_headp)
187				chunk++;	/* newline */
188			if (nextp == curbp->b_headp)
189				break;		/* EOL */
190		}
191	} else {
192		/* n <= 0 */
193		chunk = curwp->w_doto;
194		curwp->w_doto = 0;
195		i = n;
196		while (i++) {
197			if (lforw(curwp->w_dotp))
198				chunk++;
199			curwp->w_dotp = lback(curwp->w_dotp);
200			curwp->w_rflag |= WFMOVE;
201			chunk += llength(curwp->w_dotp);
202		}
203	}
204	/*
205	 * KFORW here is a bug.  Should be KBACK/KFORW, but we need to
206	 * rewrite the ldelete code (later)?
207	 */
208done:
209	if (chunk)
210		return (ldelete(chunk, KFORW));
211	return (TRUE);
212}
213
214/*
215 * Yank text back from the kill buffer.  This is really easy.  All of the work
216 * is done by the standard insert routines.  All you do is run the loop, and
217 * check for errors.  The blank lines are inserted with a call to "newline"
218 * instead of a call to "lnewline" so that the magic stuff that happens when
219 * you type a carriage return also happens when a carriage return is yanked
220 * back from the kill buffer.  An attempt has been made to fix the cosmetic
221 * bug associated with a yank when dot is on the top line of the window
222 * (nothing moves, because all of the new text landed off screen).
223 */
224/* ARGSUSED */
225int
226yank(int f, int n)
227{
228	struct line	*lp;
229	int	 c, i, nline;
230
231	if (n < 0)
232		return (FALSE);
233
234	/* newline counting */
235	nline = 0;
236
237	undo_boundary_enable(FFRAND, 0);
238	while (n--) {
239		/* mark around last yank */
240		isetmark();
241		i = 0;
242		while ((c = kremove(i)) >= 0) {
243			if (c == '\n') {
244				if (enewline(FFRAND, 1) == FALSE)
245					return (FALSE);
246				++nline;
247			} else {
248				if (linsert(1, c) == FALSE)
249					return (FALSE);
250			}
251			++i;
252		}
253	}
254	/* cosmetic adjustment */
255	lp = curwp->w_linep;
256
257	/* if offscreen insert */
258	if (curwp->w_dotp == lp) {
259		while (nline-- && lback(lp) != curbp->b_headp)
260			lp = lback(lp);
261		/* adjust framing */
262		curwp->w_linep = lp;
263		curwp->w_rflag |= WFFULL;
264	}
265	undo_boundary_enable(FFRAND, 1);
266	return (TRUE);
267}
268
269