[BACK]Return to yank.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / mg

File: [local] / src / usr.bin / mg / yank.c (download)

Revision 1.9, Fri Jun 5 18:02:06 2009 UTC (15 years ago) by kjell
Branch: MAIN
CVS Tags: OPENBSD_4_9_BASE, OPENBSD_4_9, OPENBSD_4_8_BASE, OPENBSD_4_8, OPENBSD_4_7_BASE, OPENBSD_4_7, OPENBSD_4_6_BASE, OPENBSD_4_6
Changes since 1.8: +4 -5 lines

emacs handles the undo of a region kill (C-w) differently than a line kill
(C-k) with respect to cursor position. The former leaves the cursor at the end,
the latter at the beginning of the region.

emacs is wacky.

Make mg undo do the same. ok deraadt

/*	$OpenBSD: yank.c,v 1.9 2009/06/05 18:02:06 kjell 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 + 1;
		nextp = lforw(curwp->w_dotp);
		i = n;
		while (--i) {
			if (nextp == curbp->b_headp)
				break;
			chunk += llength(nextp) + 1;
			nextp = lforw(nextp);
		}
	} else {
		/* n <= 0 */
		chunk = curwp->w_doto;
		curwp->w_doto = 0;
		i = n;
		while (i++) {
			if (lback(curwp->w_dotp) == curbp->b_headp)
				break;
			curwp->w_dotp = lback(curwp->w_dotp);
			curwp->w_rflag |= WFMOVE;
			chunk += llength(curwp->w_dotp) + 1;
		}
	}
	/*
	 * KFORW here is a bug.  Should be KBACK/KFORW, but we need to
	 * rewrite the ldelete code (later)?
	 */
	return (ldelete(chunk, KFORW));
}

/*
 * 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);
}