[alt.sources] elvis 1.3 - a clone of vi/ex, part 4 of 6

kirkenda@eecs.cs.pdx.edu (Steve Kirkendall) (08/25/90)

Archive-name: elvis1.3/part4
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	modify.c
#	move1.c
#	move2.c
#	move3.c
#	move4.c
#	move5.c
#	nomagic.c
#	opts.c
#	pc.c
#	recycle.c
#	redraw.c
# This archive created: Fri Aug 24 10:29:57 1990
export PATH; PATH=/bin:/usr/bin:$PATH
if test -f 'modify.c'
then
	echo shar: "will not over-write existing file 'modify.c'"
else
cat << \SHAR_EOF > 'modify.c'
/* modify.c */

/* This file contains the low-level file modification functions:
 *	delete(frommark, tomark)	- removes line or portions of lines
 *	add(frommark, text)		- inserts new text
 *	change(frommark, tomark, text)	- delete, then add
 */

#include "config.h"
#include "vi.h"


/* delete a range of text from the file */
delete(frommark, tomark)
	MARK		frommark;	/* first char to be deleted */
	MARK		tomark;		/* AFTER last char to be deleted */
{
	int		i;		/* used to move thru logical blocks */
	register char	*scan;		/* used to scan thru text of the blk */
	register char	*cpy;		/* used when copying chars */
	BLK		*blk;		/* a text block */
	long		l;		/* a line number */
	MARK		m;		/* a traveling version of frommark */

	/* if not deleting anything, quit now */
	if (frommark == tomark)
	{
		return;
	}

	/* This is a change */
	changes++;

	/* if this is a multi-line change, then we'll have to redraw */
	if (markline(frommark) != markline(tomark))
	{
		mustredraw = TRUE;
		redrawrange(markline(frommark), markline(tomark), markline(frommark));
	}

	/* adjust marks 'a through 'z and '' as needed */
	l = markline(tomark);
	for (i = 0; i < NMARKS; i++)
	{
		if (mark[i] < frommark)
		{
			continue;
		}
		else if (mark[i] < tomark)
		{
			mark[i] = MARK_UNSET;
		}
		else if (markline(mark[i]) == l)
		{
			if (markline(frommark) == l)
			{
				mark[i] -= markidx(tomark) - markidx(frommark);
			}
			else
			{
				mark[i] -= markidx(tomark);
			}
		}
		else
		{
			mark[i] -= MARK_AT_LINE(l - markline(frommark));
		}
	}

	/* Reporting... */
	if (markidx(frommark) == 0 && markidx(tomark) == 0)
	{
		rptlines = markline(tomark) - markline(frommark);
		rptlabel = "deleted";
	}

	/* find the block containing frommark */
	l = markline(frommark);
	for (i = 1; lnum[i] < l; i++)
	{
	}

	/* process each affected block... */
	for (m = frommark;
	     m < tomark && lnum[i] < INFINITY;
	     m = MARK_AT_LINE(lnum[i - 1] + 1))
	{
		/* fetch the block */
		blk = blkget(i);

		/* find the mark in the block */
		scan = blk->c;
		for (l = markline(m) - lnum[i - 1] - 1; l > 0; l--)
		{
			while (*scan++ != '\n')
			{
			}
		}
		scan += markidx(m);

		/* figure out where the changes to this block end */
		if (markline(tomark) > lnum[i])
		{
			cpy = blk->c + BLKSIZE;
		}
		else if (markline(tomark) == markline(m))
		{
			cpy = scan - markidx(m) + markidx(tomark);
		}
		else
		{
			cpy = scan;
			for (l = markline(tomark) - markline(m);
			     l > 0;
			     l--)
			{
				while (*cpy++ != '\n')
				{
				}
			}
			cpy += markidx(tomark);
		}

		/* delete the stuff by moving chars within this block */
		while (cpy < blk->c + BLKSIZE)
		{
			*scan++ = *cpy++;
		}
		while (scan < blk->c + BLKSIZE)
		{
			*scan++ = '\0';
		}

		/* adjust tomark to allow for lines deleted from this block */
		tomark -= MARK_AT_LINE(lnum[i] + 1 - markline(m));

		/* if this block isn't empty now, then advance i */
		if (*blk->c)
		{
			i++;
		}

		/* the buffer has changed.  Update hdr and lnum. */
		blkdirty(blk);
	}

	/* must have at least 1 line */
	if (nlines == 0)
	{
		blk = blkadd(1);
		blk->c[0] = '\n';
		blkdirty(blk);
		cursor = MARK_FIRST;
	}
}


/* add some text at a specific place in the file */
add(atmark, newtext)
	MARK		atmark;		/* where to insert the new text */
	char		*newtext;	/* NUL-terminated string to insert */
{
	register char	*scan;		/* used to move through string */
	register char	*build;		/* used while copying chars */
	int		addlines;	/* number of lines we're adding */
	int		lastpart;	/* size of last partial line */
	BLK		*blk;		/* the block to be modified */
	int		blkno;		/* the logical block# of (*blk) */
	register char	*newptr;	/* where new text starts in blk */
	BLK		buf;		/* holds chars from orig blk */
	BLK		linebuf;	/* holds part of line that didn't fit */
	BLK		*following;	/* the BLK following the last BLK */
	int		i;
	long		l;

	/* if not adding anything, return now */
	if (!*newtext)
	{
		return;
	}

	/* This is a change */
	changes++;

	/* count the number of lines in the new text */
	for (scan = newtext, lastpart = addlines = 0; *scan; )
	{
		if (*scan++ == '\n')
		{
			addlines++;
			lastpart = 0;
		}
		else
		{
			lastpart++;
		}
	}

	/* Reporting... */
	if (lastpart == 0 && markidx(atmark) == 0)
	{
		rptlines = addlines;
		rptlabel = "added";
	}

	/* extract the line# from atmark */
	l = markline(atmark);

	/* if more than 0 lines, then we'll have to redraw the screen */
	if (addlines > 0)
	{
		mustredraw = TRUE;
		if (markidx(atmark) == 0)
		{
			redrawrange(l, l, l + addlines);
		}
		else
		{
			/* make sure the last line gets redrawn -- it was
			 * split, so its appearance has changed
			 */
			redrawrange(l, l + 1L, l + addlines + 1L);
		}
	}

	/* adjust marks 'a through 'z and '' as needed */
	for (i = 0; i < NMARKS; i++)
	{
		if (mark[i] < atmark)
		{
			continue;
		}
		else if (markline(mark[i]) > l)
		{
			mark[i] += MARK_AT_LINE(addlines);
		}
		else
		{
			mark[i] += MARK_AT_LINE(addlines) + lastpart;
		}
	}

	/* get the block to be modified */
	for (blkno = 1; lnum[blkno] < l && lnum[blkno + 1] < INFINITY; blkno++)
	{
	}
	blk = blkget(blkno);
	buf = *blk;

	/* figure out where the new text starts */
	for (newptr = buf.c, l = markline(atmark) - lnum[blkno - 1] - 1;
	     l > 0;
	     l--)
	{
		while (*newptr++ != '\n')
		{
		}
	}
	newptr += markidx(atmark);

	/* keep start of old block */
	build = blk->c + (newptr - buf.c);

	/* fill this block (or blocks) from the newtext string */
	while (*newtext)
	{
		while (*newtext && build < blk->c + BLKSIZE - 1)
		{
			*build++ = *newtext++;
		}
		if (*newtext)
		{
			/* save the excess */
			for (scan = linebuf.c + BLKSIZE;
			     build > blk->c && build[-1] != '\n';
			     )
			{
				*--scan = *--build;
			}

			/* write the block */
			while (build < blk->c + BLKSIZE)
			{
				*build++ = '\0';
			}
			blkdirty(blk);

			/* add another block */
			blkno++;
			blk = blkadd(blkno);

			/* copy in the excess from last time */
			for (build = blk->c; scan < linebuf.c + BLKSIZE; )
			{
				*build++ = *scan++;
			}
		}
	}

	/* fill this block(s) from remainder of orig block */
	while (newptr < buf.c + BLKSIZE && *newptr)
	{
		while (newptr < buf.c + BLKSIZE
		    && *newptr
		    && build < blk->c + BLKSIZE - 1)
		{
			*build++ = *newptr++;
		}
		if (newptr < buf.c + BLKSIZE && *newptr)
		{
			/* save the excess */
			for (scan = linebuf.c + BLKSIZE;
			     build > blk->c && build[-1] != '\n';
			     )
			{
				*--scan = *--build;
			}

			/* write the block */
			while (build < blk->c + BLKSIZE)
			{
				*build++ = '\0';
			}
			blkdirty(blk);

			/* add another block */
			blkno++;
			blk = blkadd(blkno);

			/* copy in the excess from last time */
			for (build = blk->c; scan < linebuf.c + BLKSIZE; )
			{
				*build++ = *scan++;
			}
		}
	}

	/* see if we can combine our last block with the following block */
	if (lnum[blkno] < nlines && lnum[blkno + 1] - lnum[blkno] < (BLKSIZE >> 6))
	{
		/* hey, we probably can!  Get the following block & see... */
		following = blkget(blkno + 1);
		if (strlen(following->c) + (build - blk->c) < BLKSIZE - 1)
		{
			/* we can!  Copy text from following to blk */
			for (scan = following->c; *scan; )
			{
				*build++ = *scan++;
			}
			while (build < blk->c + BLKSIZE)
			{
				*build++ = '\0';
			}
			blkdirty(blk);

			/* pretend the following was the last blk */
			blk = following;
			build = blk->c;
		}
	}

	/* that last block is dirty by now */
	while (build < blk->c + BLKSIZE)
	{
		*build++ = '\0';
	}
	blkdirty(blk);
}


/* change the text of a file */
change(frommark, tomark, newtext)
	MARK	frommark, tomark;
	char	*newtext;
{
	int	i;
	long	l;
	char	*text;
	BLK	*blk;

	/* optimize for single-character replacement */
	if (frommark + 1 == tomark && newtext[0] && !newtext[1] && newtext[0] != '\n')
	{
		/* find the block containing frommark */
		l = markline(frommark);
		for (i = 1; lnum[i] < l; i++)
		{
		}

		/* get the block */
		blk = blkget(i);

		/* find the line within the block */
		for (text = blk->c, i = l - lnum[i - 1] - 1; i > 0; text++)
		{
			if (*text == '\n')
			{
				i--;
			}
		}

		/* replace the char */
		text += markidx(frommark);
		if (*text == newtext[0])
		{
			/* no change was needed - same char */
			return;
		}
		else if (*text != '\n')
		{
			/* This is a change */
			changes++;
			ChangeText
			{
				*text = newtext[0];
				blkdirty(blk);
			}
			return;
		}
		/* else it is a complex change involving newline... */
	}

	/* couldn't optimize, so do delete & add */
	ChangeText
	{
		delete(frommark, tomark);
		add(frommark, newtext);
		rptlabel = "changed";
	}
}
SHAR_EOF
fi
if test -f 'move1.c'
then
	echo shar: "will not over-write existing file 'move1.c'"
else
cat << \SHAR_EOF > 'move1.c'
/* m_1.c */

/* Author:
 *	Steve Kirkendall
 *	16820 SW Tallac Way
 *	Beaverton, OR 97006
 *	kirkenda@jove.cs.pdx.edu, or ...uunet!tektronix!psueea!jove!kirkenda
 */


/* This file contains most movement functions */

#include "config.h"
#include <ctype.h>
#include "vi.h"

#ifndef isascii
# define isascii(c)	!((c) & ~0x7f)
#endif

/*ARGSUSED*/
MARK	m_up(m, cnt)
	MARK	m;	/* movement is relative to this mark */
	long	cnt;	/* a numeric argument */
{
	DEFAULT(1);

	/* if at top already, don't move */
	if (markline(m) - cnt < 1)
	{
		return MARK_UNSET;
	}

	/* else move up one line */
	m -= MARK_AT_LINE(cnt);

	return m;
}

/*ARGSUSED*/
MARK	m_down(m, cnt)
	MARK	m;	/* movement is relative to this mark */
	long	cnt;	/* a numeric argument */
{
	DEFAULT(1);

	/* if at bottom already, don't move */
	if (markline(m) + cnt > nlines)
	{
		return MARK_UNSET;
	}

	/* else move down one line */
	m += MARK_AT_LINE(cnt);

	/* adjust column number */
	if (markidx(m) >= plen)
	{
		m = (m & ~(BLKSIZE - 1));
		if (plen > 0)
		{
			m += plen - 1;
		}
	}

	return m;
}

/*ARGSUSED*/
MARK	m_right(m, cnt)
	MARK	m;	/* movement is relative to this mark */
	long	cnt;	/* a numeric argument */
{
	int		idx;	/* index of the new cursor position */

	DEFAULT(1);

	/* move to right, if that's OK */
	pfetch(markline(m));
	idx = markidx(m) + cnt;
	if (idx < plen)
	{
		m += cnt;
	}
	else
	{
		return MARK_UNSET;
	}

	return m;
}

/*ARGSUSED*/
MARK	m_left(m, cnt)
	MARK	m;	/* movement is relative to this mark */
	long	cnt;	/* a numeric argument */
{
	DEFAULT(1);

	/* move to the left, if that's OK */
	if (markidx(m) >= cnt)
	{
		m -= cnt;
	}
	else
	{
		return MARK_UNSET;
	}

	return m;
}

/*ARGSUSED*/
MARK	m_toline(m, cnt)
	MARK	m;	/* movement is relative to this mark */
	long	cnt;	/* a numeric line number */
{
	/* if no number specified, assume last line */
	DEFAULT(nlines);

	/* if invalid line number, don't move */
	if (cnt > nlines)
	{
		msg("Line numbers range from 1 to %ld", nlines);
		return MARK_UNSET;
	}

	/* move to first character of the selected line */
	m = MARK_AT_LINE(cnt);
	return m;
}

/*ARGSUSED*/
MARK	m_tocol(m, cnt)
	MARK	m;	/* movement is relative to this mark */
	long	cnt;	/* a numeric argument */
{
	char	*text;	/* text of the line */
	int	col;	/* column number */
	int	idx;	/* index into the line */

	DEFAULT(1);

	/* internally, columns are numbered 0..COLS-1, not 1..COLS */
	cnt--;

	/* if 0, that's easy */
	if (cnt == 0)
	{
		m &= ~(BLKSIZE - 1);
		return m;
	}

	/* find that column within the line */
	pfetch(markline(m));
	text = ptext;
	for (col = idx = 0; col < cnt && *text; text++, idx++)
	{
		if (*text == '\t' && !*o_list)
		{
			col += *o_tabstop;
			col -= col % *o_tabstop;
		}
		else if (UCHAR(*text) < ' ' || *text == '\177')
		{
			col += 2;
		}
#ifndef NO_CHARATTR
		else if (text[0] == '\\' && text[1] == 'f' && text[2] && *o_charattr)
		{
			text += 2; /* plus one more as part of for loop */
		}
#endif
		else
		{
			col++;
		}
	}
	if (!*text)
	{
		return MARK_UNSET;
	}
	else
	{
		m = (m & ~(BLKSIZE - 1)) + idx;
	}
	return m;
}

/*ARGSUSED*/
MARK	m_front(m, cnt)
	MARK	m;	/* movement is relative to this mark */
	long	cnt;	/* a numeric argument (ignored) */
{
	char	*scan;

	/* move to the first non-whitespace character */
	pfetch(markline(m));
	scan = ptext;
	m &= ~(BLKSIZE - 1);
	while (*scan == ' ' || *scan == '\t')
	{
		scan++;
		m++;
	}

	return m;
}

/*ARGSUSED*/
MARK	m_rear(m, cnt)
	MARK	m;	/* movement is relative to this mark */
	long	cnt;	/* a numeric argument (ignored) */
{
	/* Try to move *EXTREMELY* far to the right.  It is fervently hoped
	 * that other code will convert this to a more reasonable MARK before
	 * anything tries to actually use it.  (See adjmove() in vi.c)
	 */
	return m | (BLKSIZE - 1);
}

#ifndef NO_SENTENCE
/*ARGSUSED*/
MARK	m_fsentence(m, cnt)
	MARK	m;	/* movement is relative to this mark */
	long	cnt;	/* a numeric argument */
{
	register char	*text;
	register long	l;

	DEFAULT(1);

	/* get the current line */
	l = markline(m);
	pfetch(l);
	text = ptext + markidx(m);

	/* for each requested sentence... */
	while (cnt-- > 0)
	{
		/* search forward for one of [.?!] followed by spaces or EOL */
		do
		{
			/* wrap at end of line */
			if (!text[0])
			{
				if (l >= nlines)
				{
					return MARK_UNSET;
				}
				l++;
				pfetch(l);
				text = ptext;
			}
			else
			{
				text++;
			}
		} while (text[0] != '.' && text[0] != '?' && text[0] != '!'
			|| text[1] && (text[1] != ' ' || text[2] && text[2] != ' '));
	}

	/* construct a mark for this location */
	m = buildmark(text);

	/* move forward to the first word of the next sentence */
	m = m_fword(m, 1L);

	return m;
}

/*ARGSUSED*/
MARK	m_bsentence(m, cnt)
	MARK	m;	/* movement is relative to this mark */
	long	cnt;	/* a numeric argument */
{
	register char	*text;	/* used to scan thru text */
	register long	l;	/* current line number */
	int		flag;	/* have we passed at least one word? */

	DEFAULT(1);

	/* get the current line */
	l = markline(m);
	pfetch(l);
	text = ptext + markidx(m);

	/* for each requested sentence... */
	flag = TRUE;
	while (cnt-- > 0)
	{
		/* search backward for one of [.?!] followed by spaces or EOL */
		do
		{
			/* wrap at beginning of line */
			if (text == ptext)
			{
				do
				{
					if (l <= 1)
					{
						return MARK_UNSET;
					}
					l--;
					pfetch(l);
				} while (!*ptext);
				text = ptext + plen - 1;
			}
			else
			{
				text--;
			}

			/* are we moving past a "word"? */
			if (text[0] >= '0')
			{
				flag = FALSE;
			}
		} while (flag || text[0] != '.' && text[0] != '?' && text[0] != '!'
			|| text[1] && (text[1] != ' ' || text[2] && text[2] != ' '));
	}

	/* construct a mark for this location */
	m = buildmark(text);

	/* move to the front of the following sentence */
	m = m_fword(m, 1L);

	return m;
}
#endif

/*ARGSUSED*/
MARK	m_fparagraph(m, cnt)
	MARK	m;	/* movement is relative to this mark */
	long	cnt;	/* a numeric argument */
{
	char	*text;
	char	*pscn;	/* used to scan thru value of "paragraphs" option */
	long	l;

	DEFAULT(1);

	for (l = markline(m); cnt > 0 && l++ < nlines; )
	{
		text = fetchline(l);
		if (!*text)
		{
			cnt--;
		}
#ifndef NO_SENTENCE
		else if (*text == '.')
		{
			for (pscn = o_paragraphs; pscn[0] && pscn[1]; pscn += 2)
			{
				if (pscn[0] == text[1] && pscn[1] == text[2])
				{
					cnt--;
					break;
				}
			}
		}
#endif
	}
	if (l <= nlines)
	{
		m = MARK_AT_LINE(l);
	}
	else
	{
		m = MARK_LAST;
	}
	return m;
}

/*ARGSUSED*/
MARK	m_bparagraph(m, cnt)
	MARK	m;	/* movement is relative to this mark */
	long	cnt;	/* a numeric argument */
{
	char	*text;
	char	*pscn;	/* used to scan thru value of "paragraph" option */
	long	l;

	DEFAULT(1);

	for (l = markline(m); cnt > 0 && l-- > 1; )
	{
		text = fetchline(l);
		if (!*text)
		{
			cnt--;
		}
#ifndef NO_SENTENCE
		else if (*text == '.')
		{
			for (pscn = o_paragraphs; pscn[0] && pscn[1]; pscn += 2)
			{
				if (pscn[0] == text[1] && pscn[1] == text[2])
				{
					cnt--;
					break;
				}
			}
		}
#endif
	}
	if (l >= 1)
	{
		m = MARK_AT_LINE(l);
	}
	else
	{
		m = MARK_FIRST;
	}
	return m;
}

/*ARGSUSED*/
MARK	m_fsection(m, cnt, key)
	MARK	m;	/* movement is relative to this mark */
	long	cnt;	/* (ignored) */
	int	key;	/* second key stroke - must be ']' */
{
	char	*text;
	char	*sscn;	/* used to scan thru value of "sections" option */
	long	l;

	/* make sure second key was ']' */
	if (key != ']')
	{
		return MARK_UNSET;
	}

	for (l = markline(m); l++ < nlines; )
	{
		text = fetchline(l);
		if (*text == '{')
		{
			break;
		}
#ifndef NO_SENTENCE
		else if (*text == '.')
		{
			for (sscn = o_sections; sscn[0] && sscn[1]; sscn += 2)
			{
				if (sscn[0] == text[1] && sscn[1] == text[2])
				{
					goto BreakBreak;
				}
			}
		}
#endif
	}
BreakBreak:
	if (l <= nlines)
	{
		m = MARK_AT_LINE(l);
	}
	else
	{
		m = MARK_LAST;
	}
	return m;
}

/*ARGSUSED*/
MARK	m_bsection(m, cnt, key)
	MARK	m;	/* movement is relative to this mark */
	long	cnt;	/* (ignored) */
	int	key;	/* second key stroke - must be '[' */
{
	char	*text;
	char	*sscn;	/* used to scan thru value of "sections" option */
	long	l;

	/* make sure second key was '[' */
	if (key != '[')
	{
		return MARK_UNSET;
	}

	for (l = markline(m); l-- > 1; )
	{
		text = fetchline(l);
		if (*text == '{')
		{
			break;
		}
#ifndef NO_SENTENCE
		else if (*text == '.')
		{
			for (sscn = o_sections; sscn[0] && sscn[1]; sscn += 2)
			{
				if (sscn[0] == text[1] && sscn[1] == text[2])
				{
					goto BreakBreak;
				}
			}
		}
#endif
	}
BreakBreak:
	if (l >= 1)
	{
		m = MARK_AT_LINE(l);
	}
	else
	{
		m = MARK_FIRST;
	}
	return m;
}


/*ARGSUSED*/
MARK	m_match(m, cnt)
	MARK	m;	/* movement is relative to this mark */
	long	cnt;	/* a numeric argument */
{
	long	l;
	register char	*text;
	register char	match;
	register char	nest;
	register int	count;

	/* get the current line */
	l = markline(m);
	pfetch(l);
	text = ptext + markidx(m);

	/* search forward within line for one of "[](){}" */
	for (match = '\0'; !match && *text; text++)
	{
		/* tricky way to recognize 'em in ASCII */
		nest = *text;
		if ((nest & 0xdf) == ']' || (nest & 0xdf) == '[')
		{
			match = nest ^ ('[' ^ ']');
		}
		else if ((nest & 0xfe) == '(')
		{
			match = nest ^ ('(' ^ ')');
		}
		else
		{
			match = 0;
		}
	}
	if (!match)
	{
		return MARK_UNSET;
	}
	text--;

	/* search forward or backward for match */
	if (match == '(' || match == '[' || match == '{')
	{
		/* search backward */
		for (count = 1; count > 0; )
		{
			/* wrap at beginning of line */
			if (text == ptext)
			{
				do
				{
					if (l <= 1L)
					{
						return MARK_UNSET;
					}
					l--;
					pfetch(l);
				} while (!*ptext);
				text = ptext + plen - 1;
			}
			else
			{
				text--;
			}

			/* check the char */
			if (*text == match)
				count--;
			else if (*text == nest)
				count++;
		}
	}
	else
	{
		/* search forward */
		for (count = 1; count > 0; )
		{
			/* wrap at end of line */
			if (!*text)
			{
				if (l >= nlines)
				{
					return MARK_UNSET;
				}
				l++;
				pfetch(l);
				text = ptext;
			}
			else
			{
				text++;
			}

			/* check the char */
			if (*text == match)
				count--;
			else if (*text == nest)
				count++;
		}
	}

	/* construct a mark for this place */
	m = buildmark(text);
	return m;
}

/*ARGSUSED*/
MARK	m_tomark(m, cnt, key)
	MARK	m;	/* movement is relative to this mark */
	long	cnt;	/* (ignored) */
	int	key;	/* keystroke - the mark to move to */
{
	/* mark '' is a special case */
	if (key == '\'' || key == '`')
	{
		if (mark[26] == MARK_UNSET)
		{
			return MARK_FIRST;
		}
		else
		{
			return mark[26];
		}
	}

	/* if not a valid mark number, don't move */
	if (key < 'a' || key > 'z')
	{
		return MARK_UNSET;
	}

	/* return the selected mark -- may be MARK_UNSET */
	if (!mark[key - 'a'])
	{
		msg("mark '%c is unset", key);
	}
	return mark[key - 'a'];
}

SHAR_EOF
fi
if test -f 'move2.c'
then
	echo shar: "will not over-write existing file 'move2.c'"
else
cat << \SHAR_EOF > 'move2.c'
/* m_2.c */

/* Author:
 *	Steve Kirkendall
 *	16820 SW Tallac Way
 *	Beaverton, OR 97006
 *	kirkenda@jove.cs.pdx.edu, or ...uunet!tektronix!psueea!jove!kirkenda
 */


/* This function contains the mvoement functions that perform RE searching */

#include "config.h"
#include "vi.h"
#include "regexp.h"

static regexp	*re;	/* compiled version of the pattern to search for */
static		prevsf;	/* boolean: previous search direction was forward? */

MARK	m_nsrch(m)
	MARK	m;	/* where to start searching */
{
	if (prevsf)
	{
		m = m_fsrch(m, "");
		prevsf = TRUE;
	}
	else
	{
		m = m_bsrch(m, "");
		prevsf = FALSE;
	}
	return m;
}

MARK	m_Nsrch(m)
	MARK	m;	/* where to start searching */
{
	if (prevsf)
	{
		m = m_bsrch(m, "");
		prevsf = TRUE;
	}
	else
	{
		m = m_fsrch(m, "");
		prevsf = FALSE;
	}
	return m;
}

MARK	m_fsrch(m, ptrn)
	MARK	m;	/* where to start searching */
	char	*ptrn;	/* pattern to search for */
{
	long	l;	/* line# of line to be searched */
	char	*line;	/* text of line to be searched */
	int	wrapped;/* boolean: has our search wrapped yet? */
	int	pos;	/* where we are in the line */

	/* remember: "previous search was forward" */
	prevsf = TRUE;

	if (ptrn && *ptrn)
	{
		/* free the previous pattern */
		if (re) free(re);

		/* compile the pattern */
		re = regcomp(ptrn);
		if (!re)
		{
			return MARK_UNSET;
		}
	}
	else if (!re)
	{
		msg("No previous expression");
		return MARK_UNSET;
	}

	/* search forward for the pattern */
	pos = markidx(m) + 1;
	pfetch(markline(m));
	if (pos >= plen)
	{
		pos = 0;
		m = (m | (BLKSIZE - 1)) + 1;
	}
	wrapped = FALSE;
	for (l = markline(m); l != markline(m) + 1 || !wrapped; l++)
	{
		/* wrap search */
		if (l > nlines)
		{
			/* if we wrapped once already, then the search failed */
			if (wrapped)
			{
				break;
			}

			/* else maybe we should wrap now? */
			if (*o_wrapscan)
			{
				l = 0;
				wrapped = TRUE;
				continue;
			}
			else
			{
				break;
			}
		}

		/* get this line */
		line = fetchline(l);

		/* check this line */
		if (regexec(re, &line[pos], (pos == 0)))
		{
			/* match! */
			if (wrapped && *o_warn)
				msg("(wrapped)");
			return MARK_AT_LINE(l) + (int)(re->startp[0] - line);
		}
		pos = 0;
	}

	/* not found */
	msg(*o_wrapscan ? "Not found" : "Hit bottom without finding RE");
	return MARK_UNSET;
}

MARK	m_bsrch(m, ptrn)
	MARK	m;	/* where to start searching */
	char	*ptrn;	/* pattern to search for */
{
	long	l;	/* line# of line to be searched */
	char	*line;	/* text of line to be searched */
	int	wrapped;/* boolean: has our search wrapped yet? */
	int	pos;	/* last acceptable idx for a match on this line */
	int	last;	/* remembered idx of the last acceptable match on this line */
	int	try;	/* an idx at which we strat searching for another match */

	/* remember: "previous search was not forward" */
	prevsf = FALSE;

	if (ptrn && *ptrn)
	{
		/* free the previous pattern, if any */
		if (re) free(re);

		/* compile the pattern */
		re = regcomp(ptrn);
		if (!re)
		{
			return MARK_UNSET;
		}
	}
	else if (!re)
	{
		msg("No previous expression");
		return MARK_UNSET;
	}

	/* search backward for the pattern */
	pos = markidx(m);
	wrapped = FALSE;
	for (l = markline(m); l != markline(m) - 1 || !wrapped; l--)
	{
		/* wrap search */
		if (l < 1)
		{
			if (*o_wrapscan)
			{
				l = nlines + 1;
				wrapped = TRUE;
				continue;
			}
			else
			{
				break;
			}
		}

		/* get this line */
		line = fetchline(l);

		/* check this line */
		if (regexec(re, line, 1) && (int)(re->startp[0] - line) < pos)
		{
			/* match!  now find the last acceptable one in this line */
			do
			{
				last = (int)(re->startp[0] - line);
				try = (int)(re->endp[0] - line);
			} while (try > 0
				 && regexec(re, &line[try], FALSE)
				 && (int)(re->startp[0] - line) < pos);

			if (wrapped && *o_warn)
				msg("(wrapped)");
			return MARK_AT_LINE(l) + last;
		}
		pos = BLKSIZE;
	}

	/* not found */
	msg(*o_wrapscan ? "Not found" : "Hit top without finding RE");
	return MARK_UNSET;
}

SHAR_EOF
fi
if test -f 'move3.c'
then
	echo shar: "will not over-write existing file 'move3.c'"
else
cat << \SHAR_EOF > 'move3.c'
/* m_3.c */

/* Author:
 *	Steve Kirkendall
 *	16820 SW Tallac Way
 *	Beaverton, OR 97006
 *	kirkenda@jove.cs.pdx.edu, or ...uunet!tektronix!psueea!jove!kirkenda
 */


/* This file contains movement functions that perform character searches */

#include "config.h"
#include "vi.h"

#ifndef NO_CHARSEARCH
static MARK	(*prevfwdfn)();	/* function to search in same direction */
static MARK	(*prevrevfn)();	/* function to search in opposite direction */
static char	prev_key;	/* sought cvhar from previous [fFtT] */

MARK	m__ch(m, cnt, cmd)
	MARK	m;	/* current position */
	long	cnt;
	char	cmd;	/* command: either ',' or ';' */
{
	MARK	(*tmp)();

	if (!prevfwdfn)
	{
		msg("No previous f, F, t, or T command");
		return MARK_UNSET;
	}

	if (cmd == ',')
	{
		m =  (*prevrevfn)(m, cnt, prev_key);

		/* Oops! we didn't want to change the prev*fn vars! */
		tmp = prevfwdfn;
		prevfwdfn = prevrevfn;
		prevrevfn = tmp;

		return m;
	}
	else
	{
		return (*prevfwdfn)(m, cnt, prev_key);
	}
}

/* move forward within this line to next occurrence of key */
MARK	m_fch(m, cnt, key)
	MARK	m;	/* where to search from */
	long	cnt;
	char	key;	/* what to search for */
{
	register char	*text;

	DEFAULT(1);

	prevfwdfn = m_fch;
	prevrevfn = m_Fch;
	prev_key = key;

	pfetch(markline(m));
	text = ptext + markidx(m);
	while (cnt-- > 0)
	{
		do
		{
			m++;
			text++;
		} while (*text && *text != key);
	}
	if (!*text)
	{
		return MARK_UNSET;
	}
	return m;
}

/* move backward within this line to previous occurrence of key */
MARK	m_Fch(m, cnt, key)
	MARK	m;	/* where to search from */
	long	cnt;
	char	key;	/* what to search for */
{
	register char	*text;

	DEFAULT(1);

	prevfwdfn = m_Fch;
	prevrevfn = m_fch;
	prev_key = key;

	pfetch(markline(m));
	text = ptext + markidx(m);
	while (cnt-- > 0)
	{
		do
		{
			m--;
			text--;
		} while (text >= ptext && *text != key);
	}
	if (text < ptext)
	{
		return MARK_UNSET;
	}
	return m;
}

/* move forward within this line almost to next occurrence of key */
MARK	m_tch(m, cnt, key)
	MARK	m;	/* where to search from */
	long	cnt;
	char	key;	/* what to search for */
{
	/* skip the adjacent char */
	pfetch(markline(m));
	if (plen <= markidx(m))
	{
		return MARK_UNSET;
	}
	m++;

	m = m_fch(m, cnt, key);
	if (m == MARK_UNSET)
	{
		return MARK_UNSET;
	}

	prevfwdfn = m_tch;
	prevrevfn = m_Tch;

	return m - 1;
}

/* move backward within this line almost to previous occurrence of key */
MARK	m_Tch(m, cnt, key)
	MARK	m;	/* where to search from */
	long	cnt;
	char	key;	/* what to search for */
{
	/* skip the adjacent char */
	if (markidx(m) == 0)
	{
		return MARK_UNSET;
	}
	m--;

	m = m_Fch(m, cnt, key);
	if (m == MARK_UNSET)
	{
		return MARK_UNSET;
	}

	prevfwdfn = m_Tch;
	prevrevfn = m_tch;

	return m + 1;
}
#endif
SHAR_EOF
fi
if test -f 'move4.c'
then
	echo shar: "will not over-write existing file 'move4.c'"
else
cat << \SHAR_EOF > 'move4.c'
/* m_4.c */

/* Author:
 *	Steve Kirkendall
 *	16820 SW Tallac Way
 *	Beaverton, OR 97006
 *	kirkenda@jove.cs.pdx.edu, or ...uunet!tektronix!psueea!jove!kirkenda
 */


/* This file contains movement functions which are screen-relative */

#include "config.h"
#include "vi.h"

/* This m_s the cursor to a particular row on the screen */
/*ARGSUSED*/
MARK m_row(m, cnt, key)
	MARK	m;	/* the cursor position */
	long	cnt;	/* the row we'll move to */
	int	key;	/* the keystroke of this move - H/L/M */
{
	DEFAULT(1);

	/* calculate destination line based on key */
	cnt--;
	switch (key)
	{
	  case 'H':
		cnt = topline + cnt;
		break;

	  case 'M':
		cnt = topline + (LINES - 1) / 2;
		break;

	  case 'L':
		cnt = botline - cnt;
		break;
	}

	/* return the mark of the destination line */
	return MARK_AT_LINE(cnt);
}


/* This function repositions the current line to show on a given row */
/*ARGSUSED*/
MARK m_z(m, cnt, key)
	MARK	m;	/* the cursor */
	long	cnt;	/* the line number we're repositioning */
	int	key;	/* key struck after the z */
{
	long	newtop;

	/* Which line are we talking about? */
	if (cnt < 0 || cnt > nlines)
	{
		return MARK_UNSET;
	}
	if (cnt)
	{
		m = MARK_AT_LINE(cnt);
		newtop = cnt;
	}
	else
	{
		newtop = markline(m);
	}

	/* allow a "window size" number to be entered, but ignore it */
	while (key >= '0' && key <= '9')
	{
		key = getkey(0);
	}

	/* figure out which line will have to be at the top of the screen */
	switch (key)
	{
	  case '\n':
	  case '\r':
	  case '+':
		break;

	  case '.':
	  case 'z':
		newtop -= LINES / 2;
		break;

	  case '-':
		newtop -= LINES - 1;
		break;

	  default:
		return MARK_UNSET;
	}

	/* make the new topline take effect */
	if (newtop >= 1)
	{
		topline = newtop;
	}
	else
	{
		topline = 1L;
	}
	mustredraw = TRUE;

	/* The cursor doesn't move */
	return m;
}


/* This function scrolls the screen.  It does this by calling redraw() with
 * an off-screen line as the argument.  It will move the cursor if necessary
 * so that the cursor is on the new screen.
 */
/*ARGSUSED*/
MARK m_scroll(m, cnt, key)
	MARK	m;	/* the cursor position */
	long	cnt;	/* for some keys: the number of lines to scroll */
	int	key;	/* keystroke that causes this movement */
{
	MARK	tmp;	/* a temporary mark, used as arg to redraw() */

	/* adjust cnt, and maybe *o_scroll, depending of key */
	switch (key)
	{
	  case ctrl('F'):
	  case ctrl('B'):
		DEFAULT(1);
		mustredraw = TRUE;
		cnt = cnt * (LINES - 1) - 1; /* keeps one old line on screen */
		break;

	  case ctrl('E'):
	  case ctrl('Y'):
		DEFAULT(1);
		break;

	  case ctrl('U'):
	  case ctrl('D'):
		if (cnt == 0) /* default */
		{
			cnt = *o_scroll;
		}
		else
		{
			if (cnt > LINES - 1)
			{
				cnt = LINES - 1;
			}
			*o_scroll = cnt;
		}
		break;
	}

	/* scroll up or down, depending on key */
	switch (key)
	{
	  case ctrl('B'):
	  case ctrl('Y'):
	  case ctrl('U'):
		cnt = topline - cnt;
		if (cnt < 1L)
		{
			cnt = 1L;
			m = MARK_FIRST;
		}
		tmp = MARK_AT_LINE(cnt) + markidx(m);
		redraw(tmp, FALSE);
		if (markline(m) > botline)
		{
			m = MARK_AT_LINE(botline);
		}
		break;

	  case ctrl('F'):
	  case ctrl('E'):
	  case ctrl('D'):
		cnt = botline + cnt;
		if (cnt > nlines)
		{
			cnt = nlines;
			m = MARK_LAST;
		}
		tmp = MARK_AT_LINE(cnt) + markidx(m);
		redraw(tmp, FALSE);
		if (markline(m) < topline)
		{
			m = MARK_AT_LINE(topline);
		}
		break;
	}

	/* arrange for ctrl-B and ctrl-F to redraw the smart line */
	if (key == ctrl('B') || key == ctrl('F'))
	{
		changes++;
	}

	return m;
}
SHAR_EOF
fi
if test -f 'move5.c'
then
	echo shar: "will not over-write existing file 'move5.c'"
else
cat << \SHAR_EOF > 'move5.c'
/* m_5.c */

/* Author:
 *	Steve Kirkendall
 *	16820 SW Tallac Way
 *	Beaverton, OR 97006
 *	kirkenda@jove.cs.pdx.edu, or ...uunet!tektronix!psueea!jove!kirkenda
 */


/* This file contains the word-oriented movement functions */

#include <ctype.h>
#include "config.h"
#include "vi.h"

#ifndef isascii
# define isascii(c)	!((c) & ~0x7f)
#endif


MARK	m_fword(m, cnt)
	MARK	m;	/* movement is relative to this mark */
	long	cnt;	/* a numeric argument */
{
	register long	l;
	register char	*text;
	register int	i;

	DEFAULT(1);

	l = markline(m);
	pfetch(l);
	text = ptext + markidx(m);
	while (cnt-- > 0) /* yes, ASSIGNMENT! */
	{
		i = *text++;
		/* if we hit the end of the line, continue with next line */
		if (!isascii(i) || isalnum(i) || i == '_')
		{
			/* include an alphanumeric word */
			while (i && (!isascii(i) || isalnum(i) || i == '_'))
			{
				i = *text++;
			}
		}
		else
		{
			/* include contiguous punctuation */
			while (i && isascii(i) && !isalnum(i) && !isspace(i))
			{
				i = *text++;
			}
		}

		/* include trailing whitespace */
		while (!i || isascii(i) && isspace(i))
		{
			/* did we hit the end of this line? */
			if (!i)
			{
				/* move to next line, if there is one */
				l++;
				if (l > nlines)
				{
					return MARK_UNSET;
				}
				pfetch(l);
				text = ptext;
			}

			i = *text++;
		}
		text--;
	}

	/* construct a MARK for this place */
	m = buildmark(text);
	return m;
}


MARK	m_bword(m, cnt)
	MARK	m;	/* movement is relative to this mark */
	long	cnt;	/* a numeric argument */
{
	register long	l;
	register char	*text;

	DEFAULT(1);

	l = markline(m);
	pfetch(l);
	text = ptext + markidx(m);
	while (cnt-- > 0) /* yes, ASSIGNMENT! */
	{
		text--;

		/* include preceding whitespace */
		while (text < ptext || isascii(*text) && isspace(*text))
		{
			/* did we hit the end of this line? */
			if (text < ptext)
			{
				/* move to preceding line, if there is one */
				l--;
				if (l <= 0)
				{
					return MARK_UNSET;
				}
				pfetch(l);
				text = ptext + plen - 1;
			}
			else
			{
				text--;
			}
		}

		if (!isascii(*text) || isalnum(*text) || *text == '_')
		{
			/* include an alphanumeric word */
			while (text >= ptext && (!isascii(*text) || isalnum(*text) || *text == '_'))
			{
				text--;
			}
		}
		else
		{
			/* include contiguous punctuation */
			while (text >= ptext && isascii(*text) && !isalnum(*text) && !isspace(*text))
			{
				text--;
			}
		}
		text++;
	}

	/* construct a MARK for this place */
	m = buildmark(text);
	return m;
}

MARK	m_eword(m, cnt)
	MARK	m;	/* movement is relative to this mark */
	long	cnt;	/* a numeric argument */
{
	register long	l;
	register char	*text;
	register int	i;

	DEFAULT(1);

	l = markline(m);
	pfetch(l);
	text = ptext + markidx(m);
	while (cnt-- > 0) /* yes, ASSIGNMENT! */
	{
		text++;
		i = *text++;

		/* include preceding whitespace */
		while (!i || isascii(i) && isspace(i))
		{
			/* did we hit the end of this line? */
			if (!i)
			{
				/* move to next line, if there is one */
				l++;
				if (l > nlines)
				{
					return MARK_UNSET;
				}
				pfetch(l);
				text = ptext;
			}

			i = *text++;
		}

		if (!isascii(i) || isalnum(i) || i == '_')
		{
			/* include an alphanumeric word */
			while (i && (!isascii(i) || isalnum(i) || i == '_'))
			{
				i = *text++;
			}
		}
		else
		{
			/* include contiguous punctuation */
			while (i && isascii(i) && !isalnum(i) && !isspace(i))
			{
				i = *text++;
			}
		}
		text -= 2;
	}

	/* construct a MARK for this place */
	m = buildmark(text);
	return m;
}

MARK	m_fWord(m, cnt)
	MARK	m;	/* movement is relative to this mark */
	long	cnt;	/* a numeric argument */
{
	register long	l;
	register char	*text;
	register int	i;

	DEFAULT(1);

	l = markline(m);
	pfetch(l);
	text = ptext + markidx(m);
	while (cnt-- > 0) /* yes, ASSIGNMENT! */
	{
		i = *text++;
		/* if we hit the end of the line, continue with next line */
		/* include contiguous non-space characters */
		while (i && !isspace(i))
		{
			i = *text++;
		}

		/* include trailing whitespace */
		while (!i || isascii(i) && isspace(i))
		{
			/* did we hit the end of this line? */
			if (!i)
			{
				/* move to next line, if there is one */
				l++;
				if (l > nlines)
				{
					return MARK_UNSET;
				}
				pfetch(l);
				text = ptext;
			}

			i = *text++;
		}
		text--;
	}

	/* construct a MARK for this place */
	m = buildmark(text);
	return m;
}


MARK	m_bWord(m, cnt)
	MARK	m;	/* movement is relative to this mark */
	long	cnt;	/* a numeric argument */
{
	register long	l;
	register char	*text;

	DEFAULT(1);

	l = markline(m);
	pfetch(l);
	text = ptext + markidx(m);
	while (cnt-- > 0) /* yes, ASSIGNMENT! */
	{
		text--;

		/* include trailing whitespace */
		while (text < ptext || isascii(*text) && isspace(*text))
		{
			/* did we hit the end of this line? */
			if (text < ptext)
			{
				/* move to next line, if there is one */
				l--;
				if (l <= 0)
				{
					return MARK_UNSET;
				}
				pfetch(l);
				text = ptext + plen - 1;
			}
			else
			{
				text--;
			}
		}

		/* include contiguous non-whitespace */
		while (text >= ptext && (!isascii(*text) || !isspace(*text)))
		{
			text--;
		}
		text++;
	}

	/* construct a MARK for this place */
	m = buildmark(text);
	return m;
}

MARK	m_eWord(m, cnt)
	MARK	m;	/* movement is relative to this mark */
	long	cnt;	/* a numeric argument */
{
	register long	l;
	register char	*text;
	register int	i;

	DEFAULT(1);

	l = markline(m);
	pfetch(l);
	text = ptext + markidx(m);
	while (cnt-- > 0) /* yes, ASSIGNMENT! */
	{
		text++;
		i = *text++;

		/* include preceding whitespace */
		while (!i || isascii(i) && isspace(i))
		{
			/* did we hit the end of this line? */
			if (!i)
			{
				/* move to next line, if there is one */
				l++;
				if (l > nlines)
				{
					return MARK_UNSET;
				}
				pfetch(l);
				text = ptext;
			}

			i = *text++;
		}

		/* include contiguous non-whitespace */
		while (i && (!isascii(i) || !isspace(i)))
		{
			i = *text++;
		}
		text -= 2;
	}

	/* construct a MARK for this place */
	m = buildmark(text);
	return m;
}
SHAR_EOF
fi
if test -f 'nomagic.c'
then
	echo shar: "will not over-write existing file 'nomagic.c'"
else
cat << \SHAR_EOF > 'nomagic.c'
/* nomagic.c */

/* This contains versions of the regcomp() and regexec() functions which
 * do not recognize any metacharacters except ^ $ and \.  They use the same
 * data structure Henry Spencer's package, so they can continue to use his
 * regsub() function.
 *
 * This file is meant to be #included in regexp.c; it should *NOT* be
 * compiled separately.  The regexp.c file will check to see if NO_MAGIC
 * is defined, and if so then this file is used; if not, then the real
 * regexp functions are used.
 */


regexp *regcomp(exp)
	char	*exp;
{
	char	*src;
	char	*dest;
	regexp	*re;
	int	i;

	/* allocate a big enough regexp structure */
	re = (regexp *)malloc(strlen(exp) + 1 + sizeof(struct regexp));
	if (!re)
	{
		regerror("could not malloc a regexp structure");
		return (regexp *)0;
	}

	/* initialize all fields of the structure */
	for (i = 0; i < NSUBEXP; i++)
	{
		re->startp[i] = (char *)0;
		re->endp[i] = (char *)0;
	}
	re->regstart = 0;
	re->reganch = 0;
	re->regmust = &re->program[1];
	re->regmlen = 0;
	re->program[0] = MAGIC;

	/* copy the string into it, translating ^ and $ as needed */
	for (src = exp, dest = re->program + 1; *src; src++)
	{
		switch (*src)
		{
		  case '^':
			if (src == exp)
				re->regstart = 1;
			else
				*dest++ = '^';
			break;

		  case '$':
			if (!src[1])
				re->reganch = 1;
			else
				*dest++ = '$';
			break;

		  case '\\':
			if (src[1])
				*dest++ = *++src;
			else
			{
				regerror("extra \\ at end of regular expression");
			}
			break;

		  default:
			*dest++ = *src;
		}
	}
	*dest = '\0';
	re->regmlen = strlen(&re->program[1]);

	return re;
}


/* This "helper" function checks for a match at a given location.  It returns
 * 1 if it matches, 0 if it doesn't match here but might match later on in the
 * string, or -1 if it could not possibly match
 */
static int reghelp(prog, string, bolflag)
	struct regexp	*prog;
	char		*string;
	int		bolflag;
{
	char		*scan;
	char		*str;

	/* if ^, then require bolflag */
	if (prog->regstart && !bolflag)
	{
		return -1;
	}

	/* if it matches, then it will start here */
	prog->startp[0] = string;

	/* compare, possibly ignoring case */
	if (*o_ignorecase)
	{
		for (scan = &prog->program[1]; *scan; scan++, string++)
			if (tolower(*scan) != tolower(*string))
				return *string ? 0 : -1;
	}
	else
	{
		for (scan = &prog->program[1]; *scan; scan++, string++)
			if (*scan != *string)
				return *string ? 0 : -1;
	}

	/* if $, then require string to end here, too */
	if (prog->reganch && *string)
	{
		return 0;
	}

	/* if we get to here, it matches */
	prog->endp[0] = string;
	return 1;
}



int regexec(prog, string, bolflag)
	struct regexp	*prog;
	char		*string;
	int		bolflag;
{
	int		rc;

	/* keep trying to match it */
	for (rc = reghelp(prog, string, bolflag); rc == 0; rc = reghelp(prog, string, 0))
	{
		string++;
	}

	/* did we match? */
	return rc == 1;
}
SHAR_EOF
fi
if test -f 'opts.c'
then
	echo shar: "will not over-write existing file 'opts.c'"
else
cat << \SHAR_EOF > 'opts.c'
/* opts.c */

/* Author:
 *	Steve Kirkendall
 *	16820 SW Tallac Way
 *	Beaverton, OR 97006
 *	kirkenda@jove.cs.pdx.edu, or ...uunet!tektronix!psueea!jove!kirkenda
 */


/* This file contains the code that manages the run-time options -- The 
 * values that can be modified via the "set" command.
 */

#include "config.h"
#include "vi.h"
#ifndef NULL
#define NULL (char *)0
#endif
extern char	*getenv();

/* These are the default values of all options */
char	o_autoindent[1] =	{FALSE};
char	o_autowrite[1] = 	{FALSE};
#ifndef NO_CHARATTR
char	o_charattr[1] =		{FALSE};
#endif
char	o_columns[3] =		{80, 32, 255};
char	o_directory[30] =	TMPDIR;
char	o_errorbells[1] =	{TRUE};
char	o_exrefresh[1] =	{TRUE};
#ifndef NO_SENTENCE
char	o_hideformat[1] =	{FALSE};
#endif
char	o_ignorecase[1] =	{FALSE};
#ifndef NO_EXTENSIONS
char	o_inputmode[1] =	{FALSE};
#endif
char	o_keytime[3] =		{2, 0, 5};
char	o_keywordprg[80] =	KEYWORDPRG;
char	o_lines[3] =		{25, 2, 50};	/* More lines? Enlarge kbuf */
char	o_list[1] =		{FALSE};
#ifndef NO_MAGIC
char	o_magic[1] =		{TRUE};
#endif
#ifndef NO_SENTENCE
char	o_paragraphs[30] =	"PPppPApa";
#endif
#if MSDOS
char	o_pcbios[1] =		{TRUE};
#endif
char	o_readonly[1] =		{FALSE};
char	o_report[3] =		{5, 1, 127};
char	o_scroll[3] =		{12, 1, 127};
#ifndef NO_SENTENCE
char	o_sections[30] =	"SEseSHsh";
#endif
char	o_shell[60] =		"/bin/sh";
char	o_shiftwidth[3] =	{8, 1, 255};
#ifndef	NO_SHOWMODE
char	o_showmode[1] =		{FALSE};
#endif
char	o_sidescroll[3] =	{8, 1, 40};
char	o_sync[1] =		{FALSE};
char	o_tabstop[3] =		{8, 1, 40};
char	o_term[30] =		"?";
char	o_vbell[1] =		{TRUE};
char	o_warn[1] =		{TRUE};
char	o_wrapmargin[3] =	{0, 0, 255};
char	o_wrapscan[1] =		{TRUE};


/* The following describes the names & types of all options */
#define BOOL	0
#define	NUM	1
#define	STR	2
#define SET	0x01	/* this option has had its value altered */
#define CANSET	0x02	/* this option can be set at any time */
#define RCSET	0x06	/* this option can be set in a .exrc file only */
#define MR	0x40	/* does this option affect the way text is displayed? */
struct
{
	char	*name;	/* name of an option */
	char	*nm;	/* short name of an option */
	char	type;	/* type of an option */
	char	flags;	/* boolean: has this option been set? */
	char	*value;	/* value */
}
	opts[] =
{
	/* name			type	flags	redraw	value */
	{ "autoindent",	"ai",	BOOL,	CANSET	,	o_autoindent	},
	{ "autowrite",	"aw",	BOOL,	CANSET	,	o_autowrite	},
#ifndef NO_CHARATTR
	{ "charattr",	"ca",	BOOL,	CANSET	| MR,	o_charattr	},
#endif
	{ "columns",	"co",	NUM,	SET	,	o_columns	},
	{ "directory",	"dir",	STR,	RCSET	,	o_directory	},
	{ "errorbells",	"eb",	BOOL,	CANSET	,	o_errorbells	},
	{ "exrefresh",	"er",	BOOL,	CANSET	,	o_exrefresh	},
#ifndef NO_SENTENCE
	{ "hideformat",	"hf",	BOOL,	CANSET	| MR,	o_hideformat	},
#endif
	{ "ignorecase",	"ic",	BOOL,	CANSET	,	o_ignorecase	},
#ifndef NO_EXTENSIONS
	{ "inputmode",	"im",	BOOL,	CANSET	,	o_inputmode	},
#endif
	{ "keytime",	"kt",	NUM,	CANSET	,	o_keytime	},
	{ "keywordprg",	"kp",	STR,	CANSET	,	o_keywordprg	},
	{ "lines",	"ls",	NUM,	SET	,	o_lines		},
	{ "list",	"li",	BOOL,	CANSET	| MR,	o_list		},
#ifndef NO_MAGIC
	{ "magic",	"ma",	BOOL,	CANSET	,	o_magic		},
#endif
#ifndef NO_SENTENCE
	{ "paragraphs",	"pa",	STR,	CANSET	,	o_paragraphs	},
#endif
#if	MSDOS
	{ "pcbios",	"pc",	BOOL,	SET	,	o_pcbios	},
#endif
	{ "readonly",	"ro",	BOOL,	CANSET	,	o_readonly	},
	{ "report",	"re",	NUM,	CANSET	,	o_report	},
	{ "scroll",	"sc",	NUM,	CANSET	,	o_scroll	},
#ifndef NO_SENTENCE
	{ "sections",	"se",	STR,	CANSET	,	o_sections	},
#endif
	{ "shell",	"sh",	STR,	CANSET	,	o_shell		},
#ifndef	NO_SHOWMODE
	{ "showmode",	"sho",	BOOL,	CANSET	,	o_showmode	},
#endif
	{ "shiftwidth",	"sw",	NUM,	CANSET	,	o_shiftwidth	},
	{ "sidescroll",	"ss",	NUM,	CANSET	,	o_sidescroll	},
	{ "sync",	"sy",	BOOL,	CANSET	,	o_sync		},
	{ "tabstop",	"ts",	NUM,	CANSET	| MR,	o_tabstop	},
	{ "term",	"te",	STR,	SET	,	o_term		},
	{ "vbell",	"vb",	BOOL,	CANSET	,	o_vbell		},
	{ "warn",	"wa",	BOOL,	CANSET	,	o_warn		},
	{ "wrapmargin",	"wm",	NUM,	CANSET	,	o_wrapmargin	},
	{ "wrapscan",	"ws",	BOOL,	CANSET	,	o_wrapscan	},
	{ NULL, NULL, 0, CANSET, NULL }
};


/* This function initializes certain options from environment variables, etc. */
initopts()
{
	char	*val;
	int	i;

	/* set some stuff from environment variables */
#if	ANY_UNIX || TOS
	if (val = getenv("SHELL")) /* yes, ASSIGNMENT! */
	{
		strcpy(o_shell, val);
	}
	if (val = getenv("TERM")) /* yes, ASSIGNMENT! */
	{
		strcpy(o_term, val);
	}
#endif
#if	MSDOS
	if (val = getenv("COMSPEC")) /* yes, ASSIGNMENT! */
	{
		strcpy(o_shell, val);
	}
	if ((val = getenv("TERM")) /* yes, ASSIGNMENT! */
		&& strcmp(val, "pcbios"))
	{
		strcpy(o_term, val);
		o_pcbios[0] = 0;
	}
	else
	{
		strcpy(o_term, "pcbios");
		o_pcbios[0] = 1;
	}
#endif
#if	MSDOS || TOS
	if ((val = getenv("TMP")) /* yes, ASSIGNMENT! */
	||  (val = getenv("TEMP")))
		strcpy(o_directory, val);
#endif

	*o_scroll = LINES / 2 - 1;

	/* disable the vbell option if we don't know how to do a vbell */
	if (!has_VB)
	{
		for (i = 0; opts[i].value != o_vbell; i++)
		{
		}
		opts[i].flags &= ~CANSET;
		*o_vbell = FALSE;
	}
}

/* This function lists the current values of all options */
dumpopts(all)
	int	all;	/* boolean: dump all options, or just set ones? */
{
	int	i;
	int	col;
	char	nbuf[4];

	for (i = col = 0; opts[i].name; i++)
	{
		/* if not set and not all, ignore this option */
		if (!all && !(opts[i].flags & SET))
		{
			continue;
		}

		/* align this option in one of the columns */
		if (col > 52)
		{
			addch('\n');
			col = 0;
		}
		else if (col > 26)
		{
			while (col < 52)
			{
				qaddch(' ');
				col++;
			}
		}
		else if (col > 0)
		{
			while (col < 26)
			{
				qaddch(' ');
				col++;
			}
		}

		switch (opts[i].type)
		{
		  case BOOL:
			if (!*opts[i].value)
			{
				qaddch('n');
				qaddch('o');
				col += 2;
			}
			qaddstr(opts[i].name);
			col += strlen(opts[i].name);
			break;

		  case NUM:
			sprintf(nbuf, "%-3d", UCHAR(*opts[i].value));
			qaddstr(opts[i].name);
			qaddch('=');
			qaddstr(nbuf);
			col += 4 + strlen(opts[i].name);
			break;

		  case STR:
			qaddstr(opts[i].name);
			qaddch('=');
			qaddch('"');
			qaddstr(opts[i].value);
			qaddch('"');
			col += 3 + strlen(opts[i].name) + strlen(opts[i].value);
			break;
		}
		exrefresh();
	}
	if (col > 0)
	{
		addch('\n');
		exrefresh();
	}
}

/* This function saves the current configuarion of options to a file */
saveopts(fd)
	int	fd;	/* file descriptor to write to */
{
	int	i;
	char	buf[256], *pos;

	/* write each set options */
	for (i = 0; opts[i].name; i++)
	{
		/* if unset or unsettable, ignore this option */
		if (!(opts[i].flags & SET) || !(opts[i].flags & CANSET))
		{
			continue;
		}

		strcpy(buf, "set ");
		pos = &buf[4];
		switch (opts[i].type)
		{
		  case BOOL:
			if (!*opts[i].value)
			{
				*pos++='n';
				*pos++='o';
			}
			strcpy(pos, opts[i].name);
			strcat(pos, "\n");
			break;

		  case NUM:
			sprintf(pos, "%s=%-3d\n", opts[i].name, *opts[i].value & 0xff);
			break;

		  case STR:
			sprintf(pos, "%s=\"%s\"\n", opts[i].name, opts[i].value);
			break;
		}
		twrite(fd, buf, strlen(buf));
	}
}


/* This function changes the values of one or more options. */
setopts(assignments)
	char	*assignments;	/* a string containing option assignments */
{
	char	*name;		/* name of variable in assignments */
	char	*value;		/* value of the variable */
	char	*scan;		/* used for moving through strings */
	int	i, j;

	/* for each assignment... */
	for (name = assignments; *name; )
	{
		/* skip whitespace */
		if (*name == ' ' || *name == '\t')
		{
			name++;
			continue;
		}

		/* find the value, if any */
		for (scan = name; *scan >= 'a' && *scan <= 'z'; scan++)
		{
		}
		if (*scan == '=')
		{
			*scan++ = '\0';
			if (*scan == '"')
			{
				value = ++scan;
				while (*scan && *scan != '"')
				{
					scan++;
				}
				if (*scan)
				{
					*scan++ = '\0';
				}
			}
			else
			{
				value = scan;
				while (*scan && *scan != ' ' && *scan != '\t')
				{
					scan++;
				}
				if (*scan)
				{
					*scan++ = '\0';
				}
			}
		}
		else
		{
			if (*scan)
			{
				*scan++ = '\0';
			}
			value = NULL;
			if (name[0] == 'n' && name[1] == 'o')
			{
				name += 2;
			}
		}

		/* find the variable */
		for (i = 0;
		     opts[i].name && strcmp(opts[i].name, name) && strcmp(opts[i].nm, name);
		     i++)
		{
		}

		/* change the variable */
		if (!opts[i].name)
		{
			msg("invalid option name \"%s\"", name);
		}
		else if ((opts[i].flags & CANSET) != CANSET)
		{
			msg("option \"%s\" can't be altered", name);
		}
		else if ((opts[i].flags & RCSET) != CANSET && nlines >= 1L)
		{
			msg("option \"%s\" can only be set in a %s file", name, EXRC);
		}
		else if (value)
		{
			switch (opts[i].type)
			{
			  case BOOL:
				msg("option \"[no]%s\" is boolean", name);
				break;

			  case NUM:
				j = atoi(value);
				if (j == 0 && *value != '0')
				{
					msg("option \"%s\" must have a numeric value", name);
				}
				else if (j < opts[i].value[1] || j > (opts[i].value[2] & 0xff))
				{
					msg("option \"%s\" must have a value between %d and %d",
						name, opts[i].value[1], opts[i].value[2] & 0xff);
				}
				else
				{
					*opts[i].value = atoi(value);
					opts[i].flags |= SET;
				}
				break;

			  case STR:
				strcpy(opts[i].value, value);
				opts[i].flags |= SET;
				break;
			}
			if (opts[i].flags & MR)
			{
				mustredraw = TRUE;
			}
		}
		else /* valid option, no value */
		{
			if (opts[i].type == BOOL)
			{
				*opts[i].value = (name[-1] != 'o');
				opts[i].flags |= SET;
				if (opts[i].flags & MR)
				{
					mustredraw = TRUE;
				}
			}
			else
			{
				msg("option \"%s\" must be given a value", name);
			}
		}

		/* move on to the next option */
		name = scan;
	}
}
SHAR_EOF
fi
if test -f 'pc.c'
then
	echo shar: "will not over-write existing file 'pc.c'"
else
cat << \SHAR_EOF > 'pc.c'
/* pc.c */

/* Author:
 *	Guntram Blohm
 *	Buchenstrasse 19
 *	7904 Erbach, West Germany
 *	Tel. ++49-7305-6997
 *	sorry - no regular network connection
 */

/* This file implements the ibm pc bios interface. See IBM documentation
 * for details.
 * If TERM is set upon invocation of elvis, this code is ignored completely,
 * and the standard termcap functions are used, thus, even not-so-close
 * compatibles can run elvis. For close compatibles however, bios output
 * is much faster (and permits reverse scrolling, adding and deleting lines,
 * and much more ansi.sys isn't capable of). GB.
 */

#include "config.h"
#include "vi.h"

#if	MSDOS

#include <dos.h>

static void video();

/* vmode contains the screen attribute index and is set by attrset.*/

int vmode;

/* The following array contains attribute definitions for
 * color/monochrome attributes. Screen selects one of the sets.
 * Maybe i'll put them into elvis options one day.
 */

static int screen;
static char attr[2][5] =
{
	/*	:se:	:so:	:VB:	:ul:	:as:	*/
	{	0x17,	0x1d,	0x1e,	0x1a,	0x1c,	},	/* color */
	{	0x07,	0x70,	0x0f,	0x01,	0x0f,	},	/* monochrome */
};

/*
 * bios interface functions for elvis - pc version
 */

/* cursor up: determine current position, decrement row, set position */

void v_up()
{
	int dx;
	video(0x300,(int *)0,&dx);
	dx-=0x100;
	video(0x200,(int *)0,&dx);
}

#ifndef NO_CURSORSHAPE
/* cursor big: set begin scan to end scan - 4 */
void v_cb()
{
	int cx;
	video(0x300, &cx, (int *)0);
	cx=((cx&0xff)|(((cx&0xff)-4)<<8));
	video(0x100, &cx, (int *)0);
}

/* cursor small: set begin scan to end scan - 1 */
void v_cs()
{
	int cx;
	video(0x300, &cx, (int *)0);
	cx=((cx&0xff)|(((cx&0xff)-1)<<8));
	video(0x100, &cx, (int *)0);
}
#endif

/* clear to end: get cursor position and emit the aproppriate number
 * of spaces, without moving cursor.
 */
 
void v_ce()
{
	int cx, dx;
	video(0x300,(int *)0,&dx);
	cx=COLS-(dx&0xff);
	video(0x920,&cx,(int *)0);
}

/* clear screen: clear all and set cursor home */

void v_cl()
{
	int cx=0, dx=((LINES-1)<<8)+COLS-1;
	video(0x0600,&cx,&dx);
	dx=0;
	video(0x0200,&cx,&dx);
}

/* clear to bottom: get position, clear to eol, clear next line to end */

void v_cd()
{
	int cx, dx, dxtmp;
	video(0x0300,(int *)0,&dx);
	dxtmp=(dx&0xff00)|(COLS-1);
	cx=dx;
	video(0x0600,&cx,&dxtmp);
	cx=(dx&0xff00)+0x100;
	dx=((LINES-1)<<8)+COLS-1;
	video(0x600,&cx,&dx);
}

/* add line: scroll rest of screen down */

void v_al()
{
	int cx,dx;
	video(0x0300,(int *)0,&dx);
	cx=(dx&0xff00);
	dx=((LINES-1)<<8)+COLS-1;
	video(0x701,&cx,&dx);
}

/* delete line: scroll rest up */

void v_dl()
{
	int cx,dx;
	video(0x0300,(int *)0,&dx);
	cx=(dx&0xff00)/*+0x100*/;
	dx=((LINES-1)<<8)+COLS-1;
	video(0x601,&cx,&dx);
}

/* scroll reverse: scroll whole screen */

void v_sr()
{
	int cx=0, dx=((LINES-1)<<8)+COLS-1;
	video(0x0701,&cx,&dx);
}

/* set cursor */

void v_move(x,y)
	int x, y;
{
	int dx=(y<<8)+x;
	video(0x200,(int *)0,&dx);
}

/* put character: set attribute first, then execute char.
 * Also remember if current line has changed.
 */

int v_put(ch)
	int ch;
{
	int cx=1;
	ch&=0xff;
	if (ch>=' ')
		video(0x900|ch,&cx,(int *)0);
	video(0xe00|ch,(int *)0, (int *)0);
	if (ch=='\n')
	{	exwrote = TRUE;
		video(0xe0d, (int *)0, (int *)0);
	}
	return ch;
}

/* determine number of screen columns. Also set attrset according
 * to monochrome/color screen.
 */

int v_cols()
{
	union REGS regs;
	regs.h.ah=0x0f;
	int86(0x10, &regs, &regs);
	if (regs.h.al==7)			/* monochrome mode ? */
		screen=1;
	else
		screen=0;
	return regs.h.ah;
}

/* Getting the number of rows is hard. Most screens support 25 only,
 * EGA/VGA also support 43/50 lines, and some OEM's even more.
 * Unfortunately, there is no standard bios variable for the number
 * of lines, and the bios screen memory size is always rounded up
 * to 0x1000. So, we'll really have to cheat.
 * When using the screen memory size, keep in mind that each character
 * byte has an associated attribute byte.
 *
 * uses:	word at 40:4c contains	memory size
 *		byte at 40:84 		# of rows-1 (sometimes)
 *		byte at	40:4a		# of columns
 */

int v_rows()
{
	int line, oldline;

	/* screen size less then 4K? then we have 25 lines only */

	if (*(int far *)(0x0040004cl)<=4096)
		return 25;

	/* VEGA vga uses the bios byte at 0x40:0x84 for # of rows.
	 * Use that byte, if it makes sense together with memory size.
	 */

	if ((((*(unsigned char far *)(0x0040004aL)*2*
		(*(unsigned char far *)(0x00400084L)+1))+0xfff)&(~0xfff))==
		*(unsigned int far *)(0x0040004cL))
			return *(unsigned char far *)(0x00400084L)+1;

	/* uh oh. Emit '\n's until screen starts scrolling. */

	v_move(oldline=0, 0);
	for (;;)
	{
		video(0xe0a,(int *)0,(int *)0);
		video(0x300,(int *)0,&line);
		line>>=8;
		if (oldline==line)
			return line+1;
		oldline=line;	
	}
}

/* the REAL bios interface -- used internally only. */

static void video(ax, cx, dx)
	int ax, *cx, *dx;
{
	union REGS regs;

	regs.x.ax=ax;
	if ((ax&0xff00)==0x600 || (ax&0xff00)==0x700)
		regs.h.bh=attr[screen][vmode];
	else
	{
		regs.h.bh=0;
		regs.h.bl=attr[screen][vmode];
	}
	if (cx) regs.x.cx=*cx;
	if (dx) regs.x.dx=*dx;
	int86(0x10, &regs, &regs);
	if (dx) *dx=regs.x.dx;
	if (cx) *cx=regs.x.cx;
}

/* The following function determines which character is used for
 * commandline-options by command.com. This system call is undocumented
 * and valid for versions < 4.00 only.
 */
 
int switchar()
{
	union REGS regs;
	regs.x.ax=0x3700;
	int86(0x21, &regs, &regs);
	return regs.h.dl;
}

#endif
SHAR_EOF
fi
if test -f 'recycle.c'
then
	echo shar: "will not over-write existing file 'recycle.c'"
else
cat << \SHAR_EOF > 'recycle.c'
/* recycle.c */

/* Author:
 *	Steve Kirkendall
 *	16820 SW Tallac Way
 *	Beaverton, OR 97006
 *	kirkenda@jove.cs.pdx.edu, or ...uunet!tektronix!psueea!jove!kirkenda
 */


/* This file contains the functions perform garbage collection and allocate
 * reusable blocks.
 */

#include "config.h"
#include "vi.h"

#ifndef NO_RECYCLE
/* this whole file would have be skipped if NO_RECYCLE is defined */

extern long	lseek();

#define BTST(bitno, byte)	((byte) & (1 << (bitno)))
#define BSET(bitno, byte)	((byte) |= (1 << (bitno)))
#define BCLR(bitno, byte)	((byte) &= ~(1 << (bitno)))

#define TST(blkno)		((blkno) < MAXBIT ? BTST((blkno) & 7, bitmap[(blkno) >> 3]) : 1)
#define SET(blkno)		if ((blkno) < MAXBIT) BSET((blkno) & 7, bitmap[(blkno) >> 3])
#define CLR(blkno)		if ((blkno) < MAXBIT) BCLR((blkno) & 7, bitmap[(blkno) >> 3])

/* bitmap of free blocks in first 4096k of tmp file */
static unsigned char bitmap[512];
#define MAXBIT	(sizeof bitmap << 3)

/* this function locates all free blocks in the current tmp file */
garbage()
{
	int	i;
	BLK	oldhdr;

	/* start by assuming every block is free */
	for (i = 0; i < sizeof bitmap; i++)
	{
		bitmap[i] = 255;
	}

	/* header block isn't free */
	CLR(0);

	/* blocks needed for current hdr aren't free */
	for (i = 1; i < MAXBLKS; i++)
	{
		CLR(hdr.n[i]);
	}

	/* blocks needed for undo version aren't free */
	lseek(tmpfd, 0L, 0);
	if (read(tmpfd, &oldhdr, sizeof oldhdr) != sizeof oldhdr)
	{
		msg("garbage() failed to read oldhdr??");
		for (i = 0; i < sizeof bitmap; i++)
		{
			bitmap[i] = 0;
		}
		return;
	}
	for (i = 1; i < MAXBLKS; i++)
	{
		CLR(oldhdr.n[i]);
	}

	/* blocks needed for cut buffers aren't free */
	for (i = cutneeds(&oldhdr) - 1; i >= 0; i--)
	{
		CLR(oldhdr.n[i]);
	}
}

/* This function allocates the first available block in the tmp file */
long allocate()
{
	int	i;
	long	offset;

	/* search for the first byte with a free bit set */
	for (i = 0; i < sizeof bitmap && bitmap[i] == 0; i++)
	{
	}

	/* if we hit the end of the bitmap, return the end of the file */
	if (i == sizeof bitmap)
	{
		offset = lseek(tmpfd, 0L, 2);
	}
	else /* compute the offset for the free block */
	{
		for (i <<= 3; TST(i) == 0; i++)
		{
		}
		offset = (long)i * (long)BLKSIZE;

		/* mark the block as "allocated" */
		CLR(i);
	}

	return offset;
}

#endif
SHAR_EOF
fi
if test -f 'redraw.c'
then
	echo shar: "will not over-write existing file 'redraw.c'"
else
cat << \SHAR_EOF > 'redraw.c'
/* redraw.c */

/* Author:
 *	Steve Kirkendall
 *	16820 SW Tallac Way
 *	Beaverton, OR 97006
 *	kirkenda@jove.cs.pdx.edu, or ...uunet!tektronix!psueea!jove!kirkenda
 */


/* This file contains functions that draw text on the screen.  The major entry
 * points are:
 *	redrawrange()	- called from modify.c to give hints about what parts
 *			  of the screen need to be redrawn.
 *	redraw()	- redraws the screen (or part of it) and positions
 *			  the cursor where it belongs.
 *	idx2col()	- converts a markidx() value to a logical column number.
 */

#include "config.h"
#include "vi.h"

/* This variable contains the line number that smartdrawtext() knows best */
static long smartlno;

/* This function remebers where changes were made, so that the screen can be
 * redraw in a more efficient manner.
 */
redrawrange(after, pre, post)
	long	after;	/* lower bound of redrawafter */
	long	pre;	/* upper bound of preredraw */
	long	post;	/* upper bound of postredraw */
{
	long	l;

	if (after < redrawafter)
		redrawafter = after;

	l = preredraw - postredraw + pre - post;
	if (post > postredraw)
		postredraw = post;
	preredraw = postredraw + l;
}


/* This function is used in visual mode for drawing the screen (or just parts
 * of the screen, if that's all thats needed).  It also takes care of
 * scrolling.
 */
redraw(curs, inputting)
	MARK	curs;		/* where to leave the screen's cursor */
	int	inputting;	/* boolean: being called from input() ? */
{
	char		*text;		/* a line of text to display */
	static long	chgs;		/* previous changes level */
	long		l;
	int		i;

	/* if curs == MARK_UNSET, then we should reset internal vars */
	if (curs == MARK_UNSET)
	{
		if (topline < 1 || topline > nlines)
		{
			topline = 1L;
		}
		else
		{
			move(LINES - 1, 0);
			clrtoeol();
		}
		leftcol = 0;
		mustredraw = TRUE;
		redrawafter = INFINITY;
		preredraw = 0L;
		postredraw = 0L;
		chgs = 0;
		smartlno = 0L;
		return;
	}

	/* figure out which column the cursor will be in */
	l = markline(curs);
	text = fetchline(l);
	mark2phys(curs, text, inputting);

	/* adjust topline, if necessary, to get the cursor on the screen */
	if (l >= topline && l <= botline)
	{
		/* it is on the screen already */

		/* if the file was changed but !mustredraw, then redraw line */
		if (chgs != changes && !mustredraw)
		{
			smartdrawtext(text, l);
		}
	}
	else if (l < topline && l > topline - LINES && (has_SR || has_AL))
	{
		/* near top - scroll down */
		if (!mustredraw)
		{
			move(0,0);
			while (l < topline)
			{
				topline--;
				if (has_SR)
				{
					do_SR();
				}
				else
				{
					insertln();
				}
				text = fetchline(topline);
				drawtext(text);
				do_UP();
			}

			/* blank out the last line */
			move(LINES - 1, 0);
			clrtoeol();
		}
		else
		{
			topline = l;
			redrawafter = INFINITY;
			preredraw = 0L;
			postredraw = 0L;
		}
	}
	else if (l > topline && l < botline + LINES)
	{
		/* near bottom -- scroll up */
		if (!mustredraw
#if 1
		 || redrawafter == preredraw && preredraw == botline && postredraw == l
#endif
		)
		{
			move(LINES - 1,0);
			clrtoeol();
			while (l > botline)
			{
				topline++; /* <-- also adjusts botline */
				text = fetchline(botline);
				drawtext(text);
			}
			mustredraw = FALSE;
		}
		else
		{
			topline = l - (LINES - 2);
			redrawafter = INFINITY;
			preredraw = 0L;
			postredraw = 0L;
		}
	}
	else
	{
		/* distant line - center it & force a redraw */
		topline = l - (LINES / 2) - 1;
		if (topline < 1)
		{
			topline = 1;
		}
		mustredraw = TRUE;
		redrawafter = INFINITY;
		preredraw = 0L;
		postredraw = 0L;
	}

	/* Now... do we really have to redraw? */
	if (mustredraw)
	{
		/* If redrawfter (and friends) aren't set, assume we should
		 * redraw everything.
		 */
		if (redrawafter == INFINITY)
		{
			redrawafter = 0L;
			preredraw = postredraw = INFINITY;
		}

		/* adjust smartlno to correspond with inserted/deleted lines */
		if (smartlno >= redrawafter)
		{
			if (smartlno < preredraw)
			{
				smartlno = 0L;
			}
			else
			{
				smartlno += (postredraw - preredraw);
			}
		}

		/* should we insert some lines into the screen? */
		if (preredraw < postredraw && preredraw <= botline)
		{
			/* lines were inserted into the file */

			/* decide where insertion should start */
			if (preredraw < topline)
			{
				l = topline;
			}
			else
			{
				l = preredraw;
			}

			/* insert the lines... maybe */
			if (l + postredraw - preredraw > botline || !has_AL)
			{
				/* Whoa!  a whole screen full - just redraw */
				preredraw = postredraw = INFINITY;
			}
			else
			{
				/* really insert 'em */
				move((int)(l - topline), 0);
				for (i = postredraw - preredraw; i > 0; i--)
				{
					insertln();
				}

				/* NOTE: the contents of those lines will be
				 * drawn as part of the regular redraw loop.
				 */

				/* clear the last line */
				move(LINES - 1, 0);
				clrtoeol();
			}
		}

		/* do we want to delete some lines from the screen? */
		if (preredraw > postredraw && postredraw <= botline)
		{
			if (preredraw > botline || !has_DL)
			{
				postredraw = preredraw = INFINITY;
			}
			else /* we'd best delete some lines from the screen */
			{
				/* clear the last line, so it doesn't look
				 * ugly as it gets pulled up into the screen
				 */
				move(LINES - 1, 0);
				clrtoeol();

				/* delete the lines */
				move((int)(postredraw - topline), 0);
			 	for (l = postredraw;
				     l < preredraw && l <= botline;
				     l++)
				{
					deleteln();
				}

				/* draw the lines that are now newly visible
				 * at the bottom of the screen
				 */
				i = LINES - 1 + (postredraw - preredraw);
				move(i, 0);
				for (l = topline + i; l <= botline; l++)
				{
					/* clear this line */
					clrtoeol();

					/* draw the line, or ~ for non-lines */
					if (l <= nlines)
					{
						text = fetchline(l);
						drawtext(text);
					}
					else
					{
						addstr("~\n");
					}
				}
			}
		}

		/* redraw the current line */
		l = markline(curs);
		pfetch(l);
		smartdrawtext(ptext, l);

		/* decide where we should start redrawing from */
		if (redrawafter < topline)
		{
			l = topline;
		}
		else
		{
			l = redrawafter;
		}
		move((int)(l - topline), 0);

		/* draw the other lines */
		for (; l <= botline && l < postredraw; l++)
		{
			/* we already drew the current line, so skip it now */
			if (l == smartlno)
			{
				qaddch('\n');
				continue;
			}

			/* clear this line */
			clrtoeol();

			/* draw the line, or ~ for non-lines */
			if (l <= nlines)
			{
				text = fetchline(l);
				drawtext(text);
			}
			else
			{
				addstr("~\n");
			}
		}

		mustredraw = FALSE;
	}

	/* force total (non-partial) redraw next time if not set */
	redrawafter = INFINITY;
	preredraw = 0L;
	postredraw = 0L;

	/* move the cursor to where it belongs */
	move((int)(markline(curs) - topline), physcol);
	wqrefresh(stdscr);

	chgs = changes;
}


/* This function converts a MARK to a column number.  It doesn't automatically
 * adjust for leftcol; that must be done by the calling function
 */
int idx2col(curs, text, inputting)
	MARK		curs;	/* the line# & index# of the cursor */
	register char	*text;	/* the text of the line, from fetchline */
	int		inputting;	/* boolean: called from input() ? */
{
	static MARK	pcursor;/* previous cursor, for possible shortcut */
	static MARK	pcol;	/* column number for pcol */
	static long	chgs;	/* previous value of changes counter */
	register int	col;	/* used to count column numbers */
	register int	idx;	/* used to count down the index */
	register int	i;

	/* for now, assume we have to start counting at the left edge */
	col = 0;
	idx = markidx(curs);

	/* if the file hasn't changed & line number is the same & it has no
	 * embedded character attribute strings, can we do shortcuts?
	 */
	if (chgs == changes
	 && !((curs ^ pcursor) & ~(BLKSIZE - 1))
#ifndef NO_CHARATTR
	 && !hasattr(markline(curs), text)
#endif
	)
	{
		/* no movement? */
		if (curs == pcursor)
		{
			/* return the column of the char; for tabs, return its last column */
			if (text[idx] == '\t' && !inputting && !*o_list)
			{
				return pcol + *o_tabstop - (pcol % *o_tabstop) - 1;
			}
			else
			{
				return pcol;
			}
		}

		/* movement to right? */
		if (curs > pcursor)
		{
			/* start counting from previous place */
			col = pcol;
			idx = markidx(curs) - markidx(pcursor);
			text += markidx(pcursor);
		}
	}

	/* count over to the char after the idx position */
	while (idx > 0 && (i = *text)) /* yes, ASSIGNMENT! */
	{
		if (i == '\t' && !*o_list)
		{
			col += *o_tabstop;
			col -= col % *o_tabstop;
		}
		else if (i >= '\0' && i < ' ' || i == '\177')
		{
			col += 2;
		}
#ifndef NO_CHARATTR
		else if (i == '\\' && text[1] == 'f' && text[2] && *o_charattr)
		{
			text += 2; /* plus one more at bottom of loop */
			idx -= 2;
		}			
#endif
		else
		{
			col++;
		}
		text++;
		idx--;
	}

	/* save stuff to speed next call */
	pcursor = curs;
	pcol = col;
	chgs = changes;

	/* return the column of the char; for tabs, return its last column */
	if (*text == '\t' && !inputting && !*o_list)
	{
		return col + *o_tabstop - (col % *o_tabstop) - 1;
	}
	else
	{
		return col;
	}
}


/* This function is similar to idx2col except that it takes care of sideways
 * scrolling - for the given line, at least.
 */
mark2phys(m, text, inputting)
	MARK	m;		/* a mark to convert */
	char	*text;		/* the line that m refers to */
	int	inputting;	/* boolean: caled from input() ? */
{
	int	i;

	i = idx2col(cursor, text, inputting);
	while (i < leftcol)
	{
		leftcol -= *o_sidescroll;
		mustredraw = TRUE;
		redrawrange(1L, INFINITY, INFINITY);
		qaddch('\r');
		/* drawtext(text); */
	}
	while (i > rightcol)
	{
		leftcol += *o_sidescroll;
		mustredraw = TRUE;
		redrawrange(1L, INFINITY, INFINITY);
		qaddch('\r');
		/* drawtext(text); */
	}
	physcol = i - leftcol;
	physrow = markline(m) - topline;

	return physcol;
}

/* This function draws a single line of text on the screen.  The screen's
 * cursor is assumed to be located at the leftmost column of the appropriate
 * row.
 */
drawtext(text)
	register char	*text;	/* the text to draw */
{
	register int	col;	/* column number */
	register int	i;
	register int	tabstop;	/* *o_tabstop */
	register int	limitcol;	/* leftcol or leftcol + COLS */
	int		abnormal;	/* boolean: charattr != A_NORMAL? */

#ifndef NO_SENTENCE
	/* if we're hiding format lines, and this is one of them, then hide it */
	if (*o_hideformat && *text == '.')
	{
		clrtoeol();
		qaddch('\n');
		return;
	}
#endif

	/* move some things into registers... */
	limitcol = leftcol;
	tabstop = *o_tabstop;
	abnormal = FALSE;

	/* skip stuff that was scrolled off left edge */
	for (col = 0;
	     (i = *text) && col < limitcol; /* yes, ASSIGNMENT! */
	     text++)
	{
		if (i == '\t' && !*o_list)
		{
			col = col + tabstop - (col % tabstop);
		}
		else if (i >= 0 && i < ' ' || i == '\177')
		{
			col += 2;
		}
#ifndef NO_CHARATTR
		else if (i == '\\' && text[1] == 'f' && text[2] && *o_charattr)
		{
			text += 2; /* plus one more as part of "for" loop */

			/* since this attribute might carry over, we need it */
			switch (*text)
			{
			  case 'R':
			  case 'P':
				attrset(A_NORMAL);
				abnormal = FALSE;
				break;

			  case 'B':
				attrset(A_BOLD);
				abnormal = TRUE;
				break;

			  case 'U':
				attrset(A_UNDERLINE);
				abnormal = TRUE;
				break;

			  case 'I':
				attrset(A_ALTCHARSET);
				abnormal = TRUE;
				break;
			}
		}
#endif
		else
		{
			col++;
		}
	}

	/* adjust for control char that was partially visible */
	while (col > limitcol)
	{
		qaddch(' ');
		limitcol++;
	}

	/* now for the visible characters */
	for (limitcol = leftcol + COLS;
	     (i = *text) && col < limitcol;
	     text++)
	{
		if (i == '\t' && !*o_list)
		{
			i = col + tabstop - (col % tabstop);
			if (i < limitcol)
			{
				if (has_PT && !((i - leftcol) & 7))
				{
					do
					{
						qaddch('\t');
						col += 8; /* not exact! */
					} while (col < i);
					col = i; /* NOW it is exact */
				}
				else
				{
					do
					{
						qaddch(' ');
						col++;
					} while (col < i);
				}
			}
			else /* tab ending after screen? next line! */
			{
				col = limitcol;
				if (has_AM)
				{
					qaddch('\r');	/* GB */
					qaddch('\n');	/* GB */
				}
			}
		}
		else if (i >= 0 && i < ' ' || i == '\177')
		{
			col += 2;
			if (col < limitcol)
			{
				qaddch('^');
				qaddch(i ^ '@');
			}
		}
#ifndef NO_CHARATTR
		else if (i == '\\' && text[1] == 'f' && text[2] && *o_charattr)
		{
			text += 2; /* plus one more as part of "for" loop */
			switch (*text)
			{
			  case 'R':
			  case 'P':
				attrset(A_NORMAL);
				abnormal = FALSE;
				break;

			  case 'B':
				attrset(A_BOLD);
				abnormal = TRUE;
				break;

			  case 'U':
				attrset(A_UNDERLINE);
				abnormal = TRUE;
				break;

			  case 'I':
				attrset(A_ALTCHARSET);
				abnormal = TRUE;
				break;
			}
		}
#endif
		else
		{
			col++;
			qaddch(i);
		}
	}

	/* get ready for the next line */
#ifndef NO_CHARATTR
	if (abnormal)
	{
		attrset(A_NORMAL);
	}
#endif
	if (*o_list && col < limitcol)
	{
		qaddch('$');
		col++;
	}
	if (!has_AM || col < limitcol)
	{
		qaddch('\r');
		qaddch('\n');
	}
}


static nudgecursor(same, scan, new, lno)
	int	same;	/* number of chars to be skipped over */
	char	*scan;	/* where the same chars end */
	char	*new;	/* where the visible part of the line starts */
	long	lno;	/* line number of this line */
{
	if (same > 0)
	{
		if (same < 5)
		{
			/* move the cursor by overwriting */
			while (same > 0)
			{
				qaddch(scan[-same]);
				same--;
			}
		}
		else
		{
			/* move the cursor by calling move() */
			move((int)(lno - topline), (int)(scan - new));
		}
	}
}

/* This function draws a single line of text on the screen, possibly with
 * some cursor optimization.  The cursor is repositioned before drawing
 * begins, so its position before doesn't really matter.
 */
smartdrawtext(text, lno)
	register char	*text;	/* the text to draw */
	long		lno;	/* line number of the text */
{
	static char	old[256];	/* how the line looked last time */
	char		new[256];	/* how it looks now */
	char		*build;		/* used to put chars into new[] */
	char		*scan;		/* used for moving thru new[] or old[] */
	char		*end;		/* last non-blank changed char */
	char		*shift;		/* used to insert/delete chars */
	int		same;		/* length of a run of unchanged chars */
	int		limitcol;
	int		col;
	int		i;

#ifndef NO_CHARATTR
	/* if this line has attributes, do it the dumb way instead */
	if (hasattr(lno, text))
	{
		move((int)(lno - topline), 0);
		clrtoeol();
		drawtext(text);
		return;
	}
#endif
#ifndef NO_SENTENCE
	/* if this line is a format line, & we're hiding format lines, then
	 * let the dumb drawtext() function handle it
	 */
	if (*o_hideformat && *text == '.')
	{
		move((int)(lno - topline), 0);
		clrtoeol();
		drawtext(text);
		return;
	}
#endif

	/* skip stuff that was scrolled off left edge */
	limitcol = leftcol;
	for (col = 0;
	     (i = *text) && col < limitcol; /* yes, ASSIGNMENT! */
	     text++)
	{
		if (i == '\t' && !*o_list)
		{
			col = col + *o_tabstop - (col % *o_tabstop);
		}
		else if (i >= 0 && i < ' ' || i == '\177')
		{
			col += 2;
		}
		else
		{
			col++;
		}
	}

	/* adjust for control char that was partially visible */
	build = new;
	while (col > limitcol)
	{
		*build++ = ' ';
		limitcol++;
	}

	/* now for the visible characters */
	for (limitcol = leftcol + COLS;
	     (i = *text) && col < limitcol;
	     text++)
	{
		if (i == '\t' && !*o_list)
		{
			i = col + *o_tabstop - (col % *o_tabstop);
			while (col < i && col < limitcol)
			{
				*build++ = ' ';
				col++;
			}
		}
		else if (i >= 0 && i < ' ' || i == '\177')
		{
			col += 2;
			*build++ = '^';
			if (col < limitcol)
			{
				*build++ = (i ^ '@');
			}
		}
		else
		{
			col++;
			*build++ = i;
		}
	}
	if (col < limitcol && *o_list)
	{
		*build++ = '$';
		col++;
	}
	end = build;
	while (col < limitcol)
	{
		*build++ = ' ';
		col++;
	}

	/* locate the last non-blank character */
	while (end > new && end[-1] == ' ')
	{
		end--;
	}

	/* can we optimize the displaying of this line? */
	if (lno != smartlno)
	{
		/* nope, can't optimize - different line */
		move((int)(lno - topline), 0);
		for (scan = new, build = old; scan < end; )
		{
			qaddch(*scan);
			*build++ = *scan++;
		}
		if (end < new + COLS)
		{
			clrtoeol();
			while (build < old + COLS)
			{
				*build++ = ' ';
			}
		}
		smartlno = lno;
		return;
	}

	/* skip any initial unchanged characters */
	for (scan = new, build = old; scan < end && *scan == *build; scan++, build++)
	{
	}
	move((int)(lno - topline), (int)(scan - new));

	/* The in-between characters must be changed */
	same = 0;
	while (scan < end)
	{
		/* is this character a match? */
		if (scan[0] == build[0])
		{
			same++;
		}
		else /* do we want to insert? */
		if (scan < end - 1 && scan[1] == build[0] && (has_IC || has_IM))
		{
			nudgecursor(same, scan, new, lno);
			same = 0;

			insch(*scan);
			for (shift = old + COLS; --shift > build; )
			{
				shift[0] = shift[-1];
			}
			*build = *scan;
		}
		else /* do we want to delete? */
		if (build < old + COLS - 1 && scan[0] == build[1] && has_DC)
		{
			nudgecursor(same, scan, new, lno);
			same = 0;

			delch();
			same++;
			for (shift = build; shift < old + COLS - 1; shift++)
			{
				shift[0] = shift[1];
			}
			*shift = ' ';
		}
		else /* we must overwrite */
		{
			nudgecursor(same, scan, new, lno);
			same = 0;

			addch(*scan);
			*build = *scan;
		}

		build++;
		scan++;
	}

	/* maybe clear to EOL */
	while (build < old + COLS && *build == ' ')
	{
		build++;
	}
	if (build < old + COLS)
	{
		nudgecursor(same, scan, new, lno);
		same = 0;

		clrtoeol();
		while (build < old + COLS)
		{
			*build++ = ' ';
		}
	}
}


#ifndef NO_CHARATTR
/* see if a given line uses character attribute strings */
int hasattr(lno, text)
	long		lno;	/* the line# of the cursor */
	register char	*text;	/* the text of the line, from fetchline */
{
	static long	plno;	/* previous line number */
	static long	chgs;	/* previous value of changes counter */
	static int	panswer;/* previous answer */
	char		*scan;

	/* if charattr is off, then the answer is "no, it doesn't" */
	if (!*o_charattr)
	{
		chgs = 0; /* <- forces us to check if charattr is later set */
		return FALSE;
	}

	/* if we already know the answer, return it... */
	if (lno == plno && chgs == changes)
	{
		return panswer;
	}

	/* get the line & look for "\fX" */
	if (!text[0] || !text[1] || !text[2])
	{
		panswer = FALSE;
	}
	else
	{
		for (scan = text; scan[2] && !(scan[0] == '\\' && scan[1] == 'f'); scan++)
		{
		}
		panswer = (scan[2] != '\0');
	}

	/* save the results */
	plno = lno;
	chgs = changes;

	/* return the results */
	return panswer;
}
#endif
SHAR_EOF
fi
exit 0
#	End of shell archive
-------------------------------------------------------------------------------
Steve Kirkendall    kirkenda@cs.pdx.edu    uunet!tektronix!psueea!eecs!kirkenda