[alt.sources] Elvis 1.4, part 5 of 8

kirkenda@eecs.cs.pdx.edu (Steve Kirkendall) (12/04/90)

# --------------------------- cut here ----------------------------
# This is a shar archive.  To unpack it, save it to a file, and delete
# anything above the "cut here" line.  Then run sh on the file.
#
# -rw-r--r--  1 kirkenda     8819 Dec  2 17:57 curses.h
# -rw-r--r--  1 kirkenda    13222 Dec  2 17:57 cut.c
# -rw-r--r--  1 kirkenda    15130 Dec  2 17:57 ex.c
# -rw-r--r--  1 kirkenda    16368 Dec  2 17:57 input.c
# -rw-r--r--  1 kirkenda     7778 Dec  2 17:57 main.c
# -rw-r--r--  1 kirkenda     2166 Dec  2 17:57 misc.c
#

if test -f curses.h -a "$1" != -f
then
echo Will not overwrite curses.h
else
echo Extracting curses.h
sed 's/^X//' >curses.h <<\eof
X/* curses.h */
X
X/* Author:
X *	Steve Kirkendall
X *	14407 SW Teal Blvd. #C
X *	Beaverton, OR 97005
X *	kirkenda@cs.pdx.edu
X */
X
X
X/* This is the header file for a small, fast, fake curses package */
X
X/* termcap stuff */
Xextern char	*tgoto();
Xextern char	*tgetstr();
Xextern void	tputs();
X
X#if MSDOS
X/* BIOS interface used instead of termcap for MS-DOS */
Xextern int	vmode;
Xextern void	v_up();
Xextern void	v_cb();
Xextern void	v_cs();
Xextern void	v_ce();
Xextern void	v_cl();
Xextern void	v_cd();
Xextern void	v_al();
Xextern void	v_dl();
Xextern void	v_sr();
Xextern void	v_move();
X#endif
X
X/* faddch() is a function.  a pointer to it is passed to tputs() */
Xextern int	faddch();
X
X/* data types */
X#define WINDOW	char
X
X/* CONSTANTS & SYMBOLS */
X#define TRUE		1
X#define FALSE		0
X#define A_NORMAL	0
X#define A_STANDOUT	1
X#define A_BOLD		2
X#define A_UNDERLINE	3
X#define A_ALTCHARSET	4
X#if MSDOS
X#define KBSIZ		(10*1024)
X#else
X#define KBSIZ		(6*1024)
X#endif
X
X/* extern variables, defined in curses.c */
Xextern short	ospeed;		/* tty speed, eg B2400 */
X#if OSK
Xextern char PC_;	/* Pad char */
Xextern char	*BC;	/* Backspace char string */
X#else
Xextern char	PC;		/* Pad char */
X#endif
Xextern WINDOW	*stdscr;	/* pointer into kbuf[] */
Xextern WINDOW	kbuf[KBSIZ];	/* a very large output buffer */
Xextern int	LINES;		/* :li#: number of rows */
Xextern int	COLS;		/* :co#: number of columns */
Xextern int	AM;		/* :am:  boolean: auto margins? */
Xextern int	PT;		/* :pt:  boolean: physical tabs? */
Xextern char	*VB;		/* :vb=: visible bell */
Xextern char	*UP;		/* :up=: move cursor up */
Xextern char	*SO;		/* :so=: standout start */
Xextern char	*SE;		/* :se=: standout end */
Xextern char	*US;		/* :us=: underline start */
Xextern char	*UE;		/* :ue=: underline end */
Xextern char	*MD;		/* :md=: bold start */
Xextern char	*ME;		/* :me=: bold end */
Xextern char	*AS;		/* :as=: alternate (italic) start */
Xextern char	*AE;		/* :ae=: alternate (italic) end */
Xextern char	*CM;		/* :cm=: cursor movement */
Xextern char	*CE;		/* :ce=: clear to end of line */
Xextern char	*CD;		/* :cd=: clear to end of screen */
Xextern char	*AL;		/* :al=: add a line */
Xextern char	*DL;		/* :dl=: delete a line */
X#if OSK
Xextern char	*SR_;		/* :sr=: scroll reverse */
X#else
Xextern char	*SR;		/* :sr=: scroll reverse */
X#endif
Xextern char	*KS;		/* :ks=: init string for cursor */
Xextern char	*KE;		/* :ke=: restore string for cursor */
Xextern char	*KU;		/* :ku=: sequence sent by up key */
Xextern char	*KD;		/* :kd=: sequence sent by down key */
Xextern char	*KL;		/* :kl=: sequence sent by left key */
Xextern char	*KR;		/* :kr=: sequence sent by right key */
Xextern char	*PU;		/* :PU=: key sequence sent by PgUp key */
Xextern char	*PD;		/* :PD=: key sequence sent by PgDn key */
Xextern char	*HM;		/* :HM=: key sequence sent by Home key */
Xextern char	*EN;		/* :EN=: key sequence sent by End key */
Xextern char	*IM;		/* :im=: insert mode start */
Xextern char	*IC;		/* :ic=: insert following char */
Xextern char	*EI;		/* :ei=: insert mode end */
Xextern char	*DC;		/* :dc=: delete a character */
Xextern char	*TI;		/* :ti=: terminal init */	/* GB */
Xextern char	*TE;		/* :te=: terminal exit */	/* GB */
X#ifndef NO_CURSORSHAPE
Xextern char	*CQ;		/* :cQ=: normal cursor */
Xextern char	*CX;		/* :cX=: cursor used for EX command/entry */
Xextern char	*CV;		/* :cV=: cursor used for VI command mode */
Xextern char	*CI;		/* :cI=: cursor used for VI input mode */
Xextern char	*CR;		/* :cR=: cursor used for VI replace mode */
X#endif
Xextern char	*aend;		/* end an attribute -- either UE or ME */
Xextern char	ERASEKEY;	/* taken from the ioctl structure */
X
X/* Msdos-versions may use bios; others always termcap.
X * Will emit some 'code has no effect' warnings in unix.
X */
X 
X#if MSDOS
Xextern char o_pcbios[1];		/* BAH! */
X#define	CHECKBIOS(x,y)	(*o_pcbios ? (x) : (y))
X#define VOIDBIOS(x,y)	{if (*o_pcbios) {x;} else {y;}}
X#else
X#define	CHECKBIOS(x,y)	(y)
X#define VOIDBIOS(x,y)	{y;}
X#endif
X
X#define	do_VB()		VOIDBIOS(;, tputs(VB, 1, faddch))
X#define	do_UP()		VOIDBIOS(v_up(), tputs(UP, 1, faddch))
X#define	do_SO()		VOIDBIOS((vmode=A_STANDOUT), tputs(SO, 1, faddch))
X#define	do_SE()		VOIDBIOS((vmode=A_NORMAL), tputs(SE, 1, faddch))
X#define	do_US()		VOIDBIOS((vmode=A_UNDERLINE), tputs(US, 1, faddch))
X#define	do_UE()		VOIDBIOS((vmode=A_NORMAL), tputs(UE, 1, faddch))
X#define	do_MD()		VOIDBIOS((vmode=A_BOLD), tputs(MD, 1, faddch))
X#define	do_ME()		VOIDBIOS((vmode=A_NORMAL), tputs(ME, 1, faddch))
X#define	do_AS()		VOIDBIOS((vmode=A_ALTCHARSET), tputs(AS, 1, faddch))
X#define	do_AE()		VOIDBIOS((vmode=A_NORMAL), tputs(AE, 1, faddch))
X#undef	do_CM		/* move */
X#define	do_CE()		VOIDBIOS(v_ce(), tputs(CE, 1, faddch))
X#define	do_CD()		VOIDBIOS(v_cd(), tputs(CD, 1, faddch))
X#define	do_AL()		VOIDBIOS(v_al(), tputs(AL, LINES, faddch))
X#define	do_DL()		VOIDBIOS(v_dl(), tputs(DL, LINES, faddch))
X#if OSK
X#define	do_SR()		VOIDBIOS(v_sr(), tputs(SR_, 1, faddch))
X#else
X#define	do_SR()		VOIDBIOS(v_sr(), tputs(SR, 1, faddch))
X#endif
X#define do_KS()		VOIDBIOS(1, tputs(KS, 1, faddch))
X#define do_KE()		VOIDBIOS(1, tputs(KE, 1, faddch))
X#define	do_IM()		VOIDBIOS(;, tputs(IM, 1, faddch))
X#define	do_IC()		VOIDBIOS(;, tputs(IC, 1, faddch))
X#define	do_EI()		VOIDBIOS(;, tputs(EI, 1, faddch))
X#define	do_DC()		VOIDBIOS(;, tputs(DC, COLS, faddch))
X#define	do_TI()		VOIDBIOS(;, (void)ttywrite(TI, (unsigned)strlen(TI)))
X#define	do_TE()		VOIDBIOS(;, (void)ttywrite(TE, (unsigned)strlen(TE)))
X#ifndef NO_CURSORSHAPE
X# define do_CQ()	VOIDBIOS(v_cs(), tputs(CQ, 1, faddch))
X# define do_CX()	VOIDBIOS(v_cs(), tputs(CX, 1, faddch))
X# define do_CV()	VOIDBIOS(v_cs(), tputs(CV, 1, faddch))
X# define do_CI()	VOIDBIOS(v_cb(), tputs(CI, 1, faddch))
X# define do_CR()	VOIDBIOS(v_cb(), tputs(CR, 1, faddch))
X#endif
X#define	do_aend()	VOIDBIOS((vmode=A_NORMAL), tputs(aend, 1, faddch))
X
X#define	has_AM		CHECKBIOS(1, AM)
X#define	has_PT		CHECKBIOS(0, PT)
X#define	has_VB		CHECKBIOS((char *)0, VB)
X#define	has_UP		CHECKBIOS((char *)1, UP)
X#define	has_SO		CHECKBIOS((char)1, (*SO))
X#define	has_SE		CHECKBIOS((char)1, (*SE))
X#define	has_US		CHECKBIOS((char)1, (*US))
X#define	has_UE		CHECKBIOS((char)1, (*UE))
X#define	has_MD		CHECKBIOS((char)1, (*MD))
X#define	has_ME		CHECKBIOS((char)1, (*ME))
X#define	has_AS		CHECKBIOS((char)1, (*AS))
X#define	has_AE		CHECKBIOS((char)1, (*AE))
X#undef	has_CM		/* cursor move: don't need */
X#define	has_CB		CHECKBIOS(1, 0)
X#define	has_CS		CHECKBIOS(1, 0)
X#define	has_CE		CHECKBIOS((char *)1, CE)
X#define	has_CD		CHECKBIOS((char *)1, CD)
X#define	has_AL		CHECKBIOS((char *)1, AL)
X#define	has_DL		CHECKBIOS((char *)1, DL)
X#if OSK
X#define	has_SR		CHECKBIOS((char *)1, SR_)
X#else
X#define	has_SR		CHECKBIOS((char *)1, SR)
X#endif
X#define has_KS		CHECKBIOS((char)1, (*KS))
X#define has_KE		CHECKBIOS((char)1, (*KE))
X#define	has_KU		CHECKBIOS("#H", KU)
X#define	has_KD		CHECKBIOS("#P", KD)
X#define	has_KL		CHECKBIOS("#K", KL)
X#define	has_KR		CHECKBIOS("#M", KR)
X#define has_HM		CHECKBIOS("#G", HM)
X#define has_EN		CHECKBIOS("#O", EN)
X#define has_PU		CHECKBIOS("#I", PU)
X#define has_PD		CHECKBIOS("#Q", PD)
X#define	has_IM		CHECKBIOS((char)0, (*IM))
X#define	has_IC		CHECKBIOS((char)0, (*IC))
X#define	has_EI		CHECKBIOS((char)0, (*EI))
X#define	has_DC		CHECKBIOS((char *)0, DC)
X#define	has_TI		CHECKBIOS((char)0, (*TI))
X#define	has_TE		CHECKBIOS((char)0, (*TE))
X#ifndef NO_CURSORSHAPE
X#define has_CQ		CHECKBIOS((char *)1, CQ)
X#endif
X
X/* (pseudo)-Curses-functions */
X
X#ifdef lint
X# define _addCR		VOIDBIOS(;, (stdscr[-1] == '\n' ? qaddch('\r') : (stdscr[-1] = '\n')))
X#else
X# if OSK
X#  define _addCR		VOIDBIOS(;, (stdscr[-1] == '\n' ? qaddch('\l') : (stdscr[-1] = stdscr[-1])))
X# else
X# define _addCR		VOIDBIOS(;, (stdscr[-1] == '\n' ? qaddch('\r') : 0))
X#endif
X#endif
X#define qaddch(ch)	CHECKBIOS(v_put(ch), (*stdscr++ = (ch)))
X#if OSK
X#define addch(ch)	if (qaddch(ch) == '\n') qaddch('\l'); else
X#else
X#define addch(ch)	if (qaddch(ch) == '\n') qaddch('\r'); else
X#endif
X
Xextern void initscr();
Xextern void endwin();
Xextern void suspend_curses();
Xextern void resume_curses();
Xextern void attrset();
Xextern void insch();
Xextern void qaddstr();
X#define addstr(str)	{qaddstr(str); _addCR;}
X#define move(y,x)	VOIDBIOS(v_move(x,y), \
X					tputs(tgoto(CM, x, y), 1, faddch))
X#define mvaddch(y,x,ch)	{move(y,x); addch(ch);}
X#define refresh()	VOIDBIOS(;, wrefresh(stdscr))
X#define wrefresh(w)	if ((w) != kbuf) VOIDBIOS((w) = kbuf, {ttywrite(kbuf, (unsigned)((w) - kbuf)); (w) = kbuf;}) else
X#define wqrefresh(w)	if ((w) - kbuf > 2000) VOIDBIOS((w) = kbuf, {ttywrite(kbuf, (unsigned)((w) - kbuf)); (w) = kbuf;}) else
X#define standout()	do_SO()
X#define standend()	do_SE()
X#define clrtoeol()	do_CE()
X#define clrtobot()	do_CD()
X#define insertln()	do_AL()
X#define deleteln()	do_DL()
X#define delch()		do_DC()
X#define scrollok(w,b)
X#define raw()
X#define echo()
X#define cbreak()
X#define noraw()
X#define noecho()
X#define nocbreak()
eof
if test `wc -c <curses.h` -ne 8819
then
echo curses.h damaged!
fi
fi

if test -f cut.c -a "$1" != -f
then
echo Will not overwrite cut.c
else
echo Extracting cut.c
sed 's/^X//' >cut.c <<\eof
X/* cut.c */
X
X/* Author:
X *	Steve Kirkendall
X *	14407 SW Teal Blvd. #C
X *	Beaverton, OR 97005
X *	kirkenda@cs.pdx.edu
X */
X
X
X/* This file contains function which manipulate the cut buffers. */
X
X#include "config.h"
X#include "vi.h"
X#if TURBOC
X#include <process.h>		/* needed for getpid */
X#endif
X#if TOS
X#include <osbind.h>
X#define	rename(a,b)	Frename(0,a,b)
X#endif
X
X# define NANNONS	9	/* number of annonymous buffers */
X
Xstatic struct cutbuf
X{
X	short	*phys;	/* pointer to an array of #s of BLKs containing text */
X	int	nblks;	/* number of blocks in phys[] array */
X	int	start;	/* offset into first block of start of cut */
X	int	end;	/* offset into last block of end of cut */
X	int	fd;	/* fd of tmp file, or -1 to use tmpfd */
X	char	lnmode;	/* boolean: line-mode cut? (as opposed to char-mode) */
X}
X	named[27],	/* cut buffers "a through "z and ". */
X	annon[NANNONS];	/* annonymous cut buffers */
X
Xstatic char	cbname;	/* name chosen for next cut/paste operation */
X
X
X#ifndef NO_RECYCLE
X/* This function builds a list of all blocks needed in the current tmp file
X * for the contents of cut buffers.
X * !!! WARNING: if you have more than ~450000 bytes of text in all of the
X * cut buffers, then this will fail disastrously, because buffer overflow
X * is *not* allowed for.
X */
Xint cutneeds(need)
X	BLK		*need;	/* this is where we deposit the list */
X{
X	struct cutbuf	*cb;	/* used to count through cut buffers */
X	int		i;	/* used to count through blocks of a cut buffer */
X	int		n;	/* total number of blocks in list */
X
X	n = 0;
X
X	/* first the named buffers... */
X	for (cb = named; cb < &named[27]; cb++)
X	{
X		if (cb->fd > 0)
X			continue;
X
X		for (i = cb->nblks; i-- > 0; )
X		{
X			need->n[n++] = cb->phys[i];
X		}
X	}
X
X	/* then the anonymous buffers */
X	for (cb = annon; cb < &annon[NANNONS]; cb++)
X	{
X		if (cb->fd > 0)
X			continue;
X
X		for (i = cb->nblks; i-- > 0; )
X		{
X			need->n[n++] = cb->phys[i];
X		}
X	}
X
X	return n;
X}
X#endif
X
X/* This function frees a cut buffer */
Xstatic void cutfree(buf)
X	struct cutbuf	*buf;
X{
X	char	cutfname[50];
X	int	i;
X
X	/* return immediately if the buffer is already empty */
X	if (buf->nblks <= 0)
X	{
X		return;
X	}
X
X	/* else free up stuff */
X	buf->nblks = 0;
X#ifdef DEBUG
X	if (!buf->phys)
X		msg("cutfree() tried to free an NULL buf->phys pointer.");
X#endif
X	free((char *)buf->phys);
X
X	/* see if anybody else needs this tmp file */
X	if (buf->fd >= 0)
X	{
X		for (i = 0; i < 27; i++)
X		{
X			if (named[i].nblks > 0 && named[i].fd == buf->fd)
X			{
X				break;
X			}
X		}
X	}
X
X	/* if nobody else needs it, then discard the tmp file */
X	if (buf->fd >= 0 && i == 27)
X	{
X		close(buf->fd);
X#if MSDOS || TOS
X		strcpy(cutfname, o_directory);
X		if ((i = strlen(cutfname)) && !strchr(":/\\", cutfname[i-1]))
X			cutfname[i++]=SLASH;
X		sprintf(cutfname+i, CUTNAME+3, getpid(), buf->fd);
X#else
X		sprintf(cutfname, CUTNAME, o_directory, getpid(), buf->fd);
X#endif
X		unlink(cutfname);
X	}
X}
X
X/* This function is called when we are about to abort a tmp file.  If any
X * cut buffers still need the file, then a copy of the file should be
X * created for use by the cut buffers.
X *
X * To minimize the number of extra files lying around, only named cut buffers
X * are preserved in a file switch; the annonymous buffers just go away.
X */
Xvoid cutswitch(tmpname)
X	char	*tmpname; /* name of the tmp file */
X{
X	char	cutfname[50];	/* used to build a new name for the tmp file */
X	int	fd;		/* a new fd for the current tmp file */
X	int	i;
X#if MSDOS || TOS
X	int	j;
X#endif
X
X	/* discard all annonymous cut buffers */
X	for (i = 0; i < NANNONS; i++)
X	{
X		cutfree(&annon[i]);
X	}
X
X	/* find the first named buffer that uses this tmp file */
X	for (i = 0; i < 27; i++)
X	{
X		if (named[i].nblks > 0 && named[i].fd < 0)
X		{
X			break;
X		}
X	}
X
X	/* if none of them use this tmp file, then we're done */
X	if (i == 27)
X	{
X		return;
X	}
X
X	/* else we'll need this file and an fd a little longer */
X#if MSDOS || TOS
X	strcpy(cutfname, o_directory);
X	if ((j = strlen(cutfname)) && !strchr(":/\\", cutfname[j-1]))
X		cutfname[j++]=SLASH;
X	close(tmpfd);
X	fd = open(tmpname, O_RDONLY|O_BINARY);
X	close(fd);
X	sprintf(cutfname+j, CUTNAME+3, getpid(), fd);
X	rename(tmpname, cutfname);
X	fd = open(cutfname, O_RDONLY|O_BINARY);
X	tmpfd = -1; /* we'll try to close this in tmp.c, but who cares? */
X#else
X	fd = dup(tmpfd);
X# if OSK
X	sprintf(cutfname, CUTNAME, "", getpid(), fd);
X	if (!link(tmpname, &cutfname[1])) /* skip slash */
X		unlink(tmpname);
X# else	
X	sprintf(cutfname, CUTNAME, o_directory, getpid(), fd);
X	link(tmpname, cutfname) || unlink(tmpname);
X# endif
X#endif
X
X	/* have all cut buffers use the new fd instead */
X	for (; i < 27; i++)
X	{
X		if (named[i].nblks > 0 && named[i].fd < 0)
X		{
X			named[i].fd = fd;
X		}
X	}
X}
X
X/* This function should be called just before termination of vi */
Xvoid cutend()
X{
X	int	i;
X
X	/* free all named cut buffers, since they might be forcing an older
X	 * tmp file to be retained.
X	 */
X	for (i = 0; i < 27; i++)
X	{
X		cutfree(&named[i]);
X	}
X}
X
X
X/* This function is used to select the cut buffer to be used next */
Xvoid cutname(name)
X	int	name;	/* a single character */
X{
X	cbname = name;
X}
X
X
X
X
X/* This function copies a selected segment of text to a cut buffer */
Xvoid cut(from, to)
X	MARK	from;		/* start of text to cut */
X	MARK	to;		/* end of text to cut */
X{
X	int		first;	/* logical number of first block in cut */
X	int		last;	/* logical number of last block used in cut */
X	long		line;	/* a line number */
X	int		lnmode;	/* boolean: will this be a line-mode cut? */
X	MARK		delthru;/* end of text temporarily inserted for apnd */
X	REG struct cutbuf *cb;
X	REG long	l;
X	REG int		i;
X	REG char	*scan;
X	char		*blkc;
X
X	/* detect whether this must be a line-mode cut or char-mode cut */
X	if (markidx(from) == 0 && markidx(to) == 0)
X		lnmode = TRUE;
X	else
X		lnmode = FALSE;
X
X	/* by default, we don't "delthru" anything */
X	delthru = MARK_UNSET;
X
X	/* decide which cut buffer to use */
X	if (!cbname)
X	{
X		/* free up the last annonymous cut buffer */
X		cutfree(&annon[NANNONS - 1]);
X
X		/* shift the annonymous cut buffers */
X		for (i = NANNONS - 1; i > 0; i--)
X		{
X			annon[i] = annon[i - 1];
X		}
X
X		/* use the first annonymous cut buffer */
X		cb = annon;
X		cb->nblks = 0;
X	}
X	else if (cbname >= 'a' && cbname <= 'z')
X	{
X		cb = &named[cbname - 'a'];
X		cutfree(cb);
X	}
X#ifndef CRUNCH
X	else if (cbname >= 'A' && cbname <= 'Z')
X	{
X		cb = &named[cbname - 'A'];
X		if (cb->nblks > 0)
X		{
X			/* resolve linemode/charmode differences */
X			if (!lnmode && cb->lnmode)
X			{
X				from &= ~(BLKSIZE - 1);
X				if (markidx(to) != 0 || to == from)
X				{
X					to = to + BLKSIZE - markidx(to);
X				}
X				lnmode = TRUE;
X			}
X
X			/* insert the old cut-buffer before the new text */
X			mark[28] = to;
X			delthru = paste(from, FALSE, TRUE);
X			if (delthru == MARK_UNSET)
X			{
X				return;
X			}
X			delthru++;
X			to = mark[28];
X		}
X		cutfree(cb);
X	}
X#endif /* not CRUNCH */
X	else if (cbname == '.')
X	{
X		cb = &named[26];
X		cutfree(cb);
X	}
X	else
X	{
X		msg("Invalid cut buffer name: \"%c", cbname);
X		cbname = '\0';
X		return;
X	}
X	cbname = '\0';
X	cb->fd = -1;
X
X	/* detect whether we're doing a line mode cut */
X	cb->lnmode = lnmode;
X
X	/* ---------- */
X
X	/* Reporting... */	
X	if (markidx(from) == 0 && markidx(to) == 0)
X	{
X		rptlines = markline(to) - markline(from);
X		rptlabel = "yanked";
X	}
X
X	/* ---------- */
X
X	/* make sure each block has a physical disk address */
X	blksync();
X
X	/* find the first block in the cut */
X	line = markline(from);
X	for (first = 1; line > lnum[first]; first++)
X	{
X	}
X
X	/* fetch text of the block containing that line */
X	blkc = scan = blkget(first)->c;
X
X	/* find the mark in the block */
X	for (l = lnum[first - 1]; ++l < line; )
X	{
X		while (*scan++ != '\n')
X		{
X		}
X	}
X	scan += markidx(from);
X
X	/* remember the offset of the start */
X	cb->start = scan - blkc;
X
X	/* ---------- */
X
X	/* find the last block in the cut */
X	line = markline(to);
X	for (last = first; line > lnum[last]; last++)
X	{
X	}
X
X	/* fetch text of the block containing that line */
X	if (last != first)
X	{
X		blkc = scan = blkget(last)->c;
X	}
X	else
X	{
X		scan = blkc;
X	}
X
X	/* find the mark in the block */
X	for (l = lnum[last - 1]; ++l < line; )
X	{
X		while (*scan++ != '\n')
X		{
X		}
X	}
X	if (markline(to) <= nlines)
X	{
X		scan += markidx(to);
X	}
X
X	/* remember the offset of the end */
X	cb->end = scan - blkc;
X
X	/* ------- */
X
X	/* remember the physical block numbers of all included blocks */
X	cb->nblks = last - first;
X	if (cb->end > 0)
X	{
X		cb->nblks++;
X	}
X#ifdef lint
X	cb->phys = (short *)0;
X#else
X	cb->phys = (short *)malloc((unsigned)(cb->nblks * sizeof(short)));
X#endif
X	for (i = 0; i < cb->nblks; i++)
X	{
X		cb->phys[i] = hdr.n[first++];
X	}
X
X#ifndef CRUNCH
X	/* if we temporarily inserted text for appending, then delete that
X	 * text now -- before the user sees it.
X	 */
X	if (delthru)
X	{
X		line = rptlines;
X		delete(from, delthru);
X		rptlines = line;
X		rptlabel = "yanked";
X	}
X#endif /* not CRUNCH */
X}
X
X
Xstatic void readcutblk(cb, blkno)
X	struct cutbuf	*cb;
X	int		blkno;
X{
X	int		fd;	/* either tmpfd or cb->fd */
X
X	/* decide which fd to use */
X	if (cb->fd >= 0)
X	{
X		fd = cb->fd;
X	}
X	else
X	{
X		fd = tmpfd;
X	}
X
X	/* get the block */
X	lseek(fd, (long)cb->phys[blkno] * (long)BLKSIZE, 0);
X	if (read(fd, tmpblk.c, (unsigned)BLKSIZE) != BLKSIZE)
X	{
X		msg("Error reading back from tmp file for pasting!");
X	}
X}
X
X
X/* This function inserts text from a cut buffer, and returns the MARK where
X * insertion ended.  Return MARK_UNSET on errors.
X */
XMARK paste(at, after, retend)
X	MARK	at;	/* where to insert the text */
X	int	after;	/* boolean: insert after mark? (rather than before) */
X	int	retend;	/* boolean: return end of text? (rather than start) */
X{
X	REG struct cutbuf	*cb;
X	REG int			i;
X
X	/* decide which cut buffer to use */
X	if (cbname >= 'A' && cbname <= 'Z')
X	{
X		cb = &named[cbname - 'A'];
X	}
X	else if (cbname >= 'a' && cbname <= 'z')
X	{
X		cb = &named[cbname - 'a'];
X	}
X	else if (cbname >= '1' && cbname <= '9')
X	{
X		cb = &annon[cbname - '1'];
X	}
X	else if (cbname == '.')
X	{
X		cb = &named[26];
X	}
X	else if (!cbname)
X	{
X		cb = annon;
X	}
X	else
X	{
X		msg("Invalid cut buffer name: \"%c", cbname);
X		cbname = '\0';
X		return MARK_UNSET;
X	}
X
X	/* make sure it isn't empty */
X	if (cb->nblks == 0)
X	{
X		if (cbname)
X			msg("Cut buffer \"%c is empty", cbname);
X		else
X			msg("Cut buffer is empty");
X		cbname = '\0';
X		return MARK_UNSET;
X	}
X	cbname = '\0';
X
X	/* adjust the insertion MARK for "after" and line-mode cuts */
X	if (cb->lnmode)
X	{
X		at &= ~(BLKSIZE - 1);
X		if (after)
X		{
X			at += BLKSIZE;
X		}
X	}
X	else if (after)
X	{
X		/* careful! if markidx(at) == 0 we might be pasting into an
X		 * empty line -- so we can't blindly increment "at".
X		 */
X		if (markidx(at) == 0)
X		{
X			pfetch(markline(at));
X			if (plen != 0)
X			{
X				at++;
X			}
X		}
X		else
X		{
X			at++;
X		}
X	}
X
X	/* put a copy of the "at" mark in the mark[] array, so it stays in
X	 * sync with changes made via add().
X	 */
X	mark[27] = at;
X
X	/* simple one-block paste? */
X	if (cb->nblks == 1)
X	{
X		/* get the block */
X		readcutblk(cb, 0);
X
X		/* isolate the text we need within it */
X		if (cb->end)
X		{
X			tmpblk.c[cb->end] = '\0';
X		}
X
X		/* insert it */
X		ChangeText
X		{
X			add(at, &tmpblk.c[cb->start]);
X		}
X	}
X	else
X	{
X		/* multi-block paste */
X
X		ChangeText
X		{
X			i = cb->nblks - 1;
X
X			/* add text from the last block first */
X			if (cb->end > 0)
X			{
X				readcutblk(cb, i);
X				tmpblk.c[cb->end] = '\0';
X				add(at, tmpblk.c);
X				i--;
X			}
X
X			/* add intervening blocks */
X			while (i > 0)
X			{
X				readcutblk(cb, i);
X				add(at, tmpblk.c);
X				i--;
X			}
X
X			/* add text from the first cut block */
X			readcutblk(cb, 0);
X			add(at, &tmpblk.c[cb->start]);
X		}
X	}
X
X	/* Reporting... */
X	rptlines = markline(mark[27]) - markline(at);
X	rptlabel = "pasted";
X
X	/* return the mark at the beginning/end of inserted text */
X	if (retend)
X	{
X		return mark[27] - 1L;
X	}
X	return at;
X}
X
X
X
X
X#ifndef NO_AT
X
X/* This function copies characters from a cut buffer into a string.
X * It returns the number of characters in the cut buffer.  If the cut
X * buffer is too large to fit in the string (i.e. if cb2str() returns
X * a number >= size) then the characters will not have been copied.
X * It returns 0 if the cut buffer is empty, and -1 for invalid cut buffers.
X */
Xint cb2str(name, buf, size)
X	int	name;	/* the name of a cut-buffer to get: a-z only! */
X	char	*buf;	/* where to put the string */
X	unsigned size;	/* size of buf */
X{
X	REG struct cutbuf	*cb;
X	REG char		*src;
X	REG char		*dest;
X
X	/* decide which cut buffer to use */
X	if (name >= 'a' && name <= 'z')
X	{
X		cb = &named[name - 'a'];
X	}
X	else
X	{
X		return -1;
X	}
X
X	/* if the buffer is empty, return 0 */
X	if (cb->nblks == 0)
X	{
X		return 0;
X	}
X
X	/* !!! if not a single-block cut, then fail */
X	if (cb->nblks != 1)
X	{
X		return size;
X	}
X
X	/* if too big, return the size now, without doing anything */
X	if (cb->end - cb->start >= size)
X	{
X		return cb->end - cb->start;
X	}
X
X	/* get the block */
X	readcutblk(cb, 0);
X
X	/* isolate the string within that blk */
X	if (cb->start == 0)
X	{
X		tmpblk.c[cb->end] = '\0';
X	}
X	else
X	{
X		for (dest = tmpblk.c, src = dest + cb->start; src < tmpblk.c + cb->end; )
X		{
X			*dest++ = *src++;
X		}
X		*dest = '\0';
X	}
X
X	/* copy the string into the buffer */
X	if (buf != tmpblk.c)
X	{
X		strcpy(buf, tmpblk.c);
X	}
X
X	/* return the length */
X	return cb->end - cb->start;
X}
X#endif
eof
if test `wc -c <cut.c` -ne 13222
then
echo cut.c damaged!
fi
fi

if test -f ex.c -a "$1" != -f
then
echo Will not overwrite ex.c
else
echo Extracting ex.c
sed 's/^X//' >ex.c <<\eof
X/* ex.c */
X
X/* Author:
X *	Steve Kirkendall
X *	14407 SW Teal Blvd. #C
X *	Beaverton, OR 97005
X *	kirkenda@cs.pdx.edu
X */
X
X
X/* This file contains the code for reading ex commands. */
X
X#include "config.h"
X#include <ctype.h>
X#include "vi.h"
X
X#ifndef isascii
X# define isascii(c) !((c)&~0x7f)
X#endif
X
X/* This data type is used to describe the possible argument combinations */
Xtypedef short ARGT;
X#define FROM	1		/* allow a linespec */
X#define	TO	2		/* allow a second linespec */
X#define BANG	4		/* allow a ! after the command name */
X#define EXTRA	8		/* allow extra args after command name */
X#define XFILE	16		/* expand wildcards in extra part */
X#define NOSPC	32		/* no spaces allowed in the extra part */
X#define	DFLALL	64		/* default file range is 1,$ */
X#define DFLNONE	128		/* no default file range */
X#define NODFL	256		/* do not default to the current file name */
X#define EXRCOK	512		/* can be in a .exrc file */
X#define NL	1024		/* if mode!=MODE_EX, then write a newline first */
X#define PLUS	2048		/* allow a line number, as in ":e +32 foo" */
X#define ZERO	4096		/* allow 0 to be given as a line number */
X#define FILES	(XFILE + EXTRA)	/* multiple extra files allowed */
X#define WORD1	(EXTRA + NOSPC)	/* one extra word allowed */
X#define FILE1	(FILES + NOSPC)	/* 1 file allowed, defaults to current file */
X#define NAMEDF	(FILE1 + NODFL)	/* 1 file allowed, defaults to "" */
X#define NAMEDFS	(FILES + NODFL)	/* multiple files allowed, default is "" */
X#define RANGE	(FROM + TO)	/* range of linespecs allowed */
X#define NONE	0		/* no args allowed at all */
X
X/* This array maps ex command names to command codes. The order in which
X * command names are listed below is significant -- ambiguous abbreviations
X * are always resolved to be the first possible match.  (e.g. "r" is taken
X * to mean "read", not "rewind", because "read" comes before "rewind")
X */
Xstatic struct
X{
X	char	*name;	/* name of the command */
X	CMD	code;	/* enum code of the command */
X	void	(*fn)();/* function which executes the command */
X	ARGT	argt;	/* command line arguments permitted/needed/used */
X}
X	cmdnames[] =
X{   /*	cmd name	cmd code	function	arguments */
X	{"append",	CMD_APPEND,	cmd_append,	FROM+ZERO	},
X#ifdef DEBUG
X	{"bug",		CMD_DEBUG,	cmd_debug,	RANGE+BANG+EXTRA+NL},
X#endif
X	{"change",	CMD_CHANGE,	cmd_append,	RANGE		},
X	{"delete",	CMD_DELETE,	cmd_delete,	RANGE+WORD1	},
X	{"edit",	CMD_EDIT,	cmd_edit,	BANG+FILE1+PLUS	},
X	{"file",	CMD_FILE,	cmd_file,	NAMEDF		},
X	{"global",	CMD_GLOBAL,	cmd_global,	RANGE+BANG+EXTRA+DFLALL},
X	{"insert",	CMD_INSERT,	cmd_append,	FROM		},
X	{"join",	CMD_INSERT,	cmd_join,	RANGE		},
X	{"k",		CMD_MARK,	cmd_mark,	FROM+WORD1	},
X	{"list",	CMD_LIST,	cmd_print,	RANGE+NL	},
X	{"move",	CMD_MOVE,	cmd_move,	RANGE+EXTRA	},
X	{"next",	CMD_NEXT,	cmd_next,	BANG+NAMEDFS	},
X	{"Next",	CMD_PREVIOUS,	cmd_next,	BANG		},
X	{"print",	CMD_PRINT,	cmd_print,	RANGE+NL	},
X	{"quit",	CMD_QUIT,	cmd_xit,	BANG		},
X	{"read",	CMD_READ,	cmd_read,	FROM+ZERO+NAMEDF},
X	{"substitute",	CMD_SUBSTITUTE,	cmd_substitute,	RANGE+EXTRA	},
X	{"to",		CMD_COPY,	cmd_move,	RANGE+EXTRA	},
X	{"undo",	CMD_UNDO,	cmd_undo,	NONE		},
X	{"vglobal",	CMD_VGLOBAL,	cmd_global,	RANGE+EXTRA+DFLALL},
X	{"write",	CMD_WRITE,	cmd_write,	RANGE+BANG+FILE1+DFLALL},
X	{"xit",		CMD_XIT,	cmd_xit,	BANG+NL		},
X	{"yank",	CMD_YANK,	cmd_delete,	RANGE+WORD1	},
X
X	{"!",		CMD_BANG,	cmd_shell,	EXRCOK+RANGE+NAMEDFS+DFLNONE+NL},
X	{"<",		CMD_SHIFTL,	cmd_shift,	RANGE		},
X	{">",		CMD_SHIFTR,	cmd_shift,	RANGE		},
X	{"=",		CMD_EQUAL,	cmd_file,	RANGE		},
X	{"&",		CMD_SUBAGAIN,	cmd_substitute,	RANGE		},
X#ifndef NO_AT
X	{"@",		CMD_AT,		cmd_at,		EXTRA		},
X#endif
X
X#ifndef NO_ABBR
X	{"abbreviate",	CMD_ABBR,	cmd_abbr,	EXRCOK+EXTRA	},
X#endif
X	{"args",	CMD_ARGS,	cmd_args,	EXRCOK+NAMEDFS	},
X#ifndef NO_ERRLIST
X	{"cc",		CMD_CC,		cmd_make,	BANG+FILES	},
X#endif
X	{"cd",		CMD_CD,		cmd_cd,		EXRCOK+NAMEDF	},
X	{"copy",	CMD_COPY,	cmd_move,	RANGE+EXTRA	},
X#ifndef NO_DIGRAPH
X	{"digraph",	CMD_DIGRAPH,	cmd_digraph,	EXRCOK+BANG+EXTRA},
X#endif
X#ifndef NO_ERRLIST
X	{"errlist",	CMD_ERRLIST,	cmd_errlist,	BANG+NAMEDF	},
X#endif
X	{"ex",		CMD_EDIT,	cmd_edit,	BANG+FILE1	},
X	{"map",		CMD_MAP,	cmd_map,	EXRCOK+BANG+EXTRA},
X#ifndef NO_MKEXRC
X	{"mkexrc",	CMD_MKEXRC,	cmd_mkexrc,	NAMEDF		},
X#endif
X	{"number",	CMD_NUMBER,	cmd_print,	RANGE+NL	},
X	{"put",		CMD_PUT,	cmd_put,	FROM+ZERO+WORD1	},
X	{"set",		CMD_SET,	cmd_set,	EXRCOK+EXTRA	},
X	{"shell",	CMD_SHELL,	cmd_shell,	NL		},
X	{"source",	CMD_SOURCE,	cmd_source,	EXRCOK+NAMEDF	},
X	{"tag",		CMD_TAG,	cmd_tag,	BANG+WORD1	},
X	{"version",	CMD_VERSION,	cmd_version,	EXRCOK+NONE	},
X	{"visual",	CMD_VISUAL,	cmd_visual,	NONE		},
X	{"wq",		CMD_WQUIT,	cmd_xit,	NL		},
X
X#ifdef DEBUG
X	{"debug",	CMD_DEBUG,	cmd_debug,	RANGE+BANG+EXTRA+NL},
X	{"validate",	CMD_VALIDATE,	cmd_validate,	BANG+NL		},
X#endif
X	{"chdir",	CMD_CD,		cmd_cd,		EXRCOK+NAMEDF	},
X#ifndef NO_ERRLIST
X	{"make",	CMD_MAKE,	cmd_make,	BANG+NAMEDFS	},
X#endif
X	{"mark",	CMD_MARK,	cmd_mark,	FROM+WORD1	},
X	{"previous",	CMD_PREVIOUS,	cmd_next,	BANG		},
X	{"rewind",	CMD_REWIND,	cmd_next,	BANG		},
X	{"unmap",	CMD_UNMAP,	cmd_map,	EXRCOK+BANG+EXTRA},
X#ifndef NO_ABBR
X	{"unabbreviate",CMD_UNABBR,	cmd_abbr,	EXRCOK+WORD1	},
X#endif
X
X	{(char *)0}
X};
X
X
X/* This function parses a search pattern - given a pointer to a / or ?,
X * it replaces the ending / or ? with a \0, and returns a pointer to the
X * stuff that came after the pattern.
X */
Xchar	*parseptrn(ptrn)
X	REG char	*ptrn;
X{
X	REG char 	*scan;
X
X	for (scan = ptrn + 1;
X	     *scan && *scan != *ptrn;
X	     scan++)
X	{
X		/* allow backslashed versions of / and ? in the pattern */
X		if (*scan == '\\' && scan[1] != '\0')
X		{
X			scan++;
X		}
X	}
X	if (*scan)
X	{
X		*scan++ = '\0';
X	}
X
X	return scan;
X}
X
X
X/* This function parses a line specifier for ex commands */
Xchar *linespec(s, markptr)
X	REG char	*s;		/* start of the line specifier */
X	MARK		*markptr;	/* where to store the mark's value */
X{
X	long		num;
X	REG char	*t;
X
X	/* parse each ;-delimited clause of this linespec */
X	do
X	{
X		/* skip an initial ';', if any */
X		if (*s == ';')
X		{
X			s++;
X		}
X
X		/* skip leading spaces */
X		while (isascii(*s) && isspace(*s))
X		{
X			s++;
X		}
X	
X		/* dot means current position */
X		if (*s == '.')
X		{
X			s++;
X			*markptr = cursor;
X		}
X		/* '$' means the last line */
X		else if (*s == '$')
X		{
X			s++;
X			*markptr = MARK_LAST;
X		}
X		/* digit means an absolute line number */
X		else if (isascii(*s) && isdigit(*s))
X		{
X			for (num = 0; isascii(*s) && isdigit(*s); s++)
X			{
X				num = num * 10 + *s - '0';
X			}
X			*markptr = MARK_AT_LINE(num);
X		}
X		/* appostrophe means go to a set mark */
X		else if (*s == '\'')
X		{
X			s++;
X			*markptr = m_tomark(cursor, 1L, (int)*s);
X			s++;
X		}
X		/* slash means do a search */
X		else if (*s == '/' || *s == '?')
X		{
X			/* put a '\0' at the end of the search pattern */
X			t = parseptrn(s);
X	
X			/* search for the pattern */
X			*markptr &= ~(BLKSIZE - 1);
X			if (*s == '/')
X			{
X				pfetch(markline(*markptr));
X				if (plen > 0)
X					*markptr += plen - 1;
X				*markptr = m_fsrch(*markptr, s);
X			}
X			else
X			{
X				*markptr = m_bsrch(*markptr, s);
X			}
X	
X			/* adjust command string pointer */
X			s = t;
X		}
X	
X		/* if linespec was faulty, quit now */
X		if (!*markptr)
X		{
X			return s;
X		}
X	
X		/* maybe add an offset */
X		t = s;
X		if (*t == '-' || *t == '+')
X		{
X			s++;
X			for (num = 0; *s >= '0' && *s <= '9'; s++)
X			{
X				num = num * 10 + *s - '0';
X			}
X			if (num == 0)
X			{
X				num = 1;
X			}
X			*markptr = m_updnto(*markptr, num, *t);
X		}
X	} while (*s == ';' || *s == '+' || *s == '-');
X
X	return s;
X}
X
X
X
X/* This function reads an ex command and executes it. */
Xvoid ex()
X{
X	char		cmdbuf[80];
X	REG int		cmdlen;
X	static long	oldline;
X	
X	significant = FALSE;
X	oldline = markline(cursor);
X
X	while (mode == MODE_EX)
X	{
X		/* read a line */
X		cmdlen = vgets(':', cmdbuf, sizeof cmdbuf);
X		if (cmdlen < 0)
X		{
X			return;
X		}
X
X		/* if empty line, assume ".+1" */
X		if (cmdlen == 0)
X		{
X			strcpy(cmdbuf, ".+1");
X			qaddch('\r');
X			clrtoeol();
X		}
X		else
X		{
X			addch('\n');
X		}
X		refresh();
X
X		/* parse & execute the command */
X		doexcmd(cmdbuf);
X
X		/* handle autoprint */
X		if (significant || markline(cursor) != oldline)
X		{
X			significant = FALSE;
X			oldline = markline(cursor);
X			if (*o_autoprint && mode == MODE_EX)
X			{
X				cmd_print(cursor, cursor, CMD_PRINT, FALSE, "");
X			}
X		}
X	}
X}
X
Xvoid doexcmd(cmdbuf)
X	char		*cmdbuf;	/* string containing an ex command */
X{
X	REG char	*scan;		/* used to scan thru cmdbuf */
X	MARK		frommark;	/* first linespec */
X	MARK		tomark;		/* second linespec */
X	REG int		cmdlen;		/* length of the command name given */
X	CMD		cmd;		/* what command is this? */
X	ARGT		argt;		/* argument types for this command */
X	short		forceit;	/* bang version of a command? */
X	REG int		cmdidx;		/* index of command */
X	REG char	*build;		/* used while copying filenames */
X	int		iswild;		/* boolean: filenames use wildcards? */
X	int		isdfl;		/* using default line ranges? */
X	int		didsub;		/* did we substitute file names for % or # */
X
X
X	/* ex commands can't be undone via the shift-U command */
X	U_line = 0L;
X
X	/* ignore command lines that start with a double-quote */
X	if (*cmdbuf == '"')
X	{
X		return;
X	}
X
X	/* permit extra colons at the start of the line */
X	while (*cmdbuf == ':')
X	{
X		cmdbuf++;
X	}
X
X	/* parse the line specifier */
X	scan = cmdbuf;
X	if (nlines < 1)
X	{
X		/* no file, so don't allow addresses */
X	}
X	else if (*scan == '%')
X	{
X		/* '%' means all lines */
X		frommark = MARK_FIRST;
X		tomark = MARK_LAST;
X		scan++;
X	}
X	else if (*scan == '0')
X	{
X		frommark = tomark = MARK_UNSET;
X		scan++;
X	}
X	else
X	{
X		frommark = cursor;
X		scan = linespec(scan, &frommark);
X		tomark = frommark;
X		if (frommark && *scan == ',')
X		{
X			scan++;
X			scan = linespec(scan, &tomark);
X		}
X		if (!tomark)
X		{
X			/* faulty line spec -- fault already described */
X			return;
X		}
X		if (frommark > tomark)
X		{
X			msg("first address exceeds the second");
X			return;
X		}
X	}
X	isdfl = (scan == cmdbuf);
X
X	/* skip whitespace */
X	while (isascii(*scan) && isspace(*scan))
X	{
X		scan++;
X	}
X
X	/* if no command, then just move the cursor to the mark */
X	if (!*scan)
X	{
X		cursor = tomark;
X		return;
X	}
X
X	/* figure out how long the command name is */
X	if (isascii(*scan) && !isalpha(*scan))
X	{
X		cmdlen = 1;
X	}
X	else
X	{
X		for (cmdlen = 1;
X		     !isascii(scan[cmdlen]) || isalpha(scan[cmdlen]);
X		     cmdlen++)
X		{
X		}
X	}
X
X	/* lookup the command code */
X	for (cmdidx = 0;
X	     cmdnames[cmdidx].name && strncmp(scan, cmdnames[cmdidx].name, cmdlen);
X	     cmdidx++)
X	{
X	}
X	argt = cmdnames[cmdidx].argt;
X	cmd = cmdnames[cmdidx].code;
X	if (cmd == CMD_NULL)
X	{
X#if OSK
X		msg("Unknown command \"%s\"", scan);
X#else
X		msg("Unknown command \"%.*s\"", cmdlen, scan);
X#endif
X		return;
X	}
X
X	/* if the command ended with a bang, set the forceit flag */
X	scan += cmdlen;
X	if ((argt & BANG) && *scan == '!')
X	{
X		scan++;
X		forceit = 1;
X	}
X	else
X	{
X		forceit = 0;
X	}
X
X	/* skip any more whitespace, to leave scan pointing to arguments */
X	while (isascii(*scan) && isspace(*scan))
X	{
X		scan++;
X	}
X
X	/* a couple of special cases for filenames */
X	if (argt & XFILE)
X	{
X		/* if names were given, process them */
X		if (*scan)
X		{
X			for (build = tmpblk.c, iswild = didsub = FALSE; *scan; scan++)
X			{
X				switch (*scan)
X				{
X				  case '%':
X					if (!*origname)
X					{
X						msg("No filename to substitute for %%");
X						return;
X					}
X					strcpy(build, origname);
X					while (*build)
X					{
X						build++;
X					}
X					didsub = TRUE;
X					break;
X	
X				  case '#':
X					if (!*prevorig)
X					{
X						msg("No filename to substitute for #");
X						return;
X					}
X					strcpy(build, prevorig);
X					while (*build)
X					{
X						build++;
X					}
X					didsub = TRUE;
X					break;
X	
X				  case '*':
X				  case '?':
X#if !(MSDOS || TOS)
X				  case '[':
X				  case '`':
X				  case '{': /* } */
X				  case '$':
X				  case '~':
X#endif
X					*build++ = *scan;
X					iswild = TRUE;
X					break;
X
X				  default:
X					*build++ = *scan;
X				}
X			}
X			*build = '\0';
X	
X			if (cmd == CMD_BANG
X			 || cmd == CMD_READ && tmpblk.c[0] == '!'
X			 || cmd == CMD_WRITE && tmpblk.c[0] == '!')
X			{
X				if (didsub)
X				{
X					if (mode != MODE_EX)
X					{
X						addch('\n');
X					}
X					addstr(tmpblk.c);
X					addch('\n');
X					exrefresh();
X				}
X			}
X			else
X			{
X				if (iswild && tmpblk.c[0] != '>')
X				{
X					scan = wildcard(tmpblk.c);
X				}
X			}
X		}
X		else /* no names given, maybe assume origname */
X		{
X			if (!(argt & NODFL))
X			{
X				strcpy(tmpblk.c, origname);
X			}
X			else
X			{
X				*tmpblk.c = '\0';
X			}
X		}
X
X		scan = tmpblk.c;
X	}
X
X	/* bad arguments? */
X	if (!(argt & EXRCOK) && nlines < 1L)
X	{
X		msg("Can't use the \"%s\" command in a %s file", cmdnames[cmdidx].name, EXRC);
X		return;
X	}
X	if (!(argt & (ZERO | EXRCOK)) && frommark == MARK_UNSET)
X	{
X		msg("Can't use address 0 with \"%s\" command.", cmdnames[cmdidx].name);
X		return;
X	}
X	if (!(argt & FROM) && frommark != cursor && nlines >= 1L)
X	{
X		msg("Can't use address with \"%s\" command.", cmdnames[cmdidx].name);
X		return;
X	}
X	if (!(argt & TO) && tomark != frommark && nlines >= 1L)
X	{
X		msg("Can't use a range with \"%s\" command.", cmdnames[cmdidx].name);
X		return;
X	}
X	if (!(argt & EXTRA) && *scan)
X	{
X		msg("Extra characters after \"%s\" command.", cmdnames[cmdidx].name);
X		return;
X	}
X	if ((argt & NOSPC) && !(cmd == CMD_READ && (forceit || *scan == '!')))
X	{
X		build = scan;
X#ifndef CRUNCH
X		if ((argt & PLUS) && *build == '+')
X		{
X			while (*build && !(isascii(*build) && isspace(*build)))
X			{
X				build++;
X			}
X			while (*build && isascii(*build) && isspace(*build))
X			{
X				build++;
X			}
X		}
X#endif /* not CRUNCH */
X		for (; *build; build++)
X		{
X			if (isspace(*build))
X			{
X				msg("Too many %s to \"%s\" command.",
X					(argt & XFILE) ? "filenames" : "arguments",
X					cmdnames[cmdidx].name);
X				return;
X			}
X		}
X	}
X
X	/* some commands have special default ranges */
X	if (isdfl && (argt & DFLALL))
X	{
X		frommark = MARK_FIRST;
X		tomark = MARK_LAST;
X	}
X	else if (isdfl && (argt & DFLNONE))
X	{
X		frommark = tomark = 0L;
X	}
X
X	/* write a newline if called from visual mode */
X	if ((argt & NL) && mode != MODE_EX && !exwrote)
X	{
X		addch('\n');
X		exrefresh();
X	}
X
X	/* act on the command */
X	(*cmdnames[cmdidx].fn)(frommark, tomark, cmd, forceit, scan);
X}
X
X
X/* This function executes EX commands from a file.  It returns 1 normally, or
X * 0 if the file could not be opened for reading.
X */
Xint doexrc(filename)
X	char	*filename;	/* name of a ".exrc" file */
X{
X	int	fd;		/* file descriptor */
X	int	len;		/* length of the ".exrc" file */
X	char	buf[MAXRCLEN];	/* buffer, holds the entire .exrc file */
X
X	/* open the file, read it, and close */
X	fd = open(filename, O_RDONLY);
X	if (fd < 0)
X	{
X		return 0;
X	}
X	len = tread(fd, buf, MAXRCLEN);
X	close(fd);
X
X	/* execute the string */
X	exstring(buf, len);
X
X	return 1;
X}
X
Xvoid exstring(buf, len)
X	char	*buf;	/* the commands to execute */
X	int	len;	/* the length of the string */
X{
X	char	*cmd;		/* start of a command */
X	char	*end;		/* used to search for the end of cmd */
X
X	/* find & do each command */
X	for (cmd = buf; cmd < &buf[len]; cmd = end + 1)
X	{
X		/* find the end of the command */
X		for (end = cmd; end < &buf[len] && *end != '\n' && *end != '|'; end++)
X		{
X		}
X		*end = '\0';
X
X		/* do it */
X		doexcmd(cmd);
X	}
X}
eof
if test `wc -c <ex.c` -ne 15130
then
echo ex.c damaged!
fi
fi

if test -f input.c -a "$1" != -f
then
echo Will not overwrite input.c
else
echo Extracting input.c
sed 's/^X//' >input.c <<\eof
X/* input.c */
X
X/* Author:
X *	Steve Kirkendall
X *	14407 SW Teal Blvd. #C
X *	Beaverton, OR 97005
X *	kirkenda@cs.pdx.edu
X */
X
X
X/* This file contains the input() function, which implements vi's INPUT mode.
X * It also contains the code that supports digraphs.
X */
X
X#include <ctype.h>
X#include "config.h"
X#include "vi.h"
X
X
X#ifndef NO_DIGRAPH
Xstatic struct _DIG
X{
X	struct _DIG	*next;
X	char		key1;
X	char		key2;
X	char		dig;
X	char		save;
X} *digs;
X
Xchar digraph(key1, key2)
X	char	key1;	/* the underlying character */
X	char	key2;	/* the second character */
X{
X	int		newkey;
X	REG struct _DIG	*dp;
X
X	/* if digraphs are disabled, then just return the new char */
X	if (!*o_digraph)
X	{
X		return key2;
X	}
X
X	/* remember the new key, so we can return it if this isn't a digraph */
X	newkey = key2;
X
X	/* sort key1 and key2, so that their original order won't matter */
X	if (key1 > key2)
X	{
X		key2 = key1;
X		key1 = newkey;
X	}
X
X	/* scan through the digraph chart */
X	for (dp = digs;
X	     dp && (dp->key1 != key1 || dp->key2 != key2);
X	     dp = dp->next)
X	{
X	}
X
X	/* if this combination isn't in there, just use the new key */
X	if (!dp)
X	{
X		return newkey;
X	}
X
X	/* else use the digraph key */
X	return dp->dig;
X}
X
X/* this function lists or defines digraphs */
Xvoid do_digraph(bang, extra)
X	int	bang;
X	char	extra[];
X{
X	int		dig;
X	REG struct _DIG	*dp;
X	struct _DIG	*prev;
X	static int	user_defined = FALSE; /* boolean: are all later digraphs user-defined? */
X	char		listbuf[8];
X
X	/* if "extra" is NULL, then we've reached the end of the built-ins */
X	if (!extra)
X	{
X		user_defined = TRUE;
X		return;
X	}
X
X	/* if no args, then display the existing digraphs */
X	if (*extra < ' ')
X	{
X		listbuf[0] = listbuf[1] = listbuf[2] = listbuf[5] = ' ';
X		listbuf[7] = '\0';
X		for (dig = 0, dp = digs; dp; dp = dp->next)
X		{
X			if (dp->save || bang)
X			{
X				dig += 7;
X				if (dig >= COLS)
X				{
X					addch('\n');
X					exrefresh();
X					dig = 7;
X				}
X				listbuf[3] = dp->key1;
X				listbuf[4] = dp->key2;
X				listbuf[6] = dp->dig;
X				qaddstr(listbuf);
X			}
X		}
X		addch('\n');
X		exrefresh();
X		return;
X	}
X
X	/* make sure we have at least two characters */
X	if (!extra[1])
X	{
X		msg("Digraphs must be composed of two characters");
X		return;
X	}
X
X	/* sort key1 and key2, so that their original order won't matter */
X	if (extra[0] > extra[1])
X	{
X		dig = extra[0];
X		extra[0] = extra[1];
X		extra[1] = dig;
X	}
X
X	/* locate the new digraph character */
X	for (dig = 2; extra[dig] == ' ' || extra[dig] == '\t'; dig++)
X	{
X	}
X	dig = extra[dig];
X	if (!bang && dig)
X	{
X		dig |= 0x80;
X	}
X
X	/* search for the digraph */
X	for (prev = (struct _DIG *)0, dp = digs;
X	     dp && (dp->key1 != extra[0] || dp->key2 != extra[1]);
X	     prev = dp, dp = dp->next)
X	{
X	}
X
X	/* deleting the digraph? */
X	if (!dig)
X	{
X		if (!dp)
X		{
X#ifndef CRUNCH
X			msg("%c%c not a digraph", extra[0], extra[1]);
X#endif
X			return;
X		}
X		if (prev)
X			prev->next = dp->next;
X		else
X			digs = dp->next;
X		free(dp);
X		return;
X	}
X
X	/* if necessary, create a new digraph struct for the new digraph */
X	if (dig && !dp)
X	{
X		dp = (struct _DIG *)malloc(sizeof *dp);
X		if (!dp)
X		{
X			msg("Out of space in the digraph table");
X			return;
X		}
X		if (prev)
X			prev->next = dp;
X		else
X			digs = dp;
X		dp->next = (struct _DIG *)0;
X	}
X
X	/* assign it the new digraph value */
X	dp->key1 = extra[0];
X	dp->key2 = extra[1];
X	dp->dig = dig;
X	dp->save = user_defined;
X}
X
X# ifndef NO_MKEXRC
Xvoid savedigs(fd)
X	int		fd;
X{
X	static char	buf[] = "digraph! XX Y\n";
X	REG struct _DIG	*dp;
X
X	for (dp = digs; dp; dp = dp->next)
X	{
X		if (dp->save)
X		{
X			buf[9] = dp->key1;
X			buf[10] = dp->key2;
X			buf[12] = dp->dig;
X			write(fd, buf, (unsigned)14);
X		}
X	}
X}
X# endif
X#endif
X
X
X#ifndef NO_ABBR
Xstatic struct _AB
X{
X	struct _AB	*next;
X	char		*large;		/* the expanded form */
X	char		small[1];	/* the abbreviated form (appended to struct) */
X}
X	*abbrev;
X
X/* This functions lists or defines abbreviations */
Xvoid do_abbr(extra)
X	char	*extra;
X{
X	int		smlen;	/* length of the small form */
X	int		lrg;	/* index of the start of the large form */
X	REG struct _AB	*ab;	/* used to move through the abbrev list */
X	struct _AB	*prev;
X
X	/* no arguments? */
X	if (!*extra)
X	{
X		/* list all current abbreviations */
X		for (ab = abbrev; ab; ab = ab->next)
X		{
X			qaddstr("abbr ");
X			qaddstr(ab->small);
X			qaddch(' ');
X			qaddstr(ab->large);
X			addch('\n');
X			exrefresh();
X		}
X		return;
X	}
X
X	/* else one or more arguments.  Parse the first & look up in abbrev[] */
X	for (smlen = 0; extra[smlen] && isalnum(extra[smlen]); smlen++)
X	{
X	}
X	for (prev = (struct _AB *)0, ab = abbrev; ab; prev = ab, ab = ab->next)
X	{
X		if (!strncmp(extra, ab->small, smlen) && !ab->small[smlen])
X		{
X			break;
X		}
X	}
X
X	/* locate the start of the large form, if any */
X	for (lrg = smlen; extra[lrg] && isascii(extra[lrg]) && isspace(extra[lrg]); lrg++)
X	{
X	}
X
X	/* only one arg? */
X	if (!extra[lrg])
X	{
X		/* trying to undo an abbreviation which doesn't exist? */
X		if (!ab)
X		{
X#ifndef CRUNCH
X			msg("\"%s\" not an abbreviation", extra);
X#endif
X			return;
X		}
X
X		/* undo the abbreviation */
X		if (prev)
X			prev->next = ab->next;
X		else
X			abbrev = ab->next;
X		free(ab->large);
X		free(ab);
X
X		return;
X	}
X
X	/* multiple args - [re]define an abbreviation */
X	if (ab)
X	{
X		/* redefining - free the old large form */
X		free(ab->large);
X	}
X	else
X	{
X		/* adding a new definition - make a new struct */
X		ab = (struct _AB *)malloc((unsigned)(smlen + sizeof *ab));
X#ifndef CRUNCH
X		if (!ab)
X		{
X			msg("Out of memory -- Sorry");
X			return;
X		}
X#endif
X		strncpy(ab->small, extra, smlen);
X		ab->small[smlen] = '\0';
X		ab->next = (struct _AB *)0;
X		if (prev)
X			prev->next = ab;
X		else
X			abbrev = ab;
X	}
X
X	/* store the new form */
X	ab->large = (char *)malloc((unsigned)(strlen(&extra[lrg]) + 1));
X	strcpy(ab->large, &extra[lrg]);
X}
X
X
X# ifndef NO_MKEXRC
X/* This function is called from cmd_mkexrc() to save the abbreviations */
Xvoid saveabbr(fd)
X	int	fd;	/* fd to which the :abbr commands should be written */
X{
X	REG struct _AB	*ab;
X
X	for (ab = abbrev; ab; ab = ab->next)
X	{
X		twrite(fd, "abbr ", 5);
X		twrite(fd, ab->small, strlen(ab->small));
X		twrite(fd, " ", 1);
X		twrite(fd, ab->large, strlen(ab->large));
X		twrite(fd, "\n", 1);
X	}
X}
X# endif
X
X/* This function should be called before each char is inserted.  If the next
X * char is non-alphanumeric and we're at the end of a word, then that word
X * is checked against the abbrev[] array and expanded, if appropriate.  Upon
X * returning from this function, the new char still must be inserted.
X */
Xstatic MARK expandabbr(m, ch)
X	MARK		m;	/* the cursor position */
X	int		ch;	/* the character to insert */
X{
X	char		*word;	/* where the word starts */
X	int		len;	/* length of the word */
X	REG struct _AB	*ab;
X
X	/* if no abbreviations are in effect, or ch is aphanumeric, then
X	 * don't do anything
X	 */
X	if (!abbrev || !isascii(ch) || isalnum(ch))
X	{
X		return m;
X	}
X
X	/* see where the preceding word starts */
X	pfetch(markline(m));
X	for (word = ptext + markidx(m), len = 0;
X	     --word >= ptext && (!isascii(*word) || isalnum(*word));
X	     len++)
X	{
X	}
X	word++;
X
X	/* if zero-length, then it isn't a word, really -- so nothing */
X	if (len == 0)
X	{
X		return m;
X	}
X
X	/* look it up in the abbrev list */
X	for (ab = abbrev; ab; ab = ab->next)
X	{
X		if (!strncmp(ab->small, word, len) && !ab->small[len])
X		{
X			break;
X		}
X	}
X
X	/* not an abbreviation? then do nothing */
X	if (!ab)
X	{
X		return m;
X	}
X
X	/* else replace the small form with the large form */
X	add(m, ab->large);
X	delete(m - len, m);
X
X	/* return with the cursor after the end of the large form */
X	return m - len + strlen(ab->large);
X}
X#endif
X
X		
X/* This function allows the user to replace an existing (possibly zero-length)
X * chunk of text with typed-in text.  It returns the MARK of the last character
X * that the user typed in.
X */
XMARK input(from, to, when)
X	MARK	from;	/* where to start inserting text */
X	MARK	to;	/* extent of text to delete */
X	int	when;	/* either WHEN_VIINP or WHEN_VIREP */
X{
X	char	key[2];	/* key char followed by '\0' char */
X	char	*build;	/* used in building a newline+indent string */
X	char	*scan;	/* used while looking at the indent chars of a line */
X	MARK	m;	/* some place in the text */
X#ifndef NO_EXTENSIONS
X	int	quit = FALSE;	/* boolean: are we exiting after this? */
X#endif
X
X#ifdef DEBUG
X	/* if "from" and "to" are reversed, complain */
X	if (from > to)
X	{
X		msg("ERROR: input(%ld:%d, %ld:%d)",
X			markline(from), markidx(from),
X			markline(to), markidx(to));
X		return MARK_UNSET;
X	}
X#endif
X
X	key[1] = 0;
X
X	/* if we're replacing text with new text, save the old stuff */
X	/* (Alas, there is no easy way to save text for replace mode) */
X	if (from != to)
X	{
X		cut(from, to);
X	}
X
X	ChangeText
X	{
X		/* if doing a dot command, then reuse the previous text */
X		if (doingdot)
X		{
X			/* delete the text that's there now */
X			if (from != to)
X			{
X				delete(from, to);
X			}
X
X			/* insert the previous text */
X			cutname('.');
X			cursor = paste(from, FALSE, TRUE) + 1L;
X		}
X		else /* interactive version */
X		{
X			/* if doing a change within the line... */
X			if (from != to && markline(from) == markline(to))
X			{
X				/* mark the end of the text with a "$" */
X				change(to - 1, to, "$");
X			}
X			else
X			{
X				/* delete the old text right off */
X				if (from != to)
X				{
X					delete(from, to);
X				}
X				to = from;
X			}
X
X			/* handle autoindent of the first line, maybe */
X			cursor = from;
X			if (*o_autoindent && markline(cursor) > 1L && markidx(cursor) == 0)
X			{
X				/* Only autoindent blank lines. */
X				pfetch(markline(cursor));
X				if (plen == 0)
X				{
X					/* Okay, we really want to autoindent */
X					pfetch(markline(cursor) - 1L);
X					for (scan = ptext, build = tmpblk.c;
X					     *scan == ' ' || *scan == '\t';
X					     )
X					{
X						*build++ = *scan++;
X					}
X					if (build > tmpblk.c)
X					{
X						*build = '\0';
X						add(cursor, tmpblk.c);
X						cursor += (build - tmpblk.c);
X					}
X				}
X			}
X
X			/* repeatedly add characters from the user */
X			for (;;)
X			{
X				/* Get a character */
X				redraw(cursor, TRUE);
X#ifdef DEBUG
X				msg("cursor=%ld.%d, to=%ld.%d",
X					markline(cursor), markidx(cursor),
X					markline(to), markidx(to));
X#endif
X				key[0] = getkey(when);
X
X				/* if whitespace & wrapmargin is set & we're
X				 * past the warpmargin, then change the
X				 * whitespace character into a newline
X				 */
X				if ((*key == ' ' || *key == '\t')
X				 && *o_wrapmargin != 0)
X				{
X					pfetch(markline(cursor));
X					if (idx2col(cursor, ptext, TRUE) > COLS - (*o_wrapmargin & 0xff))
X					{
X						*key = '\n';
X					}
X				}
X
X				/* process it */
X				switch (*key)
X				{
X#ifndef NO_EXTENSIONS
X				  case 0: /* special movement mapped keys */
X					*key = getkey(0);
X					switch (*key)
X					{
X					  case 'h':	m = m_left(cursor, 0L);		break;
X					  case 'j':
X					  case 'k':	m = m_updnto(cursor, 0L, *key);	break;
X					  case 'l':	m = cursor + 1;			break;
X					  case 'b':	m = m_bword(cursor, 0L);	break;
X					  case 'w':	m = m_fword(cursor, 0L);	break;
X					  case '^':	m = m_front(cursor, 0L);	break;
X					  case '$':	m = m_rear(cursor, 0L);		break;
X					  case ctrl('B'):
X					  case ctrl('F'):
X							m = m_scroll(cursor, 0L, *key); break;
X					  case 'x':	m = v_xchar(cursor, 0L);	break;
X					  case 'i':	m = to = from = cursor;		break;
X					  default:	m = MARK_UNSET;			break;
X					}
X					/* adjust the moved cursor */
X					m = adjmove(cursor, m, (*key == 'j' || *key == 'k' ? 0x20 : 0));
X					if (*key == '$' || (*key == 'l' && m <= cursor))
X					{
X						m++;
X					}
X					/* if the cursor is reasonable, use it */
X					if (m == MARK_UNSET)
X					{
X						beep();
X					}
X					else
X					{
X						if (to > cursor)
X						{
X							delete(cursor, to);
X							redraw(cursor, TRUE);
X						}
X						from = to = cursor = m;
X					}
X					break;
X
X				  case ctrl('Z'):
X					if (getkey(0) == ctrl('Z'))
X					{
X						quit = TRUE;
X						goto BreakBreak;
X					}
X					break;
X#endif
X
X				  case ctrl('['):
X#ifndef NO_ABBR
X					cursor = expandabbr(cursor, ctrl('['));
X#endif
X					goto BreakBreak;
X
X				  case ctrl('U'):
X					if (markline(cursor) == markline(from))
X					{
X						cursor = from;
X					}
X					else
X					{
X						cursor &= ~(BLKSIZE - 1);
X					}
X					break;
X
X				  case ctrl('D'):
X				  case ctrl('T'):
X					if (to > cursor)
X					{
X						delete(cursor, to);
X					}
X					mark[27] = cursor;
X					cmd_shift(cursor, cursor, *key == ctrl('D') ? CMD_SHIFTL : CMD_SHIFTR, TRUE, "");
X					if (mark[27])
X					{
X						cursor = mark[27];
X					}
X					else
X					{
X						cursor = m_front(cursor, 0L);
X					}
X					to = cursor;
X					break;
X
X				  case '\b':
X					if (cursor <= from)
X					{
X						beep();
X					}
X					else if (markidx(cursor) == 0)
X					{
X						cursor -= BLKSIZE;
X						pfetch(markline(cursor));
X						cursor += plen;
X					}
X					else
X					{
X						cursor--;
X					}
X					break;
X
X				  case ctrl('W'):
X					m = m_bword(cursor, 1L);
X					if (markline(m) == markline(cursor) && m >= from)
X					{
X						cursor = m;
X						if (from > cursor)
X						{
X							from = cursor;
X						}
X					}
X					else
X					{
X						beep();
X					}
X					break;
X
X				  case '\n':
X#if OSK
X				  case '\l':
X#else				  
X				  case '\r':
X#endif
X#ifndef NO_ABBR
X					cursor = expandabbr(cursor, '\n');
X#endif
X					build = tmpblk.c;
X					*build++ = '\n';
X					if (*o_autoindent)
X					{
X						/* figure out indent for next line */
X						pfetch(markline(cursor));
X						for (scan = ptext; *scan == ' ' || *scan == '\t'; )
X						{
X							*build++ = *scan++;
X						}
X
X						/* remove indent from this line, if blank */
X						if (!*scan && plen > 0)
X						{
X							to = cursor &= ~(BLKSIZE - 1);
X							delete(cursor, cursor + plen);
X						}
X					}
X					*build = 0;
X					if (cursor >= to && when != WHEN_VIREP)
X					{
X						add(cursor, tmpblk.c);
X					}
X					else
X					{
X						change(cursor, to, tmpblk.c);
X					}
X					redraw(cursor, TRUE);
X					to = cursor = (cursor & ~(BLKSIZE - 1))
X							+ BLKSIZE
X							+ (int)(build - tmpblk.c) - 1;
X					break;
X
X				  case ctrl('A'):
X				  case ctrl('P'):
X					if (cursor < to)
X					{
X						delete(cursor, to);
X					}
X					if (*key == ctrl('A'))
X					{
X						cutname('.');
X					}
X					to = cursor = paste(cursor, FALSE, TRUE) + 1L;
X					break;
X
X				  case ctrl('V'):
X					if (cursor >= to && when != WHEN_VIREP)
X					{
X						add(cursor, "^");
X					}
X					else
X					{
X						change(cursor, to, "^");
X						to = cursor + 1;
X					}
X					redraw(cursor, TRUE);
X					*key = getkey(0);
X					if (*key == '\n')
X					{
X						/* '\n' too hard to handle */
X#if OSK
X						*key = '\l';
X#else
X						*key = '\r';
X#endif
X					}
X					change(cursor, cursor + 1, key);
X					cursor++;
X					if (cursor > to)
X					{
X						to = cursor;
X					}
X					break;
X
X				  case ctrl('L'):
X				  case ctrl('R'):
X					redraw(MARK_UNSET, FALSE);
X					break;
X
X				  default:
X					if (cursor >= to && when != WHEN_VIREP)
X					{
X#ifndef NO_ABBR
X						cursor = expandabbr(cursor, *key);
X#endif
X						add(cursor, key);
X						cursor++;
X						to = cursor;
X					}
X					else
X					{
X						pfetch(markline(cursor));
X						if (markidx(cursor) == plen)
X						{
X#ifndef NO_ABBR
X							cursor = expandabbr(cursor, *key);
X#endif
X							add(cursor, key);
X						}
X						else
X						{
X#ifndef NO_DIGRAPH
X							*key = digraph(ptext[markidx(cursor)], *key);
X#endif
X#ifndef NO_ABBR
X							cursor = expandabbr(cursor, *key);
X#endif
X							change(cursor, cursor + 1, key);
X						}
X						cursor++;
X					}
X#ifndef NO_SHOWMATCH
X					/* show matching "({[" if neceesary */
X					if (*o_showmatch && strchr(")}]", *key))
X					{
X						redraw(cursor, TRUE);
X						m = m_match(cursor - 1, 0L);
X						if (markline(m) >= topline
X						 && markline(m) <= botline)
X						{
X							redraw(m, TRUE);
X							refresh();
X							sleep(1);
X						}
X					}
X#endif
X				} /* end switch(*key) */
X			} /* end for(;;) */
XBreakBreak:;
X
X			/* delete any excess characters */
X			if (cursor < to)
X			{
X				delete(cursor, to);
X			}
X
X		} /* end if doingdot else */
X
X	} /* end ChangeText */
X
X	/* put the new text into a cut buffer for possible reuse */
X	if (!doingdot)
X	{
X		blksync();
X		cutname('.');
X		cut(from, cursor);
X	}
X
X	/* move to last char that we inputted, unless it was newline */
X	if (markidx(cursor) != 0)
X	{
X		cursor--;
X	}
X	redraw(cursor, FALSE);
X
X#ifndef NO_EXTENSIONS
X	if (quit)
X	{
X		/* if this is a nested "do", then cut it short */
X		abortdo();
X
X		/* exit, unless we can't write out the file */
X		cursor = v_xit(cursor, 0L, 'Z');
X	}
X#endif
X
X	rptlines = 0L;
X	return cursor;
X}
eof
if test `wc -c <input.c` -ne 16368
then
echo input.c damaged!
fi
fi

if test -f main.c -a "$1" != -f
then
echo Will not overwrite main.c
else
echo Extracting main.c
sed 's/^X//' >main.c <<\eof
X/* main.c */
X
X/* Author:
X *	Steve Kirkendall
X *	14407 SW Teal Blvd. #C
X *	Beaverton, OR 97005
X *	kirkenda@cs.pdx.edu
X */
X
X
X/* This file contains the main() function of vi */
X
X#include "config.h"
X#include <signal.h>
X#include <setjmp.h>
X#include "vi.h"
X
Xextern		trapint(); /* defined below */
Xextern char	*getenv();
Xjmp_buf		jmpenv;
X
X#ifndef NO_DIGRAPH
Xstatic init_digraphs();
X#endif
X
X/*---------------------------------------------------------------------*/
X
Xvoid main(argc, argv)
X	int	argc;
X	char	*argv[];
X{
X	int	i;
X	char	*cmd = (char *)0;
X	char	*tag = (char *)0;
X	char	*err = (char *)0;
X	char	*str;
X#if MSDOS || TOS
X	char firstarg[256];
X#else
X	char *firstarg;
X#endif
X
X	/* set mode to MODE_VI or MODE_EX depending on program name */
X	switch (argv[0][strlen(argv[0]) - 1])
X	{
X	  case 'x':			/* "ex" */
X		mode = MODE_EX;
X		break;
X
X	  case 'w':			/* "view" */
X		mode = MODE_VI;
X		*o_readonly = TRUE;
X		break;
X#ifndef NO_EXTENSIONS
X	  case 't':			/* "edit" or "input" */
X		mode = MODE_VI;
X		*o_inputmode = TRUE;
X		break;
X#endif
X	  default:			/* "vi" or "elvis" */
X		mode = MODE_VI;
X	}
X
X#ifndef DEBUG
X# ifdef	SIGQUIT
X	/* normally, we ignore SIGQUIT.  SIGINT is trapped later */
X	signal(SIGQUIT, SIG_IGN);
X# endif
X#endif
X
X	/* temporarily ignore SIGINT */
X	signal(SIGINT, SIG_IGN);
X
X	/* start curses */
X	initscr();
X	cbreak();
X	noecho();
X	scrollok(stdscr, TRUE);
X
X	/* initialize the options */
X	initopts();
X
X	/* map the arrow keys.  The KU,KD,KL,and KR variables correspond to
X	 * the :ku=: (etc.) termcap capabilities.  The variables are defined
X	 * as part of the curses package.
X	 */
X	if (has_KU) mapkey(has_KU, "k",    WHEN_VICMD|WHEN_INMV, "<Up>");
X	if (has_KD) mapkey(has_KD, "j",    WHEN_VICMD|WHEN_INMV, "<Down>");
X	if (has_KL) mapkey(has_KL, "h",    WHEN_VICMD|WHEN_INMV, "<Left>");
X	if (has_KR) mapkey(has_KR, "l",    WHEN_VICMD|WHEN_INMV, "<Right>");
X	if (has_HM) mapkey(has_HM, "^",    WHEN_VICMD|WHEN_INMV, "<Home>");
X	if (has_EN) mapkey(has_EN, "$",    WHEN_VICMD|WHEN_INMV, "<End>");
X	if (has_PU) mapkey(has_PU, "\002", WHEN_VICMD|WHEN_INMV, "<PgUp>");
X	if (has_PD) mapkey(has_PD, "\006", WHEN_VICMD|WHEN_INMV, "<PgDn>");
X#if MSDOS
X	if (*o_pcbios)
X	{
X		mapkey("#R", "i", WHEN_VICMD|WHEN_INMV,	"<Insrt>");
X		mapkey("#S", "x", WHEN_VICMD|WHEN_INMV,	"<Del>");
X		mapkey("#s", "B", WHEN_VICMD|WHEN_INMV,	"^<left>");
X		mapkey("#t", "W", WHEN_VICMD|WHEN_INMV,	"^<right>");
X	}
X#else
X	if (ERASEKEY != '\177')
X	{
X		mapkey("\177", "x", WHEN_VICMD|WHEN_INMV, "<Del>");
X	}
X#endif
X
X#ifndef NO_DIGRAPH
X	init_digraphs();
X#endif /* NO_DIGRAPH */
X
X	/* process any flags */
X	for (i = 1; i < argc && *argv[i] == '-'; i++)
X	{
X		switch (argv[i][1])
X		{
X		  case 'R':	/* readonly */
X			*o_readonly = TRUE;
X			break;
X
X		  case 'r':	/* recover */
X			msg("Use the `virec` program to recover lost files");
X			endmsgs();
X			refresh();
X			endwin();
X			exit(0);
X			break;
X
X		  case 't':	/* tag */
X			if (argv[i][2])
X			{
X				tag = argv[i] + 2;
X			}
X			else
X			{
X				i++;
X				tag = argv[i];
X			}
X			break;
X
X		  case 'v':	/* vi mode */
X			mode = MODE_VI;
X			break;
X
X		  case 'e':	/* ex mode */
X			mode = MODE_EX;
X			break;
X#ifndef NO_EXTENSIONS
X		  case 'i':	/* input mode */
X			*o_inputmode = TRUE;
X			break;
X#endif
X#ifndef NO_ERRLIST
X		  case 'm':	/* use "errlist" as the errlist */
X			if (argv[i][2])
X			{
X				err = argv[i] + 2;
X			}
X			else if (i + 1 < argc)
X			{
X				i++;
X				err = argv[i];
X			}
X			else
X			{
X				err = "";
X			}
X			break;
X#endif
X		  default:
X			msg("Ignoring unknown flag \"%s\"", argv[i]);
X		}
X	}
X
X	/* if we were given an initial ex command, save it... */
X	if (i < argc && *argv[i] == '+')
X	{
X		if (argv[i][1])
X		{
X			cmd = argv[i++] + 1;
X		}
X		else
X		{
X			cmd = "$"; /* "vi + file" means start at EOF */
X			i++;
X		}
X	}
X
X	/* the remaining args are file names. */
X	nargs = argc - i;
X	if (nargs > 0)
X	{
X#if ! ( MSDOS || TOS )
X		firstarg = argv[i];
X#endif
X		strcpy(args, argv[i]);
X		while (++i < argc && strlen(args) + 1 + strlen(argv[i]) < sizeof args)
X		{
X			strcat(args, " ");
X			strcat(args, argv[i]);
X		}
X	}
X#if ! ( MSDOS || TOS )
X	else
X	{
X		firstarg = "";
X	}
X#endif
X	argno = 0;
X
X#if MSDOS || TOS
X	if (nargs > 0)
X	{
X		strcpy(args, wildcard(args));
X		nargs = 1;
X		for (i = 0; args[i]; i++)
X		{
X			if (args[i] == ' ')
X			{
X				nargs++;
X			}
X		}
X		for (i = 0; args[i] && args[i] != ' '; i++)
X		{
X			firstarg[i] = args[i];
X		}
X		firstarg[i] = '\0';
X	}
X	else
X	{
X		firstarg[0] = '\0';
X	}
X#endif
X
X	/* perform the .exrc files and EXINIT environment variable */
X#ifdef SYSEXRC
X	doexrc(SYSEXRC);
X#endif
X#ifdef HMEXRC
X	str = getenv("HOME");
X	if (str)
X	{
X		sprintf(tmpblk.c, "%s%c%s", str, SLASH, HMEXRC);
X		doexrc(tmpblk.c);
X	}
X#endif
X	doexrc(EXRC);
X#ifdef EXINIT
X	str = getenv(EXINIT);
X	if (str)
X	{
X		exstring(str, strlen(str));
X	}
X#endif
X
X	/* search for a tag (or an error) now, if desired */
X	blkinit();
X	if (tag)
X	{
X		cmd_tag(MARK_FIRST, MARK_FIRST, CMD_TAG, 0, tag);
X	}
X#ifndef NO_ERRLIST
X	else if (err)
X	{
X		cmd_errlist(MARK_FIRST, MARK_FIRST, CMD_ERRLIST, 0, err);
X	}
X#endif
X
X	/* if no tag/err, or tag failed, then start with first arg */
X	if (tmpfd < 0 && tmpstart(firstarg) == 0 && *origname)
X	{
X		ChangeText
X		{
X		}
X		clrflag(file, MODIFIED);
X	}
X
X	/* now we do the immediate ex command that we noticed before */
X	if (cmd)
X	{
X		doexcmd(cmd);
X	}
X
X	/* repeatedly call ex() or vi() (depending on the mode) until the
X	 * mode is set to MODE_QUIT
X	 */
X	while (mode != MODE_QUIT)
X	{
X		if (setjmp(jmpenv))
X		{
X			/* Maybe we just aborted a change? */
X			abortdo();
X		}
X#if TURBOC
X		signal(SIGINT, (void(*)()) trapint);
X#else
X		signal(SIGINT, trapint);
X#endif
X
X		switch (mode)
X		{
X		  case MODE_VI:
X			vi();
X			break;
X
X		  case MODE_EX:
X			ex();
X			break;
X#ifdef DEBUG
X		  default:
X			msg("mode = %d?", mode);
X			mode = MODE_QUIT;
X#endif
X		}
X	}
X
X	/* free up the cut buffers */
X	cutend();
X
X	/* end curses */
X#ifndef	NO_CURSORSHAPE
X	if (has_CQ)
X		do_CQ();
X#endif
X	endmsgs();
X	move(LINES - 1, 0);
X	clrtoeol();
X	refresh();
X	endwin();
X
X	exit(0);
X	/*NOTREACHED*/
X}
X
X
X/*ARGSUSED*/
Xint trapint(signo)
X	int	signo;
X{
X	resume_curses(FALSE);
X	abortdo();
X#if OSK
X	sigmask(-1);
X#endif
X#if TURBO_C
X	signal(signo, (void (*)())trapint);
X#else
X	signal(signo, trapint);
X#endif
X	longjmp(jmpenv, 1);
X
X	return 0;
X}
X
X
X#ifndef NO_DIGRAPH
X
X/* This stuff us used to build the default digraphs table. */
Xstatic char	digtable[][4] =
X{
X# if CS_IBMPC
X	"C,\200",	"u\"\1",	"e'\2",		"a^\3",
X	"a\"\4",	"a`\5",		"a@\6",		"c,\7",
X	"e^\10",	"e\"\211",	"e`\12",	"i\"\13",
X	"i^\14",	"i`\15",	"A\"\16",	"A@\17",
X	"E'\20",	"ae\21",	"AE\22",	"o^\23",
X	"o\"\24",	"o`\25",	"u^\26",	"u`\27",
X	"y\"\30",	"O\"\31",	"U\"\32",	"a'\240",
X	"i'!",		"o'\"",		"u'#",		"n~$",
X	"N~%",		"a-&",		"o-'",		"~?(",
X	"~!-",		"\"<.",		"\">/",
X#  if CS_SPECIAL
X	"2/+",		"4/,",		"^+;",		"^q<",
X	"^c=",		"^r>",		"^t?",		"pp]",
X	"^^^",		"oo_",		"*a`",		"*ba",
X	"*pc",		"*Sd",		"*se",		"*uf",
X	"*tg",		"*Ph",		"*Ti",		"*Oj",
X	"*dk",		"*Hl",		"*hm",		"*En",
X	"*No",		"eqp",		"pmq",		"ger",
X	"les",		"*It",		"*iu",		"*/v",
X	"*=w",		"sq{",		"^n|",		"^2}",
X	"^3~",		"^_\377",
X#  endif /* CS_SPECIAL */
X# endif /* CS_IBMPC */
X# if CS_LATIN1
X	"~!!",		"a-*",		"\">+",		"o-:",
X	"\"<>",		"~??",
X
X	"A`@",		"A'A",		"A^B",		"A~C",
X	"A\"D",		"A@E",		"AEF",		"C,G",
X	"E`H",		"E'I",		"E^J",		"E\"K",
X	"I`L",		"I'M",		"I^N",		"I\"O",
X	"-DP",		"N~Q",		"O`R",		"O'S",
X	"O^T",		"O~U",		"O\"V",		"O/X",
X	"U`Y",		"U'Z",		"U^[",		"U\"\\",
X	"Y'_",
X
X	"a``",		"a'a",		"a^b",		"a~c",
X	"a\"d",		"a@e",		"aef",		"c,g",
X	"e`h",		"e'i",		"e^j",		"e\"k",
X	"i`l",		"i'm",		"i^n",		"i\"o",
X	"-dp",		"n~q",		"o`r",		"o's",
X	"o^t",		"o~u",		"o\"v",		"o/x",
X	"u`y",		"u'z",		"u^{",		"u\"|",
X	"y'~",
X# endif /* CS_LATIN1 */
X	""
X};
X
Xstatic init_digraphs()
X{
X	int	i;
X
X	for (i = 0; *digtable[i]; i++)
X	{
X		do_digraph(FALSE, digtable[i]);
X	}
X	do_digraph(FALSE, (char *)0);
X}
X#endif /* NO_DIGRAPH */
eof
if test `wc -c <main.c` -ne 7778
then
echo main.c damaged!
fi
fi

if test -f misc.c -a "$1" != -f
then
echo Will not overwrite misc.c
else
echo Extracting misc.c
sed 's/^X//' >misc.c <<\eof
X/* misc.c */
X
X/* Author:
X *	Steve Kirkendall
X *	14407 SW Teal Blvd. #C
X *	Beaverton, OR 97005
X *	kirkenda@cs.pdx.edu
X */
X
X
X/* This file contains functions which didn't seem happy anywhere else */
X
X#include "config.h"
X#include "vi.h"
X
X
X/* find a particular line & return a pointer to a copy of its text */
Xchar *fetchline(line)
X	long	line;	/* line number of the line to fetch */
X{
X	int		i;
X	REG char	*scan;	/* used to search for the line in a BLK */
X	long		l;	/* line number counter */
X	static BLK	buf;	/* holds ONLY the selected line (as string) */
X	REG char	*cpy;	/* used while copying the line */
X	static long	nextline;	/* }  These four variables are used */
X	static long	chglevel;	/*  } to implement a shortcut when  */
X	static char	*nextscan;	/*  } consecutive lines are fetched */
X	static long	nextlnum;	/* }                                */
X
X	/* can we do a shortcut? */
X	if (changes == chglevel && line == nextline)
X	{
X		scan = nextscan;
X	}
X	else
X	{
X		/* scan lnum[] to determine which block its in */
X		for (i = 1; line > lnum[i]; i++)
X		{
X		}
X		nextlnum = lnum[i];
X
X		/* fetch text of the block containing that line */
X		scan = blkget(i)->c;
X
X		/* find the line in the block */
X		for (l = lnum[i - 1]; ++l < line; )
X		{
X			while (*scan++ != '\n')
X			{
X			}
X		}
X	}
X
X	/* copy it into a block by itself, with no newline */
X	for (cpy = buf.c; *scan != '\n'; )
X	{
X		*cpy++ = *scan++;
X	}
X	*cpy = '\0';
X
X	/* maybe speed up the next call to fetchline() ? */
X	if (line < nextlnum)
X	{
X		nextline = line + 1;
X		chglevel = changes;
X		nextscan = scan + 1;
X	}
X	else
X	{
X		nextline = 0;
X	}
X
X	/* Calls to fetchline() interfere with calls to pfetch().  Make sure
X	 * that pfetch() resets itself on its next invocation.
X	 */
X	pchgs = 0L;
X
X	/* Return a pointer to the line's text */
X	return buf.c;
X}
X
X
X/* error message from the regexp code */
Xvoid regerror(txt)
X	char	*txt;	/* an error message */
X{
X	msg("RE error: %s", txt);
X}
X
X/* This function is equivelent to the pfetch() macro */
Xvoid	pfetch(l)
X	long	l;	/* line number of line to fetch */
X{
X	if(l != pline || changes != pchgs)
X	{
X		pline = (l);
X		ptext = fetchline(pline);
X		plen = strlen(ptext);
X		pchgs = changes;
X	}
X}
eof
if test `wc -c <misc.c` -ne 2166
then
echo misc.c damaged!
fi
fi

exit 0
-------------------------------------------------------------------------------
Steve Kirkendall     kirkenda@cs.pdx.edu      Grad student at Portland State U.