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