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