--- /dev/null
+/* $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 <string.h>
+
+#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);
+}
+