/***************************************************************************
 * This program is Copyright (C) 1986, 1987, 1988 by Jonathan Payne.  JOVE *
 * is provided to you without charge, and with no warranty.  You may give  *
 * away copies of JOVE, including sources, provided that this notice is    *
 * included in all the files.                                              *
 ***************************************************************************/

#include "jove.h"
#include "ctype.h"

#ifdef MAC
#	undef private
#	define private
#endif

#ifdef	LINT_ARGS
private void to_sent(int);
#else
private void to_sent();
#endif

#ifdef MAC
#	undef private
#	define private static
#endif

static int	line_pos;

void
f_char(n)
register int	n;
{
	if (n < 0) {
		b_char(-n);
		return;
	}
	while (--n >= 0) {
		if (eolp()) {			/* Go to the next Line */
			if (curline->l_next == 0)
				break;
			SetLine(curline->l_next);
		} else
			curchar += 1;
	}
}

void
b_char(n)
register int	n;
{
	if (n < 0) {
		f_char(-n);
		return;
	}
	while (--n >= 0) {
		if (bolp()) {
			if (curline->l_prev == 0)
				break;
			SetLine(curline->l_prev);
			Eol();
		} else
			curchar -= 1;
	}
}

void
ForChar()
{
	f_char(arg_value());
}

void
BackChar()
{
	b_char(arg_value());
}

void
NextLine()
{
	if ((curline == curbuf->b_last) && eolp())
		complain(NullStr);
	line_move(FORWARD, arg_value(), YES);
}

void
PrevLine()
{
	if ((curline == curbuf->b_first) && bolp())
		complain(NullStr);
	line_move(BACKWARD, arg_value(), YES);
}

/* moves to a different line in DIR; LINE_CMD says whether this is
   being called from NextLine() or PrevLine(), in which case it tries
   to line up the column with the column of the current line */

void
line_move(dir, n, line_cmd)
{
	Line	*(*proc)() = (dir == FORWARD) ? next_line : prev_line;
	Line	*line;

	line = (*proc)(curline, n);
	if (line == curline) {
		if (dir == FORWARD)
			Eol();
		else
			Bol();
		return;
	}

	if (line_cmd) {
		this_cmd = LINECMD;
		if (last_cmd != LINECMD)
			line_pos = calc_pos(linebuf, curchar);
	}
	SetLine(line);		/* curline is in linebuf now */
	if (line_cmd)
		curchar = how_far(curline, line_pos);
}

/* returns what cur_char should be for that position col */

int
how_far(line, col)
Line	*line;
{
	register char	*lp;
	register int	pos,
			c;
	char	*base;

	base = lp = lcontents(line);
	pos = 0;

	while (pos < col && (c = (*lp & CHARMASK))) {
		if (c == '\t')
			pos += (tabstop - (pos % tabstop));
		else if (isctrl(c))
			pos += 2;
		else
			pos += 1;
		lp += 1;
	}

	return lp - base;
}

void
Bol()
{
	curchar = 0;
}

void
Eol()
{
	curchar = strlen(linebuf);
}

void
Eof()
{
	PushPntp(curbuf->b_last);
	ToLast();
}

void
Bof()
{
	PushPntp(curbuf->b_first);
	ToFirst();
}

/* Move forward (if dir > 0) or backward (if dir < 0) a sentence.  Deals
   with all the kludgery involved with paragraphs, and moving backwards
   is particularly yucky. */

private void
to_sent(dir)
{
	Bufpos	*new,
		old;
	extern char	*ParaStr;

	DOTsave(&old);

	new = dosearch("^[ \t]*$\\|[?.!]", dir, 1);
	if (new == 0) {
		if (dir == BACKWARD)
			ToFirst();
		else
			ToLast();
		return;
	}
	SetDot(new);
	if (dir < 0) {
		to_word(1);
		if ((old.p_line == curline && old.p_char <= curchar) ||
		    (inorder(new->p_line, new->p_char, old.p_line, old.p_char) &&
		     inorder(old.p_line, old.p_char, curline, curchar))) {
		     	SetDot(new);
		     	to_sent(dir);
		}
		return;		/* We're there? */
	}
	if (blnkp(linebuf)) {
		Bol();
		b_char(1);
		if (old.p_line == curline && old.p_char >= curchar) {
			to_word(1);	/* Oh brother this is painful */
			to_sent(1);
		}
	} else {
		extern int	REbom;

		curchar = REbom + 1;	/* Just after the [?.!] */
		if (LookingAt("[\")]  *\\|[\")]$", linebuf, curchar))
			curchar += 1;
		else if (!eolp() && !LookingAt("  *", linebuf, curchar))
			to_sent(dir);
	}
}

void
Bos()
{
	register int	num = arg_value();

	if (num < 0) {
		negate_arg_value();
		Eos();
		return;
	}

	while (--num >= 0) {
		to_sent(-1);
		if (bobp())
			break;
	}
}

void
Eos()
{
	register int	num = arg_value();

	if (num < 0) {
		negate_arg_value();
		Bos();
		return;
	}

	while (--num >= 0) {
		to_sent(1);
		if (eobp())
			break;
	}
}

void
f_word(num)
register int	num;
{
	register char	c;
	if (num < 0) {
		b_word(-num);
		return;
	}
	while (--num >= 0) {
		to_word(FORWARD);
		while ((c = linebuf[curchar]) != 0 && isword(c))
			curchar += 1;
		if (eobp())
			break;
	}
	this_cmd = 0;	/* Semi kludge to stop some unfavorable behavior */
}

void
b_word(num)
register int	num;
{
	register char	c;

	if (num < 0) {
		f_word(-num);
		return;
	}
	while (--num >= 0) {
		to_word(BACKWARD);
		while (!bolp() && (c = linebuf[curchar - 1], isword(c)))
			curchar -= 1;
		if (bobp())
			break;
	}
	this_cmd = 0;
}

void
ForWord()
{
	f_word(arg_value());
}

void
BackWord()
{
	b_word(arg_value());
}
