LIFE - fancy new version (part 2 of 4)
David I. Bell
dbell at daisy.UUCP
Sun Feb 3 14:49:33 AEST 1985
#---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 (16638 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 <sys/ioctl.h>
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 <errno.h>
X#include <sys/ioctl.h>
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 <space> to return\n"); /* append our message */
X dpyclearwindow();
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
More information about the Comp.sources.unix
mailing list