LIFE - fancy new version (part 3 of 4)

David I. Bell dbell at daisy.UUCP
Sun Feb 3 14:51:31 AEST 1985


This is the last part of the actual life sources.  The final article just
contains life objects.

#---Cut here and place in it's own directory, then feed to Bourne shell---
# This is a shell archive.  Remove anything before this line, then
# unpack it by saving it in a file and typing "sh file".  (Files
# unpacked will be owned by you and have default permissions.)
# This archive contains:
#   main.c (2817 chars)
#   mark.c (9268 chars)
#   object.c (8845 chars)
#   scan.c (3526 chars)
#   vars.c (9419 chars)
#   view.c (9535 chars)
#
echo x - main.c
sed -e 's/^X//' > "main.c" << '//E*O*F main.c//'
Xstatic char *sccsid = "@(#)main.c	1.12	2/2/85";
Xstatic char *cpyrid = "@(#)Copyright (C) 1985 by D Bell";
X
X/*
X * The game of life on an infinite board (by David I. Bell).
X * These life sources are in the public domain, and can be copied
X * or used as desired, with the following restrictions:
X * 1.	All copyright notices (and this notice) must be preserved.
X * 2.	The life sources (even if modified) cannot be sold for profit.
X * 3.	If any sources are modified, a sentence must exist by the
X *	copyright notice of each modified source file which says that
X *	the file has been modified.
X */
X
X#include "life.h"
X#include <sgtty.h>
X
Xint	intint(), readchar();		/* routines */
Xchar	*getenv();			/* another one */
X
X
Xmain(argc, argv)
X	char	**argv;			/* argument is a life object */
X{
X	userlib = getenv(LIFEVAR);
X	strcpy(rulestring, "3,23");
X	termcell = alloccell();
X	termcell->c_next = termcell;
X	termcell->c_col = INFINITY;
X	termrow = allocrow();
X	termrow->r_next = termrow;
X	termrow->r_firstcell = termcell;
X	termrow->r_row = INFINITY;
X	mode = M_MOVE;
X	gridchar = ' ';
X	frequency = 1;
X	freqcount = 1;
X	rowradius = 1;				/* temp until find real sizes */
X	colradius = 1;
X	reserve = 1;				/* creating special objects */
X	deleteobject = getobject("..delete");
X	tempobject = getobject("..temp");
X	backupobject = getobject("..backup");
X	mainobject = getobject("main");
X	curobj = mainobject;
X	prevobj = mainobject;
X	reserve = 0;				/* no more special objects */
X	curinput = &inputs[-1];			/* initialize for tty input */
X	settty();
X	if ((argc > 1) && setfile(argv[1])) {	/* set to get input file */
X		perror(argv[1]);
X		exit(1);
X	}
X	if (dpyinit(NULL, CBREAK, ECHO)) {	/* home up and clear screen */
X		exit(1);
X	}
X	dpymove(-1, -1);			/* get screen size */
X	rowradius = (dpygetrow() - 1) / 2;
X	colradius = (dpygetcol() - 1) / 2;
X	setscale(deleteobject, 1);		/* fix scale factors now */
X	setscale(tempobject, 1);
X	setscale(backupobject, 1);
X	setscale(mainobject, 1);
X	signal(SIGINT, intint);
X	scaninit(readchar, ttyjmp);
X	while (1) {
X		docommand();
X		dogeneration(curobj);
X		updateview();
X	}
X}
X
X
X/*
X * Here on an interrupt character.  Remember to stop what we are doing soon.
X * We cannot just longjmp away since things may be in an inconsistent state.
X */
Xintint()
X{
X	signal(SIGINT, intint);		/* not needed in 4.2, but so what */
X	genleft = 0;
X	stop = 1;
X	redraw = 1;
X#ifdef DEBUG
X	dumpdata();
X#endif DEBUG
X}
X
X
X/*
X * Here on an error.  Close all but the top input level, cancel the
X * current command, beep, and set up to display the indicated message.
X * The message will remain until the next command is typed by the user.
X */
Xerror(str)
X	char	*str;		/* message to type */
X{
X	while (curinput > inputs) curinput->i_term(curinput);
X	errorstring = str;
X	redraw = 1;
X	stop = 0;
X	dowait = 0;
X	write(STDERR, "\007", 1);
X	scanabort();
X}
//E*O*F main.c//
echo x - mark.c
sed -e 's/^X//' > "mark.c" << '//E*O*F mark.c//'
X#ifdef SCCS
Xstatic char *sccsid = "@(#)mark.c	1.7	2/2/85";
Xstatic char *cpyrid = "@(#)Copyright (C) 1985 by D Bell";
X#endif
X
X#include "life.h"
X
Xstruct	loc	{		/* structure to hold row and column pairs */
X	long	l_row;
X	long	l_col;
X};
X
Xchar	rowwalk[9] = {-1,0,0,1,1,0,0,-1,0};	/* row deltas to walk a cell */
Xchar	colwalk[9] = {-1,1,1,0,0,-1,-1,0,1};	/* col deltas to walk a cell */
X
X
X/*
X * Mark the object at a given location as specified.  Returns nonzero if
X * no object exists there.  An object for this purpose is considered as a
X * king-wise connected set of live cells.
X */
Xmarkobject(obj, row, col, mark)
X	register struct	object	*obj;	/* object to mark up */
X{
X	register struct	cell	*cp;	/* object being marked */
X
X	cp = findcell(obj, row, col);
X	if (cp == NULL) return(1);
X	cp->c_marks |= mark;
X	markloop(obj, row, col, mark);
X	return(0);
X}
X
X
X/* Recursive subroutine called from markobject */
Xmarkloop(obj, row, col, mark)
X	struct	object	*obj;		/* object begin marked */
X	register int	row;		/* current row */
X	register int	col;		/* current column */
X	register int	mark;		/* marking value */
X{
X	register struct	cell	*cp;	/* current cell */
X	register struct	loc	*rp;	/* pointer into list table */
X	int	i;			/* to iterate over directions */
X	struct	loc	rclist[8];	/* row and column list */
X
X	while (1) {
X		if (stop) return;
X		rp = rclist;
X		for (i = 0; i < 8; i++) {	/* find neighbors */
X			row += rowwalk[i];
X			col += colwalk[i];
X			cp = findcell(obj, row, col);
X			if (cp == NULL) continue;
X			if (cp->c_marks & mark) continue;
X			cp->c_marks |= mark;
X			rp->l_row = row;
X			rp->l_col = col;
X			rp++;
X		}
X		if (--rp != rclist) {		/* recurse if more than one */
X			for (; rp >= rclist; rp--) {
X				markloop(obj, rp->l_row, rp->l_col, mark);
X			}
X			return;
X		}
X		row = rp->l_row;		/* else follow single cell */
X		col = rp->l_col;
X	}
X}
X
X
X/* Mark a whole object as specified. */
Xsetmarks(obj, mark)
X	struct	object	*obj;		/* object to mark */
X	register int	mark;		/* mark to be applied */
X{
X	register struct	row	*rp;	/* row pointer */
X	register struct	cell	*cp;	/* cell pointer */
X
X	mark |= MARK_ANY;
X	for (rp = obj->o_firstrow; rp != termrow; rp = rp->r_next) {
X		for (cp = rp->r_firstcell; cp != termcell; cp = cp->c_next) {
X			cp->c_marks |= mark;
X		}
X	}
X}
X
X
X/*
X * Copy the marks from one type to another for an object.  Returns nonzero
X * if there were no cells with the given mark.
X */
Xcopymarks(obj, srcmark, destmark)
X	struct	object	*obj;		/* object to mark */
X	register int	srcmark;	/* mark to be found */
X	register int	destmark;	/* mark to be set */
X{
X	register struct	row	*rp;	/* row pointer */
X	register struct	cell	*cp;	/* cell pointer */
X	int	error;
X
X	error = 1;
X	for (rp = obj->o_firstrow; rp != termrow; rp = rp->r_next) {
X		for (cp = rp->r_firstcell; cp != termcell; cp = cp->c_next) {
X			if ((cp->c_marks & srcmark) == 0) continue;
X			cp->c_marks |= destmark;
X			error = 0;
X		}
X	}
X	return(error);
X}
X
X
X/* Clear marks for a whole object as specified. */
Xclearmarks(obj, mark)
X	struct	object	*obj;		/* object to mark */
X	register int	mark;		/* mark to be cleared */
X{
X	register struct	row	*rp;	/* row pointer */
X	register struct	cell	*cp;	/* cell pointer */
X
X	mark = ~mark;
X	mark |= MARK_ANY;
X	for (rp = obj->o_firstrow; rp != termrow; rp = rp->r_next) {
X		for (cp = rp->r_firstcell; cp != termcell; cp = cp->c_next) {
X			cp->c_marks &= mark;
X		}
X	}
X}
X
X
X/*
X * Mark the cells in a specified rectangular region as desired.
X * Returns the number of cells which were marked.
X */
Xmarkregion(obj, mark, minrow, maxrow, mincol, maxcol)
X	struct	object	*obj;			/* object to mark */
X	register int	mark;			/* value to mark with */
X	long	minrow, maxrow, mincol, maxcol;	/* range to mark */
X{
X	register struct	row	*rp;		/* current row */
X	register struct	cell	*cp;		/* current cell */
X	register long	count;			/* count of cells */
X
X	count = 0;
X	for (rp = obj->o_firstrow; rp != termrow; rp = rp->r_next) {
X		if (rp->r_row < minrow) continue;
X		if (rp->r_row > maxrow) break;
X		for (cp = rp->r_firstcell; cp != termcell; cp = cp->c_next) {
X			if (cp->c_col < mincol) continue;
X			if (cp->c_col > maxcol) break;
X			cp->c_marks |= mark;
X			count++;
X		}
X	}
X	return(count);
X}
X
X
X/*
X * Find the range of all marked cells for an object.  Returns nonzero if
X * no cells were marked.
X */
Xmarkminmax(obj, mark, minrow, maxrow, mincol, maxcol)
X	struct	object	*obj;		/* object to search */
X	register int	mark;		/* mark to search for */
X	long	*minrow, *maxrow, *mincol, *maxcol;	/* results */
X{
X	register struct	row	*rp;	/* current row */
X	register struct	cell	*cp;	/* current cell */
X	register int	row;		/* current row */
X	long	minr, maxr, minc, maxc;	/* temp variables */
X
X	minr = INFINITY;
X	maxr = -INFINITY;
X	minc = INFINITY;
X	maxc = -INFINITY;
X	for (rp = obj->o_firstrow; rp != termrow; rp = rp->r_next) {
X		row = rp->r_row;
X		for (cp = rp->r_firstcell; cp != termcell; cp = cp->c_next) {
X			if ((cp->c_marks & mark) == 0) continue;
X			if (row < minr) minr = row;
X			if (row > maxr) maxr = row;
X			if (cp->c_col < minc) minc = cp->c_col;
X			if (cp->c_col > maxc) maxc = cp->c_col;
X		}
X	}
X	if (minr > maxr) return(1);
X	*minrow = minr;
X	*maxrow = maxr;
X	*mincol = minc;
X	*maxcol = maxc;
X	return(0);
X}
X
X
X/* Count the number of marked cells in an object */
Xcountmarks(obj, mark)
X	struct	object	*obj;		/* object to check */
X	register int	mark;		/* mark to be counted */
X{
X	register struct	row	*rp;	/* row pointer */
X	register struct	cell	*cp;	/* cell pointer */
X	register long	count;		/* number of cells */
X
X	count = 0;
X	for (rp = obj->o_firstrow; rp != termrow; rp = rp->r_next) {
X		for (cp = rp->r_firstcell; cp != termcell; cp = cp->c_next) {
X			if (cp->c_marks & mark) count++;
X		}
X	}
X	return(count);
X}
X
X
X/*
X * Move marked cells to another object.  If the destination object is NULL
X * the cells are just deleted.  The previous cells of the destination object
X * are deleted.
X */
Xmovemarkedobject(sobj, dobj, mark)
X	register struct	object	*sobj;	/* source object */
X	struct	object	*dobj;		/* destination object */
X{
X	register struct	row	*rp;	/* row pointer */
X	register struct	cell	*cp;	/* current cell pointer */
X	register struct	cell	*pcp;	/* previous cell pointer */
X	register struct	cell	*ncp;	/* next cell pointer */
X
X	if (sobj == dobj) error("Moving object to itself");
X	if (dobj) {
X		zeroobject(dobj);
X		dobj->o_currow = sobj->o_currow;
X		dobj->o_curcol = sobj->o_curcol;
X	}
X	for (rp = sobj->o_firstrow; rp != termrow; rp = rp->r_next) {
X		pcp = NULL;
X		cp = rp->r_firstcell;
X		while (cp != termcell) {
X			if ((cp->c_marks & mark) == 0) {
X				pcp = cp;
X				cp = cp->c_next;
X				continue;
X			}
X			if (dobj) addcell(dobj, rp->r_row, cp->c_col);
X			ncp = cp->c_next;
X			if (pcp == NULL)
X				rp->r_firstcell = ncp;
X			else
X				pcp->c_next = ncp;
X			if (ncp == termcell) rp->r_lastcell = pcp;
X			cp->c_next = freecells;
X			freecells = cp;
X			cp = ncp;
X			rp->r_count--;
X			sobj->o_count--;
X		}
X	}
X}
X
X
X/*
X * Copy marked cells to another object. The previous cells of the destination
X * object are deleted.  The source object is unaffected.
X */
Xcopymarkedobject(sobj, dobj, mark)
X	register struct	object	*sobj;	/* source object */
X	register struct	object	*dobj;	/* destination object */
X	register int	mark;		/* mark value */
X{
X	register struct	row	*rp;	/* row pointer */
X	register struct	cell	*cp;	/* current cell */
X
X	if (sobj == dobj) error("Copying object to itself");
X	zeroobject(dobj);
X	dobj->o_currow = sobj->o_currow;
X	dobj->o_curcol = sobj->o_curcol;
X	for (rp = sobj->o_firstrow; rp != termrow; rp = rp->r_next) {
X		for (cp = rp->r_firstcell; cp != termcell; cp = cp->c_next) {
X			if ((cp->c_marks & mark) == 0) continue;
X			addcell(dobj, rp->r_row, cp->c_col);
X		}
X	}
X}
X
X
X/*
X * Rotate the marked cells in the given object around the current cursor
X * location.  This deletes the marked cells, and reinserts them after
X * their position has been rotated by 90 degrees clockwise.
X */
Xrotatemarkedobject(obj, mark)
X	register struct	object	*obj;		/* current object */
X{
X	register struct	row	*rp;		/* current row */
X	register struct	cell	*cp;		/* current cell */
X	register int	row, col;		/* current row and column */
X
X	if (obj == tempobject) error("Using temp object");
X	movemarkedobject(obj, tempobject, mark);
X	for (rp = tempobject->o_firstrow; rp != termrow; rp = rp->r_next) {
X		row = rp->r_row - obj->o_currow;
X		for (cp = rp->r_firstcell; cp != termcell; cp = cp->c_next) {
X			col = cp->c_col - obj->o_curcol;
X			addcell(obj, obj->o_currow + col, obj->o_curcol - row);
X		}
X	}
X}
X
X
X/*
X * Flip the marked cells in the given object around the column of the current
X * cursor location.  This deletes the marked cells, and reinserts them after
X * their position has been flipped around the vertical axis.
X */
Xflipmarkedobject(obj, mark)
X	register struct	object	*obj;		/* current object */
X{
X	register struct	row	*rp;		/* current row */
X	register struct	cell	*cp;		/* current cell */
X	register int	row, col;		/* current row and column */
X
X	if (obj == tempobject) error("Using temp object");
X	movemarkedobject(obj, tempobject, mark);
X	for (rp = tempobject->o_firstrow; rp != termrow; rp = rp->r_next) {
X		row = rp->r_row;
X		for (cp = rp->r_firstcell; cp != termcell; cp = cp->c_next) {
X			col = cp->c_col - obj->o_curcol;
X			addcell(obj, row, obj->o_curcol - col);
X		}
X	}
X}
//E*O*F mark.c//
echo x - object.c
sed -e 's/^X//' > "object.c" << '//E*O*F object.c//'
X#ifdef SCCS
Xstatic char *sccsid = "@(#)object.c	1.17	2/2/85";
Xstatic char *cpyrid = "@(#)Copyright (C) 1985 by D Bell";
X#endif
X
X#include "life.h"
X
X
X/*
X * Find the given named object.  Returns NULL if nonexistant.  The special
X * name of "." means the current object.  The special name of ".." means
X * the previous object.
X */
Xstruct object *
Xfindobject(str)
X	register char	*str;		/* name to find */
X{
X	register struct	object	*obj;	/* current object */
X
X	if (str[0] == '.') {		/* check for "." or ".." */
X		if (str[1] == '\0') return(curobj);
X		if ((str[1] == '.') && (str[2] == '\0')) return(prevobj);
X	}
X	for (obj = objects; obj; obj = obj->o_next) {
X		if (strcmp(obj->o_name, str) == 0) return(obj);
X	}
X	return(NULL);
X}
X
X
X/* Create the given named object, or return it if it already exists. */
Xstruct object *
Xgetobject(str)
X	register char	*str;		/* name to find */
X{
X	register struct	object	*obj;	/* current object */
X
X	for (obj = objects; obj; obj = obj->o_next) {
X		if (strcmp(obj->o_name, str) == 0) return(obj);
X	}
X	if (strlen(str) > MAXNAME) error("Object name too long");
X	if ((reserve==0) && BADNAME(str)) error("Cannot create reserved name");
X	obj = allocobject();
X	obj->o_next = objects;
X	objects = obj;
X	strcpy(obj->o_name, str);
X	return(obj);
X}
X
X
X/*
X * Set an object as the current one.  The old current object is remembered
X * so that it can be referenced using "..".  This cancels any insert mode.
X */
Xsetobject(obj)
X	register struct	object	*obj;		/* new object to set */
X{
X	mode = M_MOVE;
X	if (obj == curobj) return;
X	prevobj = curobj;
X	curobj = obj;
X	redraw = 1;
X}
X
X
X/* Delete all cells of an object */
Xzeroobject(obj)
X	register struct	object	*obj;
X{
X	register struct	row	*rp;
X
X	rp = obj->o_firstrow;
X	if (rp == termrow) return;
X	for (; rp != termrow; rp = rp->r_next) {
X		if (rp->r_firstcell == termcell) continue;
X		rp->r_lastcell->c_next = freecells;
X		freecells = rp->r_firstcell;
X		obj->o_count -= rp->r_count;
X	}
X	obj->o_lastrow->r_next = freerows;
X	freerows = obj->o_firstrow;
X	obj->o_firstrow = termrow;
X	obj->o_lastrow = NULL;
X}
X
X
X/*
X * Destroy the existence of an object.  If it is the current object,
X * switch the current object back to the previous object.
X */
Xdestroyobject(obj)
X	register struct	object	*obj;		/* object to delete */
X{
X	register struct	object	*pobj;		/* previous object */
X
X	if (obj == NULL) return;
X	if (obj->o_reserved) error("Cannot destroy reserved object");
X	if (obj == prevobj) prevobj = mainobject;
X	if (obj == curobj) {
X		curobj = prevobj;
X		prevobj = mainobject;
X		redraw = 1;
X	}
X	zeroobject(obj);
X	if (objects == obj) {		/* first object in list */
X		objects = obj->o_next;
X		obj->o_next = freeobjects;
X		freeobjects = obj;
X		return;
X	}
X	for (pobj = objects; pobj->o_next != obj; pobj = pobj->o_next) ;
X	pobj->o_next = obj->o_next;
X	obj->o_next = freeobjects;
X	freeobjects = obj;
X}
X
X
X/*
X * Move one object to another.  The source object is zeroed, and the
X * previous contents of the destination object are lost.
X */
Xmoveobject(sobj, dobj)
X	register struct	object	*sobj;		/* source object */
X	register struct	object	*dobj;		/* destination object */
X{
X	if (sobj == dobj) error("Moving object to itself");
X	zeroobject(dobj);
X	dobj->o_currow = sobj->o_currow;
X	dobj->o_curcol = sobj->o_curcol;
X	dobj->o_minrow = sobj->o_minrow;
X	dobj->o_maxrow = sobj->o_maxrow;
X	dobj->o_mincol = sobj->o_mincol;
X	dobj->o_maxcol = sobj->o_maxcol;
X	dobj->o_scale = sobj->o_scale;
X	dobj->o_autoscale = sobj->o_autoscale;
X	dobj->o_prow = sobj->o_prow;
X	dobj->o_pcol = sobj->o_pcol;
X	dobj->o_firstrow = sobj->o_firstrow;
X	dobj->o_lastrow = sobj->o_lastrow;
X	dobj->o_count = sobj->o_count;
X	sobj->o_firstrow = termrow;
X	sobj->o_lastrow = NULL;
X	sobj->o_count = 0;
X}
X
X
X/*
X * Add one object to another.  The source object is unchanged.  The 
X * destination object will get all cells from both objects.  If disp is
X * RELATIVE, the object is displaced as specified by the two object's cursor
X * positions. Otherwise, the addition is performed with absolute coordinates.
X */
Xaddobject(sobj, dobj, disp)
X	register struct	object	*sobj;		/* source object */
X	register struct	object	*dobj;		/* destination object */
X{
X	register struct	row	*rp;		/* current row */
X	register struct	cell	*cp;		/* current cell */
X	register int	newrow;			/* new row number */
X	int	rowdisp, coldisp;		/* displacements */
X
X	if (sobj == dobj) error("Adding object to itself");
X	rowdisp = 0;
X	coldisp = 0;
X	if (disp == RELATIVE) {
X		rowdisp = dobj->o_currow - sobj->o_currow;
X		coldisp = dobj->o_curcol - sobj->o_curcol;
X	}
X	for (rp = sobj->o_firstrow; rp != termrow; rp = rp->r_next) {
X		newrow = rp->r_row + rowdisp;
X		for (cp = rp->r_firstcell; cp != termcell; cp = cp->c_next) {
X			addcell(dobj, newrow, cp->c_col + coldisp);
X		}
X	}
X}
X
X
X/*
X * Copy one object to another.  The source object is unchanged.
X * The current contents of the destination object are lost.
X */
Xcopyobject(sobj, dobj)
X	register struct	object	*sobj;		/* source object */
X	register struct	object	*dobj;		/* destination object */
X{
X	if (sobj == dobj) error("Copying object to itself");
X	zeroobject(dobj);
X	addobject(sobj, dobj, ABSOLUTE);
X	dobj->o_currow = sobj->o_currow;
X	dobj->o_curcol = sobj->o_curcol;
X	dobj->o_minrow = sobj->o_minrow;
X	dobj->o_maxrow = sobj->o_maxrow;
X	dobj->o_mincol = sobj->o_mincol;
X	dobj->o_maxcol = sobj->o_maxcol;
X	dobj->o_scale = sobj->o_scale;
X	dobj->o_autoscale = sobj->o_autoscale;
X	dobj->o_prow = sobj->o_prow;
X	dobj->o_pcol = sobj->o_pcol;
X}
X
X
X/*
X * Show the list of objects.  If all is nonzero, all objects will be
X * shown.  Otherwise, only objects not starting with a period are shown.
X */
Xlistobjects(all)
X{
X	register struct	object	*obj;		/* current object */
X	register int	ch;			/* current character */
X	int	minrow, maxrow, mincol, maxcol;	/* current bounds */
X
X	dpywindow(0, -1, 0, -1);
X	dpystr("cells	height	width	gen	scale	object\n");
X	dpystr("-----	------	-----	---	-----	------\n");
X	for (obj = objects; obj; obj = obj->o_next) {
X		if ((all == 0) && (obj->o_name[0] == '.')) continue;
X		ch = ' ';
X		if (obj == prevobj) ch = '+';
X		if (obj == curobj) ch = '*';
X		minmax(obj, &minrow, &maxrow, &mincol, &maxcol);
X		dpyprintf("%d\t%d\t%d\t%d\t%d\t%c %s\n",
X			obj->o_count, (maxrow - minrow + 1),
X			(maxcol - mincol + 1), obj->o_gen,
X			obj->o_scale, ch, obj->o_name);
X	}
X	dpyprintf("\n\
XIn object column, '*' = current object, '+' = previous object\n");
X	if (all == 0)
X		dpyprintf("Use -a to show objects beginning with '.'\n");
X	spacewait();
X}
X
X
X/*
X * Find the minimum and maximum row and column numbers for an object.
X * If there are no cells in the object, the mins will be one more than
X * the maxes.  Returns nonzero if the object has no cells.
X */
Xminmax(obj, minrow, maxrow, mincol, maxcol)
X	struct	object	*obj;				/* object to examine */
X	long	*minrow, *maxrow, *mincol, *maxcol;	/* pointers to result */
X{
X	register struct	row	*rp;		/* current row */
X	register int	maxr, minr, maxc, minc;	/* current results */
X	int	err;				/* return value */
X
X	minr = INFINITY;
X	maxr = -INFINITY;
X	minc = INFINITY;
X	maxc = -INFINITY;
X	err = 1;
X	for (rp = obj->o_firstrow; rp != termrow; rp = rp->r_next) {
X		if (rp->r_firstcell == termcell) continue;
X		if (rp->r_row < minr) minr = rp->r_row;
X		maxr = rp->r_row;
X		if (rp->r_firstcell->c_col<minc) minc = rp->r_firstcell->c_col;
X		if (rp->r_lastcell->c_col>maxc) maxc = rp->r_lastcell->c_col;
X		err = 0;
X	}
X	if (err) {			/* no cells in object */
X		minr = 1;
X		maxr = 0;
X		minc = 1;
X		maxc = 0;
X	}
X	*minrow = minr;
X	*maxrow = maxr;
X	*mincol = minc;
X	*maxcol = maxc;
X	return(err);
X}
X
X
X/*
X * Search forwards for the nth next object, restarting at the top if necessary.
X * If all is nonzero, or if wrap around occurs, the search will be over all
X * objects.  Otherwise, objects found in previous searches will be skipped.
X * Returns nonzero if nothing was found.
X */
Xsearchobject(obj, count, all)
X	register struct	object	*obj;		/* object to search through */
X{
X	register struct	row	*rp;	/* current row being examined */
X	register struct	cell	*cp;	/* current cell begin examined */
X	register long	row;		/* current row */
X	register long	col;		/* current column */
X
X	if (all) clearmarks(obj, MARK_SRC);
X	row = obj->o_currow;
X	col = obj->o_curcol;
X	for (rp = obj->o_firstrow; row > rp->r_row; rp = rp->r_next) ;
X	for (cp = rp->r_firstcell; col > cp->c_col; cp = cp->c_next) ;
X	if ((row == rp->r_row) && (col == cp->c_col) &&
X		((cp->c_marks & MARK_SRC) == 0)) count++;
X	while (1) {
X		if (stop) return(0);
X		if (cp == termcell) {
X			rp = rp->r_next;
X			if (rp == termrow) {
X				clearmarks(obj, MARK_SRC);
X				rp = obj->o_firstrow;
X			}
X			cp = rp->r_firstcell;
X			continue;
X		}
X		if ((cp->c_marks & MARK_SRC) == 0) {
X			markobject(obj, rp->r_row, cp->c_col, MARK_SRC);
X			if (--count <= 0) break;
X		}
X		cp = cp->c_next;
X	}
X	obj->o_currow = rp->r_row;
X	obj->o_curcol = cp->c_col;
X	return(0);
X}
//E*O*F object.c//
echo x - scan.c
sed -e 's/^X//' > "scan.c" << '//E*O*F scan.c//'
X#ifdef SCCS
Xstatic char *sccsid = "@(#)scan.c	1.4	1/27/85";
Xstatic char *cpyrid = "@(#)Copyright (C) 1985 by D Bell";
X#endif
X
X/* Module to read self-terminating input while allowing editing of the input */
X
X#include <sgtty.h>
X#include "life.h"
X
Xjmp_buf	*scanjumpbuf;			/* jump buffer to use in scanchar */
Xchar	scanbuffer[SCAN_SIZE+1];	/* storage for characters */
Xchar	*scanreadptr;			/* current read pointer */
Xchar	*scanwriteptr;			/* current write pointer */
Xchar	(*scanroutine)();		/* routine to read characters */
Xstatic	char	rubchar;		/* erase letter character */
Xstatic	char	rubword;		/* erase word character */
Xstatic	char	rubline;		/* erase line character */
Xstatic	char	litchar;		/* literal input */
X
X
X/*
X * Initialize for later calls to scanchar.
X */
Xscaninit(routine, jumpbuf)
X	char	(*routine)();		/* routine to get characters */
X	jmp_buf	*jumpbuf;		/* jump buffer to use later */
X{
X	struct	sgttyb	sgbuf;		/* basic tty structure */
X	struct	ltchars	ltbuf;		/* local tty structure */
X
X	scanroutine = routine;		/* init static variables */
X	scanjumpbuf = jumpbuf;
X	scanwriteptr = scanbuffer;
X	scanreadptr = scanbuffer;
X	sgbuf.sg_erase = CERASE;	/* set defaults in case ioctls fail */
X	sgbuf.sg_kill = CKILL;
X	ltbuf.t_werasc = CWERASE;
X	ltbuf.t_lnextc = CLNEXT;
X	ioctl(STDIN, TIOCGETP, &sgbuf);	/* get and save editing characters */
X	ioctl(STDIN, TIOCGLTC, &ltbuf);
X	rubchar = sgbuf.sg_erase;
X	rubline = sgbuf.sg_kill;
X	rubword = ltbuf.t_werasc;
X	litchar = ltbuf.t_lnextc;
X}
X
X
X/*
X * Read the next input character.  If it is an editing character,
X * abort the current context and longjmp back to the last setjmp.
X * NOTE: for proper results, the caller should not alter the global
X * state until the full command has been read in.  This includes such
X * things as prompting for input or saving values.  Otherwise, improper
X * results will occur if the user edits the command.
X */
Xscanchar()
X{
X	register int	ch;			/* current character */
X
Xloop:	if (scanreadptr < scanwriteptr)		/* get saved char if have any */
X		return(*scanreadptr++);
X	ch = scanroutine() & 0x7f;		/* get new character */
X	if (ch == litchar) {			/* literal input */
X		ch = scanroutine() & 0x7f;
X		goto store;
X	}
X	if (ch == rubchar) {			/* character erase */
X		if (scanwriteptr <= scanbuffer) {
X			write(STDERR, "\007", 1);
X			goto loop;
X		}
X		scanwriteptr--;
X		scanreadptr = scanbuffer;
X		longjmp(scanjumpbuf, SCAN_EDIT);
X	}
X	if (ch == rubword) {			/* word erase */
X		if (scanwriteptr <= scanbuffer) goto loop;
X		while ((--scanwriteptr >= scanbuffer) &&
X			((*scanwriteptr == ' ') || (*scanwriteptr == '\t'))) ;
X		scanwriteptr++;
X		while ((--scanwriteptr >= scanbuffer) &&
X			((*scanwriteptr != ' ') && (*scanwriteptr != '\t'))) ;
X		scanwriteptr++;
X		scanreadptr = scanbuffer;
X		longjmp(scanjumpbuf, SCAN_EDIT);
X	}
X	if (ch == rubline) {			/* line erase */
X		if (scanwriteptr <= scanbuffer) goto loop;
X		scanwriteptr = scanbuffer;
X		scanreadptr = scanbuffer;
X		longjmp(scanjumpbuf, SCAN_EDIT);
X	}
X
Xstore:	if (scanwriteptr >= scanbuffer + SCAN_SIZE) {
X		write(STDERR, "\007", 1);
X		goto loop;
X	}
X	*scanwriteptr++ = ch;
X	return(*scanreadptr++);
X}
X
X
X/* Abort reading of the current command */
Xscanabort()
X{
X	scanreadptr = scanbuffer;
X	scanwriteptr = scanbuffer;
X	longjmp(scanjumpbuf, SCAN_ABORT);
X}
X
X
X/* Indicate no more characters ready yet */
Xscaneof()
X{
X	scanreadptr = scanbuffer;
X	longjmp(scanjumpbuf, SCAN_EOF);
X}
X
X
X/* Simply reset input and output pointers without longjmping */
Xscanreset()
X{
X	scanreadptr = scanbuffer;
X	scanwriteptr = scanbuffer;
X}
//E*O*F scan.c//
echo x - vars.c
sed -e 's/^X//' > "vars.c" << '//E*O*F vars.c//'
X#ifdef SCCS
Xstatic char *sccsid = "@(#)vars.c	1.11	2/2/85";
Xstatic char *cpyrid = "@(#)Copyright (C) 1985 by D Bell";
X#endif
X
X#include "life.h"
X
Xenum	vars	{		/* ordering of variables */
X	v_minx, v_miny, v_maxx, v_maxy, v_cells, v_cx, v_cy, v_px, v_py,
X	v_vminx, v_vmaxx, v_vminy, v_vmaxy, v_vcells, v_vx, v_vy,
X	v_sminx, v_smaxx, v_sminy, v_smaxy, v_scells, v_gen, v_scale,
X	v_freq, v_born, v_died, v_endlist
X};
X
X
X/*
X * Table of multi-character variables.  These are arranged in this table
X * in rows of 3 so that you can see what the display will look like for
X * the listvariables routine.
X */
Xstruct	vartab	{
X	char	*v_name;	/* name of variable */
X	enum	vars v_type;	/* variable id */
X} vartab[] = {
X	"cx", v_cx,		"vx", v_vx,		"px", v_px,
X	"cy", v_cy,		"vy", v_vy,		"py", v_py,
X	"minx", v_minx,		"vminx", v_vminx,	"sminx", v_sminx,
X	"maxx", v_maxx,		"vmaxx", v_vmaxx,	"smaxx", v_smaxx,
X	"maxy", v_maxy,		"vmaxy", v_vmaxy,	"smaxy", v_smaxy,
X	"miny", v_miny,		"vminy", v_vminy,	"sminy", v_sminy,
X	"cells", v_cells,	"vcells", v_vcells,	"scells", v_scells,
X	"gen", v_gen,		"born", v_born,		"died", v_died,
X	"scale", v_scale,	"freq", v_freq,		NULL, v_endlist
X};
X
X
Xstatic	char	*curcp;		/* current character to parse */
Xstatic	long	lowervars[26];	/* lower case single-char variable values */
Xstatic	long	uppervars[26];	/* upper case single-char variable values */
X
X
X/*
X * Return the value of a multiple character variable name.  This can be
X * either a single character name, or else one of a fixed set of multi-
X * character names.  All variable names must start with either a letter
X * or a dollar sign followed by a letter.
X */
Xgetvariable(cp)
X	register char	*cp;			/* name of variable */
X{
X	register struct	vartab	*vp;		/* variable pointer */
X
X	if (*cp == '$') cp++;			/* skip any dollar sign */
X	if (cp[1] == '\0') {			/* single character name */
X		return(getvariable1(*cp));
X	}
X	if (((*cp < 'a') || (*cp > 'z')) && ((*cp < 'A') || (*cp > 'Z'))) {
X		error("Bad variable name");
X	}
X	for (vp = vartab; ; vp++)  {		/* find name in table */
X		if (vp->v_name == NULL) error("Unknown variable");
X		if (strcmp(vp->v_name, cp) == 0) break;
X	}
X	return(getvariablebytype(vp->v_type));	/* get value */
X}
X
X
X/*
X * Return the value of a variable given its enum value.
X */
Xgetvariablebytype(type)
X	enum	vars	type;			/* variable type to get */
X{
X	register struct	object	*obj;		/* current object */
X	register long	value;			/* value to return */
X	register long	*ptr;			/* pointer to minmax value */
X	register long	*sptr;			/* another one */
X	long	sign;				/* sign for result */
X	long	minrow, maxrow, mincol, maxcol;	/* results of minmax */
X
X	obj = curobj;
X	value = 0;
X	sign = 1;
X	ptr = NULL;
X	sptr = NULL;
X
X	switch (type) {			/* collect value */
X	case v_cx:	value = obj->o_curcol; break;
X	case v_cy:	value = -obj->o_currow; break;
X	case v_px:	value = obj->o_pcol; break;
X	case v_py:	value = -obj->o_prow; break;
X	case v_vx:	value = (obj->o_mincol + obj->o_maxcol) / 2; break;
X	case v_vy:	value = -(obj->o_minrow + obj->o_maxrow) / 2; break;
X	case v_vminx:	value = obj->o_mincol; break;
X	case v_vmaxx:	value = obj->o_maxcol; break;
X	case v_vminy:	value = -obj->o_maxrow; break;
X	case v_vmaxy:	value = -obj->o_minrow; break;
X	case v_vcells:	value = markregion(obj, MARK_ANY, obj->o_minrow,
X				obj->o_maxrow, obj->o_mincol, obj->o_maxcol);
X			break;
X	case v_cells:	value = obj->o_count; break;
X	case v_gen:	value = obj->o_gen; break;
X	case v_born:	value = obj->o_born; break;
X	case v_died:	value = obj->o_died; break;
X	case v_freq:	value = frequency; break;
X	case v_scale:	value = obj->o_scale; break;
X	case v_minx:	ptr = &mincol; break;
X	case v_maxx:	ptr = &maxcol; break;
X	case v_miny:	ptr = &maxrow; sign = -1; break;
X	case v_maxy:	ptr = &minrow; sign = -1; break;
X	case v_scells:	value = countmarks(obj, MARK_SEE); break;
X	case v_sminx:	sptr = &mincol; break;
X	case v_smaxx:	sptr = &maxcol; break;
X	case v_sminy:	sptr = &maxrow; sign = -1; break;
X	case v_smaxy:	sptr = &minrow; sign = -1; break;
X	}
X	/*
X	 * Call proper minmax routines if we need to
X	 */
X	if (ptr && (minmax(obj, &minrow, &maxrow, &mincol, &maxcol) == 0))
X		value = *ptr;
X	if (sptr&&(markminmax(obj,MARK_SEE,&minrow,&maxrow,&mincol,&maxcol)==0))
X		value = *sptr;
X	return(sign * value);
X}
X
X
X/*
X * Return the value of a single character variable name (a-z or A-Z).
X */
Xgetvariable1(ch)
X	register int	ch;		/* variable character */
X{
X	if ((ch >= 'a') && (ch <= 'z'))
X		return(lowervars[ch - 'a']);
X	if ((ch >= 'A') && (ch <= 'Z'))
X		return(lowervars[ch - 'A']);
X	error("Bad variable name");
X}
X
X
X/*
X * Set the value of a variable name.  Multi-character names cannot be set.
X */
Xsetvariable(cp, value)
X	register char	*cp;		/* name of variable to set */
X{
X	if (*cp == '$') cp++;		/* skip any dollar sign */
X	if (cp[1] != '\0') {
X		error("Cannot set multi-character variables");
X	}
X	setvariable1(*cp, value);	/* do it */
X}
X
X
X/*
X * Set the value of a single-character variable (a-z or A-Z).
X */
Xsetvariable1(ch, value)
X	register int	ch;		/* variable character */
X{
X	if ((ch >= 'a') && (ch <= 'z')) {
X		lowervars[ch - 'a'] = value;
X		return;
X	}
X	if ((ch >= 'A') && (ch <= 'Z')) {
X		lowervars[ch - 'A'] = value;
X		return;
X	}
X	error("Bad variable name");
X}
X
X
X/*
X * Display the current values of the variables.  Show all multi-character
X * variable values, and those single character variables which have a
X * nonzero value.  The output is given three variables per line.
X */
Xlistvariables()
X{
X	register struct	vartab	*vp;		/* variable table pointer */
X	register long	*var;			/* simple variable pointer */
X	register int	count;			/* counter for formatting */
X
X	dpywindow(0, -1, 0, -1);
X	dpyprintf("Variable names and values:");
X	count = 0;
X	for (vp = vartab; vp->v_name; vp++) {
X		dpyprintf("%s%s\t%d", (count++ % 3) ? "\t\t" : "\n",
X			vp->v_name, getvariablebytype(vp->v_type));
X	}
X	count = 0;			/* create blank line */
X	for (var = uppervars; var < &uppervars[26]; var++) {
X		if (*var == 0) continue;
X		if (count == 0) dpychar('\n');
X		dpyprintf("%s%c\t%d", (count++ % 3) ? "\t\t" : "\n",
X			'A' + (var - uppervars), *var);
X	}
X	for (var = lowervars; var < &lowervars[26]; var++) {
X		if (*var == 0) continue;
X		if (count == 0) dpychar('\n');
X		dpyprintf("%s%c\t%d", (count++ % 3) ? "\t\t" : "\n",
X			'a' + (var - lowervars), *var);
X	}
X	dpyprintf("\n\nMany names starting with 'v' refer to visible cells\n");
X	dpyprintf("Many names starting with 's' refer to selected cells\n");
X	spacewait();			/* wait for space before clearing */
X}
X
X
X/*
X * Evaluate an expression and return its value.  The expression can
X * contain numbers, variables, the normal arithmetic operators, and
X * parenthesized expressions.  The usual precedence rules are used.
X */
Xgetexpression(str)
X	register char	*str;		/* string to parse */
X{
X	long	value;			/* resulting value */
X
X	while (*str == ' ') str++;
X	if (*str == '\0') error("Null expression");
X	curcp = str;
X	value = parsesum();
X	if (*curcp != '\0') error("Bad expression");
X	return(value);
X}
X
X
X/* Parse the sum of products */
Xparsesum()
X{
X	register long	value;		/* value to return */
X
X	while (*curcp == ' ') curcp++;
X	switch (*curcp) {		/* check for uninary operators */
X		case '-':
X			curcp++;
X			value = -parseproduct();
X			break;
X		case '+':
X			curcp++;
X			/* proceed into default case */
X		default:
X			value = parseproduct();
X	}
X	while (1) switch (*curcp++) {
X		case '+':		/* sum of products */
X			value += parseproduct();
X			continue;
X		case '-':		/* difference of products */
X			value -= parseproduct();
X			continue;
X		case ' ':		/* space */
X			continue;
X		default:		/* end of sum */
X			curcp--;
X			return(value);
X	}
X}
X
X
X/* Parse the product of terms */
Xparseproduct()
X{
X	register long	value;		/* value to return */
X	register long	value2;		/* temporary value */
X
X	value = parseterm();
X	while (1) switch (*curcp++) {
X		case '*':		/* product of terms */
X			value *= parseterm();
X			continue;
X		case '/':		/* division of terms */
X			value2 = parseterm();
X			if (value2 == 0) error("division by zero");
X			value /= value2;
X			continue;
X		case '%':		/* modulo of terms */
X			value2 = parseterm();
X			if (value2 == 0) error("division by zero");
X			value %= value2;
X			continue;
X		case ' ':		/* space */
X			continue;
X		default:		/* end of product */
X			curcp--;
X			return(value);
X	}
X}
X
X
X/* Parse a single term */
Xparseterm()
X{
X	register long	value;		/* value to return */
X	register int	ch;		/* current character */
X
X	while (*curcp == ' ') curcp++;
X	ch = *curcp;
X	if ((ch >= '0') && (ch <= '9')) {	/* number */
X		value = 0;
X		do
X			value = (value * 10) + *curcp++ - '0';
X		while ((*curcp >= '0') && (*curcp <= '9'));
X		return(value);
X	}
X	if (ch == '(') {			/* parenthesized expression */
X		curcp++;
X		while (*curcp == ' ') curcp++;
X		if (*curcp == ')') error("Null expression");
X		value = parsesum();
X		while (*curcp == ' ') curcp++;
X		if (*curcp != ')') error("Unmatched parenthesis");
X		*curcp++;
X		return(value);
X	}
X	if (ch == ')') error("Unmatched parenthesis");
X	return(parsename());
X}
X
X
X/*
X * Parse a variable name and return its value.
X */
Xparsename()
X{
X	register char	*cp;		/* current character */
X	register long	value;		/* value of variable */
X	char	oldch;			/* old character after name */
X
X	cp = curcp;
X	if (*cp == '$') cp++;
X	while (((*cp >= 'a') && (*cp <= 'z')) ||
X		((*cp >= 'A') && (*cp <= 'Z')) ||
X		((*cp >= '0') && (*cp <= '9'))) cp++;
X	oldch = *cp;
X	*cp = '\0';
X	value = getvariable(curcp);
X	*cp = oldch;
X	curcp = cp;
X	return(value);
X}
//E*O*F vars.c//
echo x - view.c
sed -e 's/^X//' > "view.c" << '//E*O*F view.c//'
X#ifdef SCCS
Xstatic char *sccsid = "@(#)view.c	1.5	2/2/85";
Xstatic char *cpyrid = "@(#)Copyright (C) 1985 by D Bell";
X#endif
X
X#include "life.h"
X
X
X/*
X * Set the scaling factor for the specified object.  This also centers
X * the view around the current cursor location.
X */
Xsetscale(obj, sf)
X	register struct	object	*obj;	/* object to set scale of */
X	register int	sf;		/* scaling factor */
X{
X	if (sf <= 0) sf = 1;
X	if (sf > MAXSCALE) sf = MAXSCALE;
X	obj->o_scale = sf;
X	obj->o_minrow = obj->o_currow - (rowradius * sf) + (sf / 2);
X	obj->o_maxrow = obj->o_minrow + (2 * rowradius * sf);
X	obj->o_mincol = obj->o_curcol - (colradius * sf) + (sf / 2);
X	obj->o_maxcol = obj->o_mincol + (2 * colradius * sf);
X	if (obj == curobj) redraw = 1;		/* update if object visible */
X}
X
X
X/*
X * Perform auto-scaling of the current object.  This implies picking a
X * scaling factor such that the whole object fits in the screen.  The
X * scale factor is never decreased.  When the scale factor is large,
X * convenient ones are picked.  Returns the new scale factor.
X */
Xautoscale()
X{
X	register struct	object	*obj;		/* current object */
X	register int	sf;			/* scaling factor */
X	int	minrow, maxrow, mincol, maxcol;	/* limits of object */
X
X	obj = curobj;
X	minmax(obj, &minrow, &maxrow, &mincol, &maxcol);
X	sf = obj->o_scale;
X	if (mincol > maxcol) return(sf);
X	while ((sf <= MAXSCALE) &&
X		((minrow < obj->o_minrow) || (maxrow > obj->o_maxrow) ||
X		(mincol < obj->o_mincol) || (maxcol > obj->o_maxcol))) {
X			sf++;
X			if (sf > 20) sf += (5 - (sf % 5));
X			if (sf > 50) sf += (10 - (sf % 10));
X			if (sf > 200) sf += (100 - (sf % 100));
X			setscale(obj, sf);
X	}
X	return(obj->o_scale);
X}
X
X
X/*
X * Position the view of the current object to show both the given region and
X * the current cursor location.  If this is impossible, just the cursor
X * location will be positioned.
X */
Xpositionview(minrow, maxrow, mincol, maxcol)
X	register long	minrow, maxrow, mincol, maxcol;	/* region to show */
X{
X	register struct	object	*obj;	/* current object */
X	register int	sf;		/* current scale factor */
X
X	obj = curobj;
X	sf = obj->o_scale;
X	if (minrow > obj->o_currow) minrow = obj->o_currow;
X	if (maxrow < obj->o_currow) maxrow = obj->o_currow;
X	if (mincol > obj->o_curcol) mincol = obj->o_curcol;
X	if (maxcol < obj->o_curcol) maxcol = obj->o_curcol;
X	if ((maxrow - minrow) > (2 * sf * rowradius)) {	/* too many rows */
X		minrow = obj->o_currow;
X		maxrow = obj->o_currow;
X	}
X	if ((maxcol - mincol) > (2 * sf * colradius)) {	/* too many columns */
X		mincol = obj->o_curcol;
X		maxcol = obj->o_curcol;
X	}
X	if (minrow < obj->o_minrow) {
X		obj->o_minrow = minrow;
X		obj->o_maxrow = minrow + (rowradius * sf * 2) + sf - 1;
X		redraw = 1;
X	}
X	if (maxrow > obj->o_maxrow) {
X		obj->o_maxrow = maxrow;
X		obj->o_minrow = maxrow - (rowradius * sf * 2) + sf - 1;
X		redraw = 1;
X	}
X	if (mincol < obj->o_mincol) {
X		obj->o_mincol = mincol;
X		obj->o_maxcol = mincol + (colradius * sf * 2) + sf  - 1;
X		redraw = 1;
X	}
X	if (maxcol > obj->o_maxcol) {
X		obj->o_maxcol = maxcol;
X		obj->o_mincol = maxcol - (colradius * sf * 2) + sf - 1;
X		redraw = 1;
X	}
X}
X
X
X/*
X * Show the view around the current window location if the view has changed.
X * The update flag indicates that the status line and cursor need updating.
X * The redraw flag indicates that the view of the cells also needs updating.
X */
Xupdateview()
X{
X	register struct	object	*obj;		/* current object */
X
X	if ((interact | redraw | update) == 0) return;
X	obj = curobj;
X	positionview(obj->o_currow,obj->o_currow,obj->o_curcol,obj->o_curcol);
X	if (obj->o_autoscale) autoscale();
X	if (redraw) {			/* show visible cells */
X		freqcount = frequency;
X		dpywindow(1, -1, 0, -1);
X		if (obj->o_scale <= 1)
X			viewnormal();
X		else
X			viewscale(obj->o_scale);
X		dpyclearwindow();
X	}
X	if (redraw || update) {		/* show status and position cursor */
X		viewstatus();
X		dpywindow(1, -1, 0, -1);
X		dpymove((obj->o_currow - obj->o_minrow) / obj->o_scale,
X			(obj->o_curcol - obj->o_mincol) / obj->o_scale);
X		dpyupdate();
X	}
X	update = 0;			/* no more updates until prodded */
X	redraw = 0;
X	interact = 0;
X}
X
X
X
X/*
X * Update the status line for the object.
X */
Xviewstatus()
X{
X	register struct	object	*obj;	/* current object */
X
X	dpywindow(0, 0, 0, -1);		/* output in top line */
X	if (errorstring) {		/* show error string if present */
X		dpystr(errorstring);
X		dpyclearline();
X		return;
X	}
X	obj = curobj;
X	dpyprintf("Gen:%d cells:%d", obj->o_gen, obj->o_count);
X	if (obj->o_count > seecount)
X		dpyprintf("(%du)", obj->o_count - seecount);
X	if (obj->o_born) dpyprintf(" born:%d", obj->o_born);
X	if (obj->o_died) dpyprintf(" died:%d", obj->o_died);
X	if (frequency > 1) dpyprintf(" freq:%d", frequency);
X	if ((obj->o_scale > 1) || (obj->o_autoscale))
X		dpyprintf(" %scale:%d",
X			(obj->o_autoscale ? "autos" : "s") , obj->o_scale);
X	if (strcmp(rulestring, "3,23")) dpyprintf(" rules:%s", rulestring);
X	if (obj->o_lock) dpystr(" locked");
X	if (curinput > inputs) dpyprintf(" cmd-nest:%d", curinput - inputs);
X	switch (curinput->i_type) {
X		case INP_TTY:			/* reading from terminal */
X			if (curinput != inputs) dpystr(" tty-wait");
X			break;
X		case INP_FILE:			/* reading from file */
X			dpystr(" cmd-file");
X			break;
X		case INP_LOOP:			/* reading from loop */
X			if (curinput->i_macro) {
X				dpyprintf(" macro-define-%c",curinput->i_macro);
X				break;
X			}
X			dpyprintf(" loop%s (curval:%d end:%d)",
X				curinput->i_first ? "-define" : "",
X				curinput->i_curval, curinput->i_endval);
X			break;
X		case INP_MACRO:			/* reading from macro */
X			dpyprintf(" macro-%c", curinput->i_macro);
X			break;
X	}
X	if (mode == M_INSERT) dpystr(" inserting");
X	if (mode == M_DELETE) dpystr(" deleting");
X	if (curobj != mainobject) dpyprintf(" \"%s\"", curobj->o_name);
X	dpyclearwindow();
X}
X
X
X/* Show the cells around the cursor normally (scale factor of 1) */
Xviewnormal()
X{
X	register struct	row	*rp;		/* current row */
X	register struct	cell	*cp;		/* current cell */
X	register int	row;			/* current row number */
X	register int	col;			/* current column number */
X	register char	*str;			/* characters for line */
X	register char	*endstr;		/* end of characters for line */
X	register struct	object	*obj;		/* current object */
X
X	obj = curobj;
X	rp = obj->o_firstrow;
X	row = obj->o_minrow;
X	seecount = 0;
X	while (row > rp->r_row) rp = rp->r_next;
X	for (; row <= obj->o_maxrow; row++) {
X		if (row != rp->r_row) {		/* blank row */
X			if (gridchar == ' ') {
X				dpychar('\n');
X				continue;
X			}
X			str = stringbuf;
X			for (col = obj->o_mincol; col <= obj->o_maxcol; col++) {
X				*str++ = gridchar;
X			}
X			*str++ = '\n';
X			dpywrite(stringbuf, str - stringbuf);
X			continue;
X		}
X		str = stringbuf;
X		endstr = str;
X		cp = rp->r_firstcell;
X		col = obj->o_mincol;
X		while (col > cp->c_col) cp = cp->c_next;
X		for (; col <= obj->o_maxcol; col++) {
X			if (col != cp->c_col) {		/* blank cell */
X				*str++ = gridchar;
X				if (gridchar != ' ') endstr = str;
X				continue;
X			}
X			*str = '#';
X			if ((cp->c_marks & MARK_SEE) == 0) *str = 'O';
X			endstr = ++str;
X			seecount++;
X			cp = cp->c_next;
X		}
X		*endstr++ = '\n';
X		dpywrite(stringbuf, endstr - stringbuf);
X		rp = rp->r_next;
X	}
X}
X
X
X/*
X * Show the view around the cursor with an arbitrary scale factor.
X * When in this mode, characters from 1 to 9 (or * if 10 or more)
X * are used to indicate how many cells are in each n by n square.
X */
Xviewscale(sf)
X	register int	sf;		/* scale factor */
X{
X	register int	row;		/* current row number */
X	register int	col;		/* current column number */
X	register int	sum;		/* number of cells in square */
X	register struct	cell	*cp;	/* current cell structure */
X	register struct	object	*obj;	/* current object */
X	struct	cell	**cpp;		/* pointer into cell table */
X	struct	cell	**endcpp;	/* end of cell table */
X	struct	row	*rp;		/* row pointer */
X	char	*str;			/* buffer pointer */
X	char	*endstr;		/* end of buffer */
X	struct	cell	*cptab[MAXSCALE];	/* table of rows */
X
X	obj = curobj;
X	row = obj->o_minrow;
X	col = obj->o_mincol;
X	endcpp = &cptab[sf];
X	seecount = 0;
X	for (rp = curobj->o_firstrow; (rp->r_row < row); rp = rp->r_next) ;
X	while (row <= obj->o_maxrow) {
X		/*
X		 * If there is a large gap to the next row number then
X		 * the terminal line is empty.
X		 */
X		if (rp->r_row >= (row + sf)) {		/* no rows here */
X			if (gridchar == ' ') {
X				dpychar('\n');
X				row += sf;
X				continue;
X			}
X			str = stringbuf;
X			for (col=obj->o_mincol; col<=obj->o_maxcol; col+=sf) {
X				*str++ = gridchar;
X			}
X			*str++ = '\n';
X			dpywrite(stringbuf, str - stringbuf);
X			row += sf;
X			continue;
X		}
X		/*
X		 * Collect the rows to be searched for one terminal line.
X		 * Dummy up empty rows if necessary.
X		 */
X		for (cpp = cptab; cpp < endcpp; cpp++) {
X			*cpp = termcell;
X			if (rp->r_row > row++) continue;
X			*cpp = rp->r_firstcell;
X			rp = rp->r_next;
X		}
X		str = stringbuf;
X		endstr = str;
X		/*
X		 * Advance along each row to the next range of columns,
X		 * adding cells found to get the result for each square.
X		 */
X		for (col = obj->o_mincol; col <= obj->o_maxcol; col += sf) {
X			sum = 0;
X			for (cpp = cptab; cpp < endcpp; cpp++) {
X				cp = *cpp;
X				while (col > cp->c_col) cp = cp->c_next;
X				while ((col + sf) >= cp->c_col) {
X					sum++;
X					cp = cp->c_next;
X				}
X				*cpp = cp;
X			}
X			if (sum == 0) {		/* no cells in square */
X				*str++ = gridchar;
X				if (gridchar != ' ') endstr = str;
X				continue;
X			}
X			*str = '*';		/* show number of cells */
X			if (sum <= 9) *str = '0' + sum;
X			endstr = ++str;
X			seecount += sum;
X		}
X		*endstr++ = '\n';
X		dpywrite(stringbuf, endstr - stringbuf);
X	}
X}
//E*O*F view.c//
echo done



More information about the Comp.sources.unix mailing list