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 (2 of 4) Message-ID: <2592@nsc.UUCP> Date: Fri, 12-Apr-85 05:19:11 EST Article-I.D.: nsc.2592 Posted: Fri Apr 12 05:19:11 1985 Date-Received: Sat, 13-Apr-85 06:40:10 EST References: <2591@nsc.UUCP> Distribution: net Organization: Rational Swamiconductor, Sanivale Lines: 2236 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!!! #---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: # alloc.c (2055 chars) # cell.c (3720 chars) # cmd.c (14613 chars) # cmdl.c (11895 chars) # debug.c (1295 chars) # gen.c (4492 chars) # io.c (16636 chars) # echo x - alloc.c sed -e 's/^X//' > "alloc.c" << '//E*O*F alloc.c//' X#ifdef SCCS Xstatic char *sccsid = "@(#)alloc.c 1.5 2/1/85"; Xstatic char *cpyrid = "@(#)Copyright (C) 1985 by D Bell"; X#endif X X#include "life.h" X X X/* X * Allocate a new object structure. It contains one row element, the termrow. X */ Xstruct object * Xallocobject() X{ X register struct object *obj; X X obj = freeobjects; X if (obj) X freeobjects = obj->o_next; X else { X obj = newobjects; X if (obj >= endobjects) { /* allocate new storage */ X obj = (struct object *) X sbrk(sizeof(struct object) * ALLOCOBJ); X if ((int)obj == -1) { X perror("sbrk"); X exit(1); X } X newobjects = obj; X endobjects = obj + ALLOCOBJ; X } X newobjects++; X } X obj->o_next = NULL; X obj->o_firstrow = termrow; X obj->o_lastrow = NULL; X obj->o_reserved = reserve; X obj->o_name[0] = '\0'; X obj->o_count = 0; X obj->o_gen = 0; X obj->o_lock = 0; X obj->o_currow = 0; X obj->o_curcol = 0; X obj->o_prow = 0; X obj->o_pcol = 0; X obj->o_mark = 0; X obj->o_autoscale = 0; X setscale(obj, 1); X return(obj); X} X X X/* X * Allocate a new row structure. It contains one row element, the termcell. X */ Xstruct row * Xallocrow() X{ X register struct row *rp; X X rp = freerows; X if (rp) X freerows = rp->r_next; X else { X rp = newrows; X if (rp >= endrows) { /* allocate new storage */ X rp = (struct row *) sbrk(sizeof(struct row) * ALLOCROW); X if ((int)rp == -1) { X perror("sbrk"); X exit(1); X } X newrows = rp; X endrows = rp + ALLOCROW; X } X newrows++; X } X rp->r_next = NULL; X rp->r_firstcell = termcell; X rp->r_lastcell = NULL; X rp->r_count = 0; X return(rp); X} X X X/* Allocate a new cell structure */ Xstruct cell * Xalloccell() X{ X register struct cell *cp; X X cp = freecells; X if (cp) { X freecells = cp->c_next; X cp->c_next = NULL; X cp->c_marks = MARK_ANY; X return(cp); X } X cp = newcells; X if (cp >= endcells) { /* allocate new storage */ X cp = (struct cell *) sbrk(sizeof(struct cell) * ALLOCCELL); X if ((int)cp == -1) { X perror("sbrk"); X exit(1); X } X newcells = cp; X endcells = cp + ALLOCCELL; X } X newcells++; X cp->c_next = NULL; X cp->c_marks = MARK_ANY; X return(cp); X} //E*O*F alloc.c// echo x - cell.c sed -e 's/^X//' > "cell.c" << '//E*O*F cell.c//' X#ifdef SCCS Xstatic char *sccsid = "@(#)cell.c 1.3 2/1/85"; Xstatic char *cpyrid = "@(#)Copyright (C) 1985 by D Bell"; X#endif X X#include "life.h" X X X/* X * Return a row structure for a given row number, returning NULL if not there. X */ Xstruct row * Xfindrow(obj, row) X struct object *obj; X register int row; X{ X register struct row *rp; X X for (rp = obj->o_firstrow; (row > rp->r_row); rp = rp->r_next) ; X if (rp->r_row != row) return(NULL); X return(rp); X} X X X/* X * Return a row structure for a given row number, creating it if needed. X */ Xstruct row * Xgetrow(obj, row) X register struct object *obj; X register int row; X{ X register struct row *rp; /* current row */ X register struct row *nrp; /* next row */ X register struct row *prp; /* previous row */ X X rp = obj->o_firstrow; X if (row < rp->r_row) { /* at front */ X nrp = allocrow(); X nrp->r_row = row; X nrp->r_next = obj->o_firstrow; X obj->o_firstrow = nrp; X if (nrp->r_next == termrow) obj->o_lastrow = nrp; X return(nrp); X } X if (row >= obj->o_lastrow->r_row) rp = obj->o_lastrow; X while (row > rp->r_row) { X prp = rp; X rp = rp->r_next; X } X if (row == rp->r_row) { X return(rp); X } X nrp = allocrow(); X nrp->r_row = row; X nrp->r_next = rp; X prp->r_next = nrp; X if (nrp->r_next == termrow) obj->o_lastrow = nrp; X return(nrp); X} X X X/* X * Find a cell given its coordinates, returning NULL if not found. X */ Xstruct cell * Xfindcell(obj, row, col) X register struct object *obj; X register int col; X{ X register struct row *rp; X register struct cell *cp; X X rp = findrow(obj, row); X if (rp == NULL) return(NULL); X for (cp = rp->r_firstcell; col > cp->c_col; cp = cp->c_next) ; X if (col != cp->c_col) return(NULL); X return(cp); X} X X X/* X * Create a cell at a given row and column. Returns nonzero if the cell X * already existed. If the cell is new, it is marked with the current X * mark value of the object. X */ Xaddcell(obj, row, col) X struct object *obj; X register int col; X{ X register struct row *rp; X register struct cell *cp; /* current cell */ X register struct cell *ncp; /* next cell */ X register struct cell *pcp; /* previous cell */ X X rp = getrow(obj, row); X cp = rp->r_firstcell; X if ((cp != termcell) && (col >= rp->r_lastcell->c_col)) { X pcp = rp->r_lastcell; /* at end */ X if (col == pcp->c_col) return(1); X ncp = alloccell(); X ncp->c_col = col; X ncp->c_marks |= obj->o_mark; X ncp->c_next = termcell; X pcp->c_next = ncp; X rp->r_lastcell = ncp; X rp->r_count++; X obj->o_count++; X return(0); X } X if (col < cp->c_col) { X ncp = alloccell(); /* at front */ X ncp->c_col = col; X ncp->c_marks |= obj->o_mark; X ncp->c_next = cp; X rp->r_firstcell = ncp; X if (cp == termcell) rp->r_lastcell = ncp; X rp->r_count++; X obj->o_count++; X return(0); X } X while (col > cp->c_col) { /* in middle */ X pcp = cp; X cp = pcp->c_next; X } X if (col == cp->c_col) { X return(1); X } X ncp = alloccell(); X ncp->c_col = col; X ncp->c_marks |= obj->o_mark; X ncp->c_next = cp; X pcp->c_next = ncp; X if (cp == termcell) rp->r_lastcell = ncp; X rp->r_count++; X obj->o_count++; X return(0); X} X X X/* X * Delete a cell at a given coordinate. Returns nonzero if it did not exist. X */ Xdelcell(obj, row, col) X register struct object *obj; X register int col; X{ X register struct row *rp; X register struct cell *pcp; /* previous cell */ X register struct cell *cp; /* current cell */ X X rp = findrow(obj, row); X if (rp == NULL) return(1); X pcp = NULL; X cp = rp->r_firstcell; X while (col > cp->c_col) { X pcp = cp; X cp = cp->c_next; X } X if (col != cp->c_col) return(1); X if (pcp) X pcp->c_next = cp->c_next; X else X rp->r_firstcell = cp->c_next; X if (cp->c_next == termcell) rp->r_lastcell = pcp; X cp->c_next = freecells; X freecells = cp; X rp->r_count--; X obj->o_count--; X return(0); X} //E*O*F cell.c// echo x - cmd.c sed -e 's/^X//' > "cmd.c" << '//E*O*F cmd.c//' X#ifdef SCCS Xstatic char *sccsid = "@(#)cmd.c 1.21 2/2/85"; Xstatic char *cpyrid = "@(#)Copyright (C) 1985 by D Bell"; X#endif X X#include "life.h" X X X/* X * Read commands if available, and execute them. Since we call scanchar for X * our characters, the code after the setjmp can be reentered many times in X * order to finish any command. This allows commands to be typed without X * stopping the computation of generations of an object, and allows editing X * of partially completed commands. X */ Xdocommand() X{ X register int defarg; /* first argument defaulted to one */ X register int ch; /* character read */ X register struct object *obj; /* object being manipulated */ X int arg1; /* first command argument */ X int arg2; /* second command argument */ X int got1; /* got first argument flag */ X int got2; /* got second argument flag */ X char *saveloopptr; /* crock for loop definitions */ X X switch (setjmp(ttyjmp)) { X case SCAN_EDIT: /* command edited before completion */ X curinput->i_endptr = saveloopptr; X break; X case SCAN_EOF: /* not yet enough chars for a command */ X curinput->i_endptr = saveloopptr; X return; X case SCAN_ABORT: /* normal command completion */ X default: /* normal entry point */ X saveloopptr = curinput->i_endptr; X break; X } X if (stop) error("Command aborted"); X obj = curobj; X cmark = 0; X arg2 = 0; X got2 = 0; X ch = readvalue(&arg1, &got1); X if (ch == ',') ch = readvalue(&arg2, &got2); X defarg = 1; X if (got1) defarg = arg1; X switch (ch) { X#ifdef DEBUG X case '\005': /* ^E - show debugging info */ X dumpdata(); X break; X#endif DEBUG X case '\004': /* ^D - end terminal input */ X curinput->i_term(curinput); X break; X case '\014': /* refresh screen */ X dpyredraw(); X redraw = 1; X break; X case '\n': /* move to next "line" */ X crow += defarg; X ccol = pcol; X update = 1; X break; X case '\t': /* move to next tab stop */ X while ((++ccol - pcol) % 8) ; X update = 1; X break; X case ESC: /* execute a macro command */ X ch = scanchar(); X if (ch == ESC) break; /* ignore double escape */ X backup(); X if (setmacro(arg1, arg2, ch)) error("Undefined macro"); X update = 1; X break; X case ' ': /* move to right */ X case '.': X ccol += defarg; X update = 1; X break; X case ':': /* execute line style command */ X case ';': X dolinecommand(arg1, arg2, got1, got2); X break; X case '<': /* begin loop or macro definition */ X if (got1 || got2) { X if (got2) setloop(defarg, arg2, NULL); X else setloop(1, defarg, NULL); X update = 1; X break; X } X ch = scanchar(); /* defining macro */ X if ((ch < 'a') || (ch > 'z')) { X error("Bad macro character"); X } X setloop(1, 1, ch); X update = 1; X break; X case '>': /* end loop */ X endloop(); X break; X case '+': /* increment single char variable */ X ch = scanchar(); X if (ch == '$') ch = scanchar(); X setvariable1(ch, getvariable1(ch) + defarg); X break; X case 'b': /* move lower left with action */ X domove(defarg, -defarg); X break; X case 'B': /* shift to lower left */ X doshift(defarg, -defarg); X break; X case 'c': /* pick cell as current location */ X crow = prow + arg1; X ccol = pcol + arg2; X update = 1; X break; X case 'd': /* delete selection */ X doselect(ch); X backup(); X movemarkedobject(obj, deleteobject, MARK_CMD); X redraw = 1; X break; X case 'f': /* flip selection */ X doselect(ch); X cmark = MARK_USR; X flipmarkedobject(obj, MARK_CMD); X redraw = 1; X break; X case 'g': /* compute generations */ X if (obj->o_lock) error("Object locked"); X if (genleft <= 0) backup(); X genleft = (genleft ? arg1 : defarg); X freqcount = frequency; X update = 1; X break; X case 'G': /* compute infinite generations */ X if (obj->o_lock) error("Object locked"); X if (genleft <= 0) backup(); X genleft = INFINITY; X freqcount = frequency; X update = 1; X break; X case 'h': /* move left with action */ X domove(0, -defarg); X break; X case 'H': /* shift left lots */ X doshift(0, -defarg); X break; X case 'i': /* toggle insert mode */ X checkrun(); X if (mode == M_MOVE) mode = M_INSERT; X else if (mode == M_INSERT) mode = M_DELETE; X else mode = M_MOVE; X update = 1; X break; X case 'j': /* move down with action */ X domove(defarg, 0); X break; X case 'J': /* shift down lots */ X doshift(defarg, 0); X break; X case 'k': /* move up with action */ X domove(-defarg, 0); X break; X case 'K': /* shift up lots */ X doshift(-defarg, 0); X break; X case 'l': /* move right with action */ X domove(0, defarg); X break; X case 'L': /* shift right lots */ X doshift(0, defarg); X break; X case 'm': /* mark current object */ X doselect(ch); X copymarks(obj, MARK_CMD, MARK_USR); X redraw = 1; X break; X case 'M': /* remove all marks */ X checkrun(); X clearmarks(obj, MARK_SEE); X redraw = 1; X break; X case 'n': /* move lower right with action */ X domove(defarg, defarg); X break; X case 'N': /* shift down and right */ X doshift(defarg, defarg); X break; X case 'o': /* insert new cells */ X case 'O': X checkrun(); X backup(); X while ((stop == 0) && (defarg-- > 0)) { X addcell(obj, crow, ccol++); X } X redraw = 1; X break; X case 'p': /* place deleted object */ X checkrun(); X backup(); X cmark = MARK_USR; X addobject(deleteobject, obj, RELATIVE); X redraw = 1; X break; X case 'r': /* rotate selection */ X doselect(ch); X cmark = MARK_USR; X rotatemarkedobject(obj, MARK_CMD); X redraw = 1; X break; X case 's': /* set scale factor and center object */ X obj->o_autoscale = 0; X if (got1 == 0) arg1 = obj->o_scale; X setscale(obj, arg1); X break; X case 'S': /* perform auto-scaling */ X if (got1 == 0) arg1 = obj->o_scale; X setscale(obj, arg1); X obj->o_autoscale = 1; X redraw = 1; X break; X case 't': /* toggle current cell */ X checkrun(); X backup(); X if (delcell(obj, crow, ccol)) X addcell(obj, crow, ccol); X redraw = 1; X break; X case 'u': /* move upper right with action */ X domove(-defarg, defarg); X break; X case 'U': /* shift to upper right */ X doshift(-defarg, defarg); X break; X case 'x': /* kill current cells */ X checkrun(); X backup(); X while ((stop == 0) && (defarg-- > 0)) { X delcell(obj, crow, ccol++); X } X redraw = 1; X break; X case 'y': /* move upper left with action */ X domove(-defarg, -defarg); X break; X case 'Y': /* shift to upper left */ X doshift(-defarg, -defarg); X break; X case 'z': /* clear or set generation number */ X checkrun(); X obj->o_gen = arg1; X obj->o_born = 0; X obj->o_died = 0; X update = 1; X break; X case '/': /* search for next object */ X { X int minr, maxr, minc, maxc; X X checkrun(); X if (searchobject(obj, defarg, 0)) error("Empty object"); X clearmarks(obj, MARK_CMD); X markobject(obj, crow, ccol, MARK_CMD); X markminmax(obj, MARK_CMD, &minr, &maxr, &minc, &maxc); X positionview(minr, maxr, minc, maxc); X update = 1; X } X break; X case '@': /* point at current location */ X prow = crow; X pcol = ccol; X break; X case '!': /* comment characters */ X case '#': X while (scanchar() != '\n') ; X break; X default: /* unknown commands */ X error("Unknown command"); X } X scanabort(); /* completed command */ X} X X X/* X * Read a numeric value (if any) to be used as an argument for a command. X * Pointers to the returned value and returned flag are given. X * The returned value is zero if no value is read. X * The returned flag is nonzero if a value was read. X * Return value is the first non-argument character read. X */ Xreadvalue(valueptr, flagptr) X register int *valueptr; /* pointer to returned value */ X register int *flagptr; /* pointer to got value flag */ X{ X register int ch; /* character being read */ X register struct input *ip; /* input structure */ X int sign; /* sign of result */ X X *valueptr = 0; X *flagptr = 0; X sign = 1; X ch = scanchar(); X if (ch == '-') { /* negative value */ X sign = -1; X ch = scanchar(); X } X if (ch == '$') { /* get variable value */ X *valueptr = sign * getvariable1(scanchar()); X *flagptr = 1; X return(scanchar()); X } X if (ch == '(') { /* get expression */ X *valueptr = sign * scanexpr(); X *flagptr = 1; X return(scanchar()); X } X while ((ch >= '0') && (ch <= '9')) { /* get numeric value */ X *valueptr = (*valueptr * 10) + ch - '0'; X *flagptr = 1; X ch = scanchar(); X } X if (ch == '%') { /* get loop value */ X ch = 1; X if (*flagptr) ch = *valueptr; X if (ch <= 0) error("Bad nest value"); X ip = curinput + 1; X while (ch > 0) { X if (--ip < inputs) error("Bad nest value"); X if (ip->i_type == INP_LOOP) ch--; X } X *valueptr = ip->i_curval; X *flagptr = 1; X ch = scanchar(); X } X *valueptr *= sign; X return(ch); X} X X X/* X * Routine called from above to scan and evaluate a parenthesized expression. X * This routines knows that one parenthesis has already been read. Stops X * reading on the matching parenthesis. X */ Xscanexpr() X{ X register char *cp; /* current character */ X register int nest; /* nesting depth */ X char buf[100]; /* expression buffer */ X X cp = buf; X *cp++ = '('; /* start with parenthesis */ X nest = 1; X while (nest > 0) { X if (cp >= &buf[sizeof(buf)-2]) error("expression too long"); X *cp = scanchar(); X if (*cp == '(') nest++; X if (*cp == ')') nest--; X cp++; X } X *cp = '\0'; X return(getexpression(buf)); X} X X X/* X * Select a set of cells to be used for some command. This involves reading X * the next character and marking cells based on that character. Repeating X * the command character is equivilant to selecting the current object. On a X * successful return, exactly those cells specified are marked with MARK_CMD. X * If no cells are found, an error is generated. X */ Xdoselect(cmd) X{ X register struct object *obj; /* object to examine */ X register long minrow, maxrow, mincol, maxcol; /* range for marks */ X register struct cell *cp; /* current cell */ X int ch; /* character to select on */ X X checkrun(); X ch = scanchar(); X if (ch == cmd) ch = 'o'; /* repeated char is connected object */ X minrow = -INFINITY; X maxrow = -minrow; X mincol = minrow; X maxcol = maxrow; X obj = curobj; X clearmarks(obj, MARK_CMD); X switch (ch) { X case 'a': /* all of object */ X break; X case 'b': /* below and left of cursor */ X minrow = crow; X maxcol = ccol; X break; X case 'c': /* current cell */ X cp = findcell(obj, crow, ccol); X if (cp == NULL) error("No cell at current location"); X cp->c_marks |= MARK_CMD; X return; X case 'h': /* left of cursor */ X maxcol = ccol; X break; X case 'j': /* below cursor */ X minrow = crow; X break; X case 'k': /* above cursor */ X maxrow = crow; X break; X case 'l': /* right of cursor */ X mincol = ccol; X break; X case 'm': /* marked cells */ X if (copymarks(obj, MARK_USR, MARK_CMD)) X error("No object marked"); X return; X case 'n': /* below and right of cursor */ X minrow = crow; X mincol = ccol; X break; X case 'o': /* connected object */ X if (markobject(obj, crow, ccol, MARK_CMD)) X error("No object at current location"); X return; X case 'p': /* rectangle to pointer */ X minrow = crow; X maxrow = prow; X if (minrow > maxrow) { X minrow = prow; X maxrow = crow; X } X mincol = ccol; X maxcol = pcol; X if (mincol > maxcol) { X mincol = pcol; X maxcol = ccol; X } X break; X case 'u': /* above and right of cursor */ X maxrow = crow; X mincol = ccol; X break; X case 'v': /* things visible in window */ X minrow = obj->o_minrow; X maxrow = obj->o_maxrow; X mincol = obj->o_mincol; X maxcol = obj->o_maxcol; X break; X case 'y': /* above and left of cursor */ X maxrow = crow; X maxcol = ccol; X break; X default: /* unknown */ X error("Unknown selection command"); X } X if (markregion(obj, MARK_CMD, minrow, maxrow, mincol, maxcol) == 0) X error("No cells in region"); X} X X X/* X * Move the current position by the indicated deltas, performing the X * current action to the configuration. The movement is scaled by X * the current scaling factor. X */ Xdomove(rowdelta, coldelta) X register int rowdelta; /* amount to change row by */ X register int coldelta; /* amount to change column by */ X{ X register int row1; /* increment for row */ X register int col1; /* increment for column */ X X rowdelta *= curobj->o_scale; X coldelta *= curobj->o_scale; X if (mode == M_MOVE) { /* just want to move */ X crow += rowdelta; X ccol += coldelta; X update = 1; X return; X } X checkrun(); X backup(); X row1 = 0; /* need to loop for insert or delete */ X col1 = 0; X if (rowdelta > 0) row1 = 1; X if (rowdelta < 0) row1 = -1; X if (coldelta > 0) col1 = 1; X if (coldelta < 0) col1 = -1; X rowdelta += crow; X coldelta += ccol; X while ((stop == 0) && ((crow != rowdelta) || (ccol != coldelta))) { X crow += row1; X ccol += col1; X switch (mode) { X case M_INSERT: X addcell(curobj, crow, ccol); X break; X case M_DELETE: X delcell(curobj, crow, ccol); X } X } X redraw = 1; X} X X X/* X * Shift the window lots in the indicated direction, and also shift the X * cursor location the same amount so that the cursor location on the X * screen doesn't change. "Lots" is 1/4 of the screen width or height. X * Special case: if both x and y are being shifted, we shift both by the X * same amount, which is the minimum of the two shifts. X */ Xdoshift(rowdelta, coldelta) X register int rowdelta; /* amount to change row by */ X register int coldelta; /* amount to change column by */ X{ X register struct object *obj; /* current object */ X register int rowsign; /* sign of row */ X register int colsign; /* sign of column */ X X obj = curobj; X rowdelta *= ((rowradius * obj->o_scale) / 2); X coldelta *= ((colradius * obj->o_scale) / 2); X if (rowdelta && coldelta) { /* take minimums of absolute values */ X rowsign = 1; X colsign = 1; X if (rowdelta < 0) { X rowsign = -1; X rowdelta = -rowdelta; X } X if (coldelta < 0) { X colsign = -1; X coldelta = -coldelta; X } X if (rowdelta > coldelta) rowdelta = coldelta; X if (coldelta > rowdelta) coldelta = rowdelta; X rowdelta *= rowsign; X coldelta *= colsign; X } X obj->o_currow += rowdelta; X obj->o_minrow += rowdelta; X obj->o_maxrow += rowdelta; X obj->o_curcol += coldelta; X obj->o_mincol += coldelta; X obj->o_maxcol += coldelta; X redraw = 1; X} X X X/* X * Perform a backup of the current object. This is only done if reading X * from the terminal, since it is from the point of view of the user. X */ Xbackup() X{ X if (curinput->i_type == INP_TTY) copyobject(curobj, backupobject); X} X X X/* X * Check to see that generations are not being computed before proceeding X * with the current command. X */ Xcheckrun() X{ X if (genleft > 0) error("Illegal while running"); X} //E*O*F cmd.c// echo x - cmdl.c sed -e 's/^X//' > "cmdl.c" << '//E*O*F cmdl.c//' X#ifdef SCCS Xstatic char *sccsid = "@(#)cmdl.c 1.31 2/2/85"; Xstatic char *cpyrid = "@(#)Copyright (C) 1985 by D Bell"; X#endif X X#include "life.h" X#include X X/* list of line command routines */ Xint c_create(), c_destroy(), c_edit(), c_dumpmacros(), c_quit(); Xint c_frequency(), c_listobjects(), c_zero(), c_lock(), c_unlock(); Xint c_read(), c_write(), c_ttyinput(), c_gridchar(), c_nogrid(); Xint c_insert(), c_copy(), c_copyselect(), c_move(), c_moveselect(); Xint c_help(), c_rules(), c_endinputlevel(), c_undo(), c_rename(); Xint c_set(), c_variables(), c_type(), c_update(), c_wait(); X X/* flags for line commands */ X#define F_NONE 0x0 /* no special condition */ X#define F_NOARG 0x1 /* must not have an argument */ X#define F_ARG1 0x2 /* must have at least one argument */ X#define F_NORUN 0x4 /* illegal if running generations */ X#define F_UPDATE 0x8 /* update status if command completes */ X#define F_REDRAW 0x10 /* redraw screen if command completes */ X#define F_ABBR 0x20 /* abbreviation works even if ambiguous */ X X X/* X * Dispatch table for line commands. Those commands which are ambiguous X * must be defined so as to be contiguous in the table. A spaces delimits X * the command itself from the help string for the command. X */ Xstruct cmdtab { X char *c_name; /* command name */ X int (*c_func)(); /* function to call */ X int c_flags; /* flags for command */ X} cmdtab[] = { X "copy (current object to) obj", c_copy, F_ARG1|F_NORUN|F_ABBR, X "copyselection (to) obj", c_copyselect, F_ARG1|F_NORUN, X "create (object named) obj", c_create, F_ARG1|F_NORUN|F_REDRAW, X "destroy (object named) obj", c_destroy, F_ARG1|F_NORUN, X "dumpmacros (to) file", c_dumpmacros, F_ARG1|F_NORUN, X "edit (object named) obj", c_edit, F_NORUN|F_REDRAW|F_ABBR, X "endinputlevel", c_endinputlevel, F_NOARG|F_UPDATE, X "frequency (of typeout is) expr", c_frequency, F_UPDATE, X "gridcharacter (is) char", c_gridchar, F_REDRAW, X "help", c_help, F_NONE|F_ABBR, X "insert (object from) obj", c_insert, F_ARG1|F_NORUN|F_REDRAW|F_ABBR, X "list (all objects)", c_listobjects, F_NORUN|F_ABBR, X "lock (current object)", c_lock, F_NOARG|F_NORUN|F_UPDATE, X "move (current object to) obj", c_move, F_ARG1|F_NORUN|F_REDRAW|F_ABBR, X "moveselection (to) obj",c_moveselect,F_ARG1|F_NORUN|F_REDRAW, X "nogrid", c_nogrid, F_NOARG|F_REDRAW, X "quit (program)", c_quit, F_NOARG|F_ABBR, X "read (commands from) file", c_read, F_ARG1|F_NORUN|F_REDRAW|F_ABBR, X "rename (current object to) obj", c_rename, F_ARG1|F_UPDATE, X "rules (for life are) born live", c_rules, F_NORUN|F_UPDATE, X "set (variable) name (to) expr", c_set, F_ARG1|F_ABBR, X "ttyinput", c_ttyinput, F_REDRAW, X "type (value of expression) expr", c_type, F_ARG1|F_UPDATE|F_ABBR, X "undo (last change)", c_undo, F_NOARG|F_NORUN|F_REDRAW|F_ABBR, X "unlock (current object)", c_unlock, F_NOARG|F_NORUN|F_UPDATE, X "updateview (to be current)", c_update, F_NOARG, X "variables (are listed)", c_variables, F_NOARG|F_REDRAW|F_ABBR, X "wait (for computations)", c_wait, F_NOARG, X "write (current object to) file", c_write, F_ARG1|F_NORUN|F_ABBR, X "zero (current object)", c_zero, F_NOARG|F_NORUN|F_REDRAW|F_ABBR, X 0 /* ends the table */ X}; X X X/* X * Read and execute a command line style command. This kind of command echoes, X * and is terminated by an end of line. Numeric arguments are available. X */ Xdolinecommand(arg1, arg2, got1, got2) X{ X register char *name; /* command name */ X register char *args; /* arguments for command */ X register struct cmdtab *cmd; /* command structure */ X register int flag; /* flags for command */ X X name = readstring("command: "); X if ((*name == '\0') || (*name == '\n')) return; X for (args = name; *args && (*args != ' ') && (*args != '\t'); args++) ; X while ((*args == ' ') || (*args == '\t')) *args++ = '\0'; X for (cmd = cmdtab; ; cmd++) { X if (cmd->c_name == NULL) error("Unknown line command"); X if (abbrev(name, cmd[0].c_name) == 0) continue; X if (cmd->c_flags & F_ABBR) break; X if (abbrev(name, cmd[1].c_name) == 0) break; X if (strcmp(name, cmd[0].c_name) == 0) break; X if (cmd[1].c_flags & F_ABBR) continue; X error("Ambiguous line command"); X } X flag = cmd->c_flags; X if (flag & F_NORUN) checkrun(); X if ((flag & F_ARG1) && (*args == '\0')) error("Missing argument"); X if ((flag & F_NOARG) && *args) error("Argument not allowed"); X cmd->c_func(args, arg1, arg2, got1, got2); X if (flag & F_UPDATE) update = 1; X if (flag & F_REDRAW) redraw = 1; X} X X X X/* Copy the current object into another object */ Xc_copy(cp) X char *cp; /* destination object name */ X{ X copyobject(curobj, getobject(cp)); X} X X X/* Copy the current selection into another object */ Xc_copyselect(cp) X char *cp; /* destination object name */ X{ X copymarkedobject(curobj, getobject(cp), MARK_USR); X} X X X/* Edit an object, creating it if necessary */ Xc_create(cp) X char *cp; /* object to create */ X{ X setobject(getobject(cp)); X} X X X/* Destroy an existing object */ Xc_destroy(cp) X char *cp; /* object name */ X{ X register struct object *obj; /* object to destroy */ X X obj = findobject(cp); X if (obj == NULL) error("No such object"); X destroyobject(obj); X} X X X/* Dump list of macros to file */ Xc_dumpmacros(cp) X char *cp; /* file name */ X{ X writemacros(cp); X} X X X/* Edit an existing object. A null argument implies the previous object. */ Xc_edit(cp) X char *cp; /* object name */ X{ X register struct object *obj; /* object to edit */ X X obj = prevobj; X if (*cp) obj = findobject(cp); X if (obj == NULL) error("No such object"); X setobject(obj); X} X X X/* Undo the last change made to the current object */ Xc_undo() X{ X moveobject(curobj, tempobject); X moveobject(backupobject, curobj); X moveobject(tempobject, backupobject); X} X X X/* End current input level */ Xc_endinputlevel() X{ X if (curinput <= inputs) error("Cannot end top level input"); X curinput->i_term(curinput); X} X X X/* Update the view even if inside of a loop or macro */ Xc_update() X{ X update = 1; X updateview(); X} X X X/* Wait until all outstanding generation computations are finished */ Xc_wait() X{ X while ((stop == 0) && (genleft > 0)) { X dogeneration(curobj); X updateview(); X } X} X X X/* Set output frequency */ Xc_frequency(cp) X register char *cp; /* frequency string */ X{ X register int freq; /* new frequency */ X X freq = 1; X if (*cp) freq = getexpression(cp); X if (freq <= 0) error("Illegal value"); X frequency = freq; X freqcount = freq; X} X X X/* Select the character used for the background of the screen */ Xc_gridchar(cp) X register char *cp; /* grid character string */ X{ X if (*cp == '\0') cp = "."; X if ((*cp < ' ') || (cp[1] != '\0')) error("Bad grid character"); X gridchar = *cp; X} X X X/* Set so we don`t see a grid on the screen */ Xc_nogrid() X{ X gridchar = ' '; X} X X X/* Type list of line commands */ Xc_help() X{ X register struct cmdtab *cmd; /* command structure */ X register int count; X X dpywindow(0, -1, 0, -1); X dpystr("\ XThe following table lists all line mode commands. Unique abbreviations are\n\ Xallowed. Commands shown with '*' can be abbreviated even when ambiguous.\n"); X count = 0; X for (cmd = cmdtab; cmd->c_name; cmd++) { X if ((count++ % 2) == 0) dpychar('\n'); X dpyprintf("%c %-35s", ((cmd->c_flags & F_ABBR) ? '*' : ' '), X cmd->c_name); X } X dpychar('\n'); X spacewait(); X} X X X/* Insert another object into this one */ Xc_insert(cp) X char *cp; /* object name */ X{ X register struct object *obj; /* object to insert */ X X obj = findobject(cp); X if (obj == NULL) error("No such object"); X cmark = MARK_USR; X backup(); X addobject(obj, curobj, RELATIVE); X cmark = MARK_ANY; X} X X X/* Show list of objects */ Xc_listobjects(cp) X register char *cp; /* option string */ X{ X int all; /* true if want to see all objects */ X X all = ((*cp == '-') || (*cp == 'a')); X listobjects(all); X} X X X/* Lock object so it can't be run */ Xc_lock() X{ X curobj->o_lock = 1; X genleft = 0; X freqcount = frequency; X} X X X/* Unlock object so it can be run */ Xc_unlock() X{ X curobj->o_lock = 0; X} X X X/* Rename the current object to something else */ Xc_rename(cp) X register char *cp; /* new name */ X{ X if (curobj->o_reserved) error("Cannot rename reserved object"); X if (strlen(cp) > MAXNAME) error("Name too long"); X if (findobject(cp)) error("Name already exists"); X if (BADNAME(cp)) error("Cannot create reserved name"); X strcpy(curobj->o_name, cp); X} X X X/* Move current object into another object */ Xc_move(cp) X char *cp; /* destination object name */ X{ X moveobject(curobj, getobject(cp)); X} X X X/* Move current selection into another object */ Xc_moveselect(cp) X char *cp; /* destination object name */ X{ X movemarkedobject(curobj, getobject(cp), MARK_USR); X} X X X/* Set the value of a variable */ Xc_set(cp) X register char *cp; /* variable name */ X{ X register char *exp; /* expression */ X X for (exp = cp; *exp && (*exp != ' ') && (*exp != '='); exp++) ; X if (*exp == ' ') { /* skip spaces */ X *exp++ = '\0'; X while (*exp == ' ') exp++; X } X if (*exp == '\0') { /* no expression, set to zero */ X setvariable(cp, 0); X return; X } X if (*exp == '=') *exp++ = '\0'; X setvariable(cp, getexpression(exp)); X} X X X/* Type the value of an expression */ Xc_type(cp) X char *cp; /* expression */ X{ X static char buf[20]; /* storage for string */ X X sprintf(buf, "%d\n", getexpression(cp));/* store value */ X errorstring = buf; /* make it seen */ X} X X X/* Display the values of all the variables */ Xc_variables() X{ X listvariables(); X} X X X/* Quit program */ Xc_quit() X{ X dpyclose(); X exit(0); X} X X X/* Read commands or object from file, defaulting extension if needed */ Xc_read(cp) X register char *cp; /* file name */ X{ X backup(); X if (setfile(cp)) error("Cannot open input file"); X} X X X/* Set new life rules */ Xc_rules(cp) X register char *cp; /* life rules string */ X{ X register char *bp; /* born string */ X register char *lp; /* live string */ X X if (*cp == '\0') cp = "3,23"; X bp = cp; X while ((*cp >= '0') && (*cp <= '8')) cp++; X if (*cp == '\0') error("Missing rule string"); X if ((*cp != ',') && (*cp != ' ') && (*cp != '\t')) { X error("Bad born string"); X } X *cp++ = '\0'; X while ((*cp == ',') || (*cp == ' ') || (*cp == '\t')) cp++; X lp = cp; X while ((*cp >= '0') && (*cp <= '8')) cp++; X if (*cp != '\0') error("Bad live string"); X for (cp = rules; cp < &rules[18]; cp++) *cp = 0; X while (*bp) rules[*bp++ - '0'] = 1; X while (*lp) rules[*lp++ - '0' + LIFE] = 1; X bp = rulestring; X for (cp = rules; cp < &rules[LIFE]; cp++) { X if (*cp) *bp++ = '0' + (cp - rules); X } X *bp++ = ','; X for (cp = &rules[LIFE]; cp < &rules[18]; cp++) { X if (*cp) *bp++ = ('0' - LIFE) + (cp - rules); X } X *bp = '\0'; X} X X X/* X * Read commands from the terminal. Useful in loops or command files. X * If the -c argument is given, we don't do it if no input is ready. X */ Xc_ttyinput(cp) X register char *cp; /* pointer to argument string */ X{ X int count; /* character count */ X X if ((*cp == '-') || (*cp == 'c')) { /* check for input */ X count = 0; X ioctl(STDIN, FIONREAD, &count); X if (count == 0) return; X } X if (settty()) error("Nesting too deep"); X} X X X/* Write object to file */ Xc_write(cp, arg1, arg2, got1, got2) X register char *cp; /* pointer to argument string */ X{ X if (got1 == 0) arg1 = WRITECOLS; X writeobject(curobj, cp, arg1?WRITEROWS:0, arg1); X} X X X/* Zero out current object */ Xc_zero() X{ X register struct object *obj; /* current object */ X X obj = curobj; X if (obj->o_lock) error("Object is locked"); X backup(); X zeroobject(obj); X obj->o_gen = 0; X obj->o_born = 0; X obj->o_died = 0; X obj->o_currow = 0; X obj->o_curcol = 0; X obj->o_prow = 0; X obj->o_pcol = 0; X obj->o_autoscale = 0; X setscale(obj, 1); X mode = M_MOVE; X frequency = 1; X freqcount = 1; X} X X X/* X * See if one string is an abbreviation of another. This knows that X * the second string contains spaces which terminate the comparison. X * Returns nonzero if first string is an abbreviation. X */ Xabbrev(str1, str2) X register char *str1, *str2; /* strings */ X{ X if ((str1 == NULL) || (str2 == NULL)) return(0); X while (*str1) if (*str1++ != *str2++) return(0); X return(1); X} //E*O*F cmdl.c// echo x - debug.c sed -e 's/^X//' > "debug.c" << '//E*O*F debug.c//' X#ifdef SCCS Xstatic char *sccsid = "@(#)debug.c 1.2 1/14/85"; Xstatic char *cpyrid = "@(#)Copyright (C) 1985 by D Bell"; X#endif X X#ifdef DEBUG X#include "life.h" X X/* X * Subroutine to dump out all object data structures for debugging. X */ Xdumpdata() X{ X register struct object *obj; X X printf("\ntermrow %x termcell %x\n", termrow, termcell); X for (obj = objects; obj; obj = obj->o_next) { X dumpobj(obj); X } X printf("done\n"); X} X X X/* X * Dump out an object. X */ Xdumpobj(obj) X register struct object *obj; X{ X register struct row *rp; X X printf("object %s (%x) firstrow %x lastrow %x\n", X obj->o_name, obj, obj->o_firstrow, obj->o_lastrow); X rp = obj->o_firstrow; X while (1) { X dumprow(rp); X if (rp == termrow) break; X rp = rp->r_next; X } X} X X X/* X * Dump out a row X */ Xdumprow(rp) X register struct row *rp; X{ X register struct cell *cp; X X if (rp == termrow) { X printf(" termrow\n"); X return; X } X printf(" row %d (%x) firstcell %x lastcell %x\n", X rp->r_row, rp, rp->r_firstcell, rp->r_lastcell); X cp = rp->r_firstcell; X while (1) { X dumpcell(cp); X if (cp == termcell) break; X cp = cp->c_next; X } X} X X X/* X * Dump out a cell X */ Xdumpcell(cp) X register struct cell *cp; X{ X if (cp == termcell) { X printf(" termcell\n"); X return; X } X printf(" cell %d (%x)\n", cp->c_col, cp); X} X X#endif DEBUG //E*O*F debug.c// echo x - gen.c sed -e 's/^X//' > "gen.c" << '//E*O*F gen.c//' X#ifdef SCCS Xstatic char *sccsid = "@(#)gen.c 1.4 2/2/85"; Xstatic char *cpyrid = "@(#)Copyright (C) 1985 by D Bell"; X#endif X X#include "life.h" X Xchar rules[18] = {0,0,0,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0}; /* life rules */ X X X/* X * Compute one full generation of the current configuration. X * This is done by calling computerow with each triple of rows which X * contain any live cells, and remembering the result. When all rows X * are finished, we change the object. X */ Xdogeneration(obj) X struct object *obj; /* object to compute */ X{ X register struct row *prp; /* previous row */ X register struct row *crp; /* current row */ X register struct row *nrp; /* next row */ X register struct row *newrp; /* current row of new list */ X register struct row *srp; /* saved row pointer */ X int row; /* current row number */ X X if (genleft <= 0) return; X if ((--genleft == 0) || (--freqcount <= 0)) redraw = 1; X obj->o_gen++; X obj->o_born = 0; X obj->o_died = 0; X newrp = &initrow; X prp = termrow; X crp = termrow; X nrp = obj->o_firstrow; X srp = nrp->r_next; X row = nrp->r_row - 1; X while (1) { /* loop over each triple of rows */ X if (row >= (INFINITY-1)) break; X prp = computerow(obj, prp, crp, nrp); X if (prp) { /* have something in current row */ X newrp->r_next = prp; X newrp = prp; X newrp->r_row = row; X obj->o_count += prp->r_count; X } X row++; X prp = crp; X crp = nrp; X nrp = srp; X if (nrp->r_row == row + 1) { X srp = nrp->r_next; X continue; X } X nrp = termrow; X if ((prp != termrow) || (crp != termrow)) continue; X nrp = srp; X srp = nrp->r_next; X row = nrp->r_row - 1; X } X zeroobject(obj); X if (newrp == &initrow) { /* died off */ X genleft = 0; X redraw = 1; X return; X } X obj->o_firstrow = initrow.r_next; X newrp->r_next = termrow; X obj->o_lastrow = newrp; X if ((obj->o_born == 0) && (obj->o_died == 0)) { /* no change */ X genleft = 0; X redraw = 1; X } X} X X X/* X * Compute the result of three adjacent rows, and return a row structure X * containing the new middle row, or NULL if no live cells are produced. X * When determining if a cell is dead or alive, each live neighbor counts X * as a 1, but the current cell counts as 9 when alive. Indexing the rules X * table with the sum then automatically produces the correct result. X */ Xstruct row * Xcomputerow(obj, prevrow, currow, nextrow) X struct object *obj; X struct row *prevrow, *currow, *nextrow; X{ X register struct cell *pcp; /* head of previous row of cells */ X register struct cell *ccp; /* head of current row of cells */ X register struct cell *ncp; /* head of next row of cells */ X register struct cell *tcp; /* temporary cell */ X register struct cell *newcp; /* new row of cells */ X register int i; /* sum of live cells and other uses */ X struct row *rp; /* new row pointer */ X int col; /* current column being examined */ X int colp1; /* one more than column */ X int count; /* live cells */ X X pcp = prevrow->r_firstcell; X ccp = currow->r_firstcell; X ncp = nextrow->r_firstcell; X newcp = &initcell; X count = 0; X col = -INFINITY; X while (1) { /* loop over cells of all 3 rows */ X /* X * Find next column where a cell exists on any row X */ X i = col - 1; X while (i > pcp->c_col) pcp = pcp->c_next; X while (i > ccp->c_col) ccp = ccp->c_next; X while (i > ncp->c_col) ncp = ncp->c_next; X i = ccp->c_col; X if (pcp->c_col < i) i = pcp->c_col; X if (ncp->c_col < i) i = ncp->c_col; X if (i == INFINITY) break; X i--; X if (col < i) col = i; X i = 0; X colp1 = col + 1; X if (pcp->c_col <= colp1) { /* add cells in previous row */ X i++; X tcp = pcp->c_next; X i += (tcp->c_col <= colp1); X tcp = tcp->c_next; X i += (tcp->c_col <= colp1); X } X if (ccp->c_col <= colp1) { /* add cells on our row */ X i++; X tcp = ccp->c_next; X if ((ccp->c_col == col) || (tcp->c_col == col)) { X i += (LIFE - 1); X } X i += (tcp->c_col <= colp1); X tcp = tcp->c_next; X i += (tcp->c_col <= colp1); X } X if (ncp->c_col <= colp1) { /* add cells in next row */ X i++; X tcp = ncp->c_next; X i += (tcp->c_col <= colp1); X tcp = tcp->c_next; X i += (tcp->c_col <= colp1); X } X if (rules[i]) { /* cell is alive */ X obj->o_born += (i < LIFE); X tcp = alloccell(); X tcp->c_col = col; X newcp->c_next = tcp; X newcp = tcp; X count++; X } else /* cell is dead */ X obj->o_died += (i >= LIFE); X col++; X } X if (newcp == &initcell) return(NULL); X newcp->c_next = termcell; X rp = allocrow(); X rp->r_firstcell = initcell.c_next; X rp->r_lastcell = newcp; X rp->r_count = count; X return(rp); X} //E*O*F gen.c// echo x - io.c sed -e 's/^X//' > "io.c" << '//E*O*F io.c//' X#ifdef SCCS Xstatic char *sccsid = "@(#)io.c 1.10 2/1/85"; Xstatic char *cpyrid = "@(#)Copyright (C) 1985 by D Bell"; X#endif X X#include "life.h" X#include X#include X Xextern int errno; /* error return value */ X Xint tty_char(), tty_term(); /* terminal routines */ Xint file_char(), file_term(); /* file routines */ Xint loop_char(), loop_term(); /* loop routines */ Xint macro_char(), macro_term(); /* macro routines */ Xint getdpychar(); /* for dpyread to call */ X X X/* X * Read the next character from the appropriate input source. X * Negative answer from source indicates end of file. X * This routine is usually called indirectly by scanchar. X */ Xreadchar() X{ X register struct input *ip; /* input structure being used */ X register int ch; /* current character */ X X while (1) { /* continue until not end of file */ X ip = curinput; X ch = ip->i_getchar(ip); /* read next character */ X if (ch >= 0) break; X ip->i_term(ip); /* end of file, clean up */ X } X return(ch); X} X X X/* X * Determine if the next input character to be read is from the terminal. X * Returns nonzero if so. X */ Xttyisinput() X{ X register struct input *ip; /* input structure being checked */ X X ip = curinput; X while ((ip->i_type == INP_LOOP) && (ip->i_first)) ip--; X return(ip->i_type == INP_TTY); X} X X X/* X * Setup to read commands from the terminal. The lowest level of input X * never quits, and is unusual in that it doesn't usually block waiting for X * input. Returns nonzero if failed. X */ Xsettty() X{ X register struct input *ip; /* current input */ X X ip = curinput + 1; /* allocate next structure */ X if (ip >= &inputs[MAXINPUT]) { X return(1); X } X ip->i_getchar = tty_char; /* set up for I/O */ X ip->i_term = tty_term; X ip->i_type = INP_TTY; X update = 1; X curinput = ip; X return(0); X} X X X/* X * Read next character from the terminal if it is ready. If nothing is X * going on we will wait for it anyway, to prevent excessive runtimes. X * We set the interactive flag to indicate we are talking to user. X */ Xtty_char() X{ X long n; /* char count */ X int ch; /* char to return */ X X interact = 1; X if ((dowait == 0) && (redraw || update || (genleft > 0))) { X if ((ioctl(STDIN, FIONREAD, &n) == 0) && (n <= 0)) { X scaneof(); /* no char available now */ X } X } X do { X if (stop) return('\0'); /* stop will be seen later */ X errno = 0; X n = read(STDIN, &ch, 1); /* read one char */ X } while ((n < 0) && (errno == EINTR)); X if (n <= 0) { X return(-1); /* error or end of file */ X } X if (errorstring) { /* disable error message */ X errorstring = NULL; X update = 1; X } X return(ch &= 0x7f); X} X X X/* X * Terminate reading from the terminal. If we are reading from the lowest X * level of terminal input, this is a no-op. X */ Xtty_term(ip) X register struct input *ip; /* input structure */ X{ X if (ip > inputs) ip--; X curinput = ip; X update = 1; X} X X X/* X * Setup to read commands from a given file name. When the file is complete, X * some parameters (like the current cursor position) will be restored to their X * original value. Returns nonzero if cannot set up to read the file. X */ Xsetfile(name) X register char *name; /* file name to read from */ X{ X register struct input *ip; /* current input structure */ X X ip = curinput + 1; /* use next structure */ X if (ip >= &inputs[MAXINPUT]) { X return(1); X } X ip->i_file = openlibfile(name, 0); /* open the file */ X if (ip->i_file < 0) { X return(1); X } X ip->i_begptr = (char *) malloc(FILESIZE); /* allocate data buffer */ X if (ip->i_begptr == NULL) { X close(ip->i_file); X return(1); X } X ip->i_endptr = ip->i_begptr; /* set up rest of io */ X ip->i_curptr = ip->i_begptr; X ip->i_getchar = file_char; X ip->i_term = file_term; X ip->i_type = INP_FILE; X ip->i_obj = curobj; /* save current state */ X ip->i_row = crow; X ip->i_col = ccol; X ip->i_prow = prow; X ip->i_pcol = pcol; X prow = crow; X pcol = ccol; X update = 1; X curinput = ip; /* this is now current input */ X return(0); X} X X X/* X * Open a life library file, trying various transformed names if necessary. X * The order of the transformations which are tried is: X * 1. Name exactly as given. X * 2. Name with ".l" appended to it. X * 3. Name in the user's library directory. X * 4. Name with ".l" appended to it in the user's library. X * 5 Name in the system's library directory. X * 6. Name with ".l" appended to it in the system library. X * Returns the file descriptor if the open is successful, or -1 if all the X * open attempts failed. X */ Xopenlibfile(name, mode) X register char *name; /* original file name */ X register int mode; /* desired open mode */ X{ X register int fd; /* file descriptor */ X char buf[2000]; /* transformed names */ X X fd = open(name, mode); /* try name straight */ X if (fd >= 0) return(fd); X sprintf(buf, "%s.l", name); /* try name with .l appended */ X fd = open(buf, mode); X if (fd >= 0) return(fd); X if (*name == '/') return(-1); /* quit now if absolute name */ X if (userlib) { /* look in user's lib area */ X sprintf(buf, "%s/%s", userlib, name); X fd = open(buf, mode); X if (fd >= 0) return(fd); X strcat(buf, ".l"); /* try with .l */ X fd = open(buf, mode); X if (fd >= 0) return(fd); X } X sprintf(buf, "%s/%s", LIFELIB, name); /* look in general lib area */ X fd = open(buf, mode); X if (fd >= 0) return(fd); X strcat(buf, ".l"); /* last try with .l */ X return(open(buf, mode)); X} X X X/* X * Here to read next character from a file. Called by readchar when X * input source is a file. X */ Xfile_char(ip) X register struct input *ip; /* input structure */ X{ X if (ip->i_curptr >= ip->i_endptr) { /* get next chunk of file */ X ip->i_curptr = ip->i_begptr; X ip->i_endptr = ip->i_begptr; X ip->i_endptr += read(ip->i_file, ip->i_begptr, FILESIZE); X if (ip->i_endptr <= ip->i_begptr) { X return(-1); /* end of file */ X } X } X return(*ip->i_curptr++ & 0x7f); X} X X X/* X * Here on end of file or error to close the input file and restore some X * of the previous state such as the cursor location. X */ Xfile_term(ip) X register struct input *ip; /* input structure */ X{ X close(ip->i_file); X free(ip->i_begptr); X curobj = ip->i_obj; X crow = ip->i_row; X ccol = ip->i_col; X prow = ip->i_prow; X pcol = ip->i_pcol; X update = 1; X curinput = (ip - 1); X} X X X/* X * Setup for execution of a loop. This remembers the initial and final X * loop values, and sets up to remember commands as they are given. X * This is also used for defining a macro command. The given character X * will be assigned the string defined by the loop. X * Returns nonzero if failed. X */ Xsetloop(begval, endval, ch) X{ X register struct input *ip; /* input structure */ X X ip = curinput + 1; /* allocate next structure */ X if (ip >= &inputs[MAXINPUT]) { X return(1); X } X ip->i_begptr = (char *) malloc(LOOPSIZE); /* allocate buffer */ X if (ip->i_begptr == NULL) { X return(1); X } X ip->i_endptr = ip->i_begptr + LOOPSIZE; /* set up for I/O */ X ip->i_curptr = ip->i_begptr; X ip->i_first = 1; X ip->i_getchar = loop_char; X ip->i_term = loop_term; X ip->i_type = INP_LOOP; X ip->i_curval = begval; X ip->i_endval = endval; X ip->i_macro = ch; X update = 1; X curinput = ip; X return(0); X} X X X/* X * End the range of the currently defined loop. At this point, all of X * the characters of the loop have been read in and saved, and we can X * just proceed to iterate over them. The next read will find out that X * the first iteration of the loop is over. X */ Xendloop() X{ X register struct input *ip; /* current input */ X X ip = curinput; X if (ip->i_type != INP_LOOP) error("Loop not being defined"); X ip->i_endptr = ip->i_curptr - 1; /* end before loop term cmd */ X ip->i_first = 0; X} X X X/* X * Read one character from a loop buffer. If at the end of the buffer, the X * pointer is reset so that the buffer is reread. When enough iterations X * have been processed, we are done. A special case exists the first time X * through the loop at the first nesting level, in that we don't yet have X * the characters necessary for the loop, and so we have to read them by X * ourself. X */ Xloop_char(ip) X register struct input *ip; /* input structure */ X{ X register int ch; X X if (ip->i_first) { /* collecting input chars */ X if (ip->i_curptr >= ip->i_endptr) error("Loop too long"); X ch = ip[-1].i_getchar(ip - 1); /* char from previous level */ X if (ch < 0) error("End of file in loop"); X *ip->i_curptr++ = ch; X return(ch); X } X if (ip->i_curptr >= ip->i_endptr) { /* done with one iteration */ X if (ip->i_curval == ip->i_endval) { X return(-1); /* end of file */ X } X ip->i_curval += ((ip->i_curval < ip->i_endval) ? 1 : -1); X ip->i_curptr = ip->i_begptr; X } X return(*ip->i_curptr++); X} X X X/* X * Terminate reading from a loop buffer. If this was the definition of X * a macro character, remember it for later. X */ Xloop_term(ip) X struct input *ip; /* input structure */ X{ X register struct macro *mp; /* macro being defined */ X X mp = ¯os[ip->i_macro - 'a']; X if ((mp >= macros) && (mp < ¯os[26])) { X if (mp->m_begptr) free(mp->m_begptr); X mp->m_begptr = ip->i_begptr; X mp->m_endptr = ip->i_endptr; X } else X free(ip->i_begptr); /* or free buffer */ X update = 1; X curinput = (ip - 1); X} X X X/* Set up to read a defined macro command. Returns nonzero if failed. */ Xsetmacro(arg1, arg2, ch) X{ X register struct input *ip; /* current input */ X register struct macro *mp; /* macro command */ X X mp = ¯os[ch - 'a']; /* verify macro character */ X if ((mp < macros) || (mp >= ¯os[26]) || (mp->m_begptr == NULL)) { X return(1); X } X ip = curinput + 1; /* use next input structure */ X if (ip >= &inputs[MAXINPUT]) { X return(1); X } X ip->i_getchar = macro_char; /* set up for I/O */ X ip->i_term = macro_term; X ip->i_begptr = mp->m_begptr; X ip->i_curptr = mp->m_begptr; X ip->i_endptr = mp->m_endptr; X ip->i_type = INP_MACRO; X ip->i_macro = ch; X ip->i_curval = arg1; X ip->i_endval = arg2; X update = 1; X curinput = ip; X return(0); X} X X X/* Here to read next character from macro definition. */ Xmacro_char(ip) X register struct input *ip; /* input structure */ X{ X if (ip->i_curptr >= ip->i_endptr) { X return(-1); /* end of file */ X } X return(*ip->i_curptr++); X} X X X/* Here to terminate reading from a macro definition. */ Xmacro_term(ip) X struct input *ip; /* input structure */ X{ X curinput = (ip - 1); X update = 1; X} X X X/* X * Read a line from the user terminated by a newline (which is removed). X * Editing of the input line is fully handled. The prompt string and the X * input line are only visible if the current input is from the terminal. X * Returns a pointer to the null-terminated string. X */ Xchar * Xreadstring(prompt) X register char *prompt; /* prompt string */ X{ X int i; /* number of characters read */ X X scanreset(); /* no more scan interference */ X if (prompt == NULL) prompt = ""; /* set prompt if given NULL */ X if (ttyisinput() == 0) prompt = NULL; /* no window stuff if not tty */ X if (prompt) dpywindow(0, 0, 0, -1); /* show input in top line */ X dowait = 1; /* must wait for tty chars */ X i = dpyread(prompt, getdpychar, stringbuf, FILESIZE); /* read it */ X dowait = 0; /* back to normal */ X stringbuf[i] = '\0'; /* terminate line */ X update = 1; X return(stringbuf); X} X X X/* X * Routine called by dpyread to read the next character of input. X * We return end of input on the newline character. X */ Xgetdpychar() X{ X register int ch; /* character just read */ X X ch = readchar(); X if (stop || (ch == '\n')) return(-1); X return(ch); X} X X X/* X * Routine called to wait until a space, newline, or escape character X * is typed. It is assumed that the screen contains a display which X * we can append our message to. X */ Xspacewait() X{ X register int ch; /* read character */ X X scanreset(); /* throw out stored chars */ X dpystr("\nType to return\n"); /* append our message */ X dpyclrwindow(); X dpyhome(); X dpyupdate(); /* show the result */ X redraw = 1; X dowait = 1; /* must wait for chars */ X do { X if (stop) break; X ch = readchar(); X } while ((ch != ' ') && (ch != ESC) && (ch != '\n')); X dowait = 0; X} X X X/* X * Write the current object out to the named file, with the given maximum X * sizes for "pretty" output. If the size is exceeded, the output is X * compressed. The file as written can be read in as commands which will X * regenerate the object. X */ Xwriteobject(obj, name, maxrows, maxcols) X struct object *obj; /* object to write */ X char *name; /* filename to use */ X{ X register FILE *fd; /* file structure */ X register struct row *rp; /* current row structure */ X register struct cell *cp; /* current cell */ X register int row; /* current row value */ X register int col; /* current column value */ X struct row *trp; /* temporary row structure */ X int minrow; /* minimum row of object */ X int maxrow; /* maximum row of object */ X int mincol; /* minimum column of object */ X int maxcol; /* maximum column of object */ X int curmin; /* current minimum column */ X int curmax; /* current maximum column */ X int testmin; /* test minimum column of rows */ X int testmax; /* test maximum column of rows */ X X minmax(obj, &minrow, &maxrow, &mincol, &maxcol); X if (minrow > maxrow) error("Null object"); /* nothing to write */ X fd = fopen(name, "w"); X if (fd == NULL) error("Cannot open output file"); X fprintf(fd, "! \"%s\" (cells %d length %d width %d generation %d)\n", X obj->o_name, obj->o_count, maxrow - minrow + 1, X maxcol - mincol + 1, obj->o_gen); X if (obj->o_currow > minrow) fprintf(fd, "%dk", obj->o_currow - minrow); X if (obj->o_currow < minrow) fprintf(fd, "%dj", minrow - obj->o_currow); X if (obj->o_curcol > mincol) fprintf(fd, "%dh", obj->o_curcol - mincol); X if (obj->o_curcol < mincol) fprintf(fd, "%dl", mincol - obj->o_curcol); X fprintf(fd, "@!\n"); X curmin = INFINITY; X curmax = -INFINITY; X rp = obj->o_firstrow; X row = minrow; X for (; rp != termrow; rp = rp->r_next) { X /* X * See if user wants to stop. X */ X if (stop) { X fclose(fd); X return; X } X /* X * Skip down to the next row with something in it. X */ X if (rp->r_firstcell == termcell) continue; X if (rp->r_row > (row + maxrows)) { /* skip to right row */ X fprintf(fd, "%d\n", rp->r_row - row); X row = rp->r_row; X } X while (rp->r_row > row) { X fputs(".\n", fd); X row++; X } X /* X * Output the current row, compressing if it is too wide. X */ X col = mincol; X cp = rp->r_firstcell; X if ((cp->c_col + maxcols) < rp->r_lastcell->c_col) { X while (cp != termcell) { X /* X * Write all adjacent blanks. X */ X if (cp->c_col > col + 2) { X fprintf(fd, "%d.", cp->c_col - col); X col = cp->c_col; X } X while (cp->c_col > col) { X fputc('.', fd); X col++; X } X /* X * Write all adjacent cells. X */ X while ((cp->c_col + 1) == cp->c_next->c_col) { X cp = cp->c_next; X } X if (cp->c_col >= col + 2) { X fprintf(fd, "%dO", cp->c_col - col + 1); X col = cp->c_col + 1; X } X while (col <= cp->c_col) { X fputc('O', fd); X col++; X } X cp = cp->c_next; X } X fputc('\n', fd); X row++; X curmin = INFINITY; X curmax = -INFINITY; X continue; X } X /* X * Here if the row doesn't need compressing. See if several X * rows from this one on can all fit in the same range. If X * so, set things up so they will align themselves nicely. X */ X if ((cp->c_col < curmin) || (rp->r_lastcell->c_col > curmax)) { X curmin = cp->c_col; X curmax = rp->r_lastcell->c_col; X trp = rp; X for (; rp != termrow; rp = rp->r_next) { X if (rp->r_firstcell == termcell) continue; X testmin = rp->r_firstcell->c_col; X if (testmin > curmin) testmin = curmin; X testmax = rp->r_lastcell->c_col; X if (testmax < curmax) testmax = curmax; X if ((testmax - testmin) >= maxcols) break; X curmin = testmin; X curmax = testmax; X } X rp = trp; X } X /* X * Type the row, with the initial shift if necessary. X */ X if (curmin != mincol) { X fprintf(fd, "%d.", curmin - mincol); X col = curmin; X } X for (cp = rp->r_firstcell; cp != termcell; cp = cp->c_next) { X while (cp->c_col > col) { X fputc('.', fd); X col++; X } X fputc('O', fd); X col++; X } X fputc('\n', fd); X row++; X } X fclose(fd); X} X X X X/* Write all defined macros to a file so they can be read back in later. */ Xwritemacros(name) X char *name; /* file name to write to */ X{ X register int f; /* file descriptor */ X register struct macro *mp; /* current macro */ X int ch; /* character */ X X f = creat(name, 0666); X if (f < 0) error("Cannot create file"); X for (mp = macros; mp < ¯os[26]; mp++) { X if ((mp->m_begptr == NULL) || (mp->m_begptr >= mp->m_endptr)) { X continue; X } X ch = 'a' + (mp - macros); X write(f, "<", 1); X write(f, &ch, 1); X write(f, mp->m_begptr, mp->m_endptr - mp->m_begptr); X write(f, ">!\n", 3); X } X close(f); X} //E*O*F io.c// echo done -- no comment is a comment.