Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Posting-Version: version B 2.10.2 9/17/84 chuqui version 1.7 9/23/84; site daisy.UUCP Path: utzoo!watmath!clyde!cbosgd!ihnp4!alberta!ubc-vision!uw-beaver!tektronix!hplabs!nsc!daisy!dbell From: dbell@daisy.UUCP (David I. Bell) Newsgroups: net.sources Subject: LIFE - fancy new version (part 3 of 4) Message-ID: <38@daisy.UUCP> Date: Sat, 2-Feb-85 23:51:31 EST Article-I.D.: daisy.38 Posted: Sat Feb 2 23:51:31 1985 Date-Received: Wed, 6-Feb-85 04:27:56 EST Distribution: net Organization: Daisy Systems Corp., Mountain View, Ca Lines: 1590 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 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_colr_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 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, <buf); 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