/* $OpenBSD: yank.c,v 1.12 2015/03/16 13:47:48 bcallah 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 */ 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)); 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 (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); }