Minix1.5/commands/elvis/blk.c

Compare this file to the similar file:
Show the results in this format:

/* blk.c */

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


/* This file contains the functions that get/put blocks from the temp file.
 * It also contains the "do" and "undo" functions.
 */

#include "vi.h"

#define NBUFS	5		/* must be at least 3 -- more is better */

extern long lseek();

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

BLK		hdr;		/* buffer for the header block */

static int	b4cnt;		/* used to count context of beforedo/afterdo */
static struct _blkbuf
{
	BLK	buf;		/* contents of a text block */
	ushort	logical;	/* logical block number */
	int	dirty;		/* must the buffer be rewritten? */
}
		blk[NBUFS],	/* buffers for text[?] blocks */
		*toonew,	/* buffer which shouldn't be recycled yet */
		*newtoo,	/* another buffer which should be recycled */
		*recycle = blk;	/* next block to be recycled */





/* This function wipes out all buffers */
blkinit()
{
	int	i;

	for (i = 0; i < NBUFS; i++)
	{
		blk[i].logical = 0;
		blk[i].dirty = FALSE;
	}
	for (i = 0; i < MAXBLKS; i++)
	{
		hdr.n[i] = 0;
	}
}

/* This function allocates a buffer and fills it with a given block's text */
BLK *blkget(logical)
	int	logical;	/* logical block number to fetch */
{
	register struct _blkbuf	*this;	/* used to step through blk[] */
	register int		i;

	/* if logical is 0, just return the hdr buffer */
	if (logical == 0)
	{
		return &hdr;
	}

	/* see if we have that block in mem already */
	for (this = blk; this < &blk[NBUFS]; this++)
	{
		if (this->logical == logical)
		{
			newtoo = toonew;
			toonew = this;
			return &this->buf;
		}
	}

	/* choose a block to be recycled */
	do
	{
		this = recycle++;
		if (recycle == &blk[NBUFS])
		{
			recycle = blk;
		}
	} while (this == toonew || this == newtoo);

	/* if it contains a block, flush that block */
	blkflush(this);

	/* fill this buffer with the desired block */
	this->logical = logical;
	if (hdr.n[logical])
	{
		/* it has been used before - fill it from tmp file */
		lseek(tmpfd, (long)hdr.n[logical] * (long)BLKSIZE, 0);
		if (read(tmpfd, this->buf.c, BLKSIZE) != BLKSIZE)
		{
			msg("Error reading back from tmp file!");
		}
	}
	else
	{
		/* it is new - zero it */
		for (i = 0; i < BLKSIZE; i++)
		{
			this->buf.c[i] = 0;
		}
	}

	/* This isn't really a change, but it does potentially invalidate
	 * the kinds of shortcuts that the "changes" variable is supposed
	 * to protect us from... so count it as a change.
	 */
	changes++;

	/* mark it as being "not dirty" */
	this->dirty = 0;

	/* return it */
	newtoo = toonew;
	toonew = this;
	return &this->buf;
}



/* This function writes a block out to the temporary file */
blkflush(this)
	register struct _blkbuf	*this;	/* the buffer to flush */
{
	long	seekpos;	/* seek position of the new block */
	ushort	physical;	/* physical block number */

	/* if its empty (an orphan blkadd() maybe?) then make it dirty */
	if (this->logical && !*this->buf.c)
	{
		blkdirty(&this->buf);
	}

	/* if it's an empty buffer or a clean version is on disk, quit */
	if (!this->logical || hdr.n[this->logical] && !this->dirty)
	{
		return;
	}

	/* find a free place in the file */
#ifndef NO_RECYCLE
	seekpos = allocate();
	lseek(tmpfd, seekpos, 0);
#else
	seekpos = lseek(tmpfd, 0L, 2);
#endif
	physical = seekpos / BLKSIZE;

	/* put the block there */
	if (write(tmpfd, this->buf.c, BLKSIZE) != BLKSIZE)
	{
		msg("Trouble writing to tmp file");
	}
	this->dirty = FALSE;

	/* update the header so it knows we put it there */
	hdr.n[this->logical] = physical;
}


/* This function sets a block's "dirty" flag or deletes empty blocks */
blkdirty(bp)
	BLK	*bp;	/* buffer returned by blkget() */
{
	register int 	i, j;
	register char	*scan;
	register int	k;

	/* find the buffer */
	for (i = 0; i < NBUFS && bp != &blk[i].buf; i++)
	{
	}
#ifdef DEBUG
	if (i >= NBUFS)
	{
		msg("blkdirty() called with unknown buffer at 0x%lx", bp);
		return;
	}
	if (blk[i].logical == 0)
	{
		msg("blkdirty called with freed buffer");
		return;
	}
#endif

	/* if this block ends with line# INFINITY, then it must have been
	 * allocated unnecessarily during tmpstart().  Forget it.
	 */
	if (lnum[blk[i].logical] == INFINITY)
	{
#ifdef DEBUG
		if (blk[i].buf.c[0])
		{
			msg("bkldirty called with non-empty extra BLK");
		}
#endif
		blk[i].logical = 0;
		blk[i].dirty = FALSE;
		return;
	}

	/* count lines in this block */
	for (j = 0, scan = bp->c; *scan && scan < bp->c + BLKSIZE; scan++)
	{
		if (*scan == '\n')
		{
			j++;
		}
	}

	/* adjust lnum, if necessary */
	k = blk[i].logical;
	j += (lnum[k - 1] - lnum[k]);
	if (j != 0)
	{
		nlines += j;
		while (k < MAXBLKS && lnum[k] != INFINITY)
		{
			lnum[k++] += j;
		}
	}

	/* if it still has text, mark it as dirty */
	if (*bp->c)
	{
		blk[i].dirty = TRUE;
	}
	else /* empty block, so delete it */
	{
		/* adjust the cache */
		k = blk[i].logical;
		for (j = 0; j < NBUFS; j++)
		{
			if (blk[j].logical >= k)
			{
				blk[j].logical--;
			}
		}

		/* delete it from hdr.n[] and lnum[] */
		blk[i].logical = 0;
		blk[i].dirty = FALSE;
		while (k < MAXBLKS - 1)
		{
			hdr.n[k] = hdr.n[k + 1];
			lnum[k] = lnum[k + 1];
			k++;
		}
		hdr.n[MAXBLKS - 1] = 0;
		lnum[MAXBLKS - 1] = INFINITY;
	}
}


/* insert a new block into hdr, and adjust the cache */
BLK *blkadd(logical)
	int		logical;	/* where to insert the new block */
{
	register int	i;

	/* adjust hdr and lnum[] */
	for (i = MAXBLKS - 1; i > logical; i--)
	{
		hdr.n[i] = hdr.n[i - 1];
		lnum[i] = lnum[i - 1];
	}
	hdr.n[logical] = 0;
	lnum[logical] = lnum[logical - 1];

	/* adjust the cache */
	for (i = 0; i < NBUFS; i++)
	{
		if (blk[i].logical >= logical)
		{
			blk[i].logical++;
		}
	}

	/* return the new block, via blkget() */
	return blkget(logical);
}


/* This function forces all dirty blocks out to disk */
blksync()
{
	int	i;

	for (i = 0; i < NBUFS; i++)
	{
		/* blk[i].dirty = TRUE; */
		blkflush(&blk[i]);
	}
	if (*o_sync)
	{
		sync();
	}
}

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

MARK	undocurs;	/* where the cursor should go if undone */


/* This function should be called before each command that changes the text.
 * It defines the state that undo() will reset the file to.
 */
beforedo(undo)
	int		undo;	/* boolean: is this for an undo? */
{
	static long	oldnlines;
	static long	oldlnum[MAXBLKS];
	register int	i;
	register long	l;

	/* if this is a nested call to beforedo, quit! Use larger context */
	if (b4cnt++ > 0)
	{
		return;
	}

	/* force all block buffers to disk */
	blksync();

#ifndef NO_RECYCLE
	/* perform garbage collection on blocks from tmp file */
	garbage();
#endif

	/* force the header out to disk */
	lseek(tmpfd, 0L, 0);
	if (write(tmpfd, hdr.c, BLKSIZE) != BLKSIZE)
	{
		msg("Trouble writing header to tmp file ");
	}

	/* set the file's "dirty" flag */
	setflag(file, MODIFIED);

	/* copy or swap oldnlines <--> nlines, oldlnum <--> lnum */
	if (undo)
	{
		for (i = 0; i < MAXBLKS; i++)
		{
			l = lnum[i];
			lnum[i] = oldlnum[i];
			oldlnum[i] = l;
		}
		l = nlines;
		nlines = oldnlines;
		oldnlines = l;
	}
	else
	{
		for (i = 0; i < MAXBLKS; i++)
		{
			oldlnum[i] = lnum[i];
		}
		oldnlines = nlines;
	}

	/* save the cursor position */
	undocurs = cursor;

	/* upon return, the calling function continues and makes changes... */
}

/* This function marks the end of a (nested?) change to the file */
afterdo()
{
	if (--b4cnt)
	{
		return;
	}

	/* make sure the cursor wasn't left stranded in deleted text */
	if (markline(cursor) > nlines)
	{
		cursor = MARK_LAST;
	}
	/* NOTE: it is still possible that markidx(cursor) is after the
	 * end of a line, so the Vi mode will have to take care of that
	 * itself */
}

/* This function discards all changes made since the last call to beforedo() */
undo()
{
	BLK		oldhdr;

	/* if beforedo() has never been run, fail */
	if (!tstflag(file, MODIFIED))
	{
		msg("You haven't modified this file yet.");
		return;
	}

	/* read the old header form the tmp file */
	lseek(tmpfd, 0L, 0);
	if (read(tmpfd, oldhdr.c, BLKSIZE) != BLKSIZE)
	{
		msg("Trouble rereading the old header from tmp file");
	}

	/* "do" the changed version, so we can undo the "undo" */
	cursor = undocurs;
	beforedo(TRUE);
	afterdo();

	/* wipe out the block buffers - we can't assume they're correct */
	blkinit();

	/* use the old header -- and therefore the old text blocks */
	hdr = oldhdr;

	/* This is a change */
	changes++;
}