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 nsc.UUCP Path: utzoo!watmath!clyde!burl!ulysses!mhuxr!mhuxt!houxm!whuxl!whuxlm!akgua!gatech!nsc!chongo From: chongo@nsc.UUCP (Landon Noll) Newsgroups: net.sources.games Subject: Reporting for DBELL - LIFE (3 of 4) Message-ID: <2593@nsc.UUCP> Date: Fri, 12-Apr-85 05:24:09 EST Article-I.D.: nsc.2593 Posted: Fri Apr 12 05:24:09 1985 Date-Received: Sat, 13-Apr-85 06:42:15 EST References: <2591@nsc.UUCP> Distribution: net Organization: Rational Swamiconductor, Sanivale Lines: 1605 In net.sources: I am reposting DBell's DPY sources in responce to all the folks who needed them for his LIFE and WAR progs. DBell is no longer on the net (in fact in a few days he will no longer be in this hemisphere) otherwise he would have done this himself. In net.sources.games: I am reposting DBell's LIFE sources complete with the changes for the new DPY. Both LIFE and WAR (which was posted by DBell not long ago) require the use of DPY so be sure and GRAB IT FROM net.sources NOW!!! ---------------------begin DBell's comments------------------------------- 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 (2819 chars) # mark.c (9268 chars) # object.c (8845 chars) # scan.c (3526 chars) # vars.c (9419 chars) # view.c (9529 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((char *)0, (char *)0)) { /* 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 dpyclrwindow(); 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 dpyclrline(); 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 dpyclrwindow(); 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 -- no comment is a comment.