X-Git-Url: https://pd.if.org/git/?p=pd_readline;a=blobdiff_plain;f=mg%2Fyank.c;fp=mg%2Fyank.c;h=d408286ecc7b6a1e96107fb97443b3c22475ed93;hp=0000000000000000000000000000000000000000;hb=a9843085ec916c175bd245a8398f30e6cc03f984;hpb=26fe4e09c6c3c250334fdeed60ce3061febecde2 diff --git a/mg/yank.c b/mg/yank.c new file mode 100644 index 0000000..d408286 --- /dev/null +++ b/mg/yank.c @@ -0,0 +1,264 @@ +/* $OpenBSD: yank.c,v 1.10 2011/07/15 16:50:52 deraadt Exp $ */ + +/* This file is in the public domain. */ + +/* + * kill ring functions + */ + +#include "def.h" + +#include + +#ifndef KBLOCK +#define KBLOCK 256 /* Kill buffer block size. */ +#endif + +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 */ + ewprintf("Kill buffer size at maximum"); + return (FALSE); + } + if ((nbufp = malloc((unsigned)(ksize + KBLOCK))) == NULL) { + 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)); + if (kbufp != NULL) + 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 (newline(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); +} +