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

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

Revision 1.33, Thu May 31 10:55:53 2012 UTC (12 years ago) by lum
Branch: MAIN
Changes since 1.32: +10 -4 lines

Currently, mg's cursor jumps from top to bottom of the screen as you
scroll upwards, make the cursor behave like emacs and stay at bottom
of window. Tested by mikeb@.

/*	$OpenBSD: basic.c,v 1.33 2012/05/31 10:55:53 lum Exp $	*/

/* This file is in the public domain */

/*
 *		Basic cursor motion commands.
 *
 * The routines in this file are the basic
 * command functions for moving the cursor around on
 * the screen, setting mark, and swapping dot with
 * mark. Only moves between lines, which might make the
 * current buffer framing bad, are hard.
 */
#include "def.h"

#include <ctype.h>

/*
 * Go to beginning of line.
 */
/* ARGSUSED */
int
gotobol(int f, int n)
{
	curwp->w_doto = 0;
	return (TRUE);
}

/*
 * Move cursor backwards. Do the
 * right thing if the count is less than
 * 0. Error if you try to move back from
 * the beginning of the buffer.
 */
/* ARGSUSED */
int
backchar(int f, int n)
{
	struct line   *lp;

	if (n < 0)
		return (forwchar(f, -n));
	while (n--) {
		if (curwp->w_doto == 0) {
			if ((lp = lback(curwp->w_dotp)) == curbp->b_headp) {
				if (!(f & FFRAND))
					ewprintf("Beginning of buffer");
				return (FALSE);
			}
			curwp->w_dotp = lp;
			curwp->w_doto = llength(lp);
			curwp->w_rflag |= WFMOVE;
			curwp->w_dotline--;
		} else
			curwp->w_doto--;
	}
	return (TRUE);
}

/*
 * Go to end of line.
 */
/* ARGSUSED */
int
gotoeol(int f, int n)
{
	curwp->w_doto = llength(curwp->w_dotp);
	return (TRUE);
}

/*
 * Move cursor forwards. Do the
 * right thing if the count is less than
 * 0. Error if you try to move forward
 * from the end of the buffer.
 */
/* ARGSUSED */
int
forwchar(int f, int n)
{
	if (n < 0)
		return (backchar(f, -n));
	while (n--) {
		if (curwp->w_doto == llength(curwp->w_dotp)) {
			curwp->w_dotp = lforw(curwp->w_dotp);
			if (curwp->w_dotp == curbp->b_headp) {
				curwp->w_dotp = lback(curwp->w_dotp);
				if (!(f & FFRAND))
					ewprintf("End of buffer");
				return (FALSE);
			}
			curwp->w_doto = 0;
			curwp->w_dotline++;
			curwp->w_rflag |= WFMOVE;
		} else
			curwp->w_doto++;
	}
	return (TRUE);
}

/*
 * Go to the beginning of the
 * buffer. Setting WFFULL is conservative,
 * but almost always the case.
 */
int
gotobob(int f, int n)
{
	(void) setmark(f, n);
	curwp->w_dotp = bfirstlp(curbp);
	curwp->w_doto = 0;
	curwp->w_rflag |= WFFULL;
	curwp->w_dotline = 1;
	return (TRUE);
}

/*
 * Go to the end of the buffer.
 * Setting WFFULL is conservative, but
 * almost always the case.
 */
int
gotoeob(int f, int n)
{
	(void) setmark(f, n);
	curwp->w_dotp = blastlp(curbp);
	curwp->w_doto = llength(curwp->w_dotp);
	curwp->w_dotline = curwp->w_bufp->b_lines;
	curwp->w_rflag |= WFFULL;
	return (TRUE);
}

/*
 * Move forward by full lines.
 * If the number of lines to move is less
 * than zero, call the backward line function to
 * actually do it. The last command controls how
 * the goal column is set.
 */
/* ARGSUSED */
int
forwline(int f, int n)
{
	struct line  *dlp;

	if (n < 0)
		return (backline(f | FFRAND, -n));
	if ((dlp = curwp->w_dotp) == curbp->b_headp)
		return(TRUE);
	if ((lastflag & CFCPCN) == 0)	/* Fix goal. */
		setgoal();
	thisflag |= CFCPCN;
	if (n == 0)
		return (TRUE);
	while (n--) {
		dlp = lforw(dlp);
		if (dlp == curbp->b_headp) {
			curwp->w_dotp = lback(dlp);
			curwp->w_doto = llength(curwp->w_dotp);
			curwp->w_rflag |= WFMOVE;
			return (TRUE);
		}
		curwp->w_dotline++;
	}
	curwp->w_rflag |= WFMOVE;
	curwp->w_dotp = dlp;
	curwp->w_doto = getgoal(dlp);

	return (TRUE);
}

/*
 * This function is like "forwline", but
 * goes backwards. The scheme is exactly the same.
 * Check for arguments that are less than zero and
 * call your alternate. Figure out the new line and
 * call "movedot" to perform the motion.
 */
/* ARGSUSED */
int
backline(int f, int n)
{
	struct line   *dlp;

	if (n < 0)
		return (forwline(f | FFRAND, -n));
	if ((lastflag & CFCPCN) == 0)	/* Fix goal. */
		setgoal();
	thisflag |= CFCPCN;
	dlp = curwp->w_dotp;
	while (n-- && lback(dlp) != curbp->b_headp) {
		dlp = lback(dlp);
		curwp->w_dotline--;
	}
	curwp->w_dotp = dlp;
	curwp->w_doto = getgoal(dlp);
	curwp->w_rflag |= WFMOVE;
	return (TRUE);
}

/*
 * Set the current goal column, which is saved in the external variable
 * "curgoal", to the current cursor column. The column is never off
 * the edge of the screen; it's more like display then show position.
 */
void
setgoal(void)
{
	curgoal = getcolpos();		/* Get the position. */
	/* we can now display past end of display, don't chop! */
}

/*
 * This routine looks at a line (pointed
 * to by the LINE pointer "dlp") and the current
 * vertical motion goal column (set by the "setgoal"
 * routine above) and returns the best offset to use
 * when a vertical motion is made into the line.
 */
int
getgoal(struct line *dlp)
{
	int c, i, col = 0;
	char tmp[5];


	for (i = 0; i < llength(dlp); i++) {
		c = lgetc(dlp, i);
		if (c == '\t'
#ifdef	NOTAB
		    && !(curbp->b_flag & BFNOTAB)
#endif
			) {
			col |= 0x07;
			col++;
		} else if (ISCTRL(c) != FALSE) {
			col += 2;
		} else if (isprint(c))
			col++;
		else {
			col += snprintf(tmp, sizeof(tmp), "\\%o", c);
		}
		if (col > curgoal)
			break;
	}
	return (i);
}

/*
 * Scroll forward by a specified number
 * of lines, or by a full page if no argument.
 * The "2" is the window overlap (this is the default
 * value from ITS EMACS). Because the top line in
 * the window is zapped, we have to do a hard
 * update and get it back.
 */
/* ARGSUSED */
int
forwpage(int f, int n)
{
	struct line  *lp;

	if (!(f & FFARG)) {
		n = curwp->w_ntrows - 2;	/* Default scroll.	 */
		if (n <= 0)			/* Forget the overlap	 */
			n = 1;			/* if tiny window.	 */
	} else if (n < 0)
		return (backpage(f | FFRAND, -n));

	lp = curwp->w_linep;
	while (n--)
		if ((lp = lforw(lp)) == curbp->b_headp)
			return(TRUE);

	curwp->w_linep = lp;
	curwp->w_rflag |= WFFULL;

	/* if in current window, don't move dot */
	for (n = curwp->w_ntrows; n-- && lp != curbp->b_headp; lp = lforw(lp))
		if (lp == curwp->w_dotp)
			return (TRUE);

	/* Advance the dot the slow way, for line nos */
	while (curwp->w_dotp != curwp->w_linep) {
		curwp->w_dotp = lforw(curwp->w_dotp);
		curwp->w_dotline++;
	}
	curwp->w_doto = 0;
	return (TRUE);
}

/*
 * This command is like "forwpage",
 * but it goes backwards. The "2", like above,
 * is the overlap between the two windows. The
 * value is from the ITS EMACS manual. The
 * hard update is done because the top line in
 * the window is zapped.
 */
/* ARGSUSED */
int
backpage(int f, int n)
{
	struct line  *lp, *lp2;

	if (!(f & FFARG)) {
		n = curwp->w_ntrows - 2;	/* Default scroll.	 */
		if (n <= 0)			/* Don't blow up if the  */
			n = 1;			/* window is tiny.	 */
	} else if (n < 0)
		return (forwpage(f | FFRAND, -n));

	lp = lp2 = curwp->w_linep;

	while (n-- && lback(lp) != curbp->b_headp) {
		lp = lback(lp);
	}
	curwp->w_linep = lp;
	curwp->w_rflag |= WFFULL;

	/* if in current window, don't move dot */
	for (n = curwp->w_ntrows; n-- && lp != curbp->b_headp; lp = lforw(lp))
		if (lp == curwp->w_dotp)
			return (TRUE);

        lp2 = lforw(lp2);

	/* Move the dot the slow way, for line nos */
	while (curwp->w_dotp != lp2) {
		curwp->w_dotp = lback(curwp->w_dotp);
		curwp->w_dotline--;
	}
	curwp->w_doto = 0;
	return (TRUE);
}

/*
 * These functions are provided for compatibility with Gosling's Emacs. They
 * are used to scroll the display up (or down) one line at a time.
 */
int
forw1page(int f, int n)
{
	if (!(f & FFARG)) {
		n = 1;
		f = FFUNIV;
	}
	forwpage(f | FFRAND, n);
	return (TRUE);
}

int
back1page(int f, int n)
{
	if (!(f & FFARG)) {
		n = 1;
		f = FFUNIV;
	}
	backpage(f | FFRAND, n);
	return (TRUE);
}

/*
 * Page the other window. Check to make sure it exists, then
 * nextwind, forwpage and restore window pointers.
 */
int
pagenext(int f, int n)
{
	struct mgwin *wp;

	if (wheadp->w_wndp == NULL) {
		ewprintf("No other window");
		return (FALSE);
	}
	wp = curwp;
	(void) nextwind(f, n);
	(void) forwpage(f, n);
	curwp = wp;
	curbp = wp->w_bufp;
	return (TRUE);
}

/*
 * Internal set mark routine, used by other functions (daveb).
 */
void
isetmark(void)
{
	curwp->w_markp = curwp->w_dotp;
	curwp->w_marko = curwp->w_doto;
	curwp->w_markline = curwp->w_dotline;
}

/*
 * Set the mark in the current window
 * to the value of dot. A message is written to
 * the echo line.  (ewprintf knows about macros)
 */
/* ARGSUSED */
int
setmark(int f, int n)
{
	isetmark();
	ewprintf("Mark set");
	return (TRUE);
}

/* Clear the mark, if set. */
/* ARGSUSED */
int
clearmark(int f, int n)
{
	if (!curwp->w_markp)
		return (FALSE);

	curwp->w_markp = NULL;
	curwp->w_marko = 0;
	curwp->w_markline = 0;

	return (TRUE);
}

/*
 * Swap the values of "dot" and "mark" in
 * the current window. This is pretty easy, because
 * all of the hard work gets done by the standard routine
 * that moves the mark about. The only possible
 * error is "no mark".
 */
/* ARGSUSED */
int
swapmark(int f, int n)
{
	struct line  *odotp;
	int odoto, odotline;

	if (curwp->w_markp == NULL) {
		ewprintf("No mark in this window");
		return (FALSE);
	}
	odotp = curwp->w_dotp;
	odoto = curwp->w_doto;
	odotline = curwp->w_dotline;
	curwp->w_dotp = curwp->w_markp;
	curwp->w_doto = curwp->w_marko;
	curwp->w_dotline = curwp->w_markline;
	curwp->w_markp = odotp;
	curwp->w_marko = odoto;
	curwp->w_markline = odotline;
	curwp->w_rflag |= WFMOVE;
	return (TRUE);
}

/*
 * Go to a specific line, mostly for
 * looking up errors in C programs, which give the
 * error a line number. If an argument is present, then
 * it is the line number, else prompt for a line number
 * to use.
 */
/* ARGSUSED */
int
gotoline(int f, int n)
{
	struct line  *clp;
	char   buf[32], *bufp;
	const char *err;

	if (!(f & FFARG)) {
		if ((bufp = eread("Goto line: ", buf, sizeof(buf),
		    EFNUL | EFNEW | EFCR)) == NULL)
			return (ABORT);
		if (bufp[0] == '\0')
			return (ABORT);
		n = (int)strtonum(buf, INT_MIN, INT_MAX, &err);
		if (err) {
			ewprintf("Line number %s", err);
			return (FALSE);
		}
	}
	if (n >= 0) {
		if (n == 0)
			n++;
		curwp->w_dotline = n;
		clp = lforw(curbp->b_headp);	/* "clp" is first line */
		while (--n > 0) {
			if (lforw(clp) == curbp->b_headp) {
				curwp->w_dotline = curwp->w_bufp->b_lines;
				break;
			}
			clp = lforw(clp);
		}
	} else {
		curwp->w_dotline = curwp->w_bufp->b_lines + n;
		clp = lback(curbp->b_headp);	/* "clp" is last line */
		while (n < 0) {
			if (lback(clp) == curbp->b_headp) {
				curwp->w_dotline = 1;
				break;
			}
			clp = lback(clp);
			n++;
		}
	}
	curwp->w_dotp = clp;
	curwp->w_doto = 0;
	curwp->w_rflag |= WFMOVE;
	return (TRUE);
}