]> pd.if.org Git - pd_readline/blobdiff - mg/yank.c
Added mg from an OpenBSD mirror site. Many thanks to the OpenBSD team and the mg...
[pd_readline] / mg / yank.c
diff --git a/mg/yank.c b/mg/yank.c
new file mode 100644 (file)
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 <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);
+}
+