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 |
|
|
|