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

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

Archive-name: elvis1.3/part3
#! /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:
#	atari.c
#	curses.c
#	curses.h
#	cut.c
#	elvis.ndx
#	ex.c
#	input.c
#	main.c
#	misc.c
# This archive created: Fri Aug 24 10:29:55 1990
export PATH; PATH=/bin:/usr/bin:$PATH
if test -f 'atari.c'
then
	echo shar: "will not over-write existing file 'atari.c'"
else
cat << \SHAR_EOF > 'atari.c'
/* atari.c */

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

/*
 * This file contains the 'standard' functions which are not supported
 * by Atari/Mark Williams, and some other TOS-only requirements.
 */
 
#include "config.h"
#include "vi.h"

#if	TOS
#include <osbind.h>

/* vi uses mode==0 only ... */
int access(file, mode)
	char *file;
{
	int fd=Fopen(file, 0);
	if (fd<0)
		return -1;
	Fclose(fd);
	return 0;
}

char *mktemp(template)
	char *template;
{
	return template;
}

/* read -- text mode, compress \r\n to \n
 * warning: might fail when maxlen==1 and at eol
 */

int tread(fd, buf, maxlen)
	int fd;
	char *buf;
	int maxlen;
{
	int i, j, nread=read(fd, buf, maxlen);

	if (nread && buf[nread-1]=='\r')
	{	nread--;
		lseek(fd, -1l, 1);
	}
	for (i=j=0; j<nread; i++,j++)
	{	if (buf[j]=='\r' && buf[j+1]=='\n')
			j++;
		buf[i]=buf[j];
	}
	return i;
}

int twrite(fd, buf, maxlen)
	int fd;
	char *buf;
	int maxlen;
{
	int i, j, nwritten=0, hadnl=0;
	char writbuf[BLKSIZE];

	for (i=j=0; j<maxlen; )
	{
		if ((writbuf[i++]=buf[j++])=='\n')
		{	writbuf[i-1]='\r';
			if (i<BLKSIZE)
				writbuf[i++]='\n';
			else
				hadnl=1;
		}
		if (i==BLKSIZE)
		{
			write(fd, writbuf, i);
			i=0;
		}
		if (hadnl)
		{
			writbuf[i++]='\n';
			hadnl=0;
		}
	}
	if (i)
		write(fd, writbuf, i);
	return j;
}
#endif
SHAR_EOF
fi
if test -f 'curses.c'
then
	echo shar: "will not over-write existing file 'curses.c'"
else
cat << \SHAR_EOF > 'curses.c'
/* curses.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 & variables needed for a tiny subset of
 * curses.  The principle advantage of this version of curses is its
 * extreme speed.  Disadvantages are potentially larger code, few supported
 * functions, limited compatibility with full curses, and only stdscr.
 */

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

#if UNIXV
# include	<termio.h>
#endif

#if BSD || UNIX7 || MINIX
# include	<sgtty.h>
#endif

#if TOS
# include	<osbind.h>
#endif

#include <signal.h>

extern char	*getenv();

/* variables, publicly available & used in the macros */
short	ospeed;		/* speed of the tty, eg B2400 */
char	PC;		/* Pad char */
WINDOW	*stdscr;	/* pointer into kbuf[] */
WINDOW	kbuf[KBSIZ];	/* a very large output buffer */
int	LINES;		/* :li#: number of rows */
int	COLS;		/* :co#: number of columns */
int	AM;		/* :am:  boolean: auto margins? */
int	PT;		/* :pt:  boolean: physical tabs? */
char	*VB;		/* :vb=: visible bell */
char	*UP;		/* :up=: move cursor up */
char	*SO;		/* :so=: standout start */
char	*SE;		/* :se=: standout end */
char	*US = "";	/* :us=: underline start */
char	*UE = "";	/* :ue=: underline end */
char	*VB_s = "";	/* :VB=: bold start */
char	*VB_e = "";	/* :Vb=: bold end */
char	*AS;		/* :as=: alternate (italic) start */
char	*AE;		/* :ae=: alternate (italic) end */
char	*CM;		/* :cm=: cursor movement */
char	*CE;		/* :ce=: clear to end of line */
char	*CD;		/* :cd=: clear to end of screen */
char	*AL;		/* :al=: add a line */
char	*DL;		/* :dl=: delete a line */
char	*SR;		/* :sr=: scroll reverse */
char	*KU;		/* :ku=: key sequence sent by up arrow */
char	*KD;		/* :kd=: key sequence sent by down arrow */
char	*KL;		/* :kl=: key sequence sent by left arrow */
char	*KR;		/* :kr=: key sequence sent by right arrow */
char	*HM;		/* :HM=: key sequence sent by the <Home> key */
char	*EN;		/* :EN=: key sequence sent by the <End> key */
char	*PU;		/* :PU=: key sequence sent by the <PgUp> key */
char	*PD;		/* :PD=: key sequence sent by the <PgDn> key */
char	*IM;		/* :im=: insert mode start */
char	*IC = "";	/* :ic=: insert the following character */
char	*EI;		/* :ei=: insert mode end */
char	*DC;		/* :dc=: delete a character */
char	*TI;		/* :ti=: terminal init */	/* GB */
char	*TE;		/* :te=: terminal exit */	/* GB */
#ifndef NO_CURSORSHAPE
char	*CQ = (char *)0;/* :cQ=: normal cursor */
char	*CX = (char *)1;/* :cX=: cursor used for EX command/entry */
char	*CV = (char *)2;/* :cV=: cursor used for VI command mode */
char	*CI = (char *)3;/* :cI=: cursor used for VI input mode */
char	*CR = (char *)4;/* :cR=: cursor used for VI replace mode */
#endif
char	*aend = "";	/* end an attribute -- either UE or VB_e */
char	ERASEKEY;	/* backspace key taken from ioctl structure */

#if UNIXV
static struct termio	oldtermio;	/* original tty mode */
static struct termio	newtermio;	/* raw/noecho tty mode */
#endif

#if BSD || UNIX7 || MINIX
static struct sgttyb	oldsgttyb;	/* original tty mode */
static struct sgttyb	newsgttyb;	/* raw/nl/noecho tty mode */
static int		oldint;
#endif

static char	*capbuf;	/* capability string buffer */


initscr()
{
	/* make sure TERM variable is set */
#if	MSDOS
	char *val;
	if (! (val = getenv("TERM"))
	|| !strcmp(val, "pcbios"))
#else
	if (!getenv("TERM"))
#endif
	{
#if ANY_UNIX || TOS
		write(2, "Environment variable TERM must be set\n", 38);
		exit(1);
#endif
#if MSDOS
		getsize(0);
#endif
	}
	else
	{
#if MSDOS
		*o_pcbios=0;
#endif
		/* start termcap stuff */
		starttcap();
	}

	/* create stdscr and curscr */
	stdscr = kbuf;

	/* change the terminal mode to raw/noecho */
#if UNIXV
	ioctl(2, TCGETA, &oldtermio);
#endif

#if BSD || UNIX7 || MINIX
	ioctl(2, TIOCGETP, &oldsgttyb);
#endif
	resume_curses(TRUE);
}


endwin()
{
	/* change the terminal mode back the way it was */
	suspend_curses();
}

suspend_curses()
{
#if BSD || UNIX7 || MINIX
	struct tchars	tbuf;
#endif
#ifndef NO_CURSORSHAPE
	if (has_CQ)
	{
		do_CQ();
	}
#endif
	if (has_TE)					/* GB */
	{
		do_TE();
	}

	/* change the terminal mode back the way it was */
#if UNIXV
	ioctl(2, TCSETAW, &oldtermio);
#endif
#if BSD || UNIX7 || MINIX
	ioctl(2, TIOCSETP, &oldsgttyb);

	ioctl(2, TIOCGETC, &tbuf);
	tbuf.t_intrc = oldint;
	ioctl(2, TIOCSETC, &tbuf);
#endif
}

resume_curses(quietly)
	int	quietly;
{	
	/* change the terminal mode to raw/noecho */
#if UNIXV
	ospeed = (oldtermio.c_cflag & CBAUD);
	ERASEKEY = oldtermio.c_cc[VERASE];
	newtermio = oldtermio;
	newtermio.c_iflag &= (IXON|IXOFF|IXANY|ISTRIP|IGNBRK);
	newtermio.c_oflag &= ~OPOST;
	newtermio.c_lflag &= ISIG;
	newtermio.c_cc[VINTR] = ctrl('C'); /* always use ^C for interrupts */
	newtermio.c_cc[VEOF] = 1;	/* minimum # characters to read */
	newtermio.c_cc[VEOL] = 2;	/* allow at least 0.2 seconds */
	ioctl(2, TCSETAW, &newtermio);
#endif
#if BSD || UNIX7 || MINIX
	struct tchars	tbuf;

	ospeed = oldsgttyb.sg_ospeed;
	ERASEKEY = oldsgttyb.sg_erase;
	newsgttyb = oldsgttyb;
	newsgttyb.sg_flags |= CBREAK;
	newsgttyb.sg_flags &= ~(CRMOD|ECHO|XTABS);
	ioctl(2, TIOCSETP, &newsgttyb);

	ioctl(2, TIOCGETC, &tbuf);
	oldint = tbuf.t_intrc;
	tbuf.t_intrc = ctrl('C');	/* always use ^C for interrupts */
	ioctl(2, TIOCSETC, &tbuf);
#endif

	if (has_TI)					/* GB */
	{
		do_TI();
	}

	/* If we're supposed to quit quietly, then we're done */
	if (quietly)
	{
		return;
	}

	signal(SIGINT, SIG_IGN);

	move(LINES - 1, 0);
	do_SO();
	qaddstr("[Press <RETURN> to continue]");
	do_SE();
	refresh();
	ttyread(kbuf, 20); /* in RAW mode, so <20 is very likely */
	if (kbuf[0] == ':')
	{
		mode = MODE_COLON;
		addch('\n');
		refresh();
	}
	else
	{
		mode = MODE_VI;
		redraw(MARK_UNSET, FALSE);
	}	
	exwrote = FALSE;

#if	TURBOC
	signal(SIGINT, (void(*)()) trapint);
#else
	signal(SIGINT, trapint);
#endif
}

static lacking(s)
	char	*s;
{
	write(2, "This termcap entry lacks the :", 30);
	write(2, s, 2);
	write(2, "=: capability\n", 14);
	exit(1);
}

starttcap()
{
	char	*str;
	static char	cbmem[800];
#define MUSTHAVE(T,s)	if (!(T = tgetstr(s, &capbuf))) lacking(s)
#define MAYHAVE(T,s)	if (str = tgetstr(s, &capbuf)) T = str
#define PAIR(T,U,sT,sU)	T=tgetstr(sT,&capbuf);U=tgetstr(sU,&capbuf);if (!T||!U)T=U=""

	/* allocate memory for capbuf */
	capbuf = cbmem;

	/* get the termcap entry */
	switch (tgetent(kbuf, getenv("TERM")))
	{
	  case -1:
		write(2, "Can't read /etc/termcap\n", 24);
		exit(2);

	  case 0:
		write(2, "Unrecognized TERM type\n", 23);
		exit(3);
	}

	/* get strings */
	MUSTHAVE(UP, "up");
	MAYHAVE(VB, "vb");
	MUSTHAVE(CM, "cm");
	PAIR(SO, SE, "so", "se");
	PAIR(TI, TE, "ti", "te");
	if (tgetnum("ug") <= 0)
	{
		PAIR(US, UE, "us", "ue");
		PAIR(VB_s, VB_e, "VB", "Vb");

		/* get italics, or have it default to underline */
		PAIR(AS, AE, "as", "ae");
		if (!*AS)
		{
			AS = US;
			AE = UE;
		}
	}
	MAYHAVE(AL, "al");
	MAYHAVE(DL, "dl");
	MUSTHAVE(CE, "ce");
	MAYHAVE(CD, "cd");
	MAYHAVE(SR, "sr");
	PAIR(IM, EI, "im", "ei");
	MAYHAVE(IC, "ic");
	MAYHAVE(DC, "dc");

	/* other termcap stuff */
	AM = tgetflag("am");
	PT = tgetflag("pt");
	getsize(0);

	/* Key sequences */
	MAYHAVE(KU, "ku");
	MAYHAVE(KD, "kd");
	MAYHAVE(KL, "kl");
	MAYHAVE(KR, "kr");
	MAYHAVE(PU, "PU");
	MAYHAVE(PD, "PD");
	MAYHAVE(HM, "HM");
	MAYHAVE(EN, "EN");

#ifndef NO_CURSORSHAPE
	/* cursor shapes */
	CQ = tgetstr("cQ", &capbuf);
	if (has_CQ)
	{
		CX = tgetstr("cX", &capbuf);
		if (!CX) CX = CQ;
		CV = tgetstr("cV", &capbuf);
		if (!CV) CV = CQ;
		CI = tgetstr("cI", &capbuf);
		if (!CI) CI = CQ;
		CR = tgetstr("cR", &capbuf);
		if (!CR) CR = CQ;
	}
#endif

#undef MUSTHAVE
#undef MAYHAVE
#undef PAIR
}


/* This function gets the window size.  It uses the TIOCGWINSZ ioctl call if
 * your system has it, or tgetnum("li") and tgetnum("co") if it doesn't.
 * This function is called once during initialization, and thereafter it is
 * called whenever the SIGWINCH signal is sent to this process.
 */
getsize(signo)
	int	signo;
{
	int	lines;
	int	cols;
#ifdef TIOCGWINSZ
	struct winsize size;
#endif

#ifdef SIGWINCH
	/* reset the signal vector */
	signal(SIGWINCH, getsize);
#endif

	/* get the window size, one way or another. */
	lines = cols = 0;
#ifdef TIOCGWINSZ
	if (ioctl(2, TIOCGWINSZ, &size) >= 0)
	{
		lines = size.ws_row;
		cols = size.ws_col;
	}
#endif
	if ((lines == 0 || cols == 0) && signo == 0)
	{
		LINES = CHECKBIOS(v_rows(), tgetnum("li"));
		COLS = CHECKBIOS(v_cols(), tgetnum("co"));
	}
	if (lines >= 2 && cols >= 30)
	{
		LINES = lines;
		COLS = cols;
	}

	/* Make sure we got values that we can live with */
	if (LINES < 2 || COLS < 30)
	{
		write(2, "Screen too small\n", 17);
		endwin();
		exit(2);
	}

	/* !!! copy the new values into Elvis' options */
	{
		extern char	o_columns[], o_lines[];

		*o_columns = COLS;
		*o_lines = LINES;
	}
}


/* This is a function version of addch() -- it is used by tputs() */
int faddch(ch)
	int	ch;
{
	addch(ch);
}

/* These functions are equivelent to the macros of the same names... */

void qaddstr(str)
	char	*str;
{
	register char *s_, *d_;

#if MSDOS
	if (o_pcbios[0])
	{
		while (*str)
			qaddch(*str++);
		return;
	}
#endif
	for (s_=(str), d_=stdscr; *d_++ = *s_++; )
	{
	}
	stdscr = d_ - 1;
}

void attrset(a)
	int	a;
{
	do_aend();
	if (a == A_BOLD)
	{
		do_VB_s();
		aend = VB_e;
	}
	else if (a == A_UNDERLINE)
	{
		do_US();
		aend = UE;
	}
	else if (a == A_ALTCHARSET)
	{
		do_AS();
		aend = AE;
	}
	else
	{
		aend = "";
	}
}


void insch(ch)
	int	ch;
{
	if (has_IM)
		do_IM();
	do_IC();
	qaddch(ch);
	if (has_EI)
		do_EI();
}

#if MSDOS

static int alarmtime;

/* raw read - #defined to read (0, ...) on non-MSDOS.
 * With MSDOS, am maximum of 1 byte is read.
 * If more bytes should be read, just change the loop.
 * The following code uses the IBM-PC-System-Timer, so probably wont't work
 * on non-compatibles.
 */
/*ARGSUSED*/
ttyread(buf, len)
	char *buf;
	int len;
{
	volatile char far *biostimer;
	char oldtime;
	int nticks = 0;
	int pos = 0;

	biostimer = (char far *)0x0040006cl;
	oldtime = *biostimer;

	while (!pos && (!alarmtime || nticks<alarmtime))
	{	if (kbhit())
			if ((buf[pos++] = getch()) == 0) /* function key */
				buf[pos-1] = '#';
		if (oldtime != *biostimer)
		{	nticks++;
			oldtime = *biostimer;
		}
	}
	return pos;
}

alarm(time)
	int time;
{
	alarmtime = 2 * time;		/* ticks are 1/18 sec. */
}
#endif

#if TOS

static int alarmtime;
static long timer;

static gettime()
{
	timer = *(long *)(0x4ba);
}

/*ARGSUSED*/
ttyread(buf, len)
	char *buf;
	int len;
{
	int	pos=0;
	long	l;
	long	endtime;

	Supexec(gettime);
	endtime = timer+alarmtime;

	while (!pos && (!alarmtime || timer<endtime))
	{
		if (Bconstat(2))
		{
			l = Bconin(2);
			if ((buf[pos++]=l) == '\0')
			{
				buf[pos-1]='#';
				buf[pos++]=l>>16;
			}
		}
		Supexec(gettime);
	}
	return pos;
}

alarm(time)
	int time;
{
	alarmtime = 50 * time;		/* ticks are 1/200 sec. */
}

ttywrite(buf, len)
	char *buf;
	int len;
{
	while (len--)
		Bconout(2, *buf++);
}
#endif
SHAR_EOF
fi
if test -f 'curses.h'
then
	echo shar: "will not over-write existing file 'curses.h'"
else
cat << \SHAR_EOF > 'curses.h'
/* curses.h */

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


/* This is the header file for a small, fast, fake curses package */

/* termcap stuff */
extern char	*tgoto();
extern char	*tgetstr();
extern void	tputs();

#if MSDOS
/* BIOS interface used instead of termcap for MS-DOS */
extern int	vmode;
extern void	v_up();
extern void	v_cb();
extern void	v_cs();
extern void	v_ce();
extern void	v_cl();
extern void	v_cd();
extern void	v_al();
extern void	v_dl();
extern void	v_sr();
extern void	v_move();
#endif

/* faddch() is a function.  a pointer to it is passed to tputs() */
extern int	faddch();

/* data types */
#define WINDOW	char

/* CONSTANTS & SYMBOLS */
#define TRUE		1
#define FALSE		0
#define A_NORMAL	0
#define A_STANDOUT	1
#define A_BOLD		2
#define A_UNDERLINE	3
#define A_ALTCHARSET	4
#if	MSDOS
#define KBSIZ		(10*1024)
#else
#define KBSIZ		(6*1024)
#endif

/* extern variables, defined in curses.c */
extern short	ospeed;		/* tty speed, eg B2400 */
extern char	PC;		/* Pad char */
extern WINDOW	*stdscr;	/* pointer into kbuf[] */
extern WINDOW	kbuf[KBSIZ];	/* a very large output buffer */
extern int	LINES;		/* :li#: number of rows */
extern int	COLS;		/* :co#: number of columns */
extern int	AM;		/* :am:  boolean: auto margins? */
extern int	PT;		/* :pt:  boolean: physical tabs? */
extern char	*VB;		/* :vb=: visible bell */
extern char	*UP;		/* :up=: move cursor up */
extern char	*SO;		/* :so=: standout start */
extern char	*SE;		/* :se=: standout end */
extern char	*US;		/* :us=: underline start */
extern char	*UE;		/* :ue=: underline end */
extern char	*VB_s;		/* :VB=: bold start */
extern char	*VB_e;		/* :Vb=: bold end */
extern char	*AS;		/* :as=: alternate (italic) start */
extern char	*AE;		/* :ae=: alternate (italic) end */
extern char	*CM;		/* :cm=: cursor movement */
extern char	*CE;		/* :ce=: clear to end of line */
extern char	*CD;		/* :cd=: clear to end of screen */
extern char	*AL;		/* :al=: add a line */
extern char	*DL;		/* :dl=: delete a line */
extern char	*SR;		/* :sr=: scroll reverse */
extern char	*KU;		/* :ku=: sequence sent by up key */
extern char	*KD;		/* :kd=: sequence sent by down key */
extern char	*KL;		/* :kl=: sequence sent by left key */
extern char	*KR;		/* :kr=: sequence sent by right key */
extern char	*PU;		/* :PU=: key sequence sent by PgUp key */
extern char	*PD;		/* :PD=: key sequence sent by PgDn key */
extern char	*HM;		/* :HM=: key sequence sent by Home key */
extern char	*EN;		/* :EN=: key sequence sent by End key */
extern char	*IM;		/* :im=: insert mode start */
extern char	*IC;		/* :ic=: insert following char */
extern char	*EI;		/* :ei=: insert mode end */
extern char	*DC;		/* :dc=: delete a character */
extern char	*TI;		/* :ti=: terminal init */	/* GB */
extern char	*TE;		/* :te=: terminal exit */	/* GB */
#ifndef NO_CURSORSHAPE
extern char	*CQ;		/* :cQ=: normal cursor */
extern char	*CX;		/* :cX=: cursor used for EX command/entry */
extern char	*CV;		/* :cV=: cursor used for VI command mode */
extern char	*CI;		/* :cI=: cursor used for VI input mode */
extern char	*CR;		/* :cR=: cursor used for VI replace mode */
#endif
extern char	*aend;		/* end an attribute -- either UE or VB_e */
extern char	ERASEKEY;	/* taken from the ioctl structure */

/* Msdos-versions may use bios; others always termcap.
 * Will emit some 'code has no effect' warnings in unix.
 */
 
#if	MSDOS
extern char o_pcbios[1];		/* BAH! */
#define	CHECKBIOS(x,y)	(*o_pcbios ? (x) : (y))
#define VOIDBIOS(x,y)	{if (*o_pcbios) {x;} else {y;}}
#else
#define	CHECKBIOS(x,y)	(y)
#define VOIDBIOS(x,y)	{y;}
#endif

#define	do_VB()		VOIDBIOS(0, tputs(VB, 1, faddch))
#define	do_UP()		VOIDBIOS(v_up(), tputs(UP, 1, faddch))
#define	do_SO()		VOIDBIOS((vmode=A_STANDOUT), tputs(SO, 1, faddch))
#define	do_SE()		VOIDBIOS((vmode=A_NORMAL), tputs(SE, 1, faddch))
#define	do_US()		VOIDBIOS((vmode=A_UNDERLINE), tputs(US, 1, faddch))
#define	do_UE()		VOIDBIOS((vmode=A_NORMAL), tputs(UE, 1, faddch))
#define	do_VB_s()	VOIDBIOS((vmode=A_BOLD), tputs(VB_s, 1, faddch))
#define	do_VB_e()	VOIDBIOS((vmode=A_NORMAL), tputs(VB_e, 1, faddch))
#define	do_AS()		VOIDBIOS((vmode=A_ALTCHARSET), tputs(AS, 1, faddch))
#define	do_AE()		VOIDBIOS((vmode=A_NORMAL), tputs(AE, 1, faddch))
#undef	do_CM		/* move */
#define	do_CE()		VOIDBIOS(v_ce(), tputs(CE, 1, faddch))
#define	do_CD()		VOIDBIOS(v_cd(), tputs(CD, 1, faddch))
#define	do_AL()		VOIDBIOS(v_al(), tputs(AL, LINES, faddch))
#define	do_DL()		VOIDBIOS(v_dl(), tputs(DL, LINES, faddch))
#define	do_SR()		VOIDBIOS(v_sr(), tputs(SR, 1, faddch))
#define	do_IM()		VOIDBIOS(0, tputs(IM, 1, faddch))
#define	do_IC()		VOIDBIOS(0, tputs(IC, 1, faddch))
#define	do_EI()		VOIDBIOS(0, tputs(EI, 1, faddch))
#define	do_DC()		VOIDBIOS(0, tputs(DC, COLS, faddch))
#define	do_TI()		VOIDBIOS(0, (void)ttywrite(TI, strlen(TI)))
#define	do_TE()		VOIDBIOS(0, (void)ttywrite(TE, strlen(TE)))
#ifndef NO_CURSORSHAPE
# define do_CQ()	VOIDBIOS(v_cs(), tputs(CQ, 1, faddch))
# define do_CX()	VOIDBIOS(v_cs(), tputs(CX, 1, faddch))
# define do_CV()	VOIDBIOS(v_cs(), tputs(CV, 1, faddch))
# define do_CI()	VOIDBIOS(v_cb(), tputs(CI, 1, faddch))
# define do_CR()	VOIDBIOS(v_cb(), tputs(CR, 1, faddch))
#endif
#define	do_aend()	VOIDBIOS((vmode=A_NORMAL), tputs(aend, 1, faddch))

#define	has_AM		CHECKBIOS(1, AM)
#define	has_PT		CHECKBIOS(0, PT)
#define	has_VB		CHECKBIOS((char *)0, VB)
#define	has_UP		CHECKBIOS((char *)1, UP)
#define	has_SO		CHECKBIOS((char)1, (*SO))
#define	has_SE		CHECKBIOS((char)1, (*SE))
#define	has_US		CHECKBIOS((char)1, (*US))
#define	has_UE		CHECKBIOS((char)1, (*UE))
#define	has_VB_s	CHECKBIOS((char)1, (*VB_s))
#define	has_VB_e	CHECKBIOS((char)1, (*VB_e))
#define	has_AS		CHECKBIOS((char)1, (*AS))
#define	has_AE		CHECKBIOS((char)1, (*AE))
#undef	has_CM		/* cursor move: don't need */
#define	has_CB		CHECKBIOS(1, 0)
#define	has_CS		CHECKBIOS(1, 0)
#define	has_CE		CHECKBIOS((char *)1, CE)
#define	has_CD		CHECKBIOS((char *)1, CD)
#define	has_AL		CHECKBIOS((char *)1, AL)
#define	has_DL		CHECKBIOS((char *)1, DL)
#define	has_SR		CHECKBIOS((char *)1, SR)
#define	has_KU		CHECKBIOS("#H", KU)
#define	has_KD		CHECKBIOS("#P", KD)
#define	has_KL		CHECKBIOS("#K", KL)
#define	has_KR		CHECKBIOS("#M", KR)
#define has_HM		CHECKBIOS("#G", HM)
#define has_EN		CHECKBIOS("#O", EN)
#define has_PU		CHECKBIOS("#I", PU)
#define has_PD		CHECKBIOS("#Q", PD)
#define	has_IM		CHECKBIOS((char)0, (*IM))
#define	has_IC		CHECKBIOS((char)0, (*IC))
#define	has_EI		CHECKBIOS((char)0, (*EI))
#define	has_DC		CHECKBIOS((char *)0, DC)
#define	has_TI		CHECKBIOS((char)0, (*TI))
#define	has_TE		CHECKBIOS((char)0, (*TE))
#ifndef NO_CURSORSHAPE
#define has_CQ		CHECKBIOS((char *)1, CQ)
#endif

/* (pseudo)-Curses-functions */

#define _addCR		(void)CHECKBIOS(0, (stdscr[-1] == '\n' ? qaddch('\r') : 0))
#define qaddch(ch)	CHECKBIOS(v_put(ch), (*stdscr++ = (ch)))
#define addch(ch)	if (qaddch(ch) == '\n') qaddch('\r'); else

extern void attrset();
extern void insch();
extern void qaddstr();
#define addstr(str)	{qaddstr(str); _addCR;}
#define move(y,x)	VOIDBIOS(v_move(x,y), \
					tputs(tgoto(CM, x, y), 1, faddch))
#define mvaddch(y,x,ch)	{move(y,x); addch(ch);}
#define refresh()	VOIDBIOS(0, wrefresh(stdscr))
#define wrefresh(w)	if ((w) != kbuf) VOIDBIOS((w) = kbuf, {ttywrite(kbuf, (int)((w) - kbuf)); (w) = kbuf;}) else
#define wqrefresh(w)	if ((w) - kbuf > 2000) VOIDBIOS((w) = kbuf, {ttywrite(kbuf, (int)((w) - kbuf)); (w) = kbuf;}) else
#define standout()	do_SO()
#define standend()	do_SE()
#define clrtoeol()	do_CE()
#define clrtobot()	do_CD()
#define insertln()	do_AL()
#define deleteln()	do_DL()
#define delch()		do_DC()
#define scrollok(w,b)
#define raw()
#define echo()
#define cbreak()
#define noraw()
#define noecho()
#define nocbreak()
SHAR_EOF
fi
if test -f 'cut.c'
then
	echo shar: "will not over-write existing file 'cut.c'"
else
cat << \SHAR_EOF > 'cut.c'
/* cut.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 function which manipulate the cut buffers. */

#include "config.h"
#include "vi.h"
#if	TURBOC
#include <process.h>		/* needed for getpid */
#endif
#if	TOS
#include <osbind.h>
#define	rename(a,b)	Frename(0,a,b)
#endif

# define NANNONS	9	/* number of annonymous buffers */

static struct cutbuf
{
	short	*phys;	/* pointer to an array of #s of BLKs containing text */
	int	nblks;	/* number of blocks in phys[] array */
	int	start;	/* offset into first block of start of cut */
	int	end;	/* offset into last block of end of cut */
	int	fd;	/* fd of tmp file, or -1 to use tmpfd */
	char	lnmode;	/* boolean: line-mode cut? (as opposed to char-mode) */
}
	named[27],	/* cut buffers "a through "z and ". */
	annon[NANNONS];	/* annonymous cut buffers */

static char	cbname;	/* name chosen for next cut/paste operation */


#ifndef NO_RECYCLE
/* This function builds a list of all blocks needed in the current tmp file
 * for the contents of cut buffers.
 * !!! WARNING: if you have more than ~450000 bytes of text in all of the
 * cut buffers, then this will fail disastrously, because buffer overflow
 * is *not* allowed for.
 */
int cutneeds(need)
	BLK		*need;	/* this is where we deposit the list */
{
	struct cutbuf	*cb;	/* used to count through cut buffers */
	int		i;	/* used to count through blocks of a cut buffer */
	int		n;	/* total number of blocks in list */

	n = 0;

	/* first the named buffers... */
	for (cb = named; cb < &named[27]; cb++)
	{
		if (cb->fd > 0)
			continue;

		for (i = cb->nblks; i-- > 0; )
		{
			need->n[n++] = cb->phys[i];
		}
	}

	/* then the anonymous buffers */
	for (cb = annon; cb < &annon[NANNONS]; cb++)
	{
		if (cb->fd > 0)
			continue;

		for (i = cb->nblks; i-- > 0; )
		{
			need->n[n++] = cb->phys[i];
		}
	}

	return n;
}
#endif

/* This function is called when we are about to abort a tmp file.  If any
 * cut buffers still need the file, then a copy of the file should be
 * created for use by the cut buffers.
 *
 * To minimize the number of extra files lying around, only named cut buffers
 * are preserved in a file switch; the annonymous buffers just go away.
 */
cutswitch(tmpname)
	char	*tmpname; /* name of the tmp file */
{
	char	cutname[50];	/* used to build a new name for the tmp file */
	int	fd;		/* a new fd for the current tmp file */
	int	i, j;

	/* discard all annonymous cut buffers */
	for (i = 0; i < NANNONS; i++)
	{
		cutfree(&annon[i]);
	}

	/* find the first named buffer that uses this tmp file */
	for (i = 0; i < 27; i++)
	{
		if (named[i].nblks > 0 && named[i].fd < 0)
		{
			break;
		}
	}

	/* if none of them use this tmp file, then we're done */
	if (i == 27)
	{
		return;
	}

	/* else we'll need this file and an fd a little longer */
		/* !!! we could use some error checking here */
#if	TOS
	/* Hack! Tos allows dup for standard handles only. */
	fd = tmpfd;
	tmpfd = -1;
#else
	fd = dup(tmpfd);
#endif
#if MSDOS || TOS
	strcpy(cutname, o_directory);
	if ((j = strlen(cutname)) && !strchr(":/\\", cutname[j-1]))
		cutname[j++]=SLASH;
	sprintf(cutname+j, CUTNAME+3, getpid(), fd);
	rename(tmpname, cutname);
#else
	sprintf(cutname, CUTNAME, o_directory, getpid(), fd);
	link(tmpname, cutname) || unlink(tmpname);
#endif

	/* have all cut buffers use the new fd instead */
	for (; i < 27; i++)
	{
		if (named[i].nblks > 0 && named[i].fd < 0)
		{
			named[i].fd = fd;
		}
	}
}

/* This function frees a cut buffer */
static cutfree(buf)
	struct cutbuf	*buf;
{
	char	cutname[50];
	int	i;

	/* return immediately if the buffer is already empty */
	if (buf->nblks <= 0)
	{
		return;
	}

	/* else free up stuff */
	buf->nblks = 0;
	free(buf->phys);

	/* see if anybody else needs this tmp file */
	if (buf->fd >= 0)
	{
		for (i = 0; i < 27; i++)
		{
#if	0
			if (named[i].nblks > 0 && named[i].fd >= 0)
#else
			if (named[i].nblks > 0 && named[i].fd == buf->fd)
#endif
			{
				break;
			}
		}
	}

	/* if nobody else needs it, then discard the tmp file */
	if (buf->fd >= 0 && i == 27)
	{
		close(buf->fd);
#if	MSDOS || TOS
		strcpy(cutname, o_directory);
		if ((i = strlen(cutname)) && !strchr(":/\\", cutname[i-1]))
			cutname[i++]=SLASH;
		sprintf(cutname+i, CUTNAME+3, getpid(), buf->fd);
#else
		sprintf(cutname, CUTNAME, o_directory, getpid(), buf->fd);
#endif
		unlink(cutname);
	}
}

/* This function should be called just before termination of vi */
cutend()
{
	int	i;

	/* free all named cut buffers, since they might be forcing an older
	 * tmp file to be retained.
	 */
	for (i = 0; i < 27; i++)
	{
		cutfree(&named[i]);
	}
}


/* This function is used to select the cut buffer to be used next */
cutname(name)
	int	name;	/* a single character */
{
	cbname = name;
}




/* This function copies a selected segment of text to a cut buffer */
cut(from, to)
	MARK	from;		/* start of text to cut */
	MARK	to;		/* end of text to cut */
{
	int		first;	/* logical number of first block in cut */
	int		last;	/* logical number of last block used in cut */
	long		line;	/* a line number */
	register struct cutbuf *cb;
	register long	l;
	register int	i;
	register char	*scan;
	char		*blkc;

	/* decide which cut buffer to use */
	if (!cbname)
	{
		/* free up the last annonymous cut buffer */
		cutfree(&annon[NANNONS - 1]);

		/* shift the annonymous cut buffers */
		for (i = NANNONS - 1; i > 0; i--)
		{
			annon[i] = annon[i - 1];
		}

		/* use the first annonymous cut buffer */
		cb = annon;
		cb->nblks = 0;
	}
	else if (cbname >= 'a' && cbname <= 'z')
	{
		cb = &named[cbname - 'a'];
		cutfree(cb);
	}
	else if (cbname == '.')
	{
		cb = &named[26];
		cutfree(cb);
	}
	else
	{
		msg("Invalid cut buffer name: \"%c", cbname);
		cbname = '\0';
		return;
	}
	cbname = '\0';
	cb->fd = -1;

	/* detect whether we're doing a line mode cut */
	cb->lnmode = (markidx(from) == 0 && markidx(to) == 0);

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

	/* Reporting... */	
	if (markidx(from) == 0 && markidx(to) == 0)
	{
		rptlines = markline(to) - markline(from);
		rptlabel = "yanked";
	}

	/* ---------- */
blksync();
	/* find the first block in the cut */
	line = markline(from);
	for (first = 1; line > lnum[first]; first++)
	{
	}

	/* fetch text of the block containing that line */
	blkc = scan = blkget(first)->c;

	/* find the mark in the block */
	for (l = lnum[first - 1]; ++l < line; )
	{
		while (*scan++ != '\n')
		{
		}
	}
	scan += markidx(from);

	/* remember the offset of the start */
	cb->start = scan - blkc;

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

	/* find the last block in the cut */
	line = markline(to);
	for (last = first; line > lnum[last]; last++)
	{
	}

	/* fetch text of the block containing that line */
	if (last != first)
	{
		blkc = scan = blkget(last)->c;
	}
	else
	{
		scan = blkc;
	}

	/* find the mark in the block */
	for (l = lnum[last - 1]; ++l < line; )
	{
		while (*scan++ != '\n')
		{
		}
	}
	if (markline(to) <= nlines)
	{
		scan += markidx(to);
	}

	/* remember the offset of the end */
	cb->end = scan - blkc;

	/* ------- */

	/* remember the physical block numbers of all included blocks */
	cb->nblks = last - first;
	if (cb->end > 0)
	{
		cb->nblks++;
	}
	cb->phys = (short *)malloc((unsigned)(cb->nblks * sizeof(short)));
	for (i = 0; i < cb->nblks; i++)
	{
		cb->phys[i] = hdr.n[first++];
	}
}


static readcutblk(cb, blkno)
	struct cutbuf	*cb;
	int		blkno;
{
	int		fd;	/* either tmpfd or cb->fd */

	/* decide which fd to use */
	if (cb->fd >= 0)
	{
		fd = cb->fd;
	}
	else
	{
		fd = tmpfd;
	}

	/* get the block */
	lseek(fd, (long)cb->phys[blkno] * (long)BLKSIZE, 0);
	if (read(fd, tmpblk.c, BLKSIZE) != BLKSIZE)
	{
		msg("Error reading back from tmp file for pasting!");
	}
}


/* This function inserts text from a cut buffer, and returns the MARK where
 * insertion ended.  Return MARK_UNSET on errors.
 */
MARK paste(at, after, retend)
	MARK	at;	/* where to insert the text */
	int	after;	/* boolean: insert after mark? (rather than before) */
	int	retend;	/* boolean: return end marker (rather than start) */
{
	register struct cutbuf	*cb;
	register int		i;

	/* decide which cut buffer to use */
	if (cbname >= 'a' && cbname <= 'z')
	{
		cb = &named[cbname - 'a'];
	}
	else if (cbname >= '1' && cbname <= '9')
	{
		cb = &annon[cbname - '1'];
	}
	else if (cbname == '.')
	{
		cb = &named[26];
	}
	else if (!cbname)
	{
		cb = annon;
	}
	else
	{
		msg("Invalid cut buffer name: \"%c", cbname);
		return MARK_UNSET;
	}

	/* make sure it isn't empty */
	if (cb->nblks == 0)
	{
		if (cbname)
			msg("Cut buffer \"%c is empty", cbname);
		else
			msg("Cut buffer is empty");
		cbname = '\0';
		return MARK_UNSET;
	}
	cbname = '\0';

	/* adjust the insertion MARK for "after" and line-mode cuts */
	if (cb->lnmode)
	{
		at &= ~(BLKSIZE - 1);
		if (after)
		{
			at += BLKSIZE;
		}
	}
	else if (after)
	{
		/* careful! if markidx(at) == 0 we might be pasting into an
		 * empty line -- so we can't blindly increment "at".
		 */
		if (markidx(at) == 0)
		{
			pfetch(markline(at));
			if (plen != 0)
			{
				at++;
			}
		}
		else
		{
			at++;
		}
	}

	/* put a copy of the "at" mark in the mark[] array, so it stays in
	 * sync with changes made via add().
	 */
	mark[27] = at;

	/* simple one-block paste? */
	if (cb->nblks == 1)
	{
		/* get the block */
		readcutblk(cb, 0);

		/* isolate the text we need within it */
		if (cb->end)
		{
			tmpblk.c[cb->end] = '\0';
		}

		/* insert it */
		ChangeText
		{
			add(at, &tmpblk.c[cb->start]);
		}
	}
	else
	{
		/* multi-block paste */

		ChangeText
		{
			i = cb->nblks - 1;

			/* add text from the last block first */
			if (cb->end > 0)
			{
				readcutblk(cb, i);
				tmpblk.c[cb->end] = '\0';
				add(at, tmpblk.c);
				i--;
			}

			/* add intervening blocks */
			while (i > 0)
			{
				readcutblk(cb, i);
				add(at, tmpblk.c);
				i--;
			}

			/* add text from the first cut block */
			readcutblk(cb, 0);
			add(at, &tmpblk.c[cb->start]);
		}
	}

	/* Reporting... */
	rptlines = markline(mark[27]) - markline(at);
	rptlabel = "pasted";

	/* correct the redraw range */
	redrawafter = preredraw = markline(at);
	postredraw = markline(mark[27]);

	/* return the mark at the beginning of inserted text */
	if (retend)
	{
		return mark[27] - 1L;
	}
	return at;
}
SHAR_EOF
fi
if test -f 'elvis.ndx'
then
	echo shar: "will not over-write existing file 'elvis.ndx'"
else
cat << \SHAR_EOF > 'elvis.ndx'

















                   @@@@@@@  @       @     @   @@@    @@@@@
                   @        @       @     @    @    @     @
                   @        @       @     @    @    @
                   @@@@@    @       @     @    @     @@@@@
                   @        @        @   @     @          @
                   @        @         @ @      @    @     @
                   @@@@@@@  @@@@@@@    @      @@@    @@@@@

                            - a clone of vi/ex -
                                 version 1.3






















Author: Steve Kirkendall
        9665 SW Serena Way
        Beaverton, OR 97007

E-Mail: kirkenda@cs.pdx.edu
or      ...uunet!tektronix!psueea!eecs!kirkenda

Phone:  (503) 642-9905














     Introduction ..................................................  1
       Overview of Elvis ...........................................  1

     Visual Mode Commands ..........................................  2
       Input Mode ..................................................  5
       Arrow keys in Input Mode ....................................  5
       Digraphs ....................................................  6

     Colon Mode Commands ...........................................  7
       Line Specifiers .............................................  8
       Text Entry Commands .........................................  9
       Cut & Paste Commands ........................................  9
       Display Text Commands .......................................  9
       Global Operations Commands .................................. 10
       Line Editing Commands ....................................... 10
       Undo Command ................................................ 10
       Configuration & Status Commands ............................. 10
       Multiple File Commands ...................................... 12
       Switching Files Commands .................................... 12
       Exit Commands ............................................... 12
       File I/O Commands ........................................... 13
       Directory Commands .......................................... 13
       Debugging Commands .......................................... 13

     Regular Expressions ........................................... 14
       Syntax ...................................................... 14
       Options ..................................................... 14
       Substitutions ............................................... 15

     Options ....................................................... 16
       autoindent .................................................. 17
       autowrite ................................................... 17
       charattr .................................................... 17
       columns ..................................................... 17
       directory ................................................... 17
       errorbells .................................................. 18
       exrefresh ................................................... 18
       hideformat .................................................. 18
       ignorecase .................................................. 18
       inputmode ................................................... 18
       keytime ..................................................... 18
       keywordprg .................................................. 19
       lines ....................................................... 19
       list ........................................................ 19
       magic ....................................................... 20
       paragraphs .................................................. 20
       readonly .................................................... 20
       report ...................................................... 20
       scroll ...................................................... 21
       sections .................................................... 21
       shell ....................................................... 21
       shiftwidth .................................................. 21
       showmode .................................................... 21
       sidescroll .................................................. 21
       sync ........................................................ 22
       tabstop ..................................................... 22










       term ........................................................ 22
       vbell ....................................................... 22
       warn ........................................................ 22
       wrapmargin .................................................. 23
       wrapscan .................................................... 23

     Programs ...................................................... 24
       elvis, ex, vi, view, input - The editor ..................... 24
       ctags - Generates "tags" and (optionally) "refs" files ...... 24
       ref - Display a C function header ........................... 25
       virec - Recover the modified version after a crash .......... 25

     Differences between elvis and the real vi/ex .................. 26
       Extensions .................................................. 26
       Omissions ................................................... 28

     Internal ...................................................... 29
       The temporary file .......................................... 29
       Implementation of Editing ................................... 29
       Marks and the Cursor ........................................ 30
       Colon Command Interpretation ................................ 30
       Screen Control .............................................. 31
       Portability ................................................. 31

     CFLAGS ........................................................ 32
       -DM_SYSV, -DTOS, -DOS9 ...................................... 32
       -DDATE ...................................................... 32
       -DNBUFS ..................................................... 32
       -DBLKSIZE ................................................... 32
       -DTMPDIR .................................................... 33
       -DEXRC -DHMEXRC -DSYSEXRC ................................... 33
       -DKEYWORDPRG ................................................ 33
       -DDEBUG ..................................................... 33
       -DNO_CHARATTR ............................................... 33
       -DNO_RECYCLE ................................................ 33
       -DNO_SENTENCE ............................................... 34
       -DNO_CHARSEARCH ............................................. 34
       -DNO_EXTENSIONS ............................................. 34
       -DNO_MAGIC .................................................. 34
       -DNO_SHOWMODE ............................................... 34
       -DNO_CURSORSHAPE ............................................ 34
       -DNO_DIGRAPH ................................................ 35

     Termcap ....................................................... 36
       Required numeric capabilities ............................... 36
       Required string capabilities ................................ 36
       Boolean capabilities ........................................ 36
       Optional string capabilities ................................ 36
       Optional strings received from the keyboard ................. 36
       Optional capabilities that describe character attributes .... 37
       Optional capabilities that affect the shape of the cursor ... 37
       An example .................................................. 37

     Environment Variables ......................................... 38
       TERM, TERMCAP ............................................... 38
       TMP, TEMP ................................................... 38










       EXINIT ...................................................... 38
       SHELL, COMSPEC .............................................. 38
       HOME ........................................................ 38

     Versions ...................................................... 39
       BSD UNIX .................................................... 39
       System-V UNIX ............................................... 39
       SCO Xenix ................................................... 39
       Minix ....................................................... 40
       MS-DOS ...................................................... 40
       Atari TOS ................................................... 41


















































SHAR_EOF
fi
if test -f 'ex.c'
then
	echo shar: "will not over-write existing file 'ex.c'"
else
cat << \SHAR_EOF > 'ex.c'
/* ex.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 for reading ex commands. */

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

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

/* This data type is used to describe the possible argument combinations */
typedef short ARGT;
#define FROM	1		/* allow a linespec */
#define	TO	2		/* allow a second linespec */
#define BANG	4		/* allow a ! after the command name */
#define EXTRA	8		/* allow extra args after command name */
#define XFILE	16		/* expand wildcards in extra part */
#define NOSPC	32		/* no spaces allowed in the extra part */
#define	DFLALL	64		/* default file range is 1,$ */
#define DFLNONE	128		/* no default file range */
#define NODFL	256		/* do not default to the current file name */
#define EXRCOK	512		/* can be in a .exrc file */
#define FILES	(XFILE + EXTRA)	/* multiple extra files allowed */
#define WORD1	(EXTRA + NOSPC)	/* one extra word allowed */
#define FILE1	(FILES + NOSPC)	/* 1 file allowed, defaults to current file */
#define NAMEDF	(FILE1 + NODFL)	/* 1 file allowed, defaults to "" */
#define NAMEDFS	(FILES + NODFL)	/* multiple files allowed, default is "" */
#define RANGE	(FROM + TO)	/* range of linespecs allowed */
#define NONE	0		/* no args allowed at all */

/* This array maps ex command names to command codes. The order in which
 * command names are listed below is significant -- ambiguous abbreviations
 * are always resolved to be the first possible match.  (e.g. "r" is taken
 * to mean "read", not "rewind", because "read" comes before "rewind")
 */
static struct
{
	char	*name;	/* name of the command */
	CMD	code;	/* enum code of the command */
	void	(*fn)();/* function which executes the command */
	ARGT	argt;	/* command line arguments permitted/needed/used */
}
	cmdnames[] =
{   /*	cmd name	cmd code	function	arguments */
	{"append",	CMD_APPEND,	cmd_append,	FROM		},
#ifdef DEBUG
	{"bug",		CMD_DEBUG,	cmd_debug,	RANGE+BANG+EXTRA},
#endif
	{"change",	CMD_CHANGE,	cmd_append,	RANGE		},
	{"delete",	CMD_DELETE,	cmd_delete,	RANGE+WORD1	},
	{"edit",	CMD_EDIT,	cmd_edit,	BANG+FILE1	},
	{"file",	CMD_FILE,	cmd_file,	NONE		},
	{"global",	CMD_GLOBAL,	cmd_global,	RANGE+BANG+EXTRA+DFLALL},
	{"insert",	CMD_INSERT,	cmd_append,	FROM		},
	{"join",	CMD_INSERT,	cmd_join,	RANGE		},
	{"k",		CMD_MARK,	cmd_mark,	FROM+WORD1	},
	{"list",	CMD_LIST,	cmd_list,	RANGE		},
	{"move",	CMD_MOVE,	cmd_move,	RANGE+EXTRA	},
	{"next",	CMD_NEXT,	cmd_next,	BANG+NAMEDFS	},
	{"Next",	CMD_PREVIOUS,	cmd_next,	BANG		},
	{"print",	CMD_PRINT,	cmd_print,	RANGE		},
	{"quit",	CMD_QUIT,	cmd_quit,	BANG		},
	{"read",	CMD_READ,	cmd_read,	FROM+BANG+NAMEDF},
	{"substitute",	CMD_SUBSTITUTE,	cmd_substitute,	RANGE+EXTRA	},
	{"to",		CMD_COPY,	cmd_move,	RANGE+EXTRA	},
	{"undo",	CMD_UNDO,	cmd_undo,	NONE		},
	{"vglobal",	CMD_VGLOBAL,	cmd_global,	RANGE+EXTRA+DFLALL},
	{"write",	CMD_WRITE,	cmd_write,	RANGE+BANG+FILE1+DFLALL},
	{"xit",		CMD_XIT,	cmd_xit,	BANG		},
	{"yank",	CMD_YANK,	cmd_delete,	RANGE+WORD1	},

	{"!",		CMD_BANG,	cmd_shell,	EXRCOK+RANGE+NAMEDFS+DFLNONE},
	{"<",		CMD_SHIFTL,	cmd_shift,	RANGE		},
	{">",		CMD_SHIFTR,	cmd_shift,	RANGE		},
	{"=",		CMD_FILE,	cmd_file,	RANGE		},

	{"args",	CMD_ARGS,	cmd_args,	EXRCOK+NAMEDFS	},
	{"cd",		CMD_CD,		cmd_cd,		EXRCOK+NAMEDF	},
	{"copy",	CMD_COPY,	cmd_move,	RANGE+EXTRA	},
#ifndef NO_DIGRAPH
	{"digraph",	CMD_DIGRAPH,	cmd_digraph,	EXRCOK+BANG+EXTRA},
#endif
	{"ex",		CMD_EDIT,	cmd_edit,	BANG+FILE1	},
	{"map",		CMD_MAP,	cmd_map,	EXRCOK+BANG+EXTRA},
#ifndef NO_EXTENSIONS
	{"mkexrc",	CMD_MKEXRC,	cmd_mkexrc,	NONE		},
#endif
	{"put",		CMD_PUT,	cmd_put,	FROM+WORD1	},
	{"set",		CMD_SET,	cmd_set,	EXRCOK+EXTRA	},
	{"shell",	CMD_SHELL,	cmd_shell,	NONE		},
	{"source",	CMD_SOURCE,	cmd_source,	EXRCOK+NAMEDF	},
	{"tag",		CMD_TAG,	cmd_tag,	BANG+WORD1	},
	{"version",	CMD_VERSION,	cmd_version,	EXRCOK+NONE	},
	{"visual",	CMD_VISUAL,	cmd_visual,	NONE		},
	{"wq",		CMD_WQUIT,	cmd_xit,	NONE		},

#ifdef DEBUG
	{"debug",	CMD_DEBUG,	cmd_debug,	RANGE+BANG+EXTRA},
	{"validate",	CMD_VALIDATE,	cmd_validate,	BANG		},
#endif
	{"chdir",	CMD_CD,		cmd_cd,		EXRCOK+NAMEDF	},
	{"mark",	CMD_MARK,	cmd_mark,	FROM+WORD1	},
	{"previous",	CMD_PREVIOUS,	cmd_next,	BANG		},
	{"rewind",	CMD_REWIND,	cmd_next,	BANG		},
	{"unmap",	CMD_UNMAP,	cmd_map,	EXRCOK+BANG+EXTRA},

	{(char *)0}
};


/* This function parses a search pattern - given a pointer to a / or ?,
 * it replaces the ending / or ? with a \0, and returns a pointer to the
 * stuff that came after the pattern.
 */
char	*parseptrn(ptrn)
	register char	*ptrn;
{
	register char 	*scan;


	for (scan = ptrn + 1;
	     *scan && *scan != *ptrn;
	     scan++)
	{
		/* allow backslashed versions of / and ? in the pattern */
		if (*scan == '\\' && scan[1] != '\0')
		{
			scan++;
		}
	}
	if (*scan)
	{
		*scan++ = '\0';
	}

	return scan;
}


/* This function parses a line specifier for ex commands */
char *linespec(s, markptr)
	register char	*s;		/* start of the line specifier */
	MARK		*markptr;	/* where to store the mark's value */
{
	long		num;
	register char	*t;

	/* parse each ;-delimited clause of this linespec */
	do
	{
		/* skip an initial ';', if any */
		if (*s == ';')
		{
			s++;
		}

		/* skip leading spaces */
		while (isascii(*s) && isspace(*s))
		{
			s++;
		}
	
		/* dot means current position */
		if (*s == '.')
		{
			s++;
			*markptr = cursor;
		}
		/* '$' means the last line */
		else if (*s == '$')
		{
			s++;
			*markptr = m_toline(cursor, nlines);
		}
		/* digit means an absolute line number */
		else if (isascii(*s) && isdigit(*s))
		{
			for (num = 0; isascii(*s) && isdigit(*s); s++)
			{
				num = num * 10 + *s - '0';
			}
			*markptr = m_toline(cursor, num);
		}
		/* appostrophe means go to a set mark */
		else if (*s == '\'')
		{
			s++;
			*markptr = m_tomark(cursor, 1L, (int)*s);
			s++;
		}
		/* slash means do a search */
		else if (*s == '/' || *s == '?')
		{
			/* put a '\0' at the end of the search pattern */
			t = parseptrn(s);
	
			/* search for the pattern */
			if (*s == '/')
			{
				pfetch(markline(*markptr));
				*markptr = (*markptr & ~(BLKSIZE - 1)) + plen - 1;
				*markptr = m_fsrch(*markptr, s + 1);
			}
			else
			{
				*markptr &= ~(BLKSIZE - 1);
				*markptr = m_bsrch(*markptr, s + 1);
			}
	
			/* adjust command string pointer */
			s = t;
		}
	
		/* if linespec was faulty, quit now */
		if (!*markptr)
		{
			return s;
		}
	
		/* maybe add an offset */
		if (*s == '-')
		{
			s++;
			for (num = 0; *s >= '0' && *s <= '9'; s++)
			{
				num = num * 10 + *s - '0';
			}
			if (num == 0)
			{
				num = 1;
			}
			*markptr = m_up(*markptr, num);
		}
		else if (*s == '+')
		{
			s++;
			for (num = 0; *s >= '0' && *s <= '9'; s++)
			{
				num = num * 10 + *s - '0';
			}
			if (num == 0)
			{
				num = 1;
			}
			*markptr = m_down(*markptr, num);
		}
	} while (*s == ';' || *s == '+' || *s == '-');

	return s;
}



/* This function reads an ex command and executes it. */
ex()
{
	char		cmdbuf[80];
	register int	cmdlen;

	/* read a line */
	cmdlen = vgets(':', cmdbuf, sizeof cmdbuf);
	if (cmdlen < 0)
	{
		return;
	}
	addch('\n');
	refresh();

	/* if empty line, assume ".+1" */
	if (cmdlen == 0)
	{
		strcpy(cmdbuf, ".+1");
	}

	/* parse & execute the command */
	doexcmd(cmdbuf);
}

doexcmd(cmdbuf)
	char		*cmdbuf;	/* string containing an ex command */
{
	register char	*scan;		/* used to scan thru cmdbuf */
	MARK		frommark;	/* first linespec */
	MARK		tomark;		/* second linespec */
	register int	cmdlen;		/* length of the command name given */
	CMD		cmd;		/* what command is this? */
	ARGT		argt;		/* argument types for this command */
	short		forceit;	/* bang version of a command? */
	register int	cmdidx;		/* index of command */
	register char	*build;		/* used while copying filenames */
	int		iswild;		/* boolean: filenames use wildcards? */
	int		isdfl;		/* using default line ranges? */
	int		didsub;		/* did we substitute file names for % or # */


	/* ex commands can't be undone via the shift-U command */
	U_line = 0L;

	/* ignore command lines that start with "#" */
	if (*cmdbuf == '#' || *cmdbuf == '"')
	{
		return;
	}

	/* permit extra colons at the start of the line */
	while (*cmdbuf == ':')
	{
		cmdbuf++;
	}

	/* parse the line specifier */
	scan = cmdbuf;
	if (nlines < 1)
	{
		/* no file, so don't allow addresses */
	}
	else if (*scan == '%')
	{
		/* '%' means all lines */
		frommark = m_toline(cursor, 1L);
		tomark = m_toline(cursor, nlines);
		scan++;
	}
	else
	{
		frommark = cursor;
		scan = linespec(scan, &frommark);
		tomark = frommark;
		if (frommark && *scan == ',')
		{
			scan++;
			scan = linespec(scan, &tomark);
		}
		if (!tomark)
		{
			/* faulty line spec -- fault already described */
			return;
		}
		if (frommark > tomark)
		{
			msg("first address exceeds the second");
			return;
		}
	}
	isdfl = (scan == cmdbuf);

	/* skip whitespace */
	while (isascii(*scan) && isspace(*scan))
	{
		scan++;
	}

	/* if no command, then just move the cursor to the mark & print */
	if (!*scan)
	{
		cursor = tomark;
		if (mode != MODE_EX)
		{
			return;
		}
		scan = "p";
	}

	/* figure out how long the command name is */
	if (isascii(*scan) && !isalpha(*scan))
	{
		cmdlen = 1;
	}
	else
	{
		for (cmdlen = 1;
		     !isascii(scan[cmdlen]) || isalpha(scan[cmdlen]);
		     cmdlen++)
		{
		}
	}

	/* lookup the command code */
	for (cmdidx = 0;
	     cmdnames[cmdidx].name && strncmp(scan, cmdnames[cmdidx].name, cmdlen);
	     cmdidx++)
	{
	}
	argt = cmdnames[cmdidx].argt;
	cmd = cmdnames[cmdidx].code;
	if (cmd == CMD_NULL)
	{
		msg("Unknown command \"%.*s\"", cmdlen, scan);
		return;
	}

	/* if the command ended with a bang, set the forceit flag */
	scan += cmdlen;
	if ((argt & BANG) && *scan == '!')
	{
		scan++;
		forceit = 1;
	}
	else
	{
		forceit = 0;
	}

	/* skip any more whitespace, to leave scan pointing to arguments */
	while (isascii(*scan) && isspace(*scan))
	{
		scan++;
	}

	/* a couple of special cases for filenames */
	if (argt & XFILE)
	{
		/* if names were given, process them */
		if (*scan)
		{
			for (build = tmpblk.c, iswild = didsub = FALSE; *scan; scan++)
			{
				switch (*scan)
				{
				  case '%':
					if (!*origname)
					{
						msg("No filename to substitute for %");
						return;
					}
					strcpy(build, origname);
					while (*build)
					{
						build++;
					}
					didsub = TRUE;
					break;
	
				  case '#':
					if (!*prevorig)
					{
						msg("No filename to substitute for #");
						return;
					}
					strcpy(build, prevorig);
					while (*build)
					{
						build++;
					}
					didsub = TRUE;
					break;
	
				  case '*':
				  case '?':
#if	! (MSDOS || TOS)
				  case '[':
				  case '`':
				  case '{': /* } */
				  case '$':
				  case '~':
#endif
					*build++ = *scan;
					iswild = TRUE;
					break;

				  default:
					*build++ = *scan;
				}
			}
			*build = '\0';
	
			if (cmd == CMD_BANG
			 || cmd == CMD_READ && (forceit || tmpblk.c[0] != '!'))
			{
				if (didsub)
				{
					addch('\n');
					addstr(tmpblk.c);
					addch('\n');
					exrefresh();
				}
			}
			else
			{
				if (iswild && tmpblk.c[0] != '>')
				{
					scan = wildcard(tmpblk.c);
				}
			}
		}
		else /* no names given, maybe assume origname */
		{
			if (!(argt & NODFL))
			{
				strcpy(tmpblk.c, origname);
			}
			else
			{
				*tmpblk.c = '\0';
			}
		}

		scan = tmpblk.c;
	}

	/* bad arguments? */
	if (!(argt & EXRCOK) && nlines < 1L)
	{
		msg("Can't use the \"%s\" command in a %s file", cmdnames[cmdidx].name, EXRC);
		return;
	}
	if (!(argt & FROM) && frommark != cursor && nlines >= 1L)
	{
		msg("Can't use address with \"%s\" command.", cmdnames[cmdidx].name);
		return;
	}
	if (!(argt & TO) && tomark != frommark && nlines >= 1L)
	{
		msg("Can't use a range with \"%s\" command.", cmdnames[cmdidx].name);
		return;
	}
	if (!(argt & EXTRA) && *scan)
	{
		msg("Extra characters after \"%s\" command.", cmdnames[cmdidx].name);
		return;
	}
	if ((argt & NOSPC) && !(cmd == CMD_READ && (forceit || *scan == '!')))
	{
		for (build = scan; *build; build++)
		{
			if (isspace(*build))
			{
				msg("Too many %s to \"%s\" command.",
					(argt & XFILE) ? "filenames" : "arguments",
					cmdnames[cmdidx].name);
				return;
			}
		}
	}

	/* some commands have special default ranges */
	if (isdfl && (argt & DFLALL))
	{
		frommark = MARK_FIRST;
		tomark = MARK_LAST;
	}
	else if (isdfl && (argt & DFLNONE))
	{
		frommark = tomark = 0L;
	}

	/* act on the command */
	(*cmdnames[cmdidx].fn)(frommark, tomark, cmd, forceit, scan);
}


/* This function executes EX commands from a file.  It returns 1 normally, or
 * 0 if the file could not be opened for reading.
 */
int doexrc(filename)
	char	*filename;	/* name of a ".exrc" file */
{
	int	fd;		/* file descriptor */
	int	len;		/* length of the ".exrc" file */
	char	*cmd;		/* start of a command */
	char	*end;		/* used to search for the end of cmd */
	char	buf[MAXRCLEN];	/* buffer, holds the entire .exrc file */

	/* open the file, read it, and close */
	fd = open(filename, O_RDONLY);
	if (fd < 0)
	{
		return 0;
	}
	len = tread(fd, buf, MAXRCLEN);
	close(fd);

	/* find & do each command */
	for (cmd = buf; cmd < &buf[len]; cmd = end + 1)
	{
		/* find the end of the command */
		for (end = cmd; *end != '\n'; end++)
		{
		}
		*end = '\0';

		/* do it */
		doexcmd(cmd);
	}

	return 1;
}
SHAR_EOF
fi
if test -f 'input.c'
then
	echo shar: "will not over-write existing file 'input.c'"
else
cat << \SHAR_EOF > 'input.c'
/* input.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 input() function, which implements vi's INPUT mode.
 * It also contains the code that supports digraphs.
 */

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


#ifndef NO_DIGRAPH
static struct
{
	char	key1;
	char	key2;
	char	dig;
} digs[MAXDIGS];

static char digraph(key1, key2)
	char	key1;	/* the first character (punctuation) */
	char	key2;	/* the overtyped character (probably a letter) */
{
	int	i;

	/* can only be a digraph if key1 is punctuation */
	if (!isascii(key1) || !ispunct(key1))
	{
		return key2;
	}

	/* scan through the digraph chart */
	for (i = 0;
	     i < MAXDIGS && (!digs[i].dig || digs[i].key1 != key1 || digs[i].key2 != key2);
	     i++)
	{
	}

	/* if this combination isn't in there, just use the new key */
	if (i >= MAXDIGS)
	{
		return key2;
	}

	/* else use the digraph key */
	return digs[i].dig;
}

do_digraph(bang, args)
	int	bang;
	char	args[];
{
	int	i;
	int	dig;

	/* if no args, then display the existing digraphs */
	if (*args < ' ')
	{
		for (i = 0; i < MAXDIGS; i++)
		{
			if (digs[i].dig)
			{
				msg("digraph %c%c %c", digs[i].key1, digs[i].key2, digs[i].dig);
			}
		}
		return;
	}

	/* first character of a digraph must be punctuation */
	if (!isascii(args[0]) || !ispunct(args[0]))
	{
		msg("The first character of a digraph must be punctuation");
		return;
	}
	if (!args[1])
	{
		msg("Digraphs must be composed of two characters");
		return;
	}

	/* locate the new digraph character */
	for (i = 2; args[i] == ' ' || args[i] == '\t'; i++)
	{
	}
	dig = args[i];
	if (!bang && dig)
	{
		dig |= 0x80;
	}

	/* search for the digraph */
	for (i = 0; i < MAXDIGS && (digs[i].key1 != args[0] || digs[i].key2 != args[1]); i++)
	{
	}
	if (i >= MAXDIGS)
	{
		for (i = 0; i < MAXDIGS && digs[i].dig; i++)
		{
		}
		if (i >= MAXDIGS)
		{
			msg("Out of space in the digraph table");
			return;
		}
	}

	/* assign it the new digraph value */
	digs[i].key1 = args[0];
	digs[i].key2 = args[1];
	digs[i].dig = dig;
}

savedigs(fd)
	int		fd;
{
	int		i;
	static char	buf[] = "digraph! XX Y\n";

	for (i = 0; i < MAXDIGS; i++)
	{
		if (digs[i].dig)
		{
			buf[9] = digs[i].key1;
			buf[10] = digs[i].key2;
			buf[12] = digs[i].dig;
			write(fd, buf, 14);
		}
	}
}
#endif


/* This function allows the user to replace an existing (possibly zero-length)
 * chunk of text with typed-in text.  It returns the MARK of the last character
 * that the user typed in.
 */
MARK input(from, to, when)
	MARK	from;	/* where to start inserting text */
	MARK	to;	/* extent of text to delete */
	int	when;	/* either WHEN_VIINP or WHEN_VIREP */
{
	char	key[2];	/* key char followed by '\0' char */
	char	*build;	/* used in building a newline+indent string */
	char	*scan;	/* used while looking at the indent chars of a line */
	MARK	m;	/* some place in the text */
#ifndef NO_EXTENSIONS
	int	quit = FALSE;	/* boolean: are we exiting after this? */
#endif

#ifdef DEBUG
	/* if "from" and "to" are reversed, complain */
	if (from > to)
	{
		msg("ERROR: input(%ld:%d, %ld:%d)",
			markline(from), markidx(from),
			markline(to), markidx(to));
		return MARK_UNSET;
	}
#endif

	key[1] = 0;

	/* if we're replacing text with new text, save the old stuff */
	/* (Alas, there is no easy way to save text for replace mode) */
	if (from != to)
	{
		cut(from, to);
	}

	ChangeText
	{
		/* if doing a dot command, then reuse the previous text */
		if (doingdot)
		{
			/* delete the text that's there now */
			if (from != to)
			{
				delete(from, to);
			}

			/* insert the previous text */
			cutname('.');
			cursor = paste(from, FALSE, TRUE) + 1L;
			/* explicitly set preredraw and postredraw */
			redrawafter = markline(from);
			preredraw = markline(to);
			postredraw = markline(cursor);
		}
		else /* interactive version */
		{
			/* if doing a change within the line... */
			if (from != to && markline(from) == markline(to))
			{
				/* mark the end of the text with a "$" */
				change(to - 1, to, "$");
			}
			else
			{
				/* delete the old text right off */
				if (from != to)
				{
					delete(from, to);
				}
				to = from;
			}

			/* handle autoindent of the first line, maybe */
			cursor = from;
			if (*o_autoindent && markline(cursor) > 1L && markidx(cursor) == 0)
			{
				/* Only autoindent blank lines. */
				pfetch(markline(cursor));
				if (plen == 0)
				{
					/* Okay, we really want to autoindent */
					pfetch(markline(cursor) - 1L);
					for (scan = ptext, build = tmpblk.c;
					     *scan == ' ' || *scan == '\t';
					     )
					{
						*build++ = *scan++;
					}
					if (build > tmpblk.c)
					{
						*build = '\0';
						add(cursor, tmpblk.c);
						cursor += (build - tmpblk.c);
					}
				}
			}

			/* repeatedly add characters from the user */
			for (;;)
			{
				/* Get a character */
				redraw(cursor, TRUE);
				key[0] = getkey(when);

				/* if whitespace & wrapmargin is set & we're
				/* past the warpmargin, then change the
				/* whitespace character into a newline
				 */
				if ((*key == ' ' || *key == '\t')
				 && *o_wrapmargin != 0)
				{
					pfetch(markline(cursor));
					if (idx2col(cursor, ptext, TRUE) > COLS - (*o_wrapmargin & 0xff))
					{
						*key = '\n';
					}
				}

				/* process it */
				switch (*key)
				{
#ifndef NO_EXTENSIONS
				  case 0: /* special movement mapped keys */
					*key = getkey(0);
					switch (*key)
					{
					  case 'h':	m = m_left(cursor, 0L);		break;
					  case 'j':	m = m_down(cursor, 0L);		break;
					  case 'k':	m = m_up(cursor, 0L);		break;
					  case 'l':	m = cursor + 1;				break;
					  case 'b':	m = m_bword(cursor, 0L);	break;
					  case 'w':	m = m_fword(cursor, 0L);	break;
					  case '^':	m = m_front(cursor, 0L);	break;
					  case '$':	m = m_rear(cursor, 0L);		break;
					  case ctrl('B'):
					  case ctrl('F'):
							m = m_scroll(cursor, 0L, *key); break;
					  case 'x':	m = v_xchar(cursor, 0L);	break;
					  case 'i':	m = to = from = cursor;		break;
					  default:	m = MARK_UNSET;			break;
					}
					/* adjust the moved cursor */
					m = adjmove(cursor, m, (*key == 'j' || *key == 'k' ? 0x20 : 0));
					if (*key == '$' || (*key == 'l' && m <= cursor))
					{
						m++;
					}
					/* if the cursor is reasonable, use it */
					if (m == MARK_UNSET)
					{
						beep();
					}
					else
					{
						if (to > cursor)
						{
							delete(cursor, to);
							redraw(cursor, TRUE);
						}
						from = to = cursor = m;
					}
					break;

				  case ctrl('Z'):
					if (getkey(0) == ctrl('Z'))
					{
						quit = TRUE;
						goto BreakBreak;
					}
					break;
#endif
				  case ctrl('['):
					goto BreakBreak;

				  case ctrl('U'):
					if (markline(cursor) == markline(from))
					{
						cursor = from;
					}
					else
					{
						cursor &= ~(BLKSIZE - 1);
					}
					break;

				  case ctrl('D'):
				  case ctrl('T'):
					mark[27] = cursor;
					cmd_shift(cursor, cursor, *key == ctrl('D') ? CMD_SHIFTL : CMD_SHIFTR, TRUE, "");
					if (mark[27])
					{
						cursor = mark[27];
					}
					else
					{
						cursor = m_front(cursor, 0L);
					}
					break;

				  case '\b':
					if (cursor <= from)
					{
						beep();
					}
					else if (markidx(cursor) == 0)
					{
						cursor -= BLKSIZE;
						pfetch(markline(cursor));
						cursor += plen;
					}
					else
					{
						cursor--;
					}
					break;

				  case ctrl('W'):
					m = m_bword(cursor, 1L);
					if (markline(m) == markline(cursor) && m >= from)
					{
						cursor = m;
						if (from > cursor)
						{
							from = cursor;
						}
					}
					else
					{
						beep();
					}
					break;

				  case '\n':
				  case '\r':
					build = tmpblk.c;
					*build++ = '\n';
					if (*o_autoindent)
					{
						pfetch(markline(cursor));
						for (scan = ptext; *scan == ' ' || *scan == '\t'; )
						{
							*build++ = *scan++;
						}
					}
					*build = 0;
					if (cursor >= to && when != WHEN_VIREP)
					{
						add(cursor, tmpblk.c);
					}
					else
					{
						change(cursor, to, tmpblk.c);
					}
					redraw(cursor, TRUE);
					to = cursor = (cursor & ~(BLKSIZE - 1))
							+ BLKSIZE
							+ (int)(build - tmpblk.c) - 1;
					break;

				  case ctrl('A'):
				  case ctrl('P'):
					if (cursor < to)
					{
						delete(cursor, to);
					}
					if (*key == ctrl('A'))
					{
						cutname('.');
					}
					to = cursor = paste(cursor, FALSE, TRUE) + 1L;
					break;

				  case ctrl('V'):
					if (cursor >= to && when != WHEN_VIREP)
					{
						add(cursor, "^");
					}
					else
					{
						change(cursor, to, "^");
					}
					redraw(cursor, TRUE);
					*key = getkey(0);
					if (*key == '\n')
					{
						/* '\n' too hard to handle */
						*key = '\r';
					}
					change(cursor, cursor + 1, key);
					cursor++;
					if (cursor > to)
					{
						to = cursor;
					}
					break;

				  case ctrl('L'):
				  case ctrl('R'):
					redraw(MARK_UNSET, FALSE);
					break;

				  default:
					if (cursor >= to && when != WHEN_VIREP)
					{
						add(cursor, key);
						cursor++;
						to = cursor;
					}
					else
					{
						pfetch(markline(cursor));
						if (markidx(cursor) == plen)
						{
							add(cursor, key);
						}
						else
						{
#ifndef NO_DIGRAPH
							*key = digraph(ptext[markidx(cursor)], *key);
#endif
							change(cursor, cursor + 1, key);
						}
						cursor++;
					}
				} /* end switch(*key) */
			} /* end for(;;) */
BreakBreak:;

			/* delete any excess characters */
			if (cursor < to)
			{
				delete(cursor, to);
			}

		} /* end if doingdot else */

	} /* end ChangeText */

	/* put the new text into a cut buffer for possible reuse */
	if (!doingdot)
	{
		blksync();
		cutname('.');
		cut(from, cursor);
	}

	/* move to last char that we inputted, unless it was newline */
	if (markidx(cursor) != 0)
	{
		cursor--;
	}
	redraw(cursor, FALSE);

#ifndef NO_EXTENSIONS
	if (quit)
	{
		refresh();
		cursor = v_xit(cursor, 0L, 'Z');
	}
#endif

	rptlines = 0L;
	return cursor;
}
SHAR_EOF
fi
if test -f 'main.c'
then
	echo shar: "will not over-write existing file 'main.c'"
else
cat << \SHAR_EOF > 'main.c'
/* main.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 main() function of vi */

#include "config.h"
#include <signal.h>
#include <setjmp.h>
#include "vi.h"

extern		trapint(); /* defined below */
extern char	*getenv();
jmp_buf		jmpenv;

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

void main(argc, argv)
	int	argc;
	char	*argv[];
{
	int	i;
	char	*cmd = (char *)0;
	char	*tag = (char *)0;
	char	*str;
#if	MSDOS || TOS
	char firstarg[256];
#else
	char *firstarg;
#endif

#ifndef NO_ARGV0
	/* set mode to MODE_VI or MODE_EX depending on program name */
	switch (argv[0][strlen(argv[0]) - 1])
	{
	  case 'x':			/* "ex" */
		mode = MODE_EX;
		break;

	  case 'w':			/* "view" */
		mode = MODE_VI;
		*o_readonly = TRUE;
		break;
#ifndef NO_EXTENSIONS
	  case 't':			/* "edit" or "input" */
		mode = MODE_VI;
		*o_inputmode = TRUE;
		break;
#endif
	  default:			/* "vi" or "elvis" */
		mode = MODE_VI;
	}
#endif

	/* start curses */
	initscr();
	cbreak();
	noecho();
	scrollok(stdscr, TRUE);

#ifndef DEBUG
#ifdef	SIGQUIT
	/* normally, we ignore SIGQUIT.  SIGINT is trapped later */
	signal(SIGQUIT, SIG_IGN);
#endif
#endif

	/* initialize the options */
	initopts();

	/* map the arrow keys.  The KU,KD,KL,and KR variables correspond to
	 * the :ku=: (etc.) termcap capabilities.  The variables are defined
	 * as part of the curses package.
	 */
	if (has_KU) mapkey(has_KU, "k",    WHEN_VICMD|WHEN_INMV, "<Up>");
	if (has_KD) mapkey(has_KD, "j",    WHEN_VICMD|WHEN_INMV, "<Down>");
	if (has_KL) mapkey(has_KL, "h",    WHEN_VICMD|WHEN_INMV, "<Left>");
	if (has_KR) mapkey(has_KR, "l",    WHEN_VICMD|WHEN_INMV, "<Right>");
	if (has_HM) mapkey(has_HM, "^",    WHEN_VICMD|WHEN_INMV, "<Home>");
	if (has_EN) mapkey(has_EN, "$",    WHEN_VICMD|WHEN_INMV, "<End>");
	if (has_PU) mapkey(has_PU, "\002", WHEN_VICMD|WHEN_INMV, "<PgUp>");
	if (has_PD) mapkey(has_PD, "\006", WHEN_VICMD|WHEN_INMV, "<PgDn>");
#if	MSDOS
	if (*o_pcbios)
	{
		mapkey("#R", "i", WHEN_VICMD|WHEN_INMV,	"<Insrt>");
		mapkey("#S", "x", WHEN_VICMD|WHEN_INMV,	"<Del>");
		mapkey("#s", "B", WHEN_VICMD|WHEN_INMV,	"^<left>");
		mapkey("#t", "W", WHEN_VICMD|WHEN_INMV,	"^<right>");
	}
#else
	if (ERASEKEY != '\177')
	{
		mapkey("\177", "x", WHEN_VICMD|WHEN_INMV, "<Del>");
	}
#endif

	/* process any flags */
	for (i = 1; i < argc && *argv[i] == '-'; i++)
	{
		switch (argv[i][1])
		{
		  case 'R':	/* readonly */
			*o_readonly = TRUE;
			break;

		  case 'r':	/* recover */
			msg("Use the `virec` command to recover lost files\n");
			refresh();
			endwin();
			exit(0);
			break;

		  case 't':	/* tag */
			if (argv[i][2])
			{
				tag = argv[i] + 2;
			}
			else
			{
				i++;
				tag = argv[i];
			}
			break;

		  case 'v':	/* vi mode */
			mode = MODE_VI;
			break;

		  case 'e':	/* ex mode */
			mode = MODE_EX;
			break;
#ifndef NO_EXTENSIONS
		  case 'i':	/* input mode */
			*o_inputmode = TRUE;
			break;
#endif
		  default:
			msg("Ignoring unknown flag \"%s\"", argv[i]);
		}
	}

	/* if we were given an initial ex command, save it... */
	if (i < argc && *argv[i] == '+')
	{
		if (argv[i][1])
		{
			cmd = argv[i++] + 1;
		}
		else
		{
			cmd = "$"; /* "vi + file" means start at EOF */
			i++;
		}
	}

	/* the remaining args are file names. */
	nargs = argc - i;
	if (nargs > 0)
	{
#if ! ( MSDOS || TOS )
		firstarg = argv[i];
#endif
		strcpy(args, argv[i]);
		while (++i < argc && strlen(args) + 1 + strlen(argv[i]) < sizeof args)
		{
			strcat(args, " ");
			strcat(args, argv[i]);
		}
	}
#if ! ( MSDOS || TOS )
	else
	{
		firstarg = "";
	}
#endif
	argno = 0;

#if MSDOS || TOS
	if (nargs > 0)
	{
		strcpy(args, wildcard(args));
		nargs = 1;
		for (i = 0; args[i]; i++)
		{
			if (args[i] == ' ')
			{
				nargs++;
			}
		}
		for (i = 0; args[i] && args[i] != ' '; i++)
		{
			firstarg[i] = args[i];
		}
		firstarg[i] = '\0';
	}
	else
	{
		firstarg[0] = '\0';
	}
#endif

	/* perform the .exrc files and EXINIT environment variable */
#ifdef SYSEXRC
	doexrc(SYSEXRC);
#endif
#ifdef HMEXRC
	str = getenv("HOME");
	if (str)
	{
		sprintf(tmpblk.c, "%s%c%s", str, SLASH, HMEXRC);
		doexrc(tmpblk.c);
	}
#endif
	doexrc(EXRC);
#ifdef EXINIT
	str = getenv(EXINIT);
	if (str)
	{
		doexcmd(str);
	}
#endif

	/* search for a tag now, if desired */
	blkinit();
	if (tag)
	{
		cmd_tag(MARK_FIRST, MARK_FIRST, CMD_TAG, 0, tag);
	}

	/* if no tag, or tag failed, then start with first arg */
	if (tmpfd < 0 && tmpstart(firstarg) == 0 && *origname)
	{
		ChangeText
		{
		}
		clrflag(file, MODIFIED);
	}
	
	/* now we do the immediate ex command that we noticed before */
	if (cmd)
	{
		doexcmd(cmd);
	}

	/* repeatedly call ex() or vi() (depending on the mode) until the
	 * mode is set to MODE_QUIT
	 */
	while (mode != MODE_QUIT)
	{
		if (setjmp(jmpenv))
		{
			/* Maybe we just aborted a change? */
			abortdo();
		}
#if	TURBOC
		signal(SIGINT, (void(*)()) trapint);
#else
		signal(SIGINT, trapint);
#endif

		switch (mode)
		{
		  case MODE_VI:
			vi();
			break;

		  case MODE_EX:
			ex();
			break;
#ifdef DEBUG
		  default:
			msg("mode = %d?", mode);
			mode = MODE_QUIT;
#endif
		}
	}

	/* free up the cut buffers */
	cutend();

	/* end curses */
#ifndef	NO_CURSORSHAPE	
	if (has_CQ)
		do_CQ();
#endif
	refresh();
	endwin();

	exit(0);
	/*NOTREACHED*/
}


/*ARGSUSED*/
trapint(signo)
	int	signo;
{
	resume_curses(FALSE);
	longjmp(jmpenv, 1);
}
SHAR_EOF
fi
if test -f 'misc.c'
then
	echo shar: "will not over-write existing file 'misc.c'"
else
cat << \SHAR_EOF > 'misc.c'
/* misc.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 which didn't seem happy anywhere else */

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


/* find a particular line & return a pointer to a copy of its text */
char *fetchline(line)
	long	line;	/* line number of the line to fetch */
{
	int		i;
	register char	*scan;	/* used to search for the line in a BLK */
	long		l;	/* line number counter */
	static BLK	buf;	/* holds ONLY the selected line (as string) */
	register char	*cpy;	/* used while copying the line */
	static long	nextline;	/* }  These four variables are used */
	static long	chglevel;	/*  } to implement a shortcut when  */
	static char	*nextscan;	/*  } consecutive lines are fetched */
	static long	nextlnum;	/* }                                */

	/* can we do a shortcut? */
	if (changes == chglevel && line == nextline)
	{
		scan = nextscan;
	}
	else
	{
		/* scan lnum[] to determine which block its in */
		for (i = 1; line > lnum[i]; i++)
		{
		}
		nextlnum = lnum[i];

		/* fetch text of the block containing that line */
		scan = blkget(i)->c;

		/* find the line in the block */
		for (l = lnum[i - 1]; ++l < line; )
		{
			while (*scan++ != '\n')
			{
			}
		}
	}

	/* copy it into a block by itself, with no newline */
	for (cpy = buf.c; *scan != '\n'; )
	{
		*cpy++ = *scan++;
	}
	*cpy = '\0';

	/* maybe speed up the next call to fetchline() ? */
	if (line < nextlnum)
	{
		nextline = line + 1;
		chglevel = changes;
		nextscan = scan + 1;
	}
	else
	{
		nextline = 0;
	}

	/* Calls to fetchline() interfere with calls to pfetch().  Make sure
	 * that pfetch() resets itself on its next invocation.
	 */
	pchgs = 0L;

	/* Return a pointer to the line's text */
	return buf.c;
}

/* find a particular line & delete it */
deleteline(line)
	long	line;	/* line number of the line to fetch */
{
	MARK	frommark, tomark;

	frommark = MARK_AT_LINE(line);
	tomark = frommark + BLKSIZE;
	delete(frommark, tomark);
}


/* insert a given line at a particular line number */
addline(l, txt)
	long	l;	/* line number where the new line should go */
	char	*txt;	/* text of line, terminated with '\0' (not '\n') */
{
	MARK	atmark;
	BLK	newtext;

	strcpy(newtext.c, txt);
	strcat(newtext.c, "\n");
	atmark = MARK_AT_LINE(l);
	add(atmark, newtext.c);
}


/* replace one version of a line with another */
changeline(l, txt)
	long	l;	/* line# of line to change */
	char	*txt;	/* new version of line, terminated with '\0' */
{
	deleteline(l);
	addline(l, txt);
}


/* error message from the regexp code */
void regerror(txt)
	char	*txt;	/* an error message */
{
	msg("RE error: %s", txt);
}

/* This function is equivelent to the pfetch() macro */
void	pfetch(l)
	long	l;	/* line number of line to fetch */
{
	if(l != pline || changes != pchgs)
	{
		pline = (l);
		ptext = fetchline(pline);
		plen = strlen(ptext);
		pchgs = changes;
	}
}
SHAR_EOF
fi
exit 0
#	End of shell archive
-------------------------------------------------------------------------------
Steve Kirkendall    kirkenda@cs.pdx.edu    uunet!tektronix!psueea!eecs!kirkenda