[alt.sources] Elvis 1.4, part 4 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     9230 Dec  2 17:57 blk.c
# -rw-r--r--  1 kirkenda    23975 Dec  2 17:57 cmd1.c
# -rw-r--r--  1 kirkenda    16797 Dec  2 17:57 cmd2.c
# -rw-r--r--  1 kirkenda     7951 Dec  2 17:57 config.h
# -rw-r--r--  1 kirkenda    14196 Dec  2 17:57 curses.c
#

if test -f blk.c -a "$1" != -f
then
echo Will not overwrite blk.c
else
echo Extracting blk.c
sed 's/^X//' >blk.c <<\eof
X/* blk.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 functions that get/put blocks from the temp file.
X * It also contains the "do" and "undo" functions.
X */
X
X#include "config.h"
X#include "vi.h"
X
X#ifndef NBUFS
X# define NBUFS	5		/* must be at least 3 -- more is better */
X#endif
X
Xextern long lseek();
X
X/*------------------------------------------------------------------------*/
X
XBLK		hdr;		/* buffer for the header block */
X
Xstatic int	b4cnt;		/* used to count context of beforedo/afterdo */
Xstatic struct _blkbuf
X{
X	BLK		buf;		/* contents of a text block */
X	unsigned short	logical;	/* logical block number */
X	int		dirty;		/* must the buffer be rewritten? */
X}
X		blk[NBUFS],	/* buffers for text[?] blocks */
X		*toonew,	/* buffer which shouldn't be recycled yet */
X		*newtoo,	/* another buffer which should be recycled */
X		*recycle = blk;	/* next block to be recycled */
X
X
X
X
X
X/* This function wipes out all buffers */
Xvoid blkinit()
X{
X	int	i;
X
X	for (i = 0; i < NBUFS; i++)
X	{
X		blk[i].logical = 0;
X		blk[i].dirty = FALSE;
X	}
X	for (i = 0; i < MAXBLKS; i++)
X	{
X		hdr.n[i] = 0;
X	}
X}
X
X/* This function allocates a buffer and fills it with a given block's text */
XBLK *blkget(logical)
X	int	logical;	/* logical block number to fetch */
X{
X	REG struct _blkbuf	*this;	/* used to step through blk[] */
X	REG int	i;
X
X	/* if logical is 0, just return the hdr buffer */
X	if (logical == 0)
X	{
X		return &hdr;
X	}
X
X	/* see if we have that block in mem already */
X	for (this = blk; this < &blk[NBUFS]; this++)
X	{
X		if (this->logical == logical)
X		{
X			newtoo = toonew;
X			toonew = this;
X			return &this->buf;
X		}
X	}
X
X	/* choose a block to be recycled */
X	do
X	{
X		this = recycle++;
X		if (recycle == &blk[NBUFS])
X		{
X			recycle = blk;
X		}
X	} while (this == toonew || this == newtoo);
X
X	/* if it contains a block, flush that block */
X	blkflush(this);
X
X	/* fill this buffer with the desired block */
X	this->logical = logical;
X	if (hdr.n[logical])
X	{
X		/* it has been used before - fill it from tmp file */
X		lseek(tmpfd, (long)hdr.n[logical] * (long)BLKSIZE, 0);
X		if (read(tmpfd, this->buf.c, (unsigned)BLKSIZE) != BLKSIZE)
X		{
X			msg("Error reading back from tmp file!");
X		}
X	}
X	else
X	{
X		/* it is new - zero it */
X		for (i = 0; i < BLKSIZE; i++)
X		{
X			this->buf.c[i] = 0;
X		}
X	}
X
X	/* This isn't really a change, but it does potentially invalidate
X	 * the kinds of shortcuts that the "changes" variable is supposed
X	 * to protect us from... so count it as a change.
X	 */
X	changes++;
X
X	/* mark it as being "not dirty" */
X	this->dirty = 0;
X
X	/* return it */
X	newtoo = toonew;
X	toonew = this;
X	return &this->buf;
X}
X
X
X
X/* This function writes a block out to the temporary file */
Xvoid blkflush(this)
X	REG struct _blkbuf	*this;	/* the buffer to flush */
X{
X	long		seekpos;	/* seek position of the new block */
X	unsigned short	physical;	/* physical block number */
X
X	/* if its empty (an orphan blkadd() maybe?) then make it dirty */
X	if (this->logical && !*this->buf.c)
X	{
X		blkdirty(&this->buf);
X	}
X
X	/* if it's an empty buffer or a clean version is on disk, quit */
X	if (!this->logical || hdr.n[this->logical] && !this->dirty)
X	{
X		return;
X	}
X
X	/* find a free place in the file */
X#ifndef NO_RECYCLE
X	seekpos = allocate();
X	lseek(tmpfd, seekpos, 0);
X#else
X	seekpos = lseek(tmpfd, 0L, 2);
X#endif
X	physical = seekpos / BLKSIZE;
X
X	/* put the block there */
X	if (write(tmpfd, this->buf.c, (unsigned)BLKSIZE) != BLKSIZE)
X	{
X		msg("Trouble writing to tmp file");
X	}
X	this->dirty = FALSE;
X
X	/* update the header so it knows we put it there */
X	hdr.n[this->logical] = physical;
X}
X
X
X/* This function sets a block's "dirty" flag or deletes empty blocks */
Xvoid blkdirty(bp)
X	BLK	*bp;	/* buffer returned by blkget() */
X{
X	REG int		i, j;
X	REG char	*scan;
X	REG int		k;
X
X	/* find the buffer */
X	for (i = 0; i < NBUFS && bp != &blk[i].buf; i++)
X	{
X	}
X#ifdef DEBUG
X	if (i >= NBUFS)
X	{
X		msg("blkdirty() called with unknown buffer at 0x%lx", bp);
X		return;
X	}
X	if (blk[i].logical == 0)
X	{
X		msg("blkdirty called with freed buffer");
X		return;
X	}
X#endif
X
X	/* if this block ends with line# INFINITY, then it must have been
X	 * allocated unnecessarily during tmpstart().  Forget it.
X	 */
X	if (lnum[blk[i].logical] == INFINITY)
X	{
X#ifdef DEBUG
X		if (blk[i].buf.c[0])
X		{
X			msg("bkldirty called with non-empty extra BLK");
X		}
X#endif
X		blk[i].logical = 0;
X		blk[i].dirty = FALSE;
X		return;
X	}
X
X	/* count lines in this block */
X	for (j = 0, scan = bp->c; *scan && scan < bp->c + BLKSIZE; scan++)
X	{
X		if (*scan == '\n')
X		{
X			j++;
X		}
X	}
X
X	/* adjust lnum, if necessary */
X	k = blk[i].logical;
X	j += (lnum[k - 1] - lnum[k]);
X	if (j != 0)
X	{
X		nlines += j;
X		while (k < MAXBLKS && lnum[k] != INFINITY)
X		{
X			lnum[k++] += j;
X		}
X	}
X
X	/* if it still has text, mark it as dirty */
X	if (*bp->c)
X	{
X		blk[i].dirty = TRUE;
X	}
X	else /* empty block, so delete it */
X	{
X		/* adjust the cache */
X		k = blk[i].logical;
X		for (j = 0; j < NBUFS; j++)
X		{
X			if (blk[j].logical >= k)
X			{
X				blk[j].logical--;
X			}
X		}
X
X		/* delete it from hdr.n[] and lnum[] */
X		blk[i].logical = 0;
X		blk[i].dirty = FALSE;
X		while (k < MAXBLKS - 1)
X		{
X			hdr.n[k] = hdr.n[k + 1];
X			lnum[k] = lnum[k + 1];
X			k++;
X		}
X		hdr.n[MAXBLKS - 1] = 0;
X		lnum[MAXBLKS - 1] = INFINITY;
X	}
X}
X
X
X/* insert a new block into hdr, and adjust the cache */
XBLK *blkadd(logical)
X	int	logical;	/* where to insert the new block */
X{
X	REG int	i;
X
X	/* adjust hdr and lnum[] */
X	for (i = MAXBLKS - 1; i > logical; i--)
X	{
X		hdr.n[i] = hdr.n[i - 1];
X		lnum[i] = lnum[i - 1];
X	}
X	hdr.n[logical] = 0;
X	lnum[logical] = lnum[logical - 1];
X
X	/* adjust the cache */
X	for (i = 0; i < NBUFS; i++)
X	{
X		if (blk[i].logical >= logical)
X		{
X			blk[i].logical++;
X		}
X	}
X
X	/* return the new block, via blkget() */
X	return blkget(logical);
X}
X
X
X/* This function forces all dirty blocks out to disk */
Xvoid blksync()
X{
X	int	i;
X
X	for (i = 0; i < NBUFS; i++)
X	{
X		/* blk[i].dirty = TRUE; */
X		blkflush(&blk[i]);
X	}
X	if (*o_sync)
X	{
X		sync();
X	}
X}
X
X/*------------------------------------------------------------------------*/
X
Xstatic MARK	undocurs;	/* where the cursor should go if undone */
Xstatic long	oldnlines;
Xstatic long	oldlnum[MAXBLKS];
X
X
X/* This function should be called before each command that changes the text.
X * It defines the state that undo() will reset the file to.
X */
Xvoid beforedo(forundo)
X	int		forundo;	/* boolean: is this for an undo? */
X{
X	REG int		i;
X	REG long	l;
X
X	/* if this is a nested call to beforedo, quit! Use larger context */
X	if (b4cnt++ > 0)
X	{
X		return;
X	}
X
X	/* force all block buffers to disk */
X	blksync();
X
X#ifndef NO_RECYCLE
X	/* perform garbage collection on blocks from tmp file */
X	garbage();
X#endif
X
X	/* force the header out to disk */
X	lseek(tmpfd, 0L, 0);
X	if (write(tmpfd, hdr.c, (unsigned)BLKSIZE) != BLKSIZE)
X	{
X		msg("Trouble writing header to tmp file ");
X	}
X
X	/* copy or swap oldnlines <--> nlines, oldlnum <--> lnum */
X	if (forundo)
X	{
X		for (i = 0; i < MAXBLKS; i++)
X		{
X			l = lnum[i];
X			lnum[i] = oldlnum[i];
X			oldlnum[i] = l;
X		}
X		l = nlines;
X		nlines = oldnlines;
X		oldnlines = l;
X	}
X	else
X	{
X		for (i = 0; i < MAXBLKS; i++)
X		{
X			oldlnum[i] = lnum[i];
X		}
X		oldnlines = nlines;
X	}
X
X	/* save the cursor position */
X	undocurs = cursor;
X
X	/* upon return, the calling function continues and makes changes... */
X}
X
X/* This function marks the end of a (nested?) change to the file */
Xvoid afterdo()
X{
X	if (--b4cnt)
X	{
X		/* after abortdo(), b4cnt may decribe nested beforedo/afterdo
X		 * pairs incorrectly.  If it is decremented to often, then
X		 * keep b4cnt sane but don't do anything else.
X		 */
X		if (b4cnt < 0)
X			b4cnt = 0;
X
X		return;
X	}
X
X	/* make sure the cursor wasn't left stranded in deleted text */
X	if (markline(cursor) > nlines)
X	{
X		cursor = MARK_LAST;
X	}
X	/* NOTE: it is still possible that markidx(cursor) is after the
X	 * end of a line, so the Vi mode will have to take care of that
X	 * itself */
X
X	/* if a significant change has been made to this file, then set the
X	 * MODIFIED flag.
X	 */
X	if (significant)
X	{
X		setflag(file, MODIFIED);
X	}	
X}
X
X/* This function cuts short the current set of changes.  It is called after
X * a SIGINT.
X */
Xvoid abortdo()
X{
X	/* finish the operation immediately. */
X	if (b4cnt > 0)
X	{
X		b4cnt = 1;
X		afterdo();
X	}
X
X	/* in visual mode, the screen is probably screwed up */
X	if (mode == MODE_COLON)
X	{
X		mode = MODE_VI;
X	}
X	if (mode == MODE_VI)
X	{
X		redraw(MARK_UNSET, FALSE);
X	}
X}
X
X/* This function discards all changes made since the last call to beforedo() */
Xint undo()
X{
X	BLK		oldhdr;
X
X	/* if beforedo() has never been run, fail */
X	if (!tstflag(file, MODIFIED))
X	{
X		msg("You haven't modified this file yet.");
X		return FALSE;
X	}
X
X	/* read the old header form the tmp file */
X	lseek(tmpfd, 0L, 0);
X	if (read(tmpfd, oldhdr.c, (unsigned)BLKSIZE) != BLKSIZE)
X	{
X		msg("Trouble rereading the old header from tmp file");
X	}
X
X	/* "do" the changed version, so we can undo the "undo" */
X	cursor = undocurs;
X	beforedo(TRUE);
X	afterdo();
X
X	/* wipe out the block buffers - we can't assume they're correct */
X	blkinit();
X
X	/* use the old header -- and therefore the old text blocks */
X	hdr = oldhdr;
X
X	/* This is a change */
X	changes++;
X
X	return TRUE;
X}
eof
if test `wc -c <blk.c` -ne 9230
then
echo blk.c damaged!
fi
fi

if test -f cmd1.c -a "$1" != -f
then
echo Will not overwrite cmd1.c
else
echo Extracting cmd1.c
sed 's/^X//' >cmd1.c <<\eof
X/* cmd1.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 some of the EX commands - mostly ones that deal with
X * files, options, etc. -- anything except text.
X */
X
X#include "config.h"
X#include <ctype.h>
X#include "vi.h"
X#include "regexp.h"
X
X#if MSDOS
X#define	DATE __DATE__
X#endif
X
X#ifdef DEBUG
X/* print the selected lines with info on the blocks */
X/*ARGSUSED*/
Xvoid cmd_debug(frommark, tomark, cmd, bang, extra)
X	MARK	frommark;
X	MARK	tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	REG char	*scan;
X	REG long	l;
X	REG int		i;
X	int		len;
X
X	/* scan lnum[] to determine which block its in */
X	l = markline(frommark);
X	for (i = 1; l > lnum[i]; i++)
X	{
X	}
X
X	do
X	{
X		/* fetch text of the block containing that line */
X		scan = blkget(i)->c;
X
X		/* calculate its length */
X		if (scan[BLKSIZE - 1])
X		{
X			len = BLKSIZE;
X		}
X		else
X		{
X			len = strlen(scan);
X		}
X
X		/* print block stats */
X		msg("##### hdr[%d]=%d, lnum[%d-1]=%ld, lnum[%d]=%ld (%ld lines)",
X			i, hdr.n[i], i, lnum[i-1], i, lnum[i], lnum[i] - lnum[i - 1]);
X		msg("##### len=%d, buf=0x%lx, %sdirty",
X			len, scan, ((int *)scan)[MAXBLKS + 1] ? "" : "not ");
X		if (bang)
X		{
X			while (--len >= 0)
X			{
X				addch(*scan);
X				scan++;
X			}
X		}
X		exrefresh();
X
X		/* next block */
X		i++;
X	} while (i < MAXBLKS && lnum[i] && lnum[i - 1] < markline(tomark));
X}
X
X
X/* This function checks a lot of conditions to make sure they aren't screwy */
X/*ARGSUSED*/
Xvoid cmd_validate(frommark, tomark, cmd, bang, extra)
X	MARK	frommark;
X	MARK	tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	char	*scan;
X	int	i;
X	int	nlcnt;	/* used to count newlines */
X	int	len;	/* counts non-NUL characters */
X
X	/* check lnum[0] */
X	if (lnum[0] != 0L)
X	{
X		msg("lnum[0] = %ld", lnum[0]);
X	}
X
X	/* check each block */
X	for (i = 1; lnum[i] <= nlines; i++)
X	{
X		scan = blkget(i)->c;
X		if (scan[BLKSIZE - 1])
X		{
X			msg("block %d has no NUL at the end", i);
X		}
X		else
X		{
X			for (nlcnt = len = 0; *scan; scan++, len++)
X			{
X				if (*scan == '\n')
X				{
X					nlcnt++;
X				}
X			}
X			if (scan[-1] != '\n')
X			{
X				msg("block %d doesn't end with '\\n' (length %d)", i, len);
X			}
X			if (bang || nlcnt != lnum[i] - lnum[i - 1])
X			{
X				msg("block %d (line %ld?) has %d lines, but should have %ld",
X					i, lnum[i - 1] + 1L, nlcnt, lnum[i] - lnum[i - 1]);
X			}
X		}
X		exrefresh();
X	}
X
X	/* check lnum again */
X	if (lnum[i] != INFINITY)
X	{
X		msg("hdr.n[%d] = %d, but lnum[%d] = %ld",
X			i, hdr.n[i], i, lnum[i]);
X	}
X
X	msg("# = \"%s\", %% = \"%s\"", prevorig, origname);
X}
X#endif /* DEBUG */
X
X
X/*ARGSUSED*/
Xvoid cmd_mark(frommark, tomark, cmd, bang, extra)
X	MARK	frommark;
X	MARK	tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	/* validate the name of the mark */
X	if (!extra || *extra < 'a' || *extra > 'z' || extra[1])
X	{
X		msg("Invalid mark name");
X		return;
X	}
X
X	mark[*extra - 'a'] = tomark;
X}
X
X/*ARGSUSED*/
Xvoid cmd_write(frommark, tomark, cmd, bang, extra)
X	MARK	frommark;
X	MARK	tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	int		fd;
X	int		append;	/* boolean: write in "append" mode? */
X	REG long	l;
X	REG char	*scan;
X	REG int		i;
X
X	/* if all lines are to be written, use tmpsave() */
X	if (frommark == MARK_FIRST && tomark == MARK_LAST)
X	{
X		tmpsave(extra, bang);
X		return;
X	}
X
X	/* see if we're going to do this in append mode or not */
X	append = FALSE;
X	if (extra[0] == '>' && extra[1] == '>')
X	{
X		extra += 2;
X		append = TRUE;
X	}
X
X	/* either the file must not exist, or we must have a ! or be appending */
X	if (access(extra, 0) == 0 && !bang && !append)
X	{
X		msg("File already exists - Use :w! to overwrite");
X		return;
X	}
X
X	/* else do it line-by-line, like cmd_print() */
X	if (append)
X	{
X#ifdef O_APPEND
X		fd = open(extra, O_WRONLY|O_APPEND);
X#else
X		fd = open(extra, O_WRONLY);
X		if (fd >= 0)
X		{
X			lseek(fd, 0L, 2);
X		}
X#endif
X	}
X	else
X	{
X		fd = -1; /* so we know the file isn't open yet */
X	}
X
X	if (fd < 0)
X	{
X		fd = creat(extra, FILEPERMS);
X		if (fd < 0)
X		{
X			msg("Can't write to \"%s\"", extra);
X			return;
X		}
X	}
X	for (l = markline(frommark); l <= markline(tomark); l++)
X	{
X		/* get the next line */
X		scan = fetchline(l);
X		i = strlen(scan);
X		scan[i++] = '\n';
X
X		/* print the line */
X		twrite(fd, scan, i);
X	}
X	close(fd);
X}	
X
X
X/*ARGSUSED*/
Xvoid cmd_shell(frommark, tomark, cmd, bang, extra)
X	MARK	frommark, tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	static char	prevextra[80];
X
X	/* special case: ":sh" means ":!sh" */
X	if (cmd == CMD_SHELL)
X	{
X		extra = o_shell;
X		frommark = tomark = 0L;
X	}
X
X	/* if extra is "!", substute previous command */
X	if (*extra == '!')
X	{
X		if (!*prevextra)
X		{
X			msg("No previous shell command to substitute for '!'");
X			return;
X		}
X		extra = prevextra;
X	}
X	else if (cmd == CMD_BANG && strlen(extra) < sizeof(prevextra) - 1)
X	{
X		strcpy(prevextra, extra);
X	}
X
X	/* if no lines were specified, just run the command */
X	suspend_curses();
X	if (frommark == 0L)
X	{
X		system(extra);
X	}
X	else /* pipe lines from the file through the command */
X	{
X		filter(frommark, tomark, extra);
X	}
X
X	/* resume curses quietly for MODE_EX, but noisily otherwise */
X	resume_curses(mode == MODE_EX);
X}
X
X
X/*ARGSUSED*/
Xvoid cmd_global(frommark, tomark, cmd, bang, extra)
X	MARK	frommark, tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;	/* rest of the command line */
X{
X	char	*cmdptr;	/* the command from the command line */
X	char	cmdln[100];	/* copy of the command from the command line */
X	char	*line;		/* a line from the file */
X	long	l;		/* used as a counter to move through lines */
X	long	lqty;		/* quantity of lines to be scanned */
X	long	nchanged;	/* number of lines changed */
X	regexp	*re;		/* the compiled search expression */
X
X	/* can't nest global commands */
X	if (doingglobal)
X	{
X		msg("Can't nest global commands.");
X		rptlines = -1L;
X		return;
X	}
X
X	/* ":g! ..." is the same as ":v ..." */
X	if (bang)
X	{
X		cmd = CMD_VGLOBAL;
X	}
X
X	/* make sure we got a search pattern */
X	if (*extra != '/' && *extra != '?')
X	{
X		msg("Usage: %c /regular expression/ command", cmd == CMD_GLOBAL ? 'g' : 'v');
X		return;
X	}
X
X	/* parse & compile the search pattern */
X	cmdptr = parseptrn(extra);
X	if (!extra[1])
X	{
X		msg("Can't use empty regular expression with '%c' command", cmd == CMD_GLOBAL ? 'g' : 'v');
X		return;
X	}
X	re = regcomp(extra + 1);
X	if (!re)
X	{
X		/* regcomp found & described an error */
X		return;
X	}
X
X	/* for each line in the range */
X	doingglobal = TRUE;
X	ChangeText
X	{
X		/* NOTE: we have to go through the lines in a forward order,
X		 * otherwise "g/re/p" would look funny.  *BUT* for "g/re/d"
X		 * to work, simply adding 1 to the line# on each loop won't
X		 * work.  The solution: count lines relative to the end of
X		 * the file.  Think about it.
X		 */
X		for (l = nlines - markline(frommark),
X			lqty = markline(tomark) - markline(frommark) + 1L,
X			nchanged = 0L;
X		     lqty > 0 && nlines - l >= 0 && nchanged >= 0L;
X		     l--, lqty--)
X		{
X			/* fetch the line */
X			line = fetchline(nlines - l);
X
X			/* if it contains the search pattern... */
X			if ((!regexec(re, line, 1)) == (cmd != CMD_GLOBAL))
X			{
X				/* move the cursor to that line */
X				cursor = MARK_AT_LINE(nlines - l);
X
X				/* do the ex command (without mucking up
X				 * the original copy of the command line)
X				 */
X				strcpy(cmdln, cmdptr);
X				rptlines = 0L;
X				doexcmd(cmdln);
X				nchanged += rptlines;
X			}
X		}
X	}
X	doingglobal = FALSE;
X
X	/* free the regexp */
X	free(re);
X
X	/* Reporting...*/
X	rptlines = nchanged;
X}
X
X
X/*ARGSUSED*/
Xvoid cmd_file(frommark, tomark, cmd, bang, extra)
X	MARK	frommark, tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X#ifndef CRUNCH
X	/* if we're given a new filename, use it as this file's name */
X	if (extra && *extra)
X	{
X		strcpy(origname, extra);
X	}
X#endif
X	if (cmd == CMD_FILE)
X	{
X		msg("\"%s\" %s%s %ld lines,  line %ld [%ld%%]",
X			*origname ? origname : "[NO FILE]",
X			tstflag(file, MODIFIED) ? "[MODIFIED]" : "",
X			tstflag(file, READONLY) ? "[READONLY]" : "",
X			nlines,
X			markline(frommark),
X			markline(frommark) * 100 / nlines);
X	}
X	else if (markline(frommark) == markline(tomark))
X	{
X		msg("%ld", markline(frommark));
X	}
X	else
X	{
X		msg("range \"%ld,%ld\" contains %ld lines",
X			markline(frommark),
X			markline(tomark),
X			markline(tomark) - markline(frommark) + 1L);
X	}
X}
X
X
X/*ARGSUSED*/
Xvoid cmd_edit(frommark, tomark, cmd, bang, extra)
X	MARK	frommark, tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	long	line = 1L;	/* might be set to prevline */
X
X	/* Editing previous file?  Then start at previous line */
X	if (!strcmp(extra, prevorig))
X	{
X		line = prevline;
X	}
X
X#ifndef CRUNCH
X	/* if we were given an explicit starting line, then start there */
X	if (*extra == '+')
X	{
X		for (extra++, line = 0L; *extra >= '0' && *extra <= '9'; extra++)
X		{
X			line *= 10L;
X			line += (*extra - '0');
X		}
X		while (isascii(*extra) && isspace(*extra))
X		{
X			extra++;
X		}
X	}
X#endif /* not CRUNCH */
X
X	/* switch files */
X	if (tmpabort(bang))
X	{
X		tmpstart(extra);
X		if (line <= nlines && line >= 1L)
X		{
X			cursor = MARK_AT_LINE(line);
X		}
X	}
X	else
X	{
X		msg("Use edit! to abort changes, or w to save changes");
X
X		/* so we can say ":e!#" next time... */
X		strcpy(prevorig, extra);
X		prevline = 1L;
X	}
X}
X
X/* This code is also used for rewind -- GB */
X
X/*ARGSUSED*/
Xvoid cmd_next(frommark, tomark, cmd, bang, extra)
X	MARK	frommark, tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	int	i, j;
X	char	*scan;
X	char	*build;
X
X	/* if extra stuff given, use ":args" to define a new args list */
X	if (cmd == CMD_NEXT && extra && *extra)
X	{
X		cmd_args(frommark, tomark, cmd, bang, extra);
X	}
X
X	/* move to the next arg */
X	if (cmd == CMD_NEXT)
X	{
X		i = argno + 1;
X	}
X	else if (cmd == CMD_PREVIOUS)
X	{
X		i = argno - 1;
X	}
X	else /* cmd == CMD_REWIND */
X	{
X		i = 0;
X	}	
X	if (i < 0 || i >= nargs)
X	{
X		msg("No %sfiles to edit", cmd == CMD_REWIND ? "" : "more ");
X		return;
X	}
X
X	/* find & isolate the name of the file to edit */
X	for (j = i, scan = args; j > 0; j--)
X	{
X		while(!isascii(*scan) || !isspace(*scan))
X		{
X			scan++;
X		}
X		while (isascii(*scan) && isspace(*scan))
X		{
X			scan++;
X		}
X	}
X	for (build = tmpblk.c; *scan && (!isascii(*scan) || !isspace(*scan)); )
X	{
X		*build++ = *scan++;
X	}
X	*build = '\0';
X
X	/* switch to the next file */
X	if (tmpabort(bang))
X	{
X		tmpstart(tmpblk.c);
X		argno = i;
X	}
X	else
X	{
X		msg("Use :%s! to abort changes, or w to save changes",
X			cmd == CMD_NEXT ? "next" :
X			cmd == CMD_PREVIOUS ? "previous" :
X					"rewind");
X	}
X}
X
X/* also called from :wq -- always writes back in this case */
X
X/*ARGSUSED*/
Xvoid cmd_xit(frommark, tomark, cmd, bang, extra)
X	MARK	frommark, tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	static long	whenwarned;	/* when the user was last warned of extra files */
X	int		oldflag;
X
X	/* if there are more files to edit, then warn user */
X	if (argno + 1 < nargs && whenwarned != changes && (!bang || cmd != CMD_QUIT))
X	{
X		msg("More files to edit -- Use \":n\" to go to next file");
X		whenwarned = changes;
X		return;
X	}
X
X	if (cmd == CMD_QUIT)
X	{
X		if (tmpabort(bang))
X		{
X			mode = MODE_QUIT;
X		}
X		else
X		{
X			msg("Use q! to abort changes, or wq to save changes");
X		}
X	}
X	else
X	{
X		/* else try to save this file */
X		oldflag = tstflag(file, MODIFIED);
X		if (cmd == CMD_WQUIT)
X			setflag(file, MODIFIED);
X		if (tmpend(bang))
X		{
X			mode = MODE_QUIT;
X		}
X		else
X		{
X			msg("Could not save file -- use quit! to abort changes, or w filename");
X		}
X		if (!oldflag)
X			clrflag(file, MODIFIED);
X	}
X}
X
X
X/*ARGSUSED*/
Xvoid cmd_args(frommark, tomark, cmd, bang, extra)
X	MARK	frommark, tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	char	*scan;
X	char	*eow;
X	int	col;
X	int	arg;
X	int	addcols;
X	int	scrolled = 0;
X
X	/* if no extra names given, or just current name, then report the args
X	 * we have now.
X	 */
X	if (!extra || !*extra)
X	{
X		for (scan = args, col=arg=0; *scan; )
X		{
X			while (*scan && isascii(*scan) && isspace(*scan))
X				scan++;
X			eow = scan;
X			while (*eow && (!isascii(*++eow) || !isspace(*eow)))
X				;
X			if (arg == argno)
X				addcols = 2;
X			else
X				addcols = 0;	
X			if (col+addcols+(int)(eow-scan)+1>=COLS)
X			{
X				addch('\n');
X				scrolled=1;
X				col=0;
X			}
X			else if (arg)
X			{	qaddch(' ');
X				col++;
X			}
X			if (arg == argno)
X				qaddch('[');
X			while (scan < eow)
X			{	qaddch(*scan++);
X				col++;
X			}
X			if (arg == argno)
X				qaddch(']');	
X			arg++;	
X			col+=addcols;
X		}
X		/* write a trailing newline */
X		if ((mode == MODE_EX || mode == MODE_COLON || scrolled) && col)
X			addch('\n');
X		exrefresh();	
X	}
X	else /* new args list given */
X	{
X		strcpy(args, extra);
X		argno = -1; /* before the first, so :next will go to first */
X
X		/* count the names */
X		for (nargs = 0, scan = args; *scan; nargs++)
X		{
X			while (*scan && (!isascii(*scan) || !isspace(*scan)))
X			{
X				scan++;
X			}
X			while (isascii(*scan) && isspace(*scan))
X			{
X				scan++;
X			}
X		}
X		msg("%d files to edit", nargs);
X	}
X}
X
X
X/*ARGSUSED*/
Xvoid cmd_cd(frommark, tomark, cmd, bang, extra)
X	MARK	frommark, tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	char	*getenv();
X
X	/* default directory name is $HOME */
X	if (!*extra)
X	{
X		extra = getenv("HOME");
X		if (!extra)
X		{
X			msg("environment variable $HOME not set");
X			return;
X		}
X	}
X
X	/* go to the directory */
X	if (chdir(extra) < 0)
X	{
X		perror(extra);
X	}
X}
X
X
X/*ARGSUSED*/
Xvoid cmd_map(frommark, tomark, cmd, bang, extra)
X	MARK	frommark, tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	char	*mapto;
X
X	/* "map" with no extra will dump the map table contents */
X	if (!*extra)
X	{
X		dumpkey(bang ? WHEN_VIINP|WHEN_VIREP : WHEN_VICMD);
X	}
X	else
X	{
X		/* "extra" is key to map, followed my what it maps to */
X		for (mapto = extra; *mapto && *mapto != ' ' && *mapto!= '\t'; mapto++)
X		{
X		}
X		while (*mapto == ' ' || *mapto == '\t')
X		{
X			*mapto++ = '\0';
X		}
X
X		mapkey(extra, mapto, bang ? WHEN_VIINP|WHEN_VIREP : WHEN_VICMD, (char *)0);
X	}
X}
X
X
X/*ARGSUSED*/
Xvoid cmd_set(frommark, tomark, cmd, bang, extra)
X	MARK	frommark, tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	if (!*extra)
X	{
X		dumpopts(FALSE);/* "FALSE" means "don't dump all" - only set */
X	}
X	else if (!strcmp(extra, "all"))
X	{
X		dumpopts(TRUE);	/* "TRUE" means "dump all" - even unset vars */
X	}
X	else
X	{
X		setopts(extra);
X
X		/* That option may have affected the appearence of text */
X		changes++;
X	}
X}
X
X/*ARGSUSED*/
Xvoid cmd_tag(frommark, tomark, cmd, bang, extra)
X	MARK	frommark, tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	char	*scan;	/* used to scan through the tmpblk.c */
X	char	*cmp;	/* char of tag name we're comparing, or NULL */
X	char	*end;	/* marks the end of chars in tmpblk.c */
X	int	fd;	/* file descriptor used to read the file */
X#ifndef NO_MAGIC
X	char	wasmagic; /* preserves the original state of o_magic */
X#endif
X	static char prevtag[30];
X
X	/* if no tag is given, use the previous tag */
X	if (!extra || !*extra)
X	{
X		if (!*prevtag)
X		{
X			msg("No previous tag");
X			return;
X		}
X		extra = prevtag;
X	}
X	else
X	{
X		strncpy(prevtag, extra, sizeof prevtag);
X	}
X
X	/* open the tags file */
X	fd = open(TAGS, O_RDONLY);
X	if (fd < 0)
X	{
X		msg("No tags file");
X		return;
X	}
X
X	/* Hmmm... this would have been a lot easier with <stdio.h> */
X
X	/* find the line with our tag in it */
X	for(scan = end = tmpblk.c, cmp = extra; ; scan++)
X	{
X		/* read a block, if necessary */
X		if (scan >= end)
X		{
X			end = tmpblk.c + tread(fd, tmpblk.c, BLKSIZE);
X			scan = tmpblk.c;
X			if (scan >= end)
X			{
X				msg("tag \"%s\" not found", extra);
X				close(fd);
X				return;
X			}
X		}
X
X		/* if we're comparing, compare... */
X		if (cmp)
X		{
X			/* matched??? wow! */
X			if (!*cmp && *scan == '\t')
X			{
X				break;
X			}
X			if (*cmp++ != *scan)
X			{
X				/* failed! skip to newline */
X				cmp = (char *)0;
X			}
X		}
X
X		/* if we're skipping to newline, do it fast! */
X		if (!cmp)
X		{
X			while (scan < end && *scan != '\n')
X			{
X				scan++;
X			}
X			if (scan < end)
X			{
X				cmp = extra;
X			}
X		}
X	}
X
X	/* found it! get the rest of the line into memory */
X	for (cmp = tmpblk.c, scan++; scan < end && *scan != '\n'; )
X	{
X		*cmp++ = *scan++;
X	}
X	if (scan == end)
X	{
X		tread(fd, cmp, BLKSIZE - (cmp - tmpblk.c));
X	}
X
X	/* we can close the tags file now */
X	close(fd);
X
X	/* extract the filename from the line, and edit the file */
X	for (cmp = tmpblk.c; *cmp != '\t'; cmp++)
X	{
X	}
X	*cmp++ = '\0';
X	if (strcmp(origname, tmpblk.c) != 0)
X	{
X		if (!tmpabort(bang))
X		{
X			msg("Use :tag! to abort changes, or :w to save changes");
X			return;
X		}
X		tmpstart(tmpblk.c);
X	}
X
X	/* move to the desired line (or to line 1 if that fails) */
X#ifndef NO_MAGIC
X	wasmagic = *o_magic;
X	*o_magic = FALSE;
X#endif
X	cursor = MARK_FIRST;
X	linespec(cmp, &cursor);
X	if (cursor == MARK_UNSET)
X	{
X		cursor = MARK_FIRST;
X	}
X#ifndef NO_MAGIC
X	*o_magic = wasmagic;
X#endif
X}
X
X
X
X/*ARGSUSED*/
Xvoid cmd_visual(frommark, tomark, cmd, bang, extra)
X	MARK	frommark, tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	mode = MODE_VI;
X	msg("");
X}
X
X
X
X
X
X/* describe this version of the program */
X/*ARGSUSED*/
Xvoid cmd_version(frommark, tomark, cmd, bang, extra)
X	MARK	frommark;
X	MARK	tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X#ifndef DATE
X	msg("%s", VERSION);
X#else
X	msg("%s  (%s)", VERSION, DATE);
X#endif
X#ifdef COMPILED_BY
X	msg("Compiled by %s", COMPILED_BY);
X#endif
X#ifdef CREDIT
X	msg("%s", CREDIT);
X#endif
X#ifdef COPYING
X	msg("%s", COPYING);
X#endif
X}
X
X
X#ifndef NO_MKEXRC
X/* make a .exrc file which describes the current configuration */
X/*ARGSUSED*/
Xvoid cmd_mkexrc(frommark, tomark, cmd, bang, extra)
X	MARK	frommark;
X	MARK	tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	int	fd;
X
X	/* the default name for the .exrc file EXRC */
X	if (!*extra)
X	{
X		extra = EXRC;
X	}
X
X	/* create the .exrc file */
X	fd = creat(extra, FILEPERMS);
X	if (fd < 0)
X	{
X		msg("Couldn't create a new \"%s\" file", extra);
X		return;
X	}
X
X	/* save stuff */
X	savekeys(fd);
X	saveopts(fd);
X#ifndef NO_DIGRAPH
X	savedigs(fd);
X#endif
X#ifndef	NO_ABBR
X	saveabbr(fd);
X#endif
X
X	/* close the file */
X	close(fd);
X	msg("Created a new \"%s\" file", extra);
X}
X#endif
X
X#ifndef NO_DIGRAPH
X/*ARGSUSED*/
Xvoid cmd_digraph(frommark, tomark, cmd, bang, extra)
X	MARK	frommark;
X	MARK	tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	do_digraph(bang, extra);
X}
X#endif
X
X
X#ifndef NO_ERRLIST 
Xstatic char	errfile[256];	/* the name of a file containing an error */
Xstatic long	errline;	/* the line number for an error */
X
X/* This static function tries to parse an error message.
X *
X * For most compilers, the first word is taken to be the name of the erroneous
X * file, and the first number after that is taken to be the line number where
X * the error was detected.  The description of the error follows, possibly
X * preceded by an "error ... :" or "warning ... :" label which is skipped.
X *
X * For Coherent, error messages look like "line#: filename: message".
X *
X * For non-error lines, or unparsable error lines, this function returns NULL.
X * Normally, though, it alters errfile and errline, and returns a pointer to
X * the description.
X */
Xstatic char *parse_errmsg(text)
X	REG char	*text;
X{
X	REG char	*cpy;
X	long		atol();
X# if COHERENT || TOS /* any Mark Williams compiler */
X	/* Get the line number.  If no line number, then ignore this line. */
X	errline = atol(text);
X	if (errline == 0L)
X		return (char *)0;
X
X	/* Skip to the start of the filename */
X	while (*text && *text++ != ':')
X	{
X	}
X	if (!*text++)
X		return (char *)0;
X
X	/* copy the filename to errfile */
X	for (cpy = errfile; *text && (*cpy++ = *text++) != ':'; )
X	{
X	}
X	if (!*text++)
X		return (char *)0;
X	cpy[-1] = '\0';
X
X	return text;
X# else /* not a Mark Williams compiler */
X	char		*errmsg;
X
X	/* the error message is the whole line, by default */
X	errmsg = text;
X
X	/* skip leading garbage */
X	while (*text && !(isascii(*text) && isalnum(*text)))
X	{
X		text++;
X	}
X
X	/* copy over the filename */
X	cpy = errfile;
X	while(isascii(*text) && isalnum(*text) || *text == '.')
X	{
X		*cpy++ = *text++;
X	}
X	*cpy = '\0';
X
X	/* ignore the name "Error" and filenames that contain a '/' */
X	if (*text == '/' || !strcmp(errfile + 1, "rror") || access(errfile, 0) < 0)
X	{
X		return (char *)0;
X	}
X
X	/* skip garbage between filename and line number */
X	while (*text && !(isascii(*text) && isdigit(*text)))
X	{
X		text++;
X	}
X
X	/* if the number is part of a larger word, then ignore this line */
X	if (*text && isascii(text[-1]) && isalpha(text[-1]))
X	{
X		return (char *)0;
X	}
X
X	/* get the error line */
X	errline = 0L;
X	while (isascii(*text) && isdigit(*text))
X	{
X		errline *= 10;
X		errline += (*text - '0');
X		text++;
X	}
X
X	/* any line which lacks a filename or line number should be ignored */
X	if (!errfile[0] || !errline)
X	{
X		return (char *)0;
X	}
X
X	/* locate the beginning of the error description */
X	while (*text && isascii(*text) && !isspace(*text))
X	{
X		text++;
X	}
X	while (*text)
X	{
X#  ifndef CRUNCH
X		/* skip "error #:" and "warning #:" clauses */
X		if (!strncmp(text + 1, "rror ", 5)
X		 || !strncmp(text + 1, "arning ", 7)
X		 || !strncmp(text + 1, "atal error", 10))
X		{
X			do
X			{
X				text++;
X			} while (*text && *text != ':');
X			continue;
X		}
X#  endif
X
X		/* anything other than whitespace or a colon is important */
X		if (!isascii(*text) || (!isspace(*text) && *text != ':'))
X		{
X			errmsg = text;
X			break;
X		}
X
X		/* else keep looking... */
X		text++;
X	}
X
X	return errmsg;
X# endif /* not COHERENT */
X}
X
X/*ARGSUSED*/
Xvoid cmd_errlist(frommark, tomark, cmd, bang, extra)
X	MARK	frommark, tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	static long	endline;/* original number of lines in this file */
X	static long	offset;	/* offset of the next line in the errlist file */
X	static int	fd = -2;/* fd of the errlist file */
X	int		i;
X	char		*errmsg;
X
X	/* if a new errlist file is named, open it */
X	if (extra && extra[0])
X	{
X		/* close the old one */
X		if (fd >= 0)
X		{
X			close(fd);
X		}
X
X		fd = open(extra, O_RDONLY);
X		offset = 0L;
X	}
X	else if (fd < 0)
X	{
X		fd = open(ERRLIST, O_RDONLY);
X		offset = 0L;
X	}
X
X	/* do we have an errlist file now? */
X	if (fd < 0)
X	{
X		msg("There is no errlist file");
X		beep();
X		return;
X	}
X
X	/* find the next error message in the file */
X	do
X	{
X		/* read the next line from the errlist */
X		lseek(fd, offset, 0);
X		if (tread(fd, tmpblk.c, (unsigned)BLKSIZE) <= 0)
X		{
X			msg("No more errors");
X			beep();
X			close(fd);
X			return;
X		}
X		for (i = 0; tmpblk.c[i] != '\n'; i++)
X		{
X		}
X		tmpblk.c[i++] = 0;
X
X		/* look for an error message in the line */
X		errmsg = parse_errmsg(tmpblk.c);
X		if (!errmsg)
X		{
X			offset += i;
X		}
X
X	} while (!errmsg);
X
X	/* switch to the file containing the error, if this isn't it */
X	if (strcmp(origname, errfile))
X	{
X		if (!tmpabort(bang))
X		{
X			msg("Use :er! to abort changes, or :w to save changes");
X			beep();
X			return;
X		}
X		tmpstart(errfile);
X		endline = nlines;
X	}
X	else if (endline == 0L)
X	{
X		endline = nlines;
X	}
X
X	/* go to the line where the error was detected */
X	cursor = MARK_AT_LINE(errline + (nlines - endline));
X	if (cursor > MARK_LAST)
X	{
X		cursor = MARK_LAST;
X	}
X	if (mode == MODE_VI)
X	{
X		redraw(cursor, FALSE);
X	}
X
X	/* display the error message */
X	if (nlines > endline)
X	{
X		msg("line %ld(+%ld): %.60s", errline, nlines - endline, errmsg);
X	}
X	else if (nlines < endline)
X	{
X		msg("line %ld(-%ld): %.60s", errline, endline - nlines, errmsg);
X	}
X	else
X	{
X		msg("line %ld: %.65s", errline, errmsg);
X	}
X
X	/* remember where the NEXT error line will start */
X	offset += i;
X}
X
X
X/*ARGSUSED*/
Xvoid cmd_make(frommark, tomark, cmd, bang, extra)
X	MARK	frommark, tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	BLK	buf;
X
X	/* if the file hasn't been saved, then complain unless ! */
X	if (tstflag(file, MODIFIED) && !bang)
X	{
X		msg("\"%s\" not saved yet", origname);
X		return;
X	}
X
X	/* build the command */
X	sprintf(buf.c, "%s %s %s%s", (cmd == CMD_CC ? o_cc : o_make), extra, REDIRECT, ERRLIST);
X	qaddstr(buf.c);
X	addch('\n');
X
X	/* run the command, with curses temporarily disabled */
X	suspend_curses();
X	system(buf.c);
X	resume_curses(mode == MODE_EX);
X	if (mode == MODE_COLON)
X		mode = MODE_VI;
X
X	/* run the "errlist" command */
X	cmd_errlist(MARK_UNSET, MARK_UNSET, cmd, bang, ERRLIST);
X}
X#endif
X
X
X#ifndef NO_ABBR
X/*ARGSUSED*/
Xvoid cmd_abbr(frommark, tomark, cmd, bang, extra)
X	MARK	frommark, tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	do_abbr(extra);
X}
X#endif
eof
if test `wc -c <cmd1.c` -ne 23975
then
echo cmd1.c damaged!
fi
fi

if test -f cmd2.c -a "$1" != -f
then
echo Will not overwrite cmd2.c
else
echo Extracting cmd2.c
sed 's/^X//' >cmd2.c <<\eof
X/* cmd2.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 some of the commands - mostly ones that change text */
X
X#include <ctype.h>
X#include "config.h"
X#include "vi.h"
X#include "regexp.h"
X#if TOS
X# include <stat.h>
X#else
X# if OSK
X#  include "osk.h"
X# else
X#  include <sys/stat.h>
X# endif
X#endif
X
X
X/*ARGSUSED*/
Xvoid cmd_substitute(frommark, tomark, cmd, bang, extra)
X	MARK	frommark;
X	MARK	tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;	/* rest of the command line */
X{
X	char	*line;	/* a line from the file */
X	regexp	*re;	/* the compiled search expression */
X	char	*subst;	/* the substitution string */
X	char	*opt;	/* substitution options */
X	long	l;	/* a line number */
X	char	*s, *d;	/* used during subtitutions */
X	char	*conf;	/* used during confirmation */
X	long	chline;	/* # of lines changed */
X	long	chsub;	/* # of substitutions made */
X	static	optp;	/* boolean option: print when done? */
X	static	optg;	/* boolean option: substitute globally in line? */
X	static	optc;	/* boolean option: confirm before subst? */
X
X
X	/* for now, assume this will fail */
X	rptlines = -1L;
X
X	if (cmd == CMD_SUBAGAIN)
X	{
X#ifndef NO_MAGIC
X		if (*o_magic)
X			subst = "~";
X		else
X#endif
X		subst = "\\~";
X		re = regcomp("");
X
X		/* if visual "&", then turn off the "p" and "c" options */
X		if (bang)
X		{
X			optp = optc = FALSE;
X		}
X	}
X	else
X	{
X		/* make sure we got a search pattern */
X		if (*extra != '/' && *extra != '?')
X		{
X			msg("Usage: s/regular expression/new text/");
X			return;
X		}
X
X		/* parse & compile the search pattern */
X		subst = parseptrn(extra);
X		re = regcomp(extra + 1);
X	}
X
X	/* abort if RE error -- error message already given by regcomp() */
X	if (!re)
X	{
X		return;
X	}
X
X	if (cmd == CMD_SUBSTITUTE)
X	{
X		/* parse the substitution string & find the option string */
X		for (opt = subst; *opt && *opt != *extra; opt++)
X		{
X			if (*opt == '\\' && opt[1])
X			{
X				opt++;
X			}
X		}
X		if (*opt)
X		{
X			*opt++ = '\0';
X		}
X
X		/* analyse the option string */
X		if (!*o_edcompatible)
X		{
X			optp = optg = optc = FALSE;
X		}
X		while (*opt)
X		{
X			switch (*opt++)
X			{
X			  case 'p':	optp = !optp;	break;
X			  case 'g':	optg = !optg;	break;
X			  case 'c':	optc = !optc;	break;
X			  case ' ':
X			  case '\t':			break;
X			  default:
X				msg("Subst options are p, c, and g -- not %c", opt[-1]);
X				return;
X			}
X		}
X	}
X
X	/* if "c" or "p" flag was given, and we're in visual mode, then NEWLINE */
X	if ((optc || optp) && mode == MODE_VI)
X	{
X		addch('\n');
X		exrefresh();
X	}
X
X	ChangeText
X	{
X		/* reset the change counters */
X		chline = chsub = 0L;
X
X		/* for each selected line */
X		for (l = markline(frommark); l <= markline(tomark); l++)
X		{
X			/* fetch the line */
X			line = fetchline(l);
X
X			/* if it contains the search pattern... */
X			if (regexec(re, line, TRUE))
X			{
X				/* increment the line change counter */
X				chline++;
X
X				/* initialize the pointers */
X				s = line;
X				d = tmpblk.c;
X
X				/* do once or globally ... */
X				do
X				{
X#ifndef CRUNCH
X					/* confirm, if necessary */
X					if (optc)
X					{
X						for (conf = line; conf < re->startp[0]; conf++)
X							addch(*conf);
X						standout();
X						for ( ; conf < re->endp[0]; conf++)
X							addch(*conf);
X						standend();
X						for (; *conf; conf++)
X							addch(*conf);
X						addch('\n');
X						exrefresh();
X						if (getkey(0) != 'y')
X						{
X							/* copy accross the original chars */
X							while (s < re->endp[0])
X								*d++ = *s++;
X
X							/* skip to next match on this line, if any */
X							continue;
X						}
X					}
X#endif /* not CRUNCH */
X
X					/* increment the substitution change counter */
X					chsub++;
X
X					/* this may be the first line to redraw */
X					redrawrange(l, l + 1L, l + 1L);
X
X					/* copy stuff from before the match */
X					while (s < re->startp[0])
X					{
X						*d++ = *s++;
X					}
X	
X					/* substitute for the matched part */
X					regsub(re, subst, d);
X					s = re->endp[0];
X					d += strlen(d);
X
X				} while (optg && regexec(re, s, FALSE));
X
X				/* copy stuff from after the match */
X				while (*d++ = *s++)	/* yes, ASSIGNMENT! */
X				{
X				}
X
X				/* replace the old version of the line with the new */
X				d[-1] = '\n';
X				d[0] = '\0';
X				change(MARK_AT_LINE(l), MARK_AT_LINE(l + 1), tmpblk.c);
X
X				/* if supposed to print it, do so */
X				if (optp)
X				{
X					addstr(tmpblk.c);
X					exrefresh();
X				}
X
X				/* move the cursor to that line */
X				cursor = MARK_AT_LINE(l);
X			}
X		}
X	}
X
X	/* tweak for redrawing */
X	mustredraw = TRUE;
X
X	/* free the regexp */
X	free(re);
X
X	/* if done from within a ":g" command, then finish silently */
X	if (doingglobal)
X	{
X		rptlines = chline;
X		rptlabel = "changed";
X		return;
X	}
X
X	/* Reporting */
X	if (chsub == 0)
X	{
X		msg("Substitution failed");
X	}
X	else if (chline >= *o_report)
X	{
X		msg("%ld substitutions on %ld lines", chsub, chline);
X	}
X}
X
X
X
X
X/*ARGSUSED*/
Xvoid cmd_delete(frommark, tomark, cmd, bang, extra)
X	MARK	frommark;
X	MARK	tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	MARK	curs2;	/* an altered form of the cursor */
X
X	/* choose your cut buffer */
X	if (*extra == '"')
X	{
X		extra++;
X	}
X	if (*extra)
X	{
X		cutname(*extra);
X	}
X
X	/* make sure we're talking about whole lines here */
X	frommark = frommark & ~(BLKSIZE - 1);
X	tomark = (tomark & ~(BLKSIZE - 1)) + BLKSIZE;
X
X	/* yank the lines */
X	cut(frommark, tomark);
X
X	/* if CMD_DELETE then delete the lines */
X	if (cmd != CMD_YANK)
X	{
X		curs2 = cursor;
X		ChangeText
X		{
X			/* delete the lines */
X			delete(frommark, tomark);
X		}
X		if (curs2 > tomark)
X		{
X			cursor = curs2 - tomark + frommark;
X		}
X		else if (curs2 > frommark)
X		{
X			cursor = frommark;
X		}
X	}
X}
X
X
X/*ARGSUSED*/
Xvoid cmd_append(frommark, tomark, cmd, bang, extra)
X	MARK	frommark;
X	MARK	tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	long	l;	/* line counter */
X
X	ChangeText
X	{
X		/* if we're doing a change, delete the old version */
X		if (cmd == CMD_CHANGE)
X		{
X			/* delete 'em */
X			cmd_delete(frommark, tomark, cmd, bang, extra);
X		}
X
X		/* new lines start at the frommark line, or after it */
X		l = markline(frommark);
X		if (cmd == CMD_APPEND)
X		{
X 			l++;
X		}
X
X		/* get lines until no more lines, or "." line, and insert them */
X		while (vgets('\0', tmpblk.c, BLKSIZE) >= 0)
X		{
X			addch('\n');
X			if (!strcmp(tmpblk.c, "."))
X			{
X				break;
X			}
X
X			strcat(tmpblk.c, "\n");
X			add(MARK_AT_LINE(l), tmpblk.c);
X			l++;
X		}
X	}
X
X	/* on the odd chance that we're calling this from vi mode ... */
X	redraw(MARK_UNSET, FALSE);
X}
X
X
X/*ARGSUSED*/
Xvoid cmd_put(frommark, tomark, cmd, bang, extra)
X	MARK	frommark;
X	MARK	tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	/* choose your cut buffer */
X	if (*extra == '"')
X	{
X		extra++;
X	}
X	if (*extra)
X	{
X		cutname(*extra);
X	}
X
X	/* paste it */
X	ChangeText
X	{
X		cursor = paste(frommark, TRUE, FALSE);
X	}
X}
X
X
X/*ARGSUSED*/
Xvoid cmd_join(frommark, tomark, cmd, bang, extra)
X	MARK	frommark;
X	MARK	tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	long	l;
X	char	*scan;
X	int	len;	/* length of the new line */
X
X	/* if only one line is specified, assume the following one joins too */
X	if (markline(frommark) == nlines)
X	{
X		msg("Nothing to join with this line");
X		return;
X	}
X	if (markline(frommark) == markline(tomark))
X	{
X		tomark += BLKSIZE;
X	}
X
X	/* get the first line */
X	l = markline(frommark);
X	strcpy(tmpblk.c, fetchline(l));
X	len = strlen(tmpblk.c);
X
X	/* build the longer line */
X	while (++l <= markline(tomark))
X	{
X		/* get the next line */
X		scan = fetchline(l);
X
X		/* remove any leading whitespace */
X		while (*scan == '\t' || *scan == ' ')
X		{
X			scan++;
X		}
X
X		/* see if the line will fit */
X		if (strlen(scan) + len + 3 > BLKSIZE)
X		{
X			msg("Can't join -- the resulting line would be too long");
X			return;
X		}
X
X		/* catenate it, with a space (or two) in between */
X		if (len >= 1 &&
X			(tmpblk.c[len - 1] == '.'
X			 || tmpblk.c[len - 1] == '?'
X			 || tmpblk.c[len - 1] == '!'))
X		{
X			 tmpblk.c[len++] = ' ';
X		}
X		tmpblk.c[len++] = ' ';
X		strcpy(tmpblk.c + len, scan);
X		len += strlen(scan);
X	}
X	tmpblk.c[len++] = '\n';
X	tmpblk.c[len] = '\0';
X
X	/* make the change */
X	ChangeText
X	{
X		frommark &= ~(BLKSIZE - 1);
X		tomark &= ~(BLKSIZE - 1);
X		tomark += BLKSIZE;
X		change(frommark, tomark, tmpblk.c);
X	}
X
X	/* Reporting... */
X	rptlines = markline(tomark) - markline(frommark) - 1L;
X	rptlabel = "joined";
X}
X
X
X
X/*ARGSUSED*/
Xvoid cmd_shift(frommark, tomark, cmd, bang, extra)
X	MARK	frommark;
X	MARK	tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	long	l;	/* line number counter */
X	int	oldidx;	/* number of chars previously used for indent */
X	int	newidx;	/* number of chars in the new indent string */
X	int	oldcol;	/* previous indent amount */
X	int	newcol;	/* new indent amount */
X	char	*text;	/* pointer to the old line's text */
X
X	/* figure out how much of the screen we must redraw (for vi mode) */
X	if (markline(frommark) != markline(tomark))
X	{
X		mustredraw = TRUE;
X		redrawrange(markline(frommark), markline(tomark) + 1L, markline(tomark) + 1L);
X	}
X
X	ChangeText
X	{
X		/* for each line to shift... */
X		for (l = markline(frommark); l <= markline(tomark); l++)
X		{
X			/* get the line - ignore empty lines unless ! mode */
X			text = fetchline(l);
X			if (!*text && !bang)
X				continue;
X
X			/* calc oldidx and oldcol */
X			for (oldidx = 0, oldcol = 0;
X			     text[oldidx] == ' ' || text[oldidx] == '\t';
X			     oldidx++)
X			{
X				if (text[oldidx] == ' ')
X				{
X					oldcol += 1;
X				}
X				else
X				{
X					oldcol += *o_tabstop - (oldcol % *o_tabstop);
X				}
X			}
X	
X			/* calc newcol */
X			if (cmd == CMD_SHIFTR)
X			{
X				newcol = oldcol + (*o_shiftwidth & 0xff);
X			}
X			else
X			{
X				newcol = oldcol - (*o_shiftwidth & 0xff);
X				if (newcol < 0)
X					newcol = 0;
X			}
X
X			/* if no change, then skip to next line */
X			if (oldcol == newcol)
X				continue;
X
X			/* build a new indent string */
X			newidx = 0;
X			while (newcol >= *o_tabstop)
X			{
X				tmpblk.c[newidx++] = '\t';
X				newcol -= *o_tabstop;
X			}
X			while (newcol > 0)
X			{
X				tmpblk.c[newidx++] = ' ';
X				newcol--;
X			}
X			tmpblk.c[newidx] = '\0';
X			
X			/* change the old indent string into the new */
X			change(MARK_AT_LINE(l), MARK_AT_LINE(l) + oldidx, tmpblk.c);
X		}
X	}
X
X	/* Reporting... */
X	rptlines = markline(tomark) - markline(frommark) + 1L;
X	if (cmd == CMD_SHIFTR)
X	{
X		rptlabel = ">ed";
X	}
X	else
X	{
X		rptlabel = "<ed";
X	}
X}
X
X
X/*ARGSUSED*/
Xvoid cmd_read(frommark, tomark, cmd, bang, extra)
X	MARK	frommark;
X	MARK	tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	int	fd, rc;	/* used while reading from the file */
X	char	*scan;	/* used for finding NUL characters */
X	int	hadnul;	/* boolean: any NULs found? */
X	int	addnl;	/* boolean: forced to add newlines? */
X	int	len;	/* number of chars in current line */
X	long	lines;	/* number of lines in current block */
X	struct stat statb;
X
X	/* special case: if ":r !cmd" then let the filter() function do it */
X	if (extra[0] == '!')
X	{
X		filter(frommark, MARK_UNSET, extra + 1);
X		return;
X	}
X
X	/* open the file */
X	fd = open(extra, O_RDONLY);
X	if (fd < 0)
X	{
X		msg("Can't open \"%s\"", extra);
X		return;
X	}
X
X#ifndef CRUNCH
X	if (stat(extra, &statb) < 0)
X	{
X		msg("Can't stat \"%s\"", extra);
X	}
X# if TOS
X	if (statb.st_mode & S_IJDIR)
X# else
X#  if OSK
X	if (statb.st_mode & S_IFDIR)
X#  else
X	if ((statb.st_mode & S_IFMT) != S_IFREG)
X#  endif
X# endif
X	{
X		msg("\"%s\" is not a regular file", extra);
X		return;
X	}
X#endif /* not CRUNCH */
X
X	/* get blocks from the file, and add them */
X	ChangeText
X	{
X		/* insertion starts at the line following frommark */
X		tomark = frommark = (frommark | (BLKSIZE - 1L)) + 1L;
X		len = 0;
X		hadnul = addnl = FALSE;
X
X		/* add an extra newline, so partial lines at the end of
X		 * the file don't trip us up
X		 */
X		add(tomark, "\n");
X
X		/* for each chunk of text... */
X		while ((rc = tread(fd, tmpblk.c, BLKSIZE - 1)) > 0)
X		{
X			/* count newlines, convert NULs, etc. ... */
X			for (lines = 0, scan = tmpblk.c; rc > 0; rc--, scan++)
X			{
X				/* break up long lines */
X				if (*scan != '\n' && len + 2 > BLKSIZE)
X				{
X					*scan = '\n';
X					addnl = TRUE;
X				}
X
X				/* protect against NUL chars in file */
X				if (!*scan)
X				{
X					*scan = 0x80;
X					hadnul = TRUE;
X				}
X
X				/* starting a new line? */
X				if (*scan == '\n')
X				{
X					/* reset length at newline */
X					len = 0;
X					lines++;
X				}
X				else
X				{
X					len++;
X				}
X			}
X
X			/* add the text */
X			*scan = '\0';
X			add(tomark, tmpblk.c);
X			tomark += MARK_AT_LINE(lines) + len - markidx(tomark);
X		}
X
X		/* if partial last line, then retain that first newline */
X		if (len > 0)
X		{
X			msg("Last line had no newline");
X			tomark += BLKSIZE; /* <- for the rptlines calc */
X		}
X		else /* delete that first newline */
X		{
X			delete(tomark, (tomark | (BLKSIZE - 1L)) + 1L);
X		}
X	}
X
X	/* close the file */
X	close(fd);
X
X	/* Reporting... */
X	rptlines = markline(tomark) - markline(frommark);
X	rptlabel = "read";
X
X	if (addnl)
X		msg("Newlines were added to break up long lines");
X	if (hadnul)
X		msg("NULs were converted to 0x80");
X}
X
X
X
X/*ARGSUSED*/
Xvoid cmd_undo(frommark, tomark, cmd, bang, extra)
X	MARK	frommark;
X	MARK	tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	undo();
X}
X
X
X/* print the selected lines */
X/*ARGSUSED*/
Xvoid cmd_print(frommark, tomark, cmd, bang, extra)
X	MARK	frommark;
X	MARK	tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	REG char	*scan;
X	REG long	l;
X	REG int		col;
X
X	for (l = markline(frommark); l <= markline(tomark); l++)
X	{
X		/* display a line number, if CMD_NUMBER */
X		if (cmd == CMD_NUMBER)
X		{
X			sprintf(tmpblk.c, "%6ld  ", l);
X			qaddstr(tmpblk.c);
X			col = 8;
X		}
X		else
X		{
X			col = 0;
X		}
X
X		/* get the next line & display it */
X		for (scan = fetchline(l); *scan; scan++)
X		{
X			/* expand tabs to the proper width */
X			if (*scan == '\t' && cmd != CMD_LIST)
X			{
X				do
X				{
X					qaddch(' ');
X					col++;
X				} while (col % *o_tabstop != 0);
X			}
X			else if (*scan >= 0 && *scan < ' ' || *scan == '\177')
X			{
X				qaddch('^');
X				qaddch(*scan ^ 0x40);
X				col += 2;
X			}
X			else if ((*scan & 0x80) && cmd == CMD_LIST)
X			{
X				sprintf(tmpblk.c, "\\%03o", *scan);
X				qaddstr(tmpblk.c);
X				col += 4;
X			}
X			else
X			{
X				qaddch(*scan);
X				col++;
X			}
X
X			/* wrap at the edge of the screen */
X			if (!has_AM && col >= COLS)
X			{
X				addch('\n');
X				col -= COLS;
X			}
X		}
X		if (cmd == CMD_LIST)
X		{
X			qaddch('$');
X		}
X		addch('\n');
X		exrefresh();
X	}
X}
X
X
X/* move or copy selected lines */
X/*ARGSUSED*/
Xvoid cmd_move(frommark, tomark, cmd, bang, extra)
X	MARK	frommark;
X	MARK	tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	MARK	destmark;
X
X	/* parse the destination linespec.  No defaults.  Line 0 is okay */
X	destmark = cursor;
X	if (!strcmp(extra, "0"))
X	{
X		destmark = 0L;
X	}
X	else if (linespec(extra, &destmark) == extra || !destmark)
X	{
X		msg("invalid destination address");
X		return;
X	}
X
X	/* flesh the marks out to encompass whole lines */
X	frommark &= ~(BLKSIZE - 1);
X	tomark = (tomark & ~(BLKSIZE - 1)) + BLKSIZE;
X	destmark = (destmark & ~(BLKSIZE - 1)) + BLKSIZE;
X
X	/* make sure the destination is valid */
X	if (cmd == CMD_MOVE && destmark >= frommark && destmark < tomark)
X	{
X		msg("invalid destination address");
X	}
X
X	/* Do it */
X	ChangeText
X	{
X		/* save the text to a cut buffer */
X		cutname('\0');
X		cut(frommark, tomark);
X
X		/* if we're not copying, delete the old text & adjust destmark */
X		if (cmd != CMD_COPY)
X		{
X			delete(frommark, tomark);
X			if (destmark >= frommark)
X			{
X				destmark -= (tomark - frommark);
X			}
X		}
X
X		/* add the new text */
X		paste(destmark, FALSE, FALSE);
X	}
X
X	/* move the cursor to the last line of the moved text */
X	cursor = destmark + (tomark - frommark) - BLKSIZE;
X	if (cursor < MARK_FIRST || cursor >= MARK_LAST + BLKSIZE)
X	{
X		cursor = MARK_LAST;
X	}
X
X	/* Reporting... */
X	rptlabel = ( (cmd == CMD_COPY) ? "copied" : "moved" );
X}
X
X
X
X/* execute EX commands from a file */
X/*ARGSUSED*/
Xvoid cmd_source(frommark, tomark, cmd, bang, extra)
X	MARK	frommark;
X	MARK	tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	/* must have a filename */
X	if (!*extra)
X	{
X		msg("\"source\" requires a filename");
X		return;
X	}
X
X	doexrc(extra);
X}
X
X
X#ifndef NO_AT
X/*ARGSUSED*/
Xvoid cmd_at(frommark, tomark, cmd, bang, extra)
X	MARK	frommark;
X	MARK	tomark;
X	CMD	cmd;
X	int	bang;
X	char	*extra;
X{
X	static	nest = FALSE;
X	int	result;
X	char	buf[MAXRCLEN];
X
X	/* don't allow nested macros */
X	if (nest)
X	{
X		msg("@ macros can't be nested");
X		return;
X	}
X	nest = TRUE;
X
X	/* require a buffer name */
X	if (*extra == '"')
X		extra++;
X	if (!*extra || !isascii(*extra) ||!islower(*extra))
X	{
X		msg("@ requires a cut buffer name (a-z)");
X	}
X
X	/* get the contents of the buffer */
X	result = cb2str(*extra, buf, (unsigned)(sizeof buf));
X	if (result <= 0)
X	{
X		msg("buffer \"%c is empty", *extra);
X	}
X	else if (result >= sizeof buf)
X	{
X		msg("buffer \"%c is too large to execute", *extra);
X	}
X	else
X	{
X		/* execute the contents of the buffer as ex commands */
X		exstring(buf, result);
X	}
X
X	nest = FALSE;
X}
X#endif
eof
if test `wc -c <cmd2.c` -ne 16797
then
echo cmd2.c damaged!
fi
fi

if test -f config.h -a "$1" != -f
then
echo Will not overwrite config.h
else
echo Extracting config.h
sed 's/^X//' >config.h <<\eof
X/*
X * vi configuration file
X * We try to automatically configure to various compilers and operating
X * systems. Extend the autoconf section as needed.
X */
X
X/*************************** autoconf section ************************/
X
X/* standard unix V (?) */
X#ifdef	M_SYSV
X# define UNIXV		1
X#endif
X
X/* xelos system, University of Ulm */
X#ifdef	xelos
X# define UNIXV		1
X#endif
X
X/* BSD UNIX? */
X#ifdef bsd
X# define BSD		1
X#endif
X
X/* Microsoft C: sorry, Watcom does the same thing */
X#ifdef	M_I86
X# ifndef M_SYSV
X#  define MSDOS		1
X#  define MICROSOFT	1
X#  define COMPILED_BY	"Microsoft C 5.10"
X# endif
X#endif
X
X/* Borlands Turbo C */
X#ifdef	__TURBOC__
X# define MSDOS		1
X# define TURBOC		1
X# define COMPILED_BY	"Turbo C 2.00"
X#endif
X
X/* Tos Mark-Williams */
X#ifdef	M68000
X# define TOS 1
X# define COMPILED_BY	"Mark Williams C"
X#endif
X
X/* OS9/68000 */
X#ifdef	OSK
X# define COMPILED_BY	"Microware C V2.3 Edition 40"
X#endif
X
X/*************************** end of autoconf section ************************/
X
X/* All undefined symbols are defined to zero here, to allow for older    */
X/* compilers which dont understand #if defined() or #if UNDEFINED_SYMBOL */
X
X/*************************** operating systems *****************************/
X 
X#ifndef	BSD
X# define BSD	0		/* UNIX - Berkeley 4.x */
X#endif
X
X#ifndef	UNIXV
X# define UNIXV	0		/* UNIX - AT&T SYSV */
X#endif
X
X#ifndef	UNIX7
X# define UNIX7	0		/* UNIX - version 7 */
X#endif
X
X#ifndef	MSDOS
X# define MSDOS	0		/* PC		*/
X#endif
X
X#ifndef	TOS
X# define TOS	0		/* Atari ST	*/
X#endif
X
X#ifndef	AMIGA
X# define AMIGA	0		/* Commodore Amiga */
X#endif
X
X#ifndef OSK
X# define OSK	0		/* OS-9 / 68k */
X#endif
X
X#ifndef COHERENT
X# define COHERENT 0		/* Coherent */
X#endif
X
X				/* Minix has no predefines */
X#if !BSD && !UNIXV && !UNIX7 && !MSDOS && !TOS && !AMIGA && !OSK && !COHERENT
X# define MINIX	1
X#else
X# define MINIX	0
X#endif
X
X				/* generic combination of Unices */
X#if UNIXV || UNIX7 || BSD || MINIX || COHERENT
X# define ANY_UNIX 1
X#else
X# define ANY_UNIX 0
X#endif
X
X/*************************** compilers **************************************/
X 
X#ifndef	MICROSOFT
X# define MICROSOFT	0
X#endif
X
X#ifndef	TURBOC
X# define TURBOC		0
X#endif
X
X/******************************* Credit ************************************/
X
X#if MSDOS
X# define CREDIT "Ported to MS-DOS by Guntram Blohm & Martin Patzel"
X#endif
X
X#if TOS
X# define CREDIT "Ported to Atari/TOS by Guntram Blohm & Martin Patzel"
X#endif
X
X#if OSK
X# define CREDIT	"Ported to Microware OS9/68k by Peter Reinig"
X#endif
X
X#if COHERENT
X# define CREDIT	"Ported to Coherent by Esa Ahola"
X#endif
X
X/*************************** functions depending on OS *********************/
X
X/* Only MSDOS, TOS, and OS9 need a special function for reading from the
X * keyboard.  All others just read from file descriptor 0.
X */
X#if !MSDOS && !TOS && !OSK
X# define ttyread(buf, len)	read(0, buf, (unsigned)len)	/* raw read */
X#endif
X#if !TOS
X# define ttywrite(buf, len)	write(1, buf, (unsigned)(len))	/* raw write */
X#endif
X
X/* The strchr() function is an official standard now, so everybody has it
X * except Unix version 7 (which is old) and BSD Unix (which is academic).
X * Those guys use something called index() to do the same thing.
X */
X#if BSD || UNIX7 || OSK
X# define strchr	index
X#endif
Xextern char *strchr();
X
X/* BSD uses bcopy() instead of memcpy() */
X#if BSD
X#define memcpy(dest, src, siz)	bcopy(src, dest, siz)
X#endif
X
X/* text versa binary mode for read/write */
X#if !TOS
X#define	tread(fd,buf,n)		read(fd,buf,(unsigned)(n))
X#define twrite(fd,buf,n)	write(fd,buf,(unsigned)(n))
X#endif
X
X/**************************** Compiler quirks *********************************/
X
X/* the UNIX version 7 and (some) TOS compilers, don't allow "void" */
X#if UNIX7 || TOS
X# define void int
X#endif
X
X/* as far as I know, all compilers except version 7 support unsigned char */
X/* NEWFLASH: the Minix-ST compiler has subtle problems with unsigned char */
X#if UNIX7 || MINIX
X# define UCHAR(c)	((c) & 0xff)
X# define uchar		char
X#else
X# define UCHAR(c)	((unsigned char)(c))
X# define uchar		unsigned char
X#endif
X
X/* Some compilers prefer to have malloc declared as returning a (void *) */
X#if BSD
Xextern void *malloc();
X#else
Xextern char *malloc();
X#endif
X
X/* Most compilers could benefit from using the "register" storage class */
X#if 1
X# define REG	register
X#endif
X
X/******************* Names of files and environment vars **********************/
X
X#if ANY_UNIX
X# ifndef TMPDIR
X#  if MINIX
X#   define TMPDIR	"/usr/tmp"	/* Keep elvis' temp files off RAM disk! */
X#  else
X#   define TMPDIR	"/tmp"		/* directory where temp files live */
X#  endif
X# endif
X# define TMPNAME	"%s/elv%x%04x%03x" /* temp file */
X# define CUTNAME	"%s/elv_%04x%03x" /* cut buffer's temp file */
X# ifndef EXRC
X#  define EXRC		".exrc"		/* init file in current directory */
X# endif
X# define SCRATCHOUT	"%s/soXXXXXX"	/* temp file used as input to filter */
X# ifndef EXINIT
X#  define EXINIT	"EXINIT"
X# endif
X# ifndef SHELL
X#  define SHELL		"/bin/sh"	/* default shell */
X# endif
X# if COHERENT
X#  ifndef REDIRECT
X#   define REDIRECT	">"		/* Coherent CC writes errors to stdout */
X#  endif
X# endif
X#endif
X
X#if MSDOS || TOS
X/* do not change TMPNAME, CUTNAME and SCRATCH*: they MUST begin with '%s\\'! */
X# ifndef TMPDIR
X#  define TMPDIR	"C:\\tmp"	/* directory where temp files live */
X# endif
X# define TMPNAME	"%s\\elv%x%04x.%03x" /* temp file */
X# define CUTNAME	"%s\\elv_%04x.%03x" /* cut buffer's temp file */
X# if MSDOS
X#  if MICROSOFT
X#   define CC_COMMAND	"cl -c"		/* C compiler */
X#  else /* TURBO_C */
X#   define CC_COMMAND	"tc"		/* C compiler */
X#  endif
X# endif
X# define SCRATCHIN	"%s\\siXXXXXX"	/* DOS ONLY - output of filter program */
X# define SCRATCHOUT	"%s\\soXXXXXX"	/* temp file used as input to filter */
X# define SLASH		'\\'
X# ifndef SHELL
X#  if TOS
X#   define SHELL	"shell.ttp"	/* default shell */
X#  else
X#   define SHELL	"command.com"	/* default shell */
X#  endif
X# endif
X# define NEEDSYNC	TRUE		/* assume ":se sync" by default */
X# define REDIRECT	">"		/* shell's redirection of stderr */
X# ifndef MAXMAPS
X#  define MAXMAPS	40
X# endif
X# ifndef EXINIT
X#  define EXINIT	"EXINIT"
X# endif
X#endif
X
X#if OSK
X# ifndef TMPDIR
X#  define TMPDIR	"/dd/tmp"	   /* directory where temp files live */
X# endif
X# define TMPNAME	"%s/elv%x%04x%03x"  /* temp file */
X# define CUTNAME	"%s/elv_%04x%03x"  /* cut buffer's temp file */
X# ifndef CC_COMMAND
X#  define CC_COMMAND	"cc -r"		   /* name of the compiler */
X# endif
X# ifndef EXRC
X#  define EXRC		".exrc"		   /* init file in current directory */
X# endif
X# define SCRATCHOUT	"%s/soXXXXXX"	   /* temp file used as input to filter */
X# ifndef SHELL
X#  define SHELL		"shell"		   /* default shell */
X# endif
X# define FILEPERMS	(S_IREAD|S_IWRITE) /* file permissions used for creat() */
X# define REDIRECT	">>-"		   /* shell's redirection of stderr */
X#endif
X
X#ifndef	TAGS
X# define TAGS		"tags"		/* tags file */
X#endif
X
X#ifndef TMPNAME
X# define TMPNAME	"%s/elv%x%04x.%03x"	/* temp file */
X#endif
X
X#ifndef CUTNAME
X# define CUTNAME	"%s/elv_%04x.%03x"	/* cut buffer's temp file */
X#endif
X
X#ifndef	EXRC
X# define EXRC		"elvis.rc"
X#endif
X
X#ifndef HMEXRC
X# if !MSDOS && !TOS
X#  define HMEXRC	EXRC
X# endif
X#endif
X
X#ifndef	KEYWORDPRG
X# define KEYWORDPRG	"ref"
X#endif
X
X#ifndef	SCRATCHOUT
X# define SCRATCHIN	"%s/SIXXXXXX"
X# define SCRATCHOUT	"%s/SOXXXXXX"
X#endif
X
X#ifndef ERRLIST
X# define ERRLIST	"errlist"
X#endif
X
X#ifndef	SLASH
X# define SLASH		'/'
X#endif
X
X#ifndef SHELL
X# define SHELL		"shell"
X#endif
X
X#ifndef REG
X# define REG
X#endif
X
X#ifndef NEEDSYNC
X# define NEEDSYNC	FALSE
X#endif
X
X#ifndef FILEPERMS
X# define FILEPERMS	0666
X#endif
X
X#ifndef CC_COMMAND
X# define CC_COMMAND	"cc -c"
X#endif
X
X#ifndef MAKE_COMMAND
X# define MAKE_COMMAND	"make"
X#endif
X
X#ifndef REDIRECT
X# define REDIRECT	"2>"
X#endif
X
X#ifndef MAXMAPS
X# define MAXMAPS	20		/* number of :map keys */
X#endif
X#ifndef MAXDIGS
X# define MAXDIGS	30		/* number of :digraph combos */
X#endif
X#ifndef MAXABBR
X# define MAXABBR	20		/* number of :abbr entries */
X#endif
eof
if test `wc -c <config.h` -ne 7951
then
echo config.h damaged!
fi
fi

if test -f curses.c -a "$1" != -f
then
echo Will not overwrite curses.c
else
echo Extracting curses.c
sed 's/^X//' >curses.c <<\eof
X/* curses.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 functions & variables needed for a tiny subset of
X * curses.  The principle advantage of this version of curses is its
X * extreme speed.  Disadvantages are potentially larger code, few supported
X * functions, limited compatibility with full curses, and only stdscr.
X */
X
X#include "config.h"
X#include "vi.h"
X
X#if ANY_UNIX
X# if UNIXV
X#  include	<termio.h>
X#  undef	TIOCWINSZ	/* we can't handle it correctly yet */
X# else
X#  include	<sgtty.h>
X# endif
X#endif
X
X#if TOS
X# include	<osbind.h>
X#endif
X
X#if OSK
X# include	<sgstat.h>
X#endif
X
X#include <signal.h>
X
Xextern char	*getenv();
Xstatic void	 starttcap();
X
X/* variables, publicly available & used in the macros */
Xshort	ospeed;		/* speed of the tty, eg B2400 */
X#if OSK
Xchar	PC_;	/* Pad char */
Xchar	*BC;	/* backspace character string */
X#else
Xchar	PC;		/* Pad char */
X#endif
XWINDOW	*stdscr;	/* pointer into kbuf[] */
XWINDOW	kbuf[KBSIZ];	/* a very large output buffer */
Xint	LINES;		/* :li#: number of rows */
Xint	COLS;		/* :co#: number of columns */
Xint	AM;		/* :am:  boolean: auto margins? */
Xint	PT;		/* :pt:  boolean: physical tabs? */
Xchar	*VB;		/* :vb=: visible bell */
Xchar	*UP;		/* :up=: move cursor up */
Xchar	*SO;		/* :so=: standout start */
Xchar	*SE;		/* :se=: standout end */
Xchar	*US = "";	/* :us=: underline start */
Xchar	*UE = "";	/* :ue=: underline end */
Xchar	*MD = "";	/* :md=: bold start */
Xchar	*ME = "";	/* :me=: bold end */
Xchar	*AS;		/* :as=: alternate (italic) start */
Xchar	*AE;		/* :ae=: alternate (italic) end */
Xchar	*CM;		/* :cm=: cursor movement */
Xchar	*CE;		/* :ce=: clear to end of line */
Xchar	*CD;		/* :cd=: clear to end of screen */
Xchar	*AL;		/* :al=: add a line */
Xchar	*DL;		/* :dl=: delete a line */
X#if OSK
Xchar	*SR_;		/* :sr=: scroll reverse */
X#else
Xchar	*SR;		/* :sr=: scroll reverse */
X#endif
Xchar	*KS;		/* :ks=: init string for cursor */
Xchar	*KE;		/* :ke=: restore string for cursor */
Xchar	*KU;		/* :ku=: key sequence sent by up arrow */
Xchar	*KD;		/* :kd=: key sequence sent by down arrow */
Xchar	*KL;		/* :kl=: key sequence sent by left arrow */
Xchar	*KR;		/* :kr=: key sequence sent by right arrow */
Xchar	*HM;		/* :HM=: key sequence sent by the <Home> key */
Xchar	*EN;		/* :EN=: key sequence sent by the <End> key */
Xchar	*PU;		/* :PU=: key sequence sent by the <PgUp> key */
Xchar	*PD;		/* :PD=: key sequence sent by the <PgDn> key */
Xchar	*IM;		/* :im=: insert mode start */
Xchar	*IC = "";	/* :ic=: insert the following character */
Xchar	*EI;		/* :ei=: insert mode end */
Xchar	*DC;		/* :dc=: delete a character */
Xchar	*TI;		/* :ti=: terminal init */	/* GB */
Xchar	*TE;		/* :te=: terminal exit */	/* GB */
X#ifndef NO_CURSORSHAPE
Xchar	*CQ = (char *)0;/* :cQ=: normal cursor */
Xchar	*CX = (char *)1;/* :cX=: cursor used for EX command/entry */
Xchar	*CV = (char *)2;/* :cV=: cursor used for VI command mode */
Xchar	*CI = (char *)3;/* :cI=: cursor used for VI input mode */
Xchar	*CR = (char *)4;/* :cR=: cursor used for VI replace mode */
X#endif
Xchar	*aend = "";	/* end an attribute -- either UE or ME */
Xchar	ERASEKEY;	/* backspace key taken from ioctl structure */
X
X#if ANY_UNIX
X# if UNIXV
Xstatic struct termio	oldtermio;	/* original tty mode */
Xstatic struct termio	newtermio;	/* cbreak/noecho tty mode */
X# else
Xstatic struct sgttyb	oldsgttyb;	/* original tty mode */
Xstatic struct sgttyb	newsgttyb;	/* cbreak/nl/noecho tty mode */
Xstatic int		oldint;		/* ^C or DEL, the "intr" character */
X#  ifdef TIOCSLTC
Xstatic int		oldswitch;	/* ^Z, the "suspend" character */
Xstatic int		oldquote;	/* ^V, the "quote next char" char */
X#  endif
X# endif
X#endif
X
X#if OSK
Xstatic struct sgbuf	oldsgttyb;	/* orginal tty mode */
Xstatic struct sgbuf	newsgttyb;	/* noecho tty mode */
X#endif
X
Xstatic char	*capbuf;	/* capability string buffer */
X
X
Xvoid initscr()
X{
X	/* make sure TERM variable is set */
X#if MSDOS
X	char *val;
X	if (! (val = getenv("TERM"))
X	|| !strcmp(val, "pcbios"))
X#else
X	if (!getenv("TERM"))
X#endif
X	{
X#if ANY_UNIX
X		write(2, "Environment variable TERM must be set\n", (unsigned)38);
X		exit(1);
X#endif
X#if OSK
X		writeln(2, "Environment variable TERM must be set\n", (unsigned)38);
X		exit(1);
X#endif
X#if MSDOS || TOS
X		getsize(0);
X#endif
X	}
X	else
X	{
X#if MSDOS
X		*o_pcbios=0;
X#endif
X		/* start termcap stuff */
X		starttcap();
X	}
X
X	/* create stdscr and curscr */
X	stdscr = kbuf;
X
X	/* change the terminal mode to cbreak/noecho */
X#if ANY_UNIX
X# if UNIXV
X	ioctl(2, TCGETA, &oldtermio);
X# else
X	ioctl(2, TIOCGETP, &oldsgttyb);
X# endif
X#endif
X
X#if OSK
X	_gs_opt(0, &oldsgttyb);
X#endif
X	resume_curses(TRUE);
X}
X
X
Xvoid endwin()
X{
X	/* change the terminal mode back the way it was */
X	suspend_curses();
X}
X
X
Xstatic int curses_active = FALSE;
X
Xvoid suspend_curses()
X{
X#if ANY_UNIX && !UNIXV
X	struct tchars	tbuf;
X# ifdef TIOCSLTC
X	struct ltchars	ltbuf;
X# endif
X#endif
X#ifndef NO_CURSORSHAPE
X	if (has_CQ)
X	{
X		do_CQ();
X	}
X#endif
X	if (has_TE)					/* GB */
X	{
X		do_TE();
X	}
X	if (has_KE)
X	{
X		do_KE();
X	}
X	refresh();
X
X	/* change the terminal mode back the way it was */
X#if ANY_UNIX
X# if UNIXV
X	ioctl(2, TCSETAW, &oldtermio);
X# else
X	ioctl(2, TIOCSETP, &oldsgttyb);
X
X	ioctl(2, TIOCGETC, &tbuf);
X	tbuf.t_intrc = oldint;
X	ioctl(2, TIOCSETC, &tbuf);
X
X#  ifdef TIOCSLTC
X	ioctl(2, TIOCGLTC, &ltbuf);
X	ltbuf.t_suspc = oldswitch;
X	ltbuf.t_lnextc = oldquote;
X	ioctl(2, TIOCSLTC, &ltbuf);
X#  endif
X# endif
X#endif
X#if OSK
X	_ss_opt(0, &oldsgttyb);
X#endif
X	curses_active = FALSE;
X}
X
Xvoid resume_curses(quietly)
X	int	quietly;
X{
X	if (!curses_active)
X	{
X		/* change the terminal mode to cbreak/noecho */
X#if ANY_UNIX
X# if UNIXV
X		ospeed = (oldtermio.c_cflag & CBAUD);
X		ERASEKEY = oldtermio.c_cc[VERASE];
X		newtermio = oldtermio;
X		newtermio.c_iflag &= (IXON|IXOFF|IXANY|ISTRIP|IGNBRK);
X		newtermio.c_oflag &= ~OPOST;
X		newtermio.c_lflag &= ISIG;
X		newtermio.c_cc[VINTR] = ctrl('C'); /* always use ^C for interrupts */
X		newtermio.c_cc[VMIN] = 1;
X		newtermio.c_cc[VTIME] = 0;
X#  ifdef VSWTCH
X		newtermio.c_cc[VSWTCH] = 0;
X#  endif
X		ioctl(2, TCSETAW, &newtermio);
X# else /* BSD or V7 or Coherent or Minix */
X		struct tchars	tbuf;
X#  ifdef TIOCSLTC
X		struct ltchars	ltbuf;
X#  endif
X
X		ospeed = oldsgttyb.sg_ospeed;
X		ERASEKEY = oldsgttyb.sg_erase;
X		newsgttyb = oldsgttyb;
X		newsgttyb.sg_flags |= CBREAK;
X		newsgttyb.sg_flags &= ~(CRMOD|ECHO|XTABS);
X		ioctl(2, TIOCSETP, &newsgttyb);
X
X		ioctl(2, TIOCGETC, &tbuf);
X		oldint = tbuf.t_intrc;
X		tbuf.t_intrc = ctrl('C');	/* always use ^C for interrupts */
X		ioctl(2, TIOCSETC, &tbuf);
X
X#  ifdef TIOCSLTC
X		ioctl(2, TIOCGLTC, &ltbuf);
X		oldswitch = ltbuf.t_suspc;
X		ltbuf.t_suspc = 0;		/* disable ^Z for elvis */
X		oldquote = ltbuf.t_lnextc;
X		ltbuf.t_lnextc = 0;		/* disable ^V for elvis */
X		ioctl(2, TIOCSLTC, &ltbuf);
X#  endif
X
X# endif
X#endif
X#if OSK
X		newsgttyb = oldsgttyb;
X		newsgttyb.sg_echo = 0;
X		newsgttyb.sg_eofch = 0;
X		newsgttyb.sg_kbach = 0;
X		newsgttyb.sg_kbich = ctrl('C');
X		_ss_opt(0, &newsgttyb);
X		ospeed = oldsgttyb.sg_baud;
X		ERASEKEY = oldsgttyb.sg_bspch;
X#endif
X
X		if (has_TI)					/* GB */
X		{
X			do_TI();
X		}
X		if (has_KS)
X		{
X			do_KS();
X		}
X
X		curses_active = TRUE;
X	}
X
X	/* If we're supposed to quit quietly, then we're done */
X	if (quietly)
X	{
X		return;
X	}
X
X	signal(SIGINT, SIG_IGN);
X
X	move(LINES - 1, 0);
X	do_SO();
X	qaddstr("[Press <RETURN> to continue]");
X	do_SE();
X	refresh();
X	ttyread(kbuf, 20); /* in RAW mode, so <20 is very likely */
X	if (kbuf[0] == ':')
X	{
X		mode = MODE_COLON;
X		addch('\n');
X		refresh();
X	}
X	else
X	{
X		mode = MODE_VI;
X		redraw(MARK_UNSET, FALSE);
X	}	
X	exwrote = FALSE;
X
X#if TURBOC
X	signal(SIGINT, (void(*)()) trapint);
X#else
X	signal(SIGINT, trapint);
X#endif
X}
X
Xstatic void lacking(s)
X	char	*s;
X{
X	write(2, "This termcap entry lacks the :", (unsigned)30);
X	write(2, s, (unsigned)2);
X	write(2, "=: capability\n", (unsigned)14);
X#if OSK
X	write(2, "\l", 1);
X#endif
X	exit(1);
X}
X
Xstatic void starttcap()
X{
X	char	*str;
X	static char	cbmem[800];
X#define MUSTHAVE(T,s)	if (!(T = tgetstr(s, &capbuf))) lacking(s)
X#define MAYHAVE(T,s)	if (str = tgetstr(s, &capbuf)) T = str
X#define PAIR(T,U,sT,sU)	T=tgetstr(sT,&capbuf);U=tgetstr(sU,&capbuf);if (!T||!U)T=U=""
X
X	/* allocate memory for capbuf */
X	capbuf = cbmem;
X
X	/* get the termcap entry */
X	switch (tgetent(kbuf, getenv("TERM")))
X	{
X	  case -1:
X		write(2, "Can't read /etc/termcap\n", (unsigned)24);
X#if OSK
X		write(2, "\l", 1);
X#endif
X		exit(2);
X
X	  case 0:
X		write(2, "Unrecognized TERM type\n", (unsigned)23);
X#if OSK
X		write(2, "\l", 1);
X#endif
X		exit(3);
X	}
X
X	/* get strings */
X	MUSTHAVE(UP, "up");
X	MAYHAVE(VB, "vb");
X	MUSTHAVE(CM, "cm");
X	PAIR(SO, SE, "so", "se");
X	PAIR(TI, TE, "ti", "te");
X	if (tgetnum("ug") <= 0)
X	{
X		PAIR(US, UE, "us", "ue");
X		PAIR(MD, ME, "md", "me");
X
X		/* get italics, or have it default to underline */
X		PAIR(AS, AE, "as", "ae");
X		if (!*AS)
X		{
X			AS = US;
X			AE = UE;
X		}
X	}
X	MAYHAVE(AL, "al");
X	MAYHAVE(DL, "dl");
X	MUSTHAVE(CE, "ce");
X	MAYHAVE(CD, "cd");
X#if OSK
X	MAYHAVE(SR_, "sr");
X#else	
X	MAYHAVE(SR, "sr");
X#endif
X	PAIR(IM, EI, "im", "ei");
X	MAYHAVE(IC, "ic");
X	MAYHAVE(DC, "dc");
X
X	/* other termcap stuff */
X	AM = tgetflag("am");
X	PT = tgetflag("pt");
X	getsize(0);
X
X	/* Key sequences */
X	PAIR(KS, KE, "ks", "ke");
X	MAYHAVE(KU, "ku");		/* up */
X	MAYHAVE(KD, "kd");		/* down */
X	MAYHAVE(KL, "kl");		/* left */
X	MAYHAVE(KR, "kr");		/* right */
X	MAYHAVE(PU, "kP");		/* PgUp */
X	MAYHAVE(PD, "kN");		/* PgDn */
X	MAYHAVE(HM, "kh");		/* Home */
X	MAYHAVE(EN, "kH");		/* End */
X#ifndef CRUNCH
X	if (!PU) MAYHAVE(PU, "K2");	/* "3x3 pad" names for PgUp, etc. */
X	if (!PD) MAYHAVE(PD, "K5");
X	if (!HM) MAYHAVE(HM, "K1");
X	if (!EN) MAYHAVE(EN, "K4");
X
X	MAYHAVE(PU, "PU");		/* old XENIX names for PgUp, etc. */
X	MAYHAVE(PD, "PD");		/* (overrides others, if used.) */
X	MAYHAVE(HM, "HM");
X	MAYHAVE(EN, "EN");
X#endif
X
X#ifndef NO_CURSORSHAPE
X	/* cursor shapes */
X	CQ = tgetstr("cQ", &capbuf);
X	if (has_CQ)
X	{
X		CX = tgetstr("cX", &capbuf);
X		if (!CX) CX = CQ;
X		CV = tgetstr("cV", &capbuf);
X		if (!CV) CV = CQ;
X		CI = tgetstr("cI", &capbuf);
X		if (!CI) CI = CQ;
X		CR = tgetstr("cR", &capbuf);
X		if (!CR) CR = CQ;
X	}
X# ifndef CRUNCH
X	else
X	{
X		PAIR(CQ, CV, "ve", "vs");
X		CX = CI = CR = CQ;
X	}
X# endif /* !CRUNCH */
X#endif /* !NO_CURSORSHAPE */
X
X#undef MUSTHAVE
X#undef MAYHAVE
X#undef PAIR
X}
X
X
X/* This function gets the window size.  It uses the TIOCGWINSZ ioctl call if
X * your system has it, or tgetnum("li") and tgetnum("co") if it doesn't.
X * This function is called once during initialization, and thereafter it is
X * called whenever the SIGWINCH signal is sent to this process.
X */
Xint getsize(signo)
X	int	signo;
X{
X	int	lines;
X	int	cols;
X#ifdef TIOCGWINSZ
X	struct winsize size;
X#endif
X
X#ifdef SIGWINCH
X	/* reset the signal vector */
X	signal(SIGWINCH, getsize);
X#endif
X
X	/* get the window size, one way or another. */
X	lines = cols = 0;
X#ifdef TIOCGWINSZ
X	if (ioctl(2, TIOCGWINSZ, &size) >= 0)
X	{
X		lines = size.ws_row;
X		cols = size.ws_col;
X	}
X#endif
X	if ((lines == 0 || cols == 0) && signo == 0)
X	{
X		LINES = CHECKBIOS(v_rows(), tgetnum("li"));
X		COLS = CHECKBIOS(v_cols(), tgetnum("co"));
X	}
X	if (lines >= 2 && cols >= 30)
X	{
X		LINES = lines;
X		COLS = cols;
X	}
X
X	/* Make sure we got values that we can live with */
X	if (LINES < 2 || COLS < 30)
X	{
X		write(2, "Screen too small\n", (unsigned)17);
X#if OSK
X		write(2, "\l", 1);
X#endif
X		endwin();
X		exit(2);
X	}
X
X	/* !!! copy the new values into Elvis' options */
X	{
X		extern char	o_columns[], o_lines[];
X
X		*o_columns = COLS;
X		*o_lines = LINES;
X	}
X
X	return 0;
X}
X
X
X/* This is a function version of addch() -- it is used by tputs() */
Xint faddch(ch)
X	int	ch;
X{
X	addch(ch);
X
X	return 0;
X}
X
X/* These functions are equivelent to the macros of the same names... */
X
Xvoid qaddstr(str)
X	char	*str;
X{
X	REG char *s_, *d_;
X
X#if MSDOS
X	if (o_pcbios[0])
X	{
X		while (*str)
X			qaddch(*str++);
X		return;
X	}
X#endif
X	for (s_=(str), d_=stdscr; *d_++ = *s_++; )
X	{
X	}
X	stdscr = d_ - 1;
X}
X
Xvoid attrset(a)
X	int	a;
X{
X	do_aend();
X	if (a == A_BOLD)
X	{
X		do_MD();
X		aend = ME;
X	}
X	else if (a == A_UNDERLINE)
X	{
X		do_US();
X		aend = UE;
X	}
X	else if (a == A_ALTCHARSET)
X	{
X		do_AS();
X		aend = AE;
X	}
X	else
X	{
X		aend = "";
X	}
X}
X
X
Xvoid insch(ch)
X	int	ch;
X{
X	if (has_IM)
X		do_IM();
X	do_IC();
X	qaddch(ch);
X	if (has_EI)
X		do_EI();
X}
X
X#if MSDOS
X
Xstatic int alarmtime;
X
X/* raw read - #defined to read (0, ...) on non-MSDOS.
X * With MSDOS, am maximum of 1 byte is read.
X * If more bytes should be read, just change the loop.
X * The following code uses the IBM-PC-System-Timer, so probably wont't work
X * on non-compatibles.
X */
X/*ARGSUSED*/
Xttyread(buf, len)
X	char *buf;
X	int len;
X{
X	volatile char far *biostimer;
X	char oldtime;
X	int nticks = 0;
X	int pos = 0;
X
X	biostimer = (char far *)0x0040006cl;
X	oldtime = *biostimer;
X
X	while (!pos && (!alarmtime || nticks<alarmtime))
X	{	if (kbhit())
X			if ((buf[pos++] = getch()) == 0) /* function key */
X				buf[pos-1] = '#';
X		if (oldtime != *biostimer)
X		{	nticks++;
X			oldtime = *biostimer;
X		}
X	}
X	return pos;
X}
X
Xalarm(time)
X	int time;
X{
X	alarmtime = 2 * time;		/* ticks are 1/18 sec. */
X}
X
Xsleep(seconds)
X	unsigned seconds;
X{
X	volatile char	far *biostimer = (char far *)0x0040006cl;
X	char		stop;
X
X	stop = *biostimer + 18 * seconds;
X	while (*biostimer != stop)
X	{
X	}
X}
X#endif
X
X#if TOS
X
Xstatic int alarmtime;
Xstatic long timer;
X
Xstatic gettime()
X{
X	timer = *(long *)(0x4ba);
X}
X
X/*ARGSUSED*/
Xttyread(buf, len)
X	char *buf;
X	int len;
X{
X	int	pos=0;
X	long	l;
X	long	endtime;
X
X	Supexec(gettime);
X	endtime = timer+alarmtime;
X
X	while (!pos && (!alarmtime || timer<endtime))
X	{
X		if (Bconstat(2))
X		{
X			l = Bconin(2);
X			if ((buf[pos++]=l) == '\0')
X			{
X				buf[pos-1]='#';
X				buf[pos++]=l>>16;
X			}
X		}
X		Supexec(gettime);
X	}
X	return pos;
X}
X
Xalarm(time)
X	int time;
X{
X	alarmtime = 50 * time;		/* ticks are 1/200 sec. */
X}
X
Xttywrite(buf, len)
X	char *buf;
X	int len;
X{
X	while (len--)
X		Bconout(2, *buf++);
X}
X#endif
X
X#if OSK
Xttyread(buf, len)
X	char *buf;
X	int len;
X{
X	REG int i;
X	if ((i = _gs_rdy(0)) > 0)
X		return read(0, buf, i < len ? i : len);
X	else
X		return read(0, buf, 1);
X}
X
Xalarm(time)
X	int time;
X{
X#define TIME(secs) ((secs << 8) | 0x80000000)
X	static int alrmid;
X
X	if (time)	
X		alrmid = alm_set(SIGQUIT, TIME(time));
X	else	
X		alm_delete(alrmid);
X}
X#endif /* OSK */
eof
if test `wc -c <curses.c` -ne 14196
then
echo curses.c damaged!
fi
fi

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