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