elvis 1.3 - a clone of vi/ex, part 6 of 6

Steve Kirkendall kirkenda at eecs.cs.pdx.edu
Sat Aug 25 04:00:44 AEST 1990


Archive-name: elvis1.3/part6
#! /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:
#	vars.c
#	vcmd.c
#	vi.c
#	vi.h
#	ctags.c
#	ref.c
#	virec.c
#	wildcard.c
#	shell.c
# This archive created: Fri Aug 24 10:30:00 1990
export PATH; PATH=/bin:/usr/bin:$PATH
if test -f 'vars.c'
then
	echo shar: "will not over-write existing file 'vars.c'"
else
cat << \SHAR_EOF > 'vars.c'
/* vars.c */

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


/* This file contains variables which weren't happy anyplace else */

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

/*------------------------------------------------------------------------*/

/* used to remember whether the file has been modified */
struct _viflags	viflags;

/* used to access the tmp file */
long		lnum[MAXBLKS];
long		nlines;
int		tmpfd = -1;

/* used to keep track of the current file & alternate file */
long		origtime;
char		origname[256];
char		prevorig[256];
long		prevline = 1;

/* used to track various places in the text */
MARK		mark[NMARKS];	/* marks 'a through 'z, plus mark '' */
MARK		cursor;		/* the cursor position within the file */

/* which mode of the editor we're in */
int		mode;		/* vi mode? ex mode? quitting? */

/* used to manage the args list */
char		args[BLKSIZE];	/* list of filenames to edit */
int		argno;		/* index of current file in args list */
int		nargs;		/* number of filenames in args[] */

/* dummy var, never explicitly referenced */
int		bavar;		/* used only in BeforeAfter macros */

/* have we made a multi-line change? */
int		mustredraw;	/* must we redraw the whole screen? */
long		redrawafter;	/* line# of first line that must be redrawn */
long		preredraw;	/* line# of last line changed, before change */
long		postredraw;	/* line# of last line changed, after change */
				/* (postredraw - preredraw) = #lines added */

/* used to detect changes that invalidate cached text/blocks */
long		changes;	/* incremented when file is changed */

/* used to support the pfetch() macro */
int		plen;		/* length of the line */
long		pline;		/* line number that len refers to */
long		pchgs;		/* "changes" level that len refers to */
char		*ptext;		/* text of previous line, if valid */

/* misc temporary storage - mostly for strings */
BLK		tmpblk;		/* a block used to accumulate changes */

/* screen oriented stuff */
long		topline;	/* file line number of top line */
int		leftcol;	/* column number of left col */
int		physcol;	/* physical column number that cursor is on */
int		physrow;	/* physical row number that cursor is on */

/* used to help minimize that "[Hit a key to continue]" message */
int		exwrote;	/* Boolean: was the last ex command wordy? */

/* This variable affects the behaviour of certain functions -- most importantly
 * the input function.
 */
int		doingdot;	/* boolean: are we doing the "." command? */

/* These are used for reporting multi-line changes to the user */
long		rptlines;	/* number of lines affected by a command */
char		*rptlabel;	/* description of how lines were affected */

/* These store info that pertains to the shift-U command */
long	U_line;			/* line# of the undoable line, or 0l for none */
char	U_text[BLKSIZE];	/* contents of the undoable line */

/* Bigger stack req'ed for TOS */

#if	TOS
long	_stksize = 16384;
#endif
SHAR_EOF
fi
if test -f 'vcmd.c'
then
	echo shar: "will not over-write existing file 'vcmd.c'"
else
cat << \SHAR_EOF > 'vcmd.c'
/* vcmd.c */

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


/* This file contains the functions that handle VI commands */


#include "config.h"
#include "vi.h"
#if	MSDOS
#include <process.h>
#include <string.h>
#endif
#if	TOS
#include <osbind.h>
#include <string.h>
#endif


/* This function puts the editor in EX mode */
MARK v_quit()
{
	mode = MODE_EX;
	return cursor;
}

/* This function causes the screen to be redrawn */
MARK v_redraw()
{
	redraw(MARK_UNSET, FALSE);
	return cursor;
}

/* This function executes a single EX command, and waits for a user keystroke
 * before returning to the VI screen.  If that keystroke is another ':', then
 * another EX command is read and executed.
 */
/*ARGSUSED*/
MARK v_1ex(m, text)
	MARK	m;	/* the current line */
	char	*text;	/* the first command to execute */
{
	/* scroll up, so we don't overwrite the command */
	if (mode == MODE_COLON)
	{
		addch('\n');
		refresh();
	}

	/* run the command.  be careful about modes & output */
	exwrote = (mode == MODE_COLON);
	doexcmd(text);
	exrefresh();

	/* if mode is no longer MODE_VI, then we should quit right away! */
	if (mode != MODE_VI && mode != MODE_COLON)
		return cursor;

	/* The command did some output.  Wait for a keystoke. */
	if (exwrote)
	{
		mode = MODE_VI;	
		msg("[Hit any key to continue]");
		if (getkey(0) == ':')
		{	mode = MODE_COLON;
			addch('\n');
		}
		else
			redraw(MARK_UNSET, FALSE);
	}

	return cursor;
}

/* This function undoes the last change */
/*ARGSUSED*/
MARK v_undo(m)
	MARK	m;	/* (ignored) */
{
	undo();
	redraw(MARK_UNSET, FALSE);
	return cursor;
}

/* This function deletes the character(s) that the cursor is on */
MARK v_xchar(m, cnt)
	MARK	m;	/* where to start deletions */
	long	cnt;	/* number of chars to delete */
{
	DEFAULT(1);

	pfetch(markline(m));
	if (markidx(m + cnt) > plen)
	{
		cnt = plen - markidx(m);
	}
	if (cnt == 0L)
	{
		return MARK_UNSET;
	}
	ChangeText
	{
		cut(m, m + cnt);
		delete(m, m + cnt);
	}
	return m;
}

/* This function deletes character to the left of the cursor */
MARK v_Xchar(m, cnt)
	MARK	m;	/* where deletions end */
	long	cnt;	/* number of chars to delete */
{
	DEFAULT(1);

	/* if we're at the first char of the line, error! */
	if (markidx(m) == 0)
	{
		return MARK_UNSET;
	}

	/* make sure we don't try to delete more chars than there are */
	if (cnt > markidx(m))
	{
		cnt = markidx(m);
	}

	/* delete 'em */
	ChangeText
	{
		cut(m - cnt, m);
		delete(m - cnt, m);
	}

	return m - cnt;
}

/* This function defines a mark */
/*ARGSUSED*/
MARK v_mark(m, count, key)
	MARK	m;	/* where the mark will be */
	long	count;	/* (ignored) */
	int	key;	/* the ASCII label of the mark */
{
	if (key < 'a' || key > 'z')
	{
		msg("Marks must be from a to z");
	}
	else
	{
		mark[key - 'a'] = m;
	}
	return m;
}

/* This function toggles upper & lower case letters */
MARK v_ulcase(m)
	MARK	m;	/* where to make the change */
{
	char	new[2];
#if	MSDOS || TOS
	char	*pos;
	char	*lower="\207\201\202\204\206\221\224\244";
	char 	*upper="\200\232\220\216\217\222\231\245";
#endif

	/* extract the char that's there now */
	pfetch(markline(m));
	new[0] = ptext[markidx(m)];
	new[1] = '\0';

	/* change it if necessary */
	if (new[0] >= 'a' && new[0] <= 'z' || new[0] >= 'A' && new[0] <= 'Z')
	{
		new[0] ^= ('A' ^ 'a');
		ChangeText
		{
			change(m, m + 1, new);
		}
	}
#if	MSDOS || TOS
	if ((pos=strchr(lower, new[0]))!=0)
		new[0]=upper[(int)(pos-lower)];
	else if ((pos=strchr(upper, new[0]))!=0)
		new[0]=lower[(int)(pos-upper)];
	else
		goto nochange;		/* Urghh - GB */
	ChangeText
	{
		change(m, m + 1, new);
	}
nochange:
#endif
	if (new[0] && ptext[markidx(m) + 1])
	{
		m++;
	}
	return m;
}


MARK v_replace(m, cnt, key)
	MARK	m;	/* first char to be replaced */
	long	cnt;	/* number of chars to replace */
	int	key;	/* what to replace them with */
{
	register char	*text;
	register int	i;
	static int	samekey;

	DEFAULT(1);

	/* map ^M to '\n' */
	if (key == '\r')
	{
		key = '\n';
	}
	else if (key == ctrl('V'))
	{
		if (doingdot)
			key = samekey;
		else
			key = samekey = getkey(0);
		if (key == 0)
			return MARK_UNSET;
	}
	else if (!doingdot && key == ctrl('['))
	{
		samekey = 0;
		return MARK_UNSET;
	}

	/* make sure the resulting line isn't too long */
	if (cnt > BLKSIZE - 2 - markidx(m))
	{
		cnt = BLKSIZE - 2 - markidx(m);
	}

	/* build a string of the desired character with the desired length */
	for (text = tmpblk.c, i = cnt; i > 0; i--)
	{
		*text++ = key;
	}
	*text = '\0';

	/* make sure cnt doesn't extend past EOL */
	pfetch(markline(m));
	key = markidx(m);
	if (key + cnt > plen)
	{
		cnt = plen - key;
	}

	/* do the replacement */
	ChangeText
	{
		change(m, m + cnt, tmpblk.c);
	}

	if (*tmpblk.c == '\n')
	{
		return (m & ~(BLKSIZE - 1)) + cnt * BLKSIZE;
	}
	else
	{
		return m + cnt - 1;
	}
}

MARK v_overtype(m)
	MARK		m;	/* where to start overtyping */
{
	MARK		end;	/* end of a substitution */
	static long	width;	/* width of a single-line replace */

	/* the "doingdot" version of replace is really a substitution */
	if (doingdot)
	{
		/* was the last one really repeatable? */
		if (width < 0)
		{
			msg("Can't repeat a multi-line overtype command");
			return MARK_UNSET;
		}

		/* replacing nothing by nothing?  Don't bother */
		if (width == 0)
		{
			return m;
		}

		/* replace some chars by repeated text */
		return v_subst(m, width);
	}

	/* Normally, we input starting here, in replace mode */
	ChangeText
	{
		end = input(m, m, WHEN_VIREP);
	}

	/* if we ended on the same line we started on, then this
	 * overtype is repeatable via the dot key.
	 */
	if (markline(end) == markline(m) && end >= m - 1L)
	{
		width = end - m + 1L;
	}
	else /* it isn't repeatable */
	{
		width = -1L;
	}

	return end;
}


/* This function selects which cut buffer to use */
/*ARGSUSED*/
MARK v_selcut(m, cnt, key)
	MARK	m;
	long	cnt;
	int	key;
{
	cutname(key);
	return m;
}

/* This function pastes text from a cut buffer */
/*ARGSUSED*/
MARK v_paste(m, cnt, cmd)
	MARK	m;	/* where to paste the text */
	long	cnt;	/* (ignored) */
	int	cmd;	/* either 'p' or 'P' */
{
	ChangeText
	{
		m = paste(m, cmd == 'p', FALSE);
	}
	return m;
}

/* This function yanks text into a cut buffer */
MARK v_yank(m, n)
	MARK	m, n;	/* range of text to yank */
{
	cut(m, n);
	return m;
}

/* This function deletes a range of text */
MARK v_delete(m, n)
	MARK	m, n;	/* range of text to delete */
{
	/* illegal to try and delete nothing */
	if (n <= m)
	{
		return MARK_UNSET;
	}
	/* Do it */
	ChangeText
	{
		cut(m, n);
		delete(m, n);
	}
	return m;
}


/* This starts input mode without deleting anything */
MARK v_insert(m, cnt, key)
	MARK	m;	/* where to start (sort of) */
	long	cnt;	/* repeat how many times? */
	int	key;	/* what command is this for? {a,A,i,I,o,O} */
{
	int	wasdot;
	long	reps;

	DEFAULT(1);

	ChangeText
	{
		/* tweak the insertion point, based on command key */
		switch (key)
		{
		  case 'i':
			break;

		  case 'a':
			pfetch(markline(m));
			if (plen > 0)
			{
				m++;
			}
			break;

		  case 'I':
			m = m_front(m, 1L);
			break;

		  case 'A':
			pfetch(markline(m));
			m = (m & ~(BLKSIZE - 1)) + plen;
			break;

		  case 'O':
			m &= ~(BLKSIZE - 1);
			add(m, "\n");
			break;

		  case 'o':
			m = (m & ~(BLKSIZE - 1)) + BLKSIZE;
			add(m, "\n");
			break;
		}

		/* insert the same text once or more */
		for (reps = cnt, wasdot = doingdot; reps > 0; reps--, doingdot = TRUE)
		{
			m = input(m, m, WHEN_VIINP);
		}

		/* compensate for inaccurate redraw clues from input() */
		if ((key == 'O' || key == 'o') && wasdot)
		{
			redraw(MARK_UNSET);
		}

		doingdot = FALSE;
	}

	return m;
}

/* This starts input mode with some text deleted */
MARK v_change(m, n)
	MARK	m, n;	/* the range of text to change */
{
	int	lnmode;	/* is this a line-mode change? */

	/* swap them if they're in reverse order */
	if (m > n)
	{
		MARK	tmp;
		tmp = m;
		m = n;
		n = tmp;
	}

	/* for line mode, retain the last newline char */
	lnmode = (markidx(m) == 0 && markidx(n) == 0 && m != n);
	if (lnmode)
	{
		n -= BLKSIZE;
		pfetch(markline(n));
		n = (n & ~(BLKSIZE - 1)) + plen;
	}

	ChangeText
	{
		cut(m, n);
		m = input(m, n, WHEN_VIINP);
	}

	/* compensate for inaccurate redraw clues from paste() */
	if (doingdot)
	{
		preredraw = markline(n);
		if (lnmode)
		{
			preredraw++;
			postredraw++;
		}
	}

	return m;
}

/* This function replaces a given number of characters with input */
MARK v_subst(m, cnt)
	MARK	m;	/* where substitutions start */
	long	cnt;	/* number of chars to replace */
{
	DEFAULT(1);

	/* make sure we don't try replacing past EOL */
	pfetch(markline(m));
	if (markidx(m) + cnt > plen)
	{
		cnt = plen - markidx(m);
	}

	/* Go for it! */
	ChangeText
	{
		cut(m, m + cnt);
		m = input(m, m + cnt, WHEN_VIINP);
	}
	return m;
}

/* This calls the ex "join" command to join some lines together */
MARK v_join(m, cnt)
	MARK	m;	/* the first line to be joined */
	long	cnt;	/* number of other lines to join */
{
	MARK	joint;	/* where the lines were joined */

	DEFAULT(1);

	/* figure out where the joint will be */
	pfetch(markline(m));
	joint = (m & ~(BLKSIZE - 1)) + plen;

	/* join the lines */
	cmd_join(m, m + MARK_AT_LINE(cnt), CMD_JOIN, 0, "");
	mustredraw = TRUE;

	/* the cursor should be left at the joint */
	return joint;
}

/* This calls the ex shifter command to shift some lines */
static MARK shift_help(m, n, excmd)
	MARK	m, n;	/* range of lines to shift */
	CMD	excmd;	/* which way do we shift? */
{
	/* make sure our endpoints aren't in reverse order */
	if (m > n)
	{
		MARK tmp;

		tmp = n;
		n = m;
		m = tmp;
	}

	/* linemode? adjust for inclusive endmarks in ex */
	if (markidx(m) == 0 && markidx(n) == 0)
	{
		n -= BLKSIZE;
	}

	cmd_shift(m, n, excmd, 0, "");
	return m;
}

/* This calls the ex "<" command to shift some lines left */
MARK v_lshift(m, n)
	MARK	m, n;	/* range of lines to shift */
{
	return shift_help(m, n, CMD_SHIFTL);
}

/* This calls the ex ">" command to shift some lines right */
MARK v_rshift(m, n)
	MARK	m, n;	/* range of lines to shift */
{
	return shift_help(m, n, CMD_SHIFTR);
}

/* This runs some lines through a filter program */
MARK v_filter(m, n)
	MARK	m, n;	/* range of lines to shift */
{
	char	cmdln[100];	/* a shell command line */

	/* linemode? adjust for inclusive endmarks in ex */
	if (markidx(m) == 0 && markidx(n) == 0)
	{
		n -= BLKSIZE;
	}

	if (vgets('!', cmdln, sizeof(cmdln)) > 0)
	{
		filter(m, n, cmdln);
	}

	redraw(MARK_UNSET, FALSE);
	return m;
}


/* This function runs the ex "file" command to show the file's status */
MARK v_status()
{
	cmd_file(cursor, cursor, CMD_FILE, 0, "");
	return cursor;
}

/* This function switches to the previous file, if possible */
MARK v_switch()
{
	if (!*prevorig)
		msg("No previous file");
	else
	{	strcpy(tmpblk.c, prevorig);
		cmd_edit(cursor, cursor, CMD_EDIT, 0, tmpblk.c);
	}
	return cursor;
}

/* This function does a tag search on a keyword */
/*ARGSUSED*/
MARK v_tag(keyword, m, cnt)
	char	*keyword;
	MARK	m;
	long	cnt;
{
	/* remember the initial change level */
	cnt = changes;

	/* move the cursor to the start of the tag name, where m is */
	cursor = m;

	/* perform the tag search */
	cmd_tag(cursor, cursor, CMD_TAG, 0, keyword);

	return cursor;
}

#ifndef NO_EXTENSIONS
/* This function looks up a keyword by calling the helpprog program */
/*ARGSUSED*/
MARK v_keyword(keyword, m, cnt)
	char	*keyword;
	MARK	m;
	long	cnt;
{
	int	status;
#if	TOS
	char cmdline[130];
#endif

	move(LINES - 1, 0);
	addstr("---------------------------------------------------------\n");
	clrtoeol();
	refresh();
	suspend_curses();

#if	ANY_UNIX

	switch (fork())
	{
	  case -1:						/* error */
		break;

	  case 0:						/* child */
		execl(o_keywordprg, o_keywordprg, keyword, (char *)0);
		exit(2); /* if we get here, the exec failed */

	  default:						/* parent */
		wait(&status);
		if (status > 0)
		{
			write(2, "<<< failed >>>\n", 15);
		}
	}

#endif
#if	MSDOS
	if ((status=spawnlp(P_WAIT, o_keywordprg, o_keywordprg, keyword,
						(char *)0))==-1)
		write(2, "<<< failed >>>\n", 15);
#endif
#if	TOS
	strcpy(cmdline+1, keyword);
	cmdline[0]=strlen(keyword);
	if ((status=Pexec(0, o_keywordprg, cmdline, "\0"))<0)
		write(2, "<<< failed >>>\n", 15);
#endif

	resume_curses(FALSE); /* "resume, but not quietly" */
	redraw(MARK_UNSET, FALSE);
	return m;
}



MARK v_increment(keyword, m, cnt)
	char	*keyword;
	MARK	m;
	long	cnt;
{
	static	sign;
	char	newval[12];
	long	atol();

	DEFAULT(1);

	/* get one more keystroke, unless doingdot */
	if (!doingdot)
	{
		sign = getkey(0);
	}

	/* adjust the number, based on that second keystroke */
	switch (sign)
	{
	  case '+':
	  case '#':
		cnt = atol(keyword) + cnt;
		break;

	  case '-':
		cnt = atol(keyword) - cnt;
		break;

	  case '=':
		break;

	  default:
		return MARK_UNSET;
	}
	sprintf(newval, "%ld", cnt);

	ChangeText
	{
		change(m, m + strlen(keyword), newval);
	}

	return m;
}
#endif


/* This function acts like the EX command "xit" */
/*ARGSUSED*/
MARK v_xit(m, cnt, key)
	MARK	m;	/* ignored */
	long	cnt;	/* ignored */
	int	key;	/* must be a second 'Z' */
{
	/* if second char wasn't 'Z', fail */
	if (key != 'Z')
	{
		return MARK_UNSET;
	}

	/* move the physical cursor to the end of the screen */
	move(LINES - 1, 0);
	clrtoeol();
	refresh();

	/* do the xit command */
	cmd_xit(m, m, CMD_XIT, FALSE, "");

	/* if we're really going to quit, then scroll the screen up 1 line */
	if (mode == MODE_QUIT)
	{
		addch('\n');
		refresh();
	}

	/* regardless of whether we succeeded or failed, return the cursor */
	return m;
}


/* This function undoes changes to a single line, if possible */
MARK v_undoline(m)
	MARK	m;	/* where we hope to undo the change */
{
	if (markline(m) != U_line)
	{
		return MARK_UNSET;
	}

	ChangeText
	{
		changeline(U_line, U_text);
	}
	return m & ~(BLKSIZE - 1);
}
SHAR_EOF
fi
if test -f 'vi.c'
then
	echo shar: "will not over-write existing file 'vi.c'"
else
cat << \SHAR_EOF > 'vi.c'
/* vi.c */

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


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



/* This array describes what each key does */
#define NO_FUNC		(MARK (*)())0
#define NO_ARGS		0
#define CURSOR_COUNT	1
#define CURSOR		2
#define CURSOR_CNT_KEY	3
#define CURSOR_MOVED	4
#define CURSOR_EOL	5
#define ZERO		6
#define DIGIT		7
#define CURSOR_TEXT	8
#define CURSOR_CNT_CMD	9
#define KEYWORD		10
#define NO_FLAGS	0x00
#define	MVMT		0x01	/* this is a movement command */
#define PTMV		0x02	/* this can be *part* of a movement command */
#define FRNT		0x04	/* after move, go to front of line */
#define INCL		0x08	/* include last char when used with c/d/y */
#define LNMD		0x10	/* use line mode of c/d/y */
#define NCOL		0x20	/* this command can't change the column# */
#define NREL		0x40	/* this is "non-relative" -- set the '' mark */
#define SDOT		0x80	/* set the "dot" variables, for the "." cmd */
static struct keystru
{
	MARK	(*func)();	/* the function to run */
	char	args;		/* description of the args needed */
	char	flags;		/* other stuff */
}
	vikeys[] =
{
/* NUL not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ^A  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ^B  page backward	*/	{m_scroll,	CURSOR_CNT_CMD,	FRNT},
/* ^C  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ^D  scroll dn 1/2page*/	{m_scroll,	CURSOR_CNT_CMD,	NCOL},
/* ^E  scroll up	*/	{m_scroll,	CURSOR_CNT_CMD,	NCOL},
/* ^F  page forward	*/	{m_scroll,	CURSOR_CNT_CMD,	FRNT},
/* ^G  show file status	*/	{v_status,	NO_ARGS, 	NO_FLAGS},
/* ^H  move left, like h*/	{m_left,	CURSOR_COUNT,	MVMT},
/* ^I  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ^J  move down	*/	{m_down,	CURSOR_COUNT,	MVMT|LNMD},
/* ^K  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ^L  redraw screen	*/	{v_redraw,	NO_ARGS,	NO_FLAGS},
/* ^M  mv front next ln */	{m_down,	CURSOR_COUNT,	MVMT|FRNT|LNMD},
/* ^N  move down	*/	{m_down,	CURSOR_COUNT,	MVMT|LNMD},
/* ^O  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ^P  not defined	*/	{m_up,		CURSOR_COUNT,	MVMT|LNMD},
/* ^Q  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ^R  redraw screen	*/	{v_redraw,	NO_ARGS,	NO_FLAGS},
/* ^S  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ^T  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ^U  scroll up 1/2page*/	{m_scroll,	CURSOR_CNT_CMD,	NCOL},
/* ^V  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ^W  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ^X  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ^Y  scroll down	*/	{m_scroll,	CURSOR_CNT_CMD,	NCOL},
/* ^Z  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ESC not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ^\  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ^]  keyword is tag	*/	{v_tag,		KEYWORD,	NO_FLAGS},
/* ^^  previous file	*/	{v_switch,	CURSOR,		NO_FLAGS},
/* ^_  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* SPC move right,like l*/	{m_right,	CURSOR_COUNT,	MVMT},
/*  !  run thru filter	*/	{v_filter,	CURSOR_MOVED,	NO_FLAGS},
/*  "  select cut buffer*/	{v_selcut,	CURSOR_CNT_KEY,	PTMV},
#ifndef NO_EXTENSIONS
/*  #  increment number	*/	{v_increment,	KEYWORD,	SDOT},
#else
/*  #  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
#endif
/*  $  move to rear	*/	{m_rear,	CURSOR,		MVMT|INCL},
/*  %  move to match	*/	{m_match,	CURSOR,		MVMT|INCL},
/*  &  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/*  '  move to a mark	*/	{m_tomark,	CURSOR_CNT_KEY,	MVMT|FRNT|NREL|LNMD},
#ifndef NO_SENTENCE
/*  (  mv back sentence	*/	{m_bsentence,	CURSOR_COUNT,	MVMT},
/*  )  mv fwd sentence	*/	{m_fsentence,	CURSOR_COUNT,	MVMT},
#else
/*  (  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/*  )  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
#endif
/*  *  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/*  +  mv front next ln */	{m_down,	CURSOR_COUNT,	MVMT|FRNT|LNMD},
#ifndef NO_CHARSEARCH
/*  ,  reverse [fFtT] cmd*/	{m__ch,		CURSOR_CNT_CMD,	MVMT|INCL},
#else
/*  ,  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
#endif
/*  -  mv front prev ln	*/	{m_up,		CURSOR_COUNT,	MVMT|FRNT|LNMD},
/*  .  special...	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/*  /  forward search	*/	{m_fsrch,	CURSOR_TEXT,	MVMT|NREL},
/*  0  part of count?	*/	{NO_FUNC,	ZERO,		MVMT|PTMV},
/*  1  part of count	*/	{NO_FUNC,	DIGIT,		PTMV},
/*  2  part of count	*/	{NO_FUNC,	DIGIT,		PTMV},
/*  3  part of count	*/	{NO_FUNC,	DIGIT,		PTMV},
/*  4  part of count	*/	{NO_FUNC,	DIGIT,		PTMV},
/*  5  part of count	*/	{NO_FUNC,	DIGIT,		PTMV},
/*  6  part of count	*/	{NO_FUNC,	DIGIT,		PTMV},
/*  7  part of count	*/	{NO_FUNC,	DIGIT,		PTMV},
/*  8  part of count	*/	{NO_FUNC,	DIGIT,		PTMV},
/*  9  part of count	*/	{NO_FUNC,	DIGIT,		PTMV},
/*  :  run single EX cmd*/	{v_1ex,		CURSOR_TEXT,	NO_FLAGS},
#ifndef NO_CHARSEARCH
/*  ;  repeat [fFtT] cmd*/	{m__ch,		CURSOR_CNT_CMD,	MVMT|INCL},
#else
/*  ;  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
#endif
/*  <  shift text left	*/	{v_lshift,	CURSOR_MOVED,	SDOT|FRNT},
/*  =  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/*  >  shift text right	*/	{v_rshift,	CURSOR_MOVED,	SDOT|FRNT},
/*  ?  backward search	*/	{m_bsrch,	CURSOR_TEXT,	MVMT|NREL},
/*  @  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/*  A  append at EOL	*/	{v_insert,	CURSOR_CNT_CMD,	SDOT},
/*  B  move back Word	*/	{m_bWord,	CURSOR_COUNT,	MVMT},
/*  C  change to EOL	*/	{v_change,	CURSOR_EOL,	SDOT},
/*  D  delete to EOL	*/	{v_delete,	CURSOR_EOL,	SDOT},
/*  E  move end of Word	*/	{m_eWord,	CURSOR_COUNT,	MVMT|INCL},
#ifndef NO_CHARSEARCH
/*  F  move bk to char	*/	{m_Fch,		CURSOR_CNT_KEY,	MVMT|INCL},
#else
/*  F  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
#endif
/*  G  move to line #	*/	{m_toline,	CURSOR_COUNT,	MVMT|NREL|LNMD},
/*  H  move to row	*/	{m_row,		CURSOR_CNT_CMD,	MVMT|FRNT},
/*  I  insert at front	*/	{v_insert,	CURSOR_CNT_CMD,	SDOT},
/*  J  join lines	*/	{v_join,	CURSOR_COUNT,	SDOT},
#ifndef NO_EXTENSIONS
/*  K  look up keyword	*/	{v_keyword,	KEYWORD,	NO_FLAGS},
#else
/*  K  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
#endif
/*  L  move to last row	*/	{m_row,		CURSOR_CNT_CMD,	MVMT|FRNT},
/*  M  move to mid row	*/	{m_row,		CURSOR_CNT_CMD,	MVMT|FRNT},
/*  N  reverse prev srch*/	{m_Nsrch,	CURSOR,		MVMT},
/*  O  insert above line*/	{v_insert,	CURSOR_CNT_CMD,	SDOT},
/*  P  paste before	*/	{v_paste,	CURSOR_CNT_CMD,	NO_FLAGS},
/*  Q  quit to EX mode	*/	{v_quit,	NO_ARGS,	NO_FLAGS},
/*  R  overtype		*/	{v_overtype,	CURSOR,		SDOT},
/*  S  change line	*/	{v_change,	CURSOR_MOVED,	SDOT},
#ifndef NO_CHARSEARCH
/*  T  move bk to char	*/	{m_Tch,		CURSOR_CNT_KEY,	MVMT|INCL},
#else
/*  T  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
#endif
/*  U  undo whole line	*/	{v_undoline,	CURSOR,		FRNT},
/*  V  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/*  W  move forward Word*/	{m_fWord,	CURSOR_COUNT,	MVMT},
/*  X  delete to left	*/	{v_Xchar,	CURSOR_COUNT,	SDOT},
/*  Y  yank text	*/	{v_yank,	CURSOR_MOVED,	NO_FLAGS},
/*  Z  save file & exit	*/	{v_xit,		CURSOR_CNT_KEY,	NO_FLAGS},
/*  [  move back section*/	{m_bsection,	CURSOR_CNT_KEY,	MVMT|LNMD|NREL},
/*  \  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/*  ]  move fwd section */	{m_fsection,	CURSOR_CNT_KEY,	MVMT|LNMD|NREL},
/*  ^  move to front	*/	{m_front,	CURSOR,		MVMT},
/*  _  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/*  `  move to mark	*/	{m_tomark,	CURSOR_CNT_KEY,	MVMT|NREL},
/*  a  append at cursor	*/	{v_insert,	CURSOR_CNT_CMD,	SDOT},
/*  b  move back word	*/	{m_bword,	CURSOR_COUNT,	MVMT},
/*  c  change text	*/	{v_change,	CURSOR_MOVED,	SDOT},
/*  d  delete op	*/	{v_delete,	CURSOR_MOVED,	SDOT},
/*  e  move end word	*/	{m_eword,	CURSOR_COUNT,	MVMT|INCL},
#ifndef NO_CHARSEARCH
/*  f  move fwd for char*/	{m_fch,		CURSOR_CNT_KEY,	MVMT|INCL},
#else
/*  f  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
#endif
/*  g  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/*  h  move left	*/	{m_left,	CURSOR_COUNT,	MVMT},
/*  i  insert at cursor	*/	{v_insert,	CURSOR_CNT_CMD,	SDOT},
/*  j  move down	*/	{m_down,	CURSOR_COUNT,	MVMT|NCOL|LNMD},
/*  k  move up		*/	{m_up,		CURSOR_COUNT,	MVMT|NCOL|LNMD},
/*  l  move right	*/	{m_right,	CURSOR_COUNT,	MVMT},
/*  m  define a mark	*/	{v_mark,	CURSOR_CNT_KEY,	NO_FLAGS},
/*  n  repeat prev srch	*/	{m_nsrch,	CURSOR, 	MVMT},
/*  o  insert below line*/	{v_insert,	CURSOR_CNT_CMD,	SDOT},
/*  p  paste after	*/	{v_paste,	CURSOR_CNT_CMD,	NO_FLAGS},
/*  q  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/*  r  replace chars	*/	{v_replace,	CURSOR_CNT_KEY,	SDOT},
/*  s  subst N chars	*/	{v_subst,	CURSOR_COUNT,	SDOT},
#ifndef NO_CHARSEARCH
/*  t  move fwd to char	*/	{m_tch,		CURSOR_CNT_KEY,	MVMT|INCL},
#else
/*  t  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
#endif
/*  u  undo		*/	{v_undo,	CURSOR,		NO_FLAGS},
/*  v  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/*  w  move fwd word	*/	{m_fword,	CURSOR_COUNT,	MVMT},
/*  x  delete character	*/	{v_xchar,	CURSOR_COUNT,	SDOT},
/*  y  yank text	*/	{v_yank,	CURSOR_MOVED,	NO_FLAGS},
/*  z  adjust scrn row	*/	{m_z, 		CURSOR_CNT_KEY,	NCOL},
/*  {  back paragraph	*/	{m_bparagraph,	CURSOR_COUNT,	MVMT|LNMD},
/*  |  move to column	*/	{m_tocol,	CURSOR_COUNT,	NREL},
/*  }  fwd paragraph	*/	{m_fparagraph,	CURSOR_COUNT,	MVMT|LNMD},
/*  ~  upper/lowercase	*/	{v_ulcase,	CURSOR,		SDOT},
/* DEL not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
};



vi()
{
	register int		key;	/* keystroke from user */
	long			count;	/* numeric argument to some functions */
	register struct keystru	*keyptr;/* pointer to vikeys[] element */
	MARK			tcurs;	/* temporary cursor */
	int			prevkey;/* previous key, if d/c/y/</>/! */
	MARK			range;	/* start of range for d/c/y/</>/! */
	char			text[100];
	int			dotkey;	/* last "key" of a change */
	int			dotpkey;/* last "prevkey" of a change */
	int			dotkey2;/* last extra "getkey()" of a change */
	int			dotcnt;	/* last "count" of a change */
	int			firstkey;
	register int		i;

	/* tell the redraw() function to start from scratch */
	redraw(MARK_UNSET, FALSE);
	msg((char *)0);

#ifdef lint
	/* lint says that "range" might be used before it is set.  This
	 * can't really happen due to the way "range" and "prevkey" are used,
	 * but lint doesn't know that.  This line is here ONLY to keep lint
	 * happy.
	 */
	range = 0L;
#endif

	/* safeguard against '.' with no previous command */
	dotkey = 0;

#ifndef NO_EXTENSIONS
	/* go immediately into insert mode, if ":set inputmode" */
	firstkey = 0;
	if (*o_inputmode)
	{
		firstkey = 'i';
	}
#endif

	/* Repeatedly handle VI commands */
	for (count = 0, prevkey = '\0'; mode == MODE_VI; )
	{
		/* if we've moved off the undoable line, then we can't undo it at all */
		if (markline(cursor) != U_line)
		{
			U_line = 0L;
		}

		/* report any changes from the previous command */
		if (rptlines >= *o_report)
		{
			redraw(cursor, FALSE);
			msg("%ld lines %s", rptlines, rptlabel);
		}
		rptlines = 0L;

		/* get the next command key.  It must be ASCII */
		if (firstkey)
		{
			key = firstkey;
			firstkey = 0;
		}
		else
		{
			do
			{
				key = getkey(WHEN_VICMD);
			} while (key < 0 || key > 127);
		}

		/* change cw and cW commands to ce and cE, respectively */
		/* (Why?  because the real vi does it that way!) */
		if (prevkey == 'c')
		{
			if (key == 'w')
				key = 'e';
			else if (key == 'W')
				key = 'E';

			/* wouldn't work right at the end of a word unless we
			 * backspace one character before doing the move.  This
			 * will fix most cases.
			 */
			if (markidx(cursor) > 0 && (key == 'e' || key == 'E'))
			{
				cursor--;
			}
		}

		/* look up the structure describing this command */
		keyptr = &vikeys[key];

		/* if we're in the middle of a d/c/y/</>/! command, reject
		 * anything but movement or a doubled version like "dd".
		 */
		if (prevkey && key != prevkey && !(keyptr->flags & (MVMT|PTMV)))
		{
			beep();
			prevkey = 0;
			count = 0;
			continue;
		}

		/* set the "dot" variables, if we're supposed to */
		if ((keyptr->flags & SDOT)
		 || (prevkey && vikeys[prevkey].flags & SDOT))
		{
			dotkey = key;
			dotpkey = prevkey;
			dotkey2 = '\0';
			dotcnt = count;

			/* remember the line before any changes are made */
			if (U_line != markline(cursor))
			{
				U_line = markline(cursor);
				strcpy(U_text, fetchline(U_line));
			}
		}

		/* if this is "." then set other vars from the "dot" vars */
		if (key == '.')
		{
			key = dotkey;
			keyptr = &vikeys[key];
			prevkey = dotpkey;
			if (prevkey)
			{
				range = cursor;
			}
			if (count == 0)
			{
				count = dotcnt;
			}
			doingdot = TRUE;

			/* remember the line before any changes are made */
			if (U_line != markline(cursor))
			{
				U_line = markline(cursor);
				strcpy(U_text, fetchline(U_line));
			}
		}
		else
		{
			doingdot = FALSE;
		}

		/* process the key as a command */
		tcurs = cursor;
		switch (keyptr->args)
		{
		  case ZERO:
			if (count == 0)
			{
				tcurs = cursor & ~(BLKSIZE - 1);
				break;
			}
			/* else fall through & treat like other digits... */

		  case DIGIT:
			count = count * 10 + key - '0';
			break;

		  case KEYWORD:
			/* if not on a keyword, fail */
			pfetch(markline(cursor));
			key = markidx(cursor);
			if (isascii(ptext[key])
				&& !isalnum(ptext[key]) && ptext[key] != '_')
			{
				tcurs = MARK_UNSET;
				break;
			}

			/* find the start of the keyword */
			while (key > 0 && (!isascii(ptext[key-1]) ||
			isalnum(ptext[key - 1]) || ptext[key - 1] == '_'))
			{
				key--;
			}
			tcurs = (cursor & ~(BLKSIZE - 1)) + key;

			/* copy it into a buffer, and NUL-terminate it */
			i = 0;
			do
			{
				text[i++] = ptext[key++];
			} while (!isascii(ptext[key]) || isalnum(ptext[key]) || ptext[key] == '_');
			text[i] = '\0';

			/* call the function */
			tcurs = (*keyptr->func)(text, tcurs, count);
			count = 0L;
			break;

		  case NO_ARGS:
			if (keyptr->func)
			{
				(*keyptr->func)();
			}
			else
			{
				beep();
			}
			count = 0L;
			break;
	
		  case CURSOR_COUNT:
			tcurs = (*keyptr->func)(cursor, count);
			count = 0L;
			break;
	
		  case CURSOR:
			tcurs = (*keyptr->func)(cursor);
			count = 0L;
			break;

		  case CURSOR_CNT_KEY:
			if (doingdot)
			{
				tcurs = (*keyptr->func)(cursor, count, dotkey2);
			}
			else if (keyptr->flags & SDOT
			 || (prevkey && vikeys[prevkey].flags & SDOT))
			{
				dotkey2 = getkey(0);
				tcurs = (*keyptr->func)(cursor, count, dotkey2);
			}
			else
			{
				tcurs = (*keyptr->func)(cursor, count, getkey(0));
			}
			count = 0L;
			break;
	
		  case CURSOR_MOVED:
			/* uppercase keys always act like doubled */
			if (isascii(key) && isupper(key))
			{
				prevkey = key;
				range = cursor;
			}

			if (prevkey)
			{
				/* doubling up a command, use complete lines */
				range &= ~(BLKSIZE - 1);
				if (count)
				{
					tcurs = range + MARK_AT_LINE(count);
					count = 0;
				}
				else
				{
					tcurs = range + BLKSIZE;
				}
			}
			else
			{
				prevkey = key;
				range = cursor;
				key = -1; /* so we don't think we doubled yet */
			}
			break;

		  case CURSOR_EOL:
			prevkey = key;
			/* a zero-length line needs special treatment */
			pfetch(markline(cursor));
			if (plen == 0)
			{
				/* act on a zero-length section of text */
				range = tcurs = cursor;
				key = ' ';
			}
			else
			{
				/* act like CURSOR_MOVED with '$' movement */
				range = cursor;
				tcurs = m_rear(cursor, 1L);
				key = '$';
			}
			count = 0L;
			keyptr = &vikeys[key];
			break;

		  case CURSOR_TEXT:
		  	do
		  	{	
				if (vgets(key, text, sizeof text) >= 0)
				{
					/* reassure user that <CR> was hit */
					qaddch('\r');
					refresh();

					/* call the function with the text */
					tcurs = (*keyptr->func)(cursor, text);
				}
				else
					mode = MODE_VI;
			} while (mode == MODE_COLON);
			count = 0L;
			break;

		  case CURSOR_CNT_CMD:
			tcurs = (*keyptr->func)(cursor, count, key);
			count = 0L;
			break;
		}

		/* if that command took us out of vi mode, then exit the loop
		 * NOW, without tweaking the cursor or anything.  This is very
		 * important when mode == MODE_QUIT.
		 */
		if (mode != MODE_VI)
		{
			break;
		}

		/* now move the cursor, as appropriate */
		if (prevkey && markline(tcurs) > nlines)
		{
			/* destination for operator may be nlines + 1 */
			cursor = MARK_AT_LINE(nlines + 1);
		}
		else if (keyptr->args == CURSOR_MOVED)
		{
			/* the < and > keys have FRNT,
			 * but it shouldn't be applied yet
			 */
			cursor = adjmove(cursor, tcurs, 0);
		}
		else
		{
			cursor = adjmove(cursor, tcurs, keyptr->flags);
		}

		/* was that the end of a d/c/y/</>/! command? */
		if (prevkey && (prevkey == key || (keyptr->flags & MVMT)))
		{
			/* if the movement command failed, cancel operation */
			if (tcurs == MARK_UNSET)
			{
				prevkey = 0;
				count = 0;
				continue;
			}

			/* make sure range=front and tcurs=rear */
			if (cursor < range)
			{
				tcurs = range;
				range = cursor;
			}
			else
			{
				tcurs = cursor;
			}

			/* adjust for line mode */
			if (keyptr->flags & LNMD)
			{
				range &= ~(BLKSIZE - 1);
				tcurs &= ~(BLKSIZE - 1);
				tcurs += BLKSIZE;
			}

			/* adjust for inclusion of last char */
			if (keyptr->flags & INCL)
			{
				tcurs++;
			}

			/* temporarily move the cursor to "range" so that
			 * beforedo() remembers the cursor's real location.
			 * This is important if the user later does undo()
			 */
			cursor = range;

			/* run the function */
			tcurs = (*vikeys[prevkey].func)(range, tcurs);
			cursor = adjmove(cursor, tcurs, vikeys[prevkey].flags);

			/* cleanup */
			prevkey = 0;
		}
	}
}

/* This function adjusts the MARK value that they return; here we make sure
 * it isn't past the end of the line, and that the column hasn't been
 * *accidentally* changed.
 */
MARK adjmove(old, new, flags)
	MARK		old;	/* the cursor position before the command */
	register MARK	new;	/* the cursor position after the command */
	int		flags;	/* various flags regarding cursor mvmt */
{
	static int	colno;	/* the column number that we want */
	register char	*text;	/* used to scan through the line's text */
	register int	i;

	/* if the command failed, bag it! */
	if (new == MARK_UNSET)
	{
		beep();
		return old;
	}

	/* if this is a non-relative movement, set the '' mark */
	if (flags & NREL)
	{
		mark[26] = old;
	}

	/* make sure it isn't past the end of the file */
	if (markline(new) < 1)
	{
		new = MARK_FIRST;
	}
	else if (markline(new) > nlines)
	{
		new = MARK_LAST;
	}

	/* fetch the new line */
	pfetch(markline(new));

	/* move to the front, if we're supposed to */
	if (flags & FRNT)
	{
		new = m_front(new, 1L);
	}

	/* change the column#, or change the mark to suit the column# */
	if (!(flags & NCOL))
	{
		/* change the column# */
		i = markidx(new);
		if (i == BLKSIZE - 1)
		{
			new &= ~(BLKSIZE - 1);
			if (plen > 0)
			{
				new += plen - 1;
			}
			colno = BLKSIZE * 8; /* one heck of a big colno */
		}
		else if (plen > 0)
		{
			if (i >= plen)
			{
				new = (new & ~(BLKSIZE - 1)) + plen - 1;
			}
			colno = idx2col(new, ptext, FALSE);
		}
		else
		{
			new &= ~(BLKSIZE - 1);
			colno = 0;
		}
	}
	else
	{
		/* adjust the mark to get as close as possible to column# */
		for (i = 0, text = ptext; i <= colno && *text; text++)
		{
			if (*text == '\t' && !*o_list)
			{
				i += *o_tabstop - (i % *o_tabstop);
			}
			else if (UCHAR(*text) < ' ' || *text == 127)
			{
				i += 2;
			}
#ifndef NO_CHARATTR
			else if (*o_charattr && text[0] == '\\' && text[1] == 'f' && text[2])
			{
				text += 2; /* plus one more in "for()" stmt */
			}
#endif
			else
			{
				i++;
			}
		}
		if (text > ptext)
		{
			text--;
		}
		new = (new & ~(BLKSIZE - 1)) + (int)(text - ptext);
	}

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

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


/* This is the header file for my version of vi. */

#define VERSION "ELVIS 1.3, by Steve Kirkendall"
#define COPYING	"This version of ELVIS is freely redistributable."

#include <errno.h>
extern int errno;
#if TOS
#define ENOENT (-AEFILNF)
#endif

#if TOS
# include <types.h>
# define O_RDONLY	0
# define O_WRONLY	1
# define O_RDWR		2
#else
# include <sys/types.h>
# include <fcntl.h>
#endif

#ifndef O_BINARY
# define O_BINARY	0
#endif

#include "curses.h"

/*------------------------------------------------------------------------*/
/* Miscellaneous constants.						  */

#define INFINITY	2000000001L	/* a very large integer */
#if MSDOS
# define MAXMAPS	50		/* then we have lots of specials */
#else
# define MAXMAPS	20		/* number of :map keys */
#endif
#define LONGKEY		10		/* longest possible raw :map key */
#define MAXDIGS		30		/* number of :digraph combos */
#define MAXRCLEN	1000		/* longest possible .exrc file */

/*------------------------------------------------------------------------*/
/* These describe how temporary files are divided into blocks             */

#define BLKSIZE	1024		/* size of blocks */
#define MAXBLKS	(BLKSIZE / sizeof(unsigned short))
typedef union
{
	char		c[BLKSIZE];	/* for text blocks */
	unsigned short	n[MAXBLKS];	/* for the header block */
}
	BLK;

/*------------------------------------------------------------------------*/
/* These are used manipulate BLK buffers.                                 */

extern BLK	hdr;		/* buffer for the header block */
extern BLK	blkbuf[2];	/* buffers for text blocks */
extern BLK	*blkget();	/* given index into hdr.c[], reads block */
extern BLK	*blkadd();	/* inserts a new block into hdr.c[] */

/*------------------------------------------------------------------------*/
/* These are used to keep track of various flags                          */
extern struct _viflags
{
	short	file;		/* file flags */
}
	viflags;

/* file flags */
#define NEWFILE		0x0001	/* the file was just created */
#define READONLY	0x0002	/* the file is read-only */
#define HADNUL		0x0004	/* the file contained NUL characters */
#define MODIFIED	0x0008	/* the file has been modified */
#define NOFILE		0x0010	/* no name is known for the current text */
#define ADDEDNL		0x0020	/* newlines were added to the file */

/* macros used to set/clear/test flags */
#define setflag(x,y)	viflags.x |= y
#define clrflag(x,y)	viflags.x &= ~y
#define tstflag(x,y)	(viflags.x & y)
#define initflags()	viflags.file = 0;

/* The options */
extern char	o_autoindent[1];
extern char	o_autowrite[1];
#ifndef NO_CHARATTR
extern char	o_charattr[1];
#endif
extern char	o_columns[3];
extern char	o_directory[30];
extern char	o_errorbells[1];
extern char	o_exrefresh[1];
#ifndef NO_SENTENCE
extern char	o_hideformat[1];
#endif
extern char	o_ignorecase[1];
#ifndef NO_EXTENSIONS
extern char	o_inputmode[1];
#endif
extern char	o_keytime[3];
extern char	o_keywordprg[80];
extern char	o_lines[3];
extern char	o_list[1];
#ifndef NO_MAGIC
extern char	o_magic[1];
#endif
#ifndef NO_SENTENCE
extern char	o_paragraphs[30];
#endif
#if MSDOS
extern char	o_pcbios[1];
#endif
extern char	o_readonly[1];
extern char	o_report[3];
extern char	o_scroll[3];
#ifndef NO_SENTENCE
extern char	o_sections[30];
#endif
extern char	o_shell[60];
extern char	o_showmode[1];
extern char	o_shiftwidth[3];
extern char	o_sidescroll[3];
extern char	o_sync[1];
extern char	o_tabstop[3];
extern char	o_term[30];
extern char	o_vbell[1];
extern char	o_warn[1];
extern char	o_wrapmargin[3];
extern char	o_wrapscan[1];

/*------------------------------------------------------------------------*/
/* These help support the single-line multi-change "undo" -- shift-U      */

extern char	U_text[BLKSIZE];
extern long	U_line;

/*------------------------------------------------------------------------*/
/* These are used to refer to places in the text 			  */

typedef long	MARK;
#define markline(x)	(long)((x) / BLKSIZE)
#define markidx(x)	(int)((x) & (BLKSIZE - 1))
#define MARK_UNSET	((MARK)0)
#define MARK_FIRST	((MARK)BLKSIZE)
#define MARK_LAST	((MARK)(nlines * BLKSIZE))
#define MARK_AT_LINE(x)	((MARK)((x) * BLKSIZE))

#define NMARKS	28
extern MARK	mark[NMARKS];	/* marks 'a through 'z, plus mark '' */
extern MARK	cursor;		/* mark where line is */

/*------------------------------------------------------------------------*/
/* These are used to keep track of the current & previous files.	  */

extern long	origtime;	/* modification date&time of the current file */
extern char	origname[256];	/* name of the current file */
extern char	prevorig[256];	/* name of the preceding file */
extern long	prevline;	/* line number from preceding file */

/*------------------------------------------------------------------------*/
/* misc housekeeping variables & functions				  */

extern int	tmpfd;		/* fd used to access the tmp file */
extern long	lnum[MAXBLKS];	/* last line# of each block */
extern long	nlines;		/* number of lines in the file */
extern char	args[BLKSIZE];	/* file names given on the command line */
extern int	argno;		/* the current element of args[] */
extern int	nargs;		/* number of filenames in args */
extern long	changes;	/* counts changes, to prohibit short-cuts */
extern int	mustredraw;	/* boolean: force total redraw of screen? */
extern long	redrawafter;	/* line# of first line to redraw */
extern long	preredraw;	/* line# of last line changed, before change */
extern long	postredraw;	/* line# of last line changed, after change */
extern BLK	tmpblk;		/* a block used to accumulate changes */
extern long	topline;	/* file line number of top line */
extern int	leftcol;	/* column number of left col */
#define		botline	 (topline + LINES - 2)
#define		rightcol (leftcol + COLS - 1)
extern int	physcol;	/* physical column number that cursor is on */
extern int	physrow;	/* physical row number that cursor is on */
extern int	exwrote;	/* used to detect verbose ex commands */
extern int	doingdot;	/* boolean: are we doing the "." command? */
extern long	rptlines;	/* number of lines affected by a command */
extern char	*rptlabel;	/* description of how lines were affected */
extern char	*fetchline();	/* read a given line from tmp file */
extern char	*parseptrn();	/* isolate a regexp in a line */
extern MARK	paste();	/* paste from cut buffer to a given point */
extern char	*wildcard();	/* expand wildcards in filenames */
extern MARK	input();	/* inserts characters from keyboard */
extern char	*linespec();	/* finds the end of a /regexp/ string */
#define		ctrl(ch) ((ch)&037)
#ifndef NO_RECYCLE
extern long	allocate();	/* allocate a free block of the tmp file */
#endif
extern int	trapint();	/* trap handler for SIGINT */

/*------------------------------------------------------------------------*/
/* macros that are used as control structures                             */

#define BeforeAfter(before, after) for((before),bavar=1;bavar;(after),bavar=0)
#define ChangeText	BeforeAfter(beforedo(FALSE),afterdo())

extern int	bavar;		/* used only in BeforeAfter macros */

/*------------------------------------------------------------------------*/
/* These are the movement commands.  Each accepts a mark for the starting */
/* location & number and returns a mark for the destination.		  */

extern MARK	m_up();			/* k */
extern MARK	m_down();		/* j */
extern MARK	m_right();		/* h */
extern MARK	m_left();		/* l */
extern MARK	m_toline();		/* G */
extern MARK	m_tocol();		/* | */
extern MARK	m_front();		/* ^ */
extern MARK	m_rear();		/* $ */
extern MARK	m_fword();		/* w */
extern MARK	m_bword();		/* b */
extern MARK	m_eword();		/* e */
extern MARK	m_fWord();		/* W */
extern MARK	m_bWord();		/* B */
extern MARK	m_eWord();		/* E */
extern MARK	m_fparagraph();	/* } */
extern MARK	m_bparagraph();	/* { */
extern MARK	m_fsection();		/* ]] */
extern MARK	m_bsection();		/* [[ */
extern MARK	m_match();		/* % */
#ifndef NO_SENTENCE
extern MARK	m_fsentence();	/* ) */
extern MARK	m_bsentence();	/* ( */
#endif
extern MARK	m_tomark();		/* 'm */
extern MARK	m_nsrch();		/* n */
extern MARK	m_Nsrch();		/* N */
extern MARK	m_fsrch();		/* /regexp */
extern MARK	m_bsrch();		/* ?regexp */
extern MARK	m__ch();		/* ; , */
extern MARK	m_fch();		/* f */
extern MARK	m_tch();		/* t */
extern MARK	m_Fch();		/* F */
extern MARK	m_Tch();		/* T */
extern MARK	m_row();		/* H L M */
extern MARK	m_z();		/* z */
extern MARK	m_scroll();		/* ^B ^F ^E ^Y ^U ^D */

/* Some stuff that is used by movement functions... */

extern MARK	adjmove();		/* a helper fn, used by move fns */

/* This macro is used to set the default value of cnt */
#define DEFAULT(val)	if (cnt < 1) cnt = (val)

/* These are used to minimize calls to fetchline() */
extern int	plen;	/* length of the line */
extern long	pline;	/* line number that len refers to */
extern long	pchgs;	/* "changes" level that len refers to */
extern char	*ptext;	/* text of previous line, if valid */
extern void	pfetch();

/* This is used to build a MARK that corresponds to a specific point in the
 * line that was most recently pfetch'ed.
 */
#define buildmark(text)	(MARK)(BLKSIZE * pline + (int)((text) - ptext))


/*------------------------------------------------------------------------*/
/* These are used to handle EX commands.				  */

#define  CMD_NULL	0	/* NOT A VALID COMMAND */
#define  CMD_ABBR	1	/* "define an abbreviation" */
#define  CMD_ARGS	2	/* "show me the args" */
#define  CMD_APPEND	3	/* "insert lines after this line" */
#define  CMD_BANG	4	/* "run a single shell command" */
#define  CMD_COPY	5	/* "copy the selected text to a given place" */
#define  CMD_CD		6	/* "change directories" */
#define  CMD_CHANGE	7	/* "change some lines" */
#define  CMD_DELETE	8	/* "delete the selected text" */
#define  CMD_DIGRAPH	9	/* "add a digraph, or display them all" */
#define  CMD_EDIT	10	/* "switch to a different file" */
#define  CMD_FILE	11	/* "show the file's status" */
#define  CMD_GLOBAL	12	/* "globally search & do a command" */
#define  CMD_INSERT	13	/* "insert lines before the current line" */
#define  CMD_JOIN	14	/* "join the selected line & the one after" */
#define  CMD_LIST	15	/* "print lines, making control chars visible" */
#define  CMD_MAP	16	/* "adjust the keyboard map" */
#define  CMD_MARK	17	/* "mark this line" */
#define  CMD_MKEXRC	18	/* "make a .exrc file" */
#define  CMD_MOVE	19	/* "move the selected text to a given place" */
#define  CMD_NEXT	20	/* "switch to next file in args" */
#define  CMD_PRESERVE	21	/* "act as though vi crashed" */
#define  CMD_PREVIOUS	22	/* "switch to the previous file in args" */
#define  CMD_PRINT	23	/* "print the selected text" */
#define  CMD_PUT	24	/* "insert any cut lines before this line" */
#define  CMD_QUIT	25	/* "quit without writing the file" */
#define  CMD_READ	26	/* "append the given file after this line */
#define  CMD_RECOVER	27	/* "recover file after vi crashes" - USE -r FLAG */
#define  CMD_REWIND	28	/* "rewind to first file" */
#define  CMD_SET	29	/* "set a variable's value" */
#define  CMD_SHELL	30	/* "run some lines through a command" */
#define  CMD_SHIFTL	31	/* "shift lines left" */
#define  CMD_SHIFTR	32	/* "shift lines right" */
#define  CMD_SOURCE	33	/* "interpret a file's contents as ex commands" */
#define  CMD_STOP	34	/* same as CMD_SUSPEND */
#define  CMD_SUSPEND	35	/* "suspend the vi session" */
#define  CMD_SUBSTITUTE	36	/* "substitute text in this line" */
#define  CMD_TR		37	/* "transliterate chars in the selected lines" */
#define  CMD_TAG	38	/* "go to a particular tag" */
#define  CMD_UNABBR	39	/* "remove an abbreviation definition" */
#define  CMD_UNDO	40	/* "undo the previous command" */
#define  CMD_UNMAP	41	/* "remove a key sequence map */
#define  CMD_VERSION	42	/* "describe which version this is" */
#define  CMD_VGLOBAL	43	/* "apply a cmd to lines NOT containing an RE" */
#define  CMD_VISUAL	44	/* "go into visual mode" */
#define  CMD_WQUIT	45	/* "write this file out (any case) & quit" */
#define  CMD_WRITE	46	/* "write the selected(?) text to a given file" */
#define  CMD_XIT	47	/* "write this file out (if modified) & quit" */
#define  CMD_YANK	48	/* "copy the selected text into the cut buffer" */
#ifdef DEBUG
# define CMD_DEBUG	49	/* access to internal data structures */
# define CMD_VALIDATE	50	/* check for internal consistency */
#endif
typedef int CMD;

extern		ex();
extern		vi();
extern		doexcmd();

extern void	cmd_append();
extern void	cmd_args();
extern void	cmd_cd();
extern void	cmd_delete();
#ifndef NO_DIGRAPH
extern void	cmd_digraph();
#endif
extern void	cmd_edit();
extern void	cmd_file();
extern void	cmd_global();
extern void	cmd_join();
extern void	cmd_mark();
extern void	cmd_list();
extern void	cmd_map();
#ifndef NO_EXTENSIONS
extern void	cmd_mkexrc();
#endif
extern void	cmd_next();
extern void	cmd_print();
extern void	cmd_put();
extern void	cmd_quit();
extern void	cmd_read();
extern void	cmd_rewind();
extern void	cmd_set();
extern void	cmd_shell();
extern void	cmd_shift();
extern void	cmd_source();
extern void	cmd_substitute();
extern void	cmd_tag();
extern void	cmd_undo();
extern void	cmd_version();
extern void	cmd_visual();
extern void	cmd_write();
extern void	cmd_xit();
extern void	cmd_move();
#ifdef DEBUG
extern void	cmd_debug();
extern void	cmd_validate();
#endif

/*----------------------------------------------------------------------*/
/* These are used to handle VI commands 				*/

extern MARK	v_1ex();	/* : */
extern MARK	v_mark();	/* m */
extern MARK	v_quit();	/* Q */
extern MARK	v_redraw();	/* ^L ^R */
extern MARK	v_ulcase();	/* ~ */
extern MARK	v_undo();	/* u */
extern MARK	v_xchar();	/* x */
extern MARK	v_Xchar();	/* X */
extern MARK	v_replace();	/* r */
extern MARK	v_overtype();	/* R */
extern MARK	v_selcut();	/* " */
extern MARK	v_paste();	/* p P */
extern MARK	v_yank();	/* y Y */
extern MARK	v_delete();	/* d D */
extern MARK	v_join();	/* J */
extern MARK	v_insert();	/* a A i I o O */
extern MARK	v_change();	/* c C */
extern MARK	v_subst();	/* s */
extern MARK	v_lshift();	/* < */
extern MARK	v_rshift();	/* > */
extern MARK	v_filter();	/* ! */
extern MARK	v_status();	/* ^G */
extern MARK	v_switch();	/* ^^ */
extern MARK	v_tag();	/* ^] */
#ifndef NO_EXTENSIONS
extern MARK	v_keyword();	/* ^K */
extern MARK	v_increment();	/* * */
#endif
extern MARK	v_xit();	/* ZZ */
extern MARK	v_undoline();	/* U */

/*----------------------------------------------------------------------*/
/* These describe what mode we're in */

#define MODE_EX		1	/* executing ex commands */
#define	MODE_VI		2	/* executing vi commands */
#define	MODE_COLON	3	/* executing an ex command from vi mode */
#define	MODE_QUIT	4
extern int	mode;

#define WHEN_VICMD	1	/* getkey: we're reading a VI command */
#define WHEN_VIINP	2	/* getkey: we're in VI's INPUT mode */
#define WHEN_VIREP	4	/* getkey: we're in VI's REPLACE mode */
#define WHEN_EX		8	/* getkey: we're in EX mode */
#define WHEN_INMV	256	/* in input mode, interpret the key in VICMD mode */
SHAR_EOF
fi
if test -f 'ctags.c'
then
	echo shar: "will not over-write existing file 'ctags.c'"
else
cat << \SHAR_EOF > 'ctags.c'
/* ctags.c */

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


/* This file contains the complete source to the ctags program. */

/* Special abilities:
 * Can also make a "refs" file for use by the "ref" program.
 */

/* Limitations:
 * This version of ctags always writes its output to the file "tags".
 * It assumes that every command-line argument (but "-r") is a C source file.
 * It does not sort the list of tags.
 * It does not recognize duplicate definitions.
 * It does not try to handle "static" functions in a clever way.
 * It probably won't scan ANSI-C source code very well.
 */

/* Implementation:
 * Lines are scanned one-at-a-time.
 * The context of lines is tracked via a finite state machine.
 * Contexts are:
 *	EXPECTFN - we're looking for a function name.
 *	ARGS	 - between function name and its opening {
 *	BODY	 - we found a function name, skip to end of body.
 *
 * Function tags are referenced by a search string, so that lines may be
 * inserted or deleted without mucking up the tag search.
 *
 * Macro tags are referenced by their line number, because 1) they usually
 * occur near the top of a file, so their line# won't change much; 2)  They
 * often contain characters that are hard to search for; and 3)  Their #define
 * line is likely to be altered.
 *
 * Each line of the resulting "tags" file describes one tag.  Lines begin with
 * the tag name, then a tab, then the file name, then a tab, and then either
 * a line number or a slash-delimited search string.
 */

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

#define REFS	"refs"

#define NUMFMT	"%.*s\t%s\t%ld\n"
#define SRCHFMT	"%.*s\t%s\t/^%s$/\n"
#define MAINFMT	"M%.*s\t%s\t/^%s$/\n"

#ifdef VERBOSE
# define SAY(x)	fprintf(stderr, "%s\n", x);
#else
# define SAY(x)
#endif

#define EXPECTFN 1
#define	ARGS	 2
#define BODY	 3

extern char	*fgets();

char		*progname;	/* argv[0], used for diagnostic output */

main(argc, argv)
	int	argc;
	char	**argv;
{
	FILE	*fp;
	int	i;
	FILE	*refs;	/* used to write to the refs file */

#if	MSDOS || TOS
	char **wildexpand();
	argv=wildexpand(&argc, argv);
#endif
	/* notice the program name */
	progname = argv[0];

	/* create the "refs" file if first arg is "-r" */
	if (argc > 1 && !strcmp(argv[1], "-r"))
	{
		/* delete the "-r" flag from the args list */
		argc--;
		argv++;

		/* open the "refs" file for writing */
		refs = fopen(REFS, "w");
		if (!refs)
		{
			fprintf(stderr, "%s: could not create \"%s\"\n", progname, REFS);
			exit(2);
		}
	}
	else
	{
		refs = (FILE *)0;
	}

	/* process each file named on the command line, or complain if none */
	if (argc > 1)
	{
		/* redirect stdout to go to the "tags" file */
		if (!freopen("tags", "w", stdout))
		{
			fprintf(stderr, "%s: could not create \"%s\"\n", progname, TAGS);
			exit(2);
		}
	
		for (i = 1; i < argc; i++)
		{
			/* process this named file */
			fp = fopen(argv[i], "r");
			if (!fp)
			{
				fprintf(stderr, "%s: could not read \"%s\"\n", progname, argv[i]);
				continue;
			}
			ctags(fp, argv[i], refs);
			fclose(fp);
		}
#ifdef SORT
		/* This is a hack which will sort the tags list.   It should
		 * on UNIX and Minix.  You may have trouble with csh.   Note
		 * that the tags list only has to be sorted if you intend to
		 * use it with the real vi;  elvis permits unsorted tags.
		 */
		fflush(stdout);
		system("sort tags >_tags$$; mv _tags$$ tags");
#endif
		exit(0);
	}
	else
	{
		fprintf(stderr, "usage: %s *.[ch]\n", progname);
		exit(2);
	}
}


/* this function finds all tags in a given file */
ctags(fp, name, refs)
	FILE	*fp;		/* stream of the file to scan */
	char	*name;		/* name of the file being scanned */
	FILE	*refs;		/* NULL, or where to write refs lines */
{
	int	context;	/* context - either EXPECTFN, ARGS, or BODY */
	long	lnum;		/* line number */
	char	text[1000];	/* a line of text from the file */
	char	*scan;		/* used for searching through text */
	int	len;		/* length of the line */

	/* for each line of the file... */
	for (context = EXPECTFN, lnum = 1; fgets(text, sizeof text, fp); lnum++)
	{
#ifdef VERBOSE
		switch(context)
		{
		  case EXPECTFN:	scan = "EXPECTFN";	break;
		  case ARGS:		scan = "ARGS    ";	break;
		  case BODY:		scan = "BODY    ";	break;
		  default:		scan = "context?";
		}
		fprintf(stderr, "%s:%s", scan, text);
#endif

		/* start of body? */
		if (text[0] == '{')
		{
			context = BODY;
			SAY("Start of BODY");
			continue;
		}

		/* argument line, to be written to "refs" ? */
		if (refs && context == ARGS)
		{
			if (text[0] != '\t')
			{
				putc('\t', refs);
			}
			fputs(text, refs);
			SAY("Argument line");
			continue;
		}

		/* ignore empty or indented lines */
		if (text[0] <= ' ')
		{
			SAY("Empty or indented");
			continue;
		}

		/* end of body? */
		if (text[0] == '}')
		{
			context = EXPECTFN;
			SAY("End of BODY");
			continue;
		}

		/* ignore lines in the body of a function */
		if (context != EXPECTFN)
		{
			SAY("BODY or ARGS");
			continue;
		}

		/* strip the newline */
		len = strlen(text);
		text[--len] = '\0';

		/* a preprocessor line? */
		if (text[0] == '#')
		{
			/* find the preprocessor directive */
			for (scan = &text[1]; isspace(*scan); scan++)
			{
			}

			/* if it's a #define, make a tag out of it */
			if (!strncmp(scan, "define", 6))
			{
				/* find the start of the symbol name */
				for (scan += 6; isspace(*scan); scan++)
				{
				}

				/* find the length of the symbol name */
				for (len = 1;
				     isalnum(scan[len]) || scan[len] == '_';
				     len++)
				{
				}
				printf(NUMFMT, len, scan, name, lnum);
			}
			SAY("Preprocessor line");
			continue;
		}

		/* an extern or static declaration? */
		if (text[len - 1] == ';'
		 || !strncmp(text, "extern", 6)
		 || !strncmp(text, "EXTERN", 6)
		 || !strncmp(text, "static", 6)
		 || !strncmp(text, "PRIVATE", 7))
		{
			SAY("Extern or static");
			continue;
		}

		/* if we get here & the first punctuation other than "*" is
		 * a "(" which is immediately preceded by a name, then
		 * assume the name is that of a function.
		 */
		for (scan = text; *scan; scan++)
		{
			if (ispunct(*scan)
			 && !isspace(*scan) /* in BSD, spaces are punctuation?*/
			 && *scan != '*' && *scan != '_' && *scan != '(')
			{
				SAY("Funny punctuation");
				goto ContinueContinue;
			}

			if (*scan == '(')
			{
				/* permit 0 or 1 spaces between name & '(' */
				if (scan > text && scan[-1] == ' ')
				{
					scan--;
				}

				/* find the start & length of the name */
				for (len = 0, scan--;
				     scan >= text && (isalnum(*scan) || *scan == '_');
				     scan--, len++)
				{
				}
				scan++;

				/* did we find a function? */
				if (len > 0)
				{
					/* found a function! */
					if (len == 4 && !strncmp(scan, "main", 4))
					{
						printf(MAINFMT, strlen(name) - 2, name, name, text);
					}
					printf(SRCHFMT, len, scan, name, text);
					context = ARGS;

					/* add a line to refs, if needed */
					if (refs)
					{
						fputs(text, refs);
						putc('\n', refs);
					}

					goto ContinueContinue;
				}
			}
			else
			{
				SAY("No parenthesis");
			}
		}
		SAY("No punctuation");

ContinueContinue:;
	}
}

#if	MSDOS || TOS
#define		WILDCARD_NO_MAIN
#include	"wildcard.c"
#endif
SHAR_EOF
fi
if test -f 'ref.c'
then
	echo shar: "will not over-write existing file 'ref.c'"
else
cat << \SHAR_EOF > 'ref.c'
/* ref.c */

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


/* This program looks up the declarations of library functions. */

#include <stdio.h>

/* This is the list of files that are searched. */
char *refslist[] = {
	"refs",
	"/usr/src/lib/refs",
	"../lib/refs",
	"/usr/local/lib/refs",
};
#define NREFS	(sizeof refslist / sizeof(char *))

main(argc, argv)
	int	argc;
	char	**argv;
{
	int	i;	/* used to step through the refslist */

	/* make sure our arguments are OK */
	if (argc != 2)
	{
		fprintf(stderr, "usage: %s function_name\n", *argv);
		exit(2);
	}

	/* check for the function in each database */
	for (i = 0; i < NREFS; i++)
	{
		if (lookinfor(refslist[i], argv[1]))
		{
			exit(0);
		}
	}

	fprintf(stderr, "%s: don't know about %s\n", argv[0], argv[1]);
	exit(2);
}


/* This function checks a single file for the function.  Returns 1 if found */
int lookinfor(filename, func)
	char	*filename;	/* name of file to look in */
	char	*func;		/* name of function to look for */
{
	FILE	*fp;	/* stream used to access the database */
	char	linebuf[300];
		/* NOTE: in actual use, the first character of linebuf is */
		/* set to ' ' and then we use all EXCEPT the 1st character */
		/* everywhere in this function.  This is because the func */
		/* which examines the linebuf could, in some circumstances */
		/* examine the character before the used part of linebuf; */
		/* we need to control what happens then.		   */


	/* open the database file */
	fp = fopen(filename, "r");
	if (!fp)
	{
		return 0;
	}

	/* find the desired entry */
	*linebuf = ' ';
	do
	{
		if (!fgets(linebuf + 1, (sizeof linebuf) - 1, fp))
		{
			fclose(fp);
			return 0;
		}
	} while (!desired(linebuf + 1, func));

	/* print it */
	do
	{
		fputs(linebuf + 1, stdout);
	} while (fgets(linebuf + 1, sizeof linebuf, fp) && linebuf[1] == '\t');

	/* cleanup & exit */
	fclose(fp);
	return 1;
}


/* This function checks to see if a given line is the first line of the */
/* entry the user wants to see.  If it is, return non-0 else return 0   */
desired(line, word)
	char	*line;	/* the line buffer */
	char	*word;	/* the string it should contain */
{
	static	wlen = -1;/* length of the "word" variable's value */
	register char *scan;

	/* if this line starts with a tab, it couldn't be desired */
	if (*line == '\t')
	{
		return 0;
	}

	/* if we haven't found word's length yet, do so */
	if (wlen < 0)
	{
		wlen = strlen(word);
	}

	/* search for the word in the line */
	for (scan = line; *scan != '('; scan++)
	{
	}
	while (*--scan == ' ')
	{
	}
	scan -= wlen;
	if (scan < line - 1 || *scan != ' ' && *scan != '\t' && *scan != '*')
	{
		return 0;
	}
	scan++;
	return !strncmp(scan, word, wlen);
}
SHAR_EOF
fi
if test -f 'virec.c'
then
	echo shar: "will not over-write existing file 'virec.c'"
else
cat << \SHAR_EOF > 'virec.c'
/* virec.c */

/* This file contains the file recovery program */

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


#include <stdio.h>
#include "config.h"
#include "vi.h"
#if	TOS
#include <stat.h>
#else
#include <sys/stat.h>
#endif

extern char	*getenv();
struct stat	stbuf;
BLK		hdr;
BLK		text;

/* the name of the directory where tmp files are stored. */
char o_directory[30] = TMPDIR;

char	*progname;

main(argc, argv)
	int	argc;
	char	**argv;
{
	char	*tmp;
#if	MSDOS || TOS
	char **wildexpand();
	argv = wildexpand(&argc, argv);
#endif
	progname = argv[0];
	/* set the o_directory variable */
	if ((tmp = getenv("TMP"))	/* yes, ASSIGNMENT! */
	 || (tmp = getenv("TEMP")))	/* yes, ASSIGNMENT! */
	{
		strcpy(o_directory, tmp);
	}
	if (argc >= 3 && !strcmp(argv[1], "-d"))
	{
		strcpy(o_directory, argv[2]);
		argc -= 2;
		argv += 2;
	}
	/* process the arguments */
	if (argc < 2)
	{
		/* maybe stdin comes from a file? */
		if (fstat(0, &stbuf) < 0 || (S_IFMT & stbuf.st_mode) != S_IFREG)
		{
			fprintf(stderr, "usage: %s [-d tmpdir] lostfile...\n", progname);
		}
		else if (read(0, &hdr, BLKSIZE) != BLKSIZE)
		{
			fprintf(stderr, "couldn't get header\n");
		}
		else
		{
			copytext(0, stdout);
		}
	}
	else
	{
		while (--argc > 0)
		{
			recover(*++argv);
		}
	}
	exit(0);
}


/* This function recovers a single file */
recover(filename)
	char	*filename;
{
	char		tmpname[100];
	int		tmpfd;
	FILE		*fp;
	long		mtime;
	int		i, j;

	/* get the file's status info */
	if (stat(filename, &stbuf) < 0)
	{
		/* if serious error, give up on this file */
		if (errno != ENOENT)
		{
			perror(filename);
			return;
		}

		/* else fake it for a new file */
		stat(".", &stbuf);
		stbuf.st_mode = S_IFREG;
		stbuf.st_mtime = 0L;
	}

	/* find the tmp file */
	sprintf(tmpname, TMPNAME, o_directory, stbuf.st_ino, stbuf.st_dev);
	tmpfd = open(tmpname, O_RDONLY);
	if (tmpfd < 0)
	{
		perror(tmpname);
		return;
	}

	/* make sure the file hasn't been modified more recently */
	mtime = stbuf.st_mtime;
	fstat(tmpfd, &stbuf);
	if (stbuf.st_mtime < mtime)
	{
		printf("\"%s\" has been modified more recently than its recoverable version\n", filename);
		puts("Do you still want to recover it?\n");
		puts("\ty - Yes, discard the current version and recover it.\n");
		puts("\tn - No, discard the recoverable version and keep the current version\n");
		puts("\tq - Quit without doing anything for this file.\n");
		puts("Enter y, n, or q --> ");
		fflush(stdout);
		for (;;)
		{
			switch (getchar())
			{
			  case 'y':
			  case 'Y':
				goto BreakBreak;

			  case 'n':
			  case 'N':
				close(tmpfd);
				unlink(tmpname);
				return;

			  case 'q':
			  case 'Q':
				close(tmpfd);
				return;
			}
		}
BreakBreak:;
	}

	/* make sure this tmp file is intact */
	if (read(tmpfd, &hdr, BLKSIZE) != BLKSIZE)
	{
		fprintf(stderr, "%s: bad header in tmp file\n", filename);
		close(tmpfd);
		unlink(tmpname);
		return;
	}
	for (i = j = 1; i < MAXBLKS && hdr.n[i]; i++)
	{
		if (hdr.n[i] > j)
		{
			j = hdr.n[i];
		}
	}
	lseek(tmpfd, (long)j * (long)BLKSIZE, 0);
	if (read(tmpfd, &text, BLKSIZE) != BLKSIZE)
	{
		fprintf(stderr, "%s: bad data block in tmp file\n", filename);
		close(tmpfd);
		unlink(tmpname);
		return;
	}

	/* open the normal text file for writing */
	fp = fopen(filename, "w");
	if (!fp)
	{
		perror(filename);
		close(tmpfd);
		return;
	}

	/* copy the text */
	copytext(tmpfd, fp);

	/* cleanup */
	close(tmpfd);
	fclose(fp);
	unlink(tmpname);
}


/* This function moves text from the tmp file to the normal file */
copytext(tmpfd, fp)
	int	tmpfd;	/* fd of the tmp file */
	FILE	*fp;	/* the stream to write it to */
{
	int	i;

	/* write the data blocks to the normal text file */
	for (i = 1; i < MAXBLKS && hdr.n[i]; i++)
	{
		lseek(tmpfd, (long)hdr.n[i] * (long)BLKSIZE, 0);
		read(tmpfd, &text, BLKSIZE);
		fputs(text.c, fp);
	}
}

#if	MSDOS || TOS
#define		WILDCARD_NO_MAIN
#include	"wildcard.c"
#endif
SHAR_EOF
fi
if test -f 'wildcard.c'
then
	echo shar: "will not over-write existing file 'wildcard.c'"
else
cat << \SHAR_EOF > 'wildcard.c'
/* wildcard.c */

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

/* this program implements wildcard expansion for elvis/dos. It works
 * like UNIX echo, but uses the dos wildcard conventions
 * (*.* matches all files, * matches files without extension only,
 * filespecs may contain drive letters, wildcards not allowed in directory
 * names).
 *
 * It is also #included into ctags.c, ref.c, ...; in this case,
 * we don't want a main function here.
 */

#include <stdio.h>
#include <ctype.h>
#ifdef	__TURBOC__
#include <dir.h>
#endif
#ifdef	M_I86
#define	findfirst(a,b,c)	_dos_findfirst(a,c,b)
#define	findnext		_dos_findnext
#define	ffblk			find_t
#define	ff_name			name
#include <dos.h>
#endif
#ifdef	M68000
#include <stat.h>
#include <osbind.h>
#define	findfirst(a,b,c)	(Fsetdta(b), (Fsfirst(a,c)))
#define	findnext(x)		(Fsnext())
#define	ff_name	d_fname
#endif
#define	MAXFILES	1000

int pstrcmp();
extern char *calloc();

char *files[MAXFILES];
int nfiles;

#ifndef	WILDCARD_NO_MAIN

main(argc, argv)
	char **argv;
{
	int i;

	for (i=1; i<argc; i++)
		expand(argv[i]);
	if (nfiles)
		printf("%s", files[0]);
	for (i=1; i<nfiles; i++)
	{
		printf(" %s", files[i]);
	}
	putchar('\n');
	return 0;
}

#else
char **wildexpand(argc, argv)
	int *argc;
	char **argv;
{
	int i;
	
	for (i=0; i<*argc; i++)
		expand(argv[i]);
	*argc=nfiles;
	return files;
}	
#endif

expand(name)
	char *name;
{
	char *filespec;
	int wildcard=0;
#ifdef	M68000
	DMABUFFER findbuf;
#else
	struct ffblk findbuf;
#endif
	int err;
	char buf[80];
	int lastn;

	strcpy(buf, name);
	for (filespec=buf; *filespec; filespec++)
		;

	while (--filespec>=buf)
	{	if (*filespec=='?' || *filespec=='*')
			wildcard=1;
		if (*filespec=='/' || *filespec=='\\' || *filespec==':')
			break;
	}
	if (!wildcard)
		addfile(buf);
	else
	{
		lastn=nfiles;
		filespec++;
		if ((err=findfirst(buf, &findbuf, 0))!=0)
			addfile(buf);
		while (!err)
		{
			strcpy(filespec, findbuf.ff_name);
			addfile(buf);
			err=findnext(&findbuf);
		}
		if (lastn!=nfiles)
			qsort(files+lastn, nfiles-lastn, sizeof(char *), pstrcmp);
	}
}

addfile(buf)
	char *buf;
{
	char *p;

	for (p=buf; *p; p++)
		*p=tolower(*p);

	if (nfiles<MAXFILES && (files[nfiles]=calloc(strlen(buf)+1, 1))!=0)
		strcpy(files[nfiles++], buf);
}

int pstrcmp(a, b)
	char **a, **b;
{
	return strcmp(*a, *b);
}
SHAR_EOF
fi
if test -f 'shell.c'
then
	echo shar: "will not over-write existing file 'shell.c'"
else
cat << \SHAR_EOF > 'shell.c'
/* shell.c */

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

/*
 * This file contains a minimal version of a shell for TOS. It allows the
 * setting of an environment, calling programs, and exiting.
 * If you don't have another one, this might be sufficient, but you should 
 * prefer *any* other shell.
 * You may, however, want to set your SHELL environment variable to this
 * shell: it implements the -c switch, which is required by Elvis, and
 * not supported by most other atari shells.
 */
 
#include <stdio.h>
#include <string.h>
#include <osbind.h>
extern char *getenv(), *malloc();
extern char **environ;
long _stksize=16384;

#define	MAXENV	50

struct
{
	char *name;
	char *value;
} myenv[MAXENV];

int cmd_set(), cmd_exit();

struct buildins
{
	char *name;
	int (*func)();
} buildins[]=
{	"exit", cmd_exit,
	"set", cmd_set,
	0,
};

main(argc, argv)
	int argc;
	char **argv;
{
	char buf[128];
	int i;

	for (i=0; environ[i] && strncmp(environ[i],"ARGV=",5); i++)
		cmd_set(environ[i]);
	script("profile.sh");

	if (argc>1 && !strcmp(argv[1], "-c"))
	{
		buf[0]='\0';
		for (i=2; i<argc; i++)
		{	if (i>2)
				strcat(buf, " ");
			strcat(buf, argv[i]);
		}
		execute(buf);
	}
	else
		while (fputs("$ ", stdout), gets(buf))
			execute(buf);
}

execute(buf)
	char *buf;
{
	char *scan=buf;
	char cmd[80];
	char line[128];
	char env[4096], *ep=env;
	int i;

	while (*scan==' ')
		scan++;
	if (!*scan)
		return;
	while (*scan && *scan!=' ')
		scan++;
	if (*scan)
		*scan++='\0';

	for (i=0; buildins[i].name; i++)
		if (!strcmp(buf, buildins[i].name))
			return (*buildins[i].func)(scan);

	if (!searchpath(buf, cmd))
	{	printf("%s: not found\n", buf);
		return -1;
	}

	strcpy(line+1, scan);
	line[0]=strlen(scan);
	for (i=0; i<MAXENV && myenv[i].name; i++)
	{	strcpy(ep, myenv[i].name);
		strcat(ep, "=");
		strcat(ep, myenv[i].value);
		ep+=strlen(ep)+1;
	}
	
	*ep='\0';

	return Pexec(0, cmd, line, env);
}

searchpath(from, to)
	char *from, *to;
{
	char *path="";
	char *scan;
	char *end;
	char *q;
	int i;

	for (i=0; i<MAXENV && myenv[i].name; i++)
		if (!strcmp(myenv[i].name,"PATH"))
			path=myenv[i].value;
	for (scan=from; *scan; scan++)
		if (*scan==':' || *scan=='\\')
		{	path=0;
			break;
		}
	if (!path)
	{	strcpy(to, from);
		end=to+strlen(to);
		strcpy(end, ".prg"); if (try(to)) return 1;
		strcpy(end, ".ttp"); if (try(to)) return 1;
		strcpy(end, ".tos"); if (try(to)) return 1;
		*to='\0'; return 0;
	}
	for (scan=path; *scan; )
	{
		for (q=to; *scan && *scan!=';' && *scan!=','; scan++)
			*q++=*scan;
		if (*scan==';' || *scan==',')
			scan++;
		*q++='\\';
		*q='\0';
		strcpy(q, from);
		end=q+strlen(q);
		strcpy(end, ".prg"); if (try(to)) return 1;
		strcpy(end, ".ttp"); if (try(to)) return 1;
		strcpy(end, ".tos"); if (try(to)) return 1;
	}
	*to='\0';
	return 0;
}

try(name)
	char *name;
{
	if (Fattrib(name, 0, 0) < 0)
		return 0;
	return 1;
}

cmd_exit()
{
	exit(0);
}

cmd_set(line)
	char *line;
{
	char *value;
	int i;

	if (!*line)
	{
		for (i=0; i<MAXENV && myenv[i].name; i++)
			printf("%s=%s\n", myenv[i].name, myenv[i].value);
		return 0;
	}

	for (value=line; *value && *value!='='; value++)
		;
	if (!*value)
	{	printf("Usage: set name=var\n");
		return -1;
	}
	*value++='\0';
	doset(line, value);
}

doset(line, value)
	char *line, *value;
{
	int i;

	for (i=0; i<MAXENV && myenv[i].name && strcmp(myenv[i].name, line); i++)
		;
	if (i==MAXENV)
	{	printf("No Space\n");
		return -1;
	}
	if (!myenv[i].name)
	{	myenv[i].name=malloc(strlen(line)+1);
		strcpy(myenv[i].name, line);
	}
	if (myenv[i].value)
		free(myenv[i].value);
	myenv[i].value=malloc(strlen(value)+1);
	strcpy(myenv[i].value, value);
	return 0;
}

script(name)
	char *name;
{
	FILE *fp;
	char buf[128], *p;

	if ((fp=fopen(name, "r"))==0)
		return;
	while (fgets(buf, sizeof buf, fp))
	{
		if ((p=strchr(buf, '\n'))!=0)
			*p='\0';
		execute(buf);
	}
	fclose(fp);
}
SHAR_EOF
fi
exit 0
#	End of shell archive
-------------------------------------------------------------------------------
Steve Kirkendall    kirkenda at cs.pdx.edu    uunet!tektronix!psueea!eecs!kirkenda



More information about the Alt.sources mailing list