[alt.sources] Elvis 1.3 fixes

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

Here are new versions of the ELvis.lnk and Elvis.prj, to fix the problem
with the "termcap" functions; and a new version of cut.c to avoid the MS-DOS
bug that causes "lost clusters" when the temporary file is renamed.

Thanks for the bug reports, everybody!  I think I have enough now!
								(Well, almost.)
-------------------------------------------------------------------------------
Steve Kirkendall    kirkenda@cs.pdx.edu    uunet!tektronix!psueea!eecs!kirkenda


# --------------------------- 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 steve    group        343 Aug 30 13:37 Elvis.lnk
# -rw-r--r--   1 steve    group        162 Aug 30 13:37 Elvis.prj
# -rw-r--r--   1 steve    group      10357 Aug 30 13:35 cut.c
#

if test -f Elvis.lnk -a "$1" != -f
then
echo Will not overwrite Elvis.lnk
else
echo Extracting Elvis.lnk
sed 's/^X//' >Elvis.lnk <<\eof
Xblk.obj cmd1.obj cmd2.obj curses.obj cut.obj +
Xex.obj input.obj main.obj misc.obj modify.obj +
Xmove1.obj move2.obj move3.obj move4.obj move5.obj +
Xopts.obj recycle.obj redraw.obj regexp.obj +
Xregsub.obj system.obj tio.obj tmp.obj vars.obj +
Xvcmd.obj vi.obj pc.obj sysdos.obj tinytcap.obj +
X/co /noi /map +
X/pac /far /stack:0x2000 
Xelvis.exe; 
eof
if test `wc -c <Elvis.lnk` -ne 343
then
echo Elvis.lnk damaged!
fi
fi

if test -f Elvis.prj -a "$1" != -f
then
echo Will not overwrite Elvis.prj
else
echo Extracting Elvis.prj
sed 's/^X//' >Elvis.prj <<\eof
Xblk
Xcmd1
Xcmd2
Xcurses
Xcut
Xex
Xinput
Xmain
Xmisc
Xmodify
Xmove1
Xmove2
Xmove3
Xmove4
Xmove5
Xopts
Xpc
Xrecycle
Xredraw
Xregexp
Xregsub
Xsysdos
Xsystem
Xtio
Xtmp
Xvars
Xvcmd
Xvi
Xtinytcap
eof
if test `wc -c <Elvis.prj` -ne 162
then
echo Elvis.prj 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 *	16820 SW Tallac Way
X *	Beaverton, OR 97006
X *	kirkenda@jove.cs.pdx.edu, or ...uunet!tektronix!psueea!jove!kirkenda
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 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 */
Xcutswitch(tmpname)
X	char	*tmpname; /* name of the tmp file */
X{
X	char	cutname[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, j;
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		/* !!! we could use some error checking here */
X#if MSDOS || TOS
X	strcpy(cutname, o_directory);
X	if ((j = strlen(cutname)) && !strchr(":/\\", cutname[j-1]))
X		cutname[j++]=SLASH;
X	close(tmpfd);
X	fd = open(tmpname, O_RDONLY|O_BINARY);
X	close(fd);
X	sprintf(cutname+j, CUTNAME+3, getpid(), fd);
X	rename(tmpname, cutname);
X	fd = open(cutname, 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	sprintf(cutname, CUTNAME, o_directory, getpid(), fd);
X	link(tmpname, cutname) || unlink(tmpname);
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
X/* This function frees a cut buffer */
Xstatic cutfree(buf)
X	struct cutbuf	*buf;
X{
X	char	cutname[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	free(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	0
X			if (named[i].nblks > 0 && named[i].fd >= 0)
X#else
X			if (named[i].nblks > 0 && named[i].fd == buf->fd)
X#endif
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(cutname, o_directory);
X		if ((i = strlen(cutname)) && !strchr(":/\\", cutname[i-1]))
X			cutname[i++]=SLASH;
X		sprintf(cutname+i, CUTNAME+3, getpid(), buf->fd);
X#else
X		sprintf(cutname, CUTNAME, o_directory, getpid(), buf->fd);
X#endif
X		unlink(cutname);
X	}
X}
X
X/* This function should be called just before termination of vi */
Xcutend()
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 */
Xcutname(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 */
Xcut(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	register struct cutbuf *cb;
X	register long	l;
X	register int	i;
X	register char	*scan;
X	char		*blkc;
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	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 = (markidx(from) == 0 && markidx(to) == 0);
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	/* ---------- */
Xblksync();
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	cb->phys = (short *)malloc((unsigned)(cb->nblks * sizeof(short)));
X	for (i = 0; i < cb->nblks; i++)
X	{
X		cb->phys[i] = hdr.n[first++];
X	}
X}
X
X
Xstatic 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, 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 marker (rather than start) */
X{
X	register struct cutbuf	*cb;
X	register 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 >= '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		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	/* correct the redraw range */
X	redrawafter = preredraw = markline(at);
X	postredraw = markline(mark[27]);
X
X	/* return the mark at the beginning of inserted text */
X	if (retend)
X	{
X		return mark[27] - 1L;
X	}
X	return at;
X}
eof
if test `wc -c <cut.c` -ne 10357
then
echo cut.c damaged!
fi
fi

exit 0
-------------------------------------------------------------------------------
Steve Kirkendall    kirkenda@cs.pdx.edu    uunet!tektronix!psueea!eecs!kirkenda