1 /* $OpenBSD: yank.c,v 1.15 2021/03/01 10:51:14 lum 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
kdelete(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
kinsert(int c,int dir)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
kgrow(int dir)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
kremove(int n)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
kchunk(char * cp1,RSIZE chunk,int kflag)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
killline(int f,int n)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
yank(int f,int n)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 == *curbp->b_nlchr) {
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
268