From: David I. Bell (dbell@pdact.pd.necisa.oz.au)
Date: Tue Mar 16 1993 - 06:29:42 UTC
#!/bin/sh
# this is LIFE.16 (part 16 of a multipart archive)
# do not concatenate these parts, unpack them in order with /bin/sh
# file life/life.doc continued
#
if touch 2>&1 | fgrep 'amc' > /dev/null
then TOUCH=touch
else TOUCH=true
fi
if test ! -r shar3_seq_.tmp; then
echo "Please unpack part 1 first!"
exit 1
fi
(read Scheck
if test "$Scheck" != 16; then
echo "Please unpack part $Scheck next!"
exit 1
else
exit 0
fi
) < shar3_seq_.tmp || exit 1
echo "x - Continuing file life/life.doc"
sed 's/^X//' << 'SHAR_EOF' >> life/life.doc &&
X
XA very useful feature is the use of marks. You can select any group of
Xcells of an object, and handle them specially, leaving the rest of the cells
Xunchanged. For example, you can save the marked cells as a different object,
Xor can move them around, or can delete them. Marked cells appear differently
Xin the display so that you can recognize which cells are marked. Marks are
Xset based on where the cursor is. You can mark all the cells in any quadrant
Xor half-plane around the cursor. You can also mark all the cells which are
Xking-wise connected to your cursor location, or which are connected with only
Xone cell gaps to your cursor location. Each mark command adds to the set of
Xmarked cells. The ';unmark' command removes all current marks so that you can
Xstart again. There are several commands which act on the marked cells in
Xinteresting ways. For example, if the cursor is on a glider, the command
X'dd' will delete just the glider. The commands which flip or rotate marked
Xcells do so with respect to the current cursor location.
X
XCommand loops and macros can be defined using the '<' and '>' commands. If
Xa numeric argument is specified, a loop is being defined. If no argument is
Xgiven, a macro is being defined. While defining a macro or loop, the commands
Xyou type are executed normally so that you can see how the definition will
Xexecute. This is useful when you define a macro to create an object, since
Xyou can easily see how to create the object. Loops and macros can contain
Xboth line mode and character mode commands. While you are defining a loop
Xor macro, the status line will indicate this and give the current depth of
Xloop or macro nesting.
X
XWhen defining a macro, the next character after the '<' is the macro name to
Xdefine (which must be a lower case letter). All of the commands between
Xthe '<' and the '>' (except for the macro name character) are saved. Once a
Xmacro has been defined, it is used by typing the ESCAPE character followed by
Xthe macro letter. As an example, the command string '<gththtuktntj>' defines
Xa macro named 'g' which causes a glider to be inserted at the current cursor
Xposition every time you type '<ESC>g'. The ';dumpmacros' command writes out
Xthe definitions of all macros so that you can read them back in later.
X
XIf a loop is being defined, the loop is executed the number of times specified
Xby the argument to '<'. If two arguments are specified, the loop runs from
Xthe first to the second argument (backwards if the second argument is less
Xthan the first argument). The value of the loop counter is available in the
Xloop itself by using '%' as you would a number. As an example of a loop,
Xthe command string '3,7<%o >' inserts five strings of cells spaced apart by
Xone cell, where the first string contains three cells, and the last string
Xcontains seven cells. When nesting loops, supplying an argument to '%' can
Xobtain the loop counter values for the outside loops while in the inner loop.
XThus, '2%' obtains the loop value for the next-to-innermost loop level.
X
XWhile defining a loop or macro, you can request user intervention by using
Xthe ';ttyinput' command. This will suspend the execution of the loop or macro
Xand notify the user that input is desired by displaying 'tty-input' in the
Xstatus line. The user can then execute any commands of his choice. When he
Xis ready to proceed, he uses the ';endinput' (or a control-D) command to
Xcontinue execution where it had paused. Different commands can be given by
Xthe user each time the loop or macro asks for terminal input. If the command
X;ttyinput -c' is used, terminal commands will only be asked for if there
Xis any terminal input available to be read.
X
XNormally, the screen is not updated while a macro or loop is executing. Thus
Xa complicated command sequence can be executed without letting the user see
Xwhat is occuring until it is finished. However, if you wish to update the
Xscreen in the middle of execution, then use the ';update' command. Commands
Xgiven from the terminal automatically do an update whenever no more input is
Xavailable.
X
XWhen running using graphics, the mouse can be used to move the cursor to any
Xposition within the view, and can also automatically execute a command macro
Xof your choice. The ';assign' command will assign a mouse button number to
Xa command macro letter. When the mouse is positioned and the button is
Xpressed, then the cursor is moved to the selected cell and then the command
Xmacro is executed. With the proper command macros being defined, this lets
Xyou use the mouse buttons for useful operations like toggling cells,
Xdeleting pieces of objects, or repositioning the view. If no command macro
Xis assigned to a mouse button, then pressing the button simply moves the
Xcursor to the specified cell and does nothing else. Mouse activity can be
Xreasonably used within a macro or loop definition since mouse events are
Xtranslated to the appropriate 'l' and 'j' relative movement commands.
X
XA few commands make use of a location called a pointer. The pointer is a
Xlocation that is remembered in an object, independent of the current cursor
Xlocation. It is used by the '\n' command, which moves to the next row and
Xback to the column of the pointer. It is also used by the 'p' selection
Xarea option, and determines the far corner of a rectangular region to be
Xselected. The '@' command sets the pointer to the current cursor location.
X
XWhen you are trying to count the number of cells between two parts of an
Xobject, then the ';grid' command is useful. For terminal output, it changes
Xthe display of dead cells from blanks to another character (typically
Xperiods). For graphics output, the grid is drawn using individual pixels.
X(The grid will not be displayed unless the scale factor is -4 or less,
Xotherwise it would be difficult to distinguish the grid from the cells.)
X
XThe program provides numeric variables which you can use to control your
Xcommands. Variables are used as numeric arguments to commands. Variables
Xcome in two kinds. These are the user-defined single character variables,
Xand a set of predefined multi-character variables.
X
XThere are 52 single-character variables, whose names are the lower and upper
Xcase letters. All of their values are initially zero. When used for a
Xcharacter mode command (and without the use of parenthesis), variable
Xnames must be preceded by a dollar sign to distinguish them from commands.
XFor example, the command '$mo' inserts as many live cells in a row as is
Xcontained in the value of variable 'm'.
X
XWhen used within an expression for a line mode command, or within a set of
Xparenthesis in a character mode command, the dollar sign can be dropped
Xsince the parser knows to expect a variable name in these cases. For
Xexample, the command ';type m' will display the value of variable 'm'.
XSimilarly, the command '(m*2)o' will set a number of live cells equal to
Xtwice the value of variable 'm'.
X
XSingle character variables are set by using the ';set' command, which
Xtakes a variable name followed by an expression. For example, the value
Xof variable 'e' can be tripled by using the command ';set e e * 3'.
XAs a shortcut for a common operation, the '+c' character mode command
Xincrements variable c. (Of course, a numeric argument can be given to
Xspecify an increment value other than than 1.)
X
XOne common use of variables is inside a macro in order to execute a command
Xstring over and over again with only small differences each time (such as
Xto vary the placement of two objects with respect to each other). As an
Xexample of this, the command '<q$ao3+a>' will define a macro called 'q' which
Xwill insert a number of live cells in a row (as determined by variable 'a'),
Xand the number of cells inserted increases by 3 for each use of the macro.
X
XThe other kind of variables are the multi-character variables. These are
Xa fixed set of names, and their values represent specific things which are
Xnot directly modifyable. These things are values associated with the current
Xobject. For example, the 'cx' variable returns the absolute x coordinate of
Xthe current cursor position. You can see the complete set of variables by
Xusing the ';variables' command.
X
XSince the parser must be able to distinguish multi-character variable names
Xfrom command letters or single-letter variable names, you cannot use them in
Xcharacter mode commands without surrounding them with a pair of parenthesis.
XAs an example of multi-character variables used as an argument, the command
Xstring '((vmaxx-vminx)/2)h' shifts the cursor to the left by an amount
Xequal to half of the screen width.
X
XHere are some of the remaining useful line mode commands. The ';quit' command
Xexits the program. The ';zero' command clears all cells of an object and
Xresets the scaling factor and cursor position. The ';copymarked name' and
X';movemarked name' commands copy or move marked cells to another object.
XThe ';advance' command takes the marked object, removes it, runs the Life
Xrules for the specified number of generations, then reinserts the result
Xback into the current object, marking it. Finally, the ';undo' command
Xwill undo the most recent change to the current object. This can undo
Xdeletions, insertions, and running of generations, among other things.
XBut you get only one level of backup. (A related backup feature is the 'p'
Xcommand, which will reinsert the last deleted object to the current cursor
Xlocation.)
X
XThe rules of Life can be changed to some degree. This allows you to explore
Xalternative Life universes. You can specify how many live cells are required
Xfor a cell to be born, or to stay alive. However, you cannot change the
Xneighborhood used for counting live cells (the program always counts all of
Xthe eight neighboring cells). To change the cells needed for birth or death,
Xuse the ';rules <born> <live>' line mode command. The <born> string lists
Xthose counts of neighbors which are required for a new cell to be born. The
X<live> string lists those counts of neighbors which are required for an
Xexisting cell to stay alive. The standard rules are thus described by the
Xcommand ';rules 3 23'.
X
XThe following table specifies the character mode commands, arranged in
Xuseful categories. Many of these have been described in detail above.
X
X--- MOVEMENT COMMANDS ---
X
XSPACE move right <arg1> cells
Xl move right <arg1> cells
Xh move left <arg1> cells
Xl move right <arg1> cells
Xk move up <arg1> cells
Xj move down <arg1> cells
Xy move upper left <arg1> cells
Xu move upper right <arg1> cells
Xb move lower left <arg1> cells
Xn move lower right <arg1> cells
XLF move to next row and to the column of the pointer
XTAB move to next multiple of 8 from the pointer column
X@ remember current location as the pointer
Xc move to location <arg1>, <arg2> relative to the pointer
X/ move cursor to next object
X
X--- SCREEN COMMANDS ---
X
Xs set viewing scale to <arg1> and center view (default current scale)
XS turn on auto-scaling and center view
XH shift view left by width/4
XL shift view right by width/4
XK shift view up by height/4
XJ shift view down by height/4
XY shift view left and up
XU shift view right and up
XB shift view left and down
XN shift view right and down
XFF refresh the screen
X
X--- SINGLE CELL COMMANDS --
X
Xt toggle cell at current location
Xo insert <arg1> cells and move cursor right
XO insert <arg1> cells and move cursor right
Xx kill <arg1> cells and move cursor right
X
X--- MULTIPLE CELL COMMANDS ---
X
Xd<mark> delete cells described by <mark>
Xp place last deleted object at current location
Xfr<mark> flip cells around the current row as described by <mark>
Xfc<mark> flip cells around the current column as described by <mark>
Xr<mark> rotate cells 90 degrees clockwise as described by <mark>
Xm<mark> mark cells as described by <mark>
X
X--- LOOP, VARIABLE, AND MACRO COMMANDS ---
X
X< begin loop which executes from <arg1> to <arg2> times (if <arg1> given)
X<<ch> begin definition of macro named <ch> (if <arg1> not given)
X> end loop or macro definition
XESC<ch> execute a macro command named <ch>
X+<ch> increment the value of the single-character variable <ch> by arg1
X
X--- MISCELLANEOUS COMMANDS ---
X
Xg compute <arg1> generations (or stop if running)
XG compute infinite generations
Xz set generation number to <arg1> (default zero)
X! ignore characters until end of line
X# ignore character until end of line
X<line> execute a line mode command
X<line> execute a line mode command
X
XArguments specified above as <arg1> and <arg2> can be any of the following:
X<var> value of variable <var>, where <var> is a lower or upper case letter
X(expr) an arithmetic expression containing constants, variables, or operators
X% current loop counter value
X<num> explicit numeric value
X
XThose commands above referencing <mark> take one of the following letters.
XThe command affects only those cells marked. As a special case, repeating
Xthe command letter is equivalent to marking 'o' (the connected object).
XExample: 'dd' deletes the connected object the cursor is on.
Xa all cells
Xc cell at current location
Xo cells in the king-wise connected object at current location
Xe cells in the king-wise connected object allowing one cell gaps
Xh cells to left of cursor
Xl cells to right of cursor
Xk cells above cursor
Xj cells below cursor
Xb cells below and left of cursor
Xn cells below and right of cursor
Xu cells above and right of cursor
Xy cells above and left of cursor
Xp cells in rectangle determined by pointer and cursor
Xm cells which are marked
Xv cells visible in window
Xi cells invisible in window
X
XWhile the program is doing something which can take a long time, such as
Xcomputing generations, defining or executing a macro or loop, or writing
Xout a large Life object, you can type the interrupt key and that action
Xwill be aborted at a convenient point and you will be returned to the top
Xcommand level.
X
XHere is a list of the possible options when starting the life program.
XAs mentioned previously, these options can also be specified in the LIFEOPTS
Xenvironment variable, and overwritten by the command line as needed.
X
X-t use terminal output
X-g use graphics output
X-s <arg> initialize the default scale to the specified value
X-l <path> set a personal library directory path
X
X
X APPENDIX
X
XThis is a short introduction to Conway's Game of Life. Life is played on an
Xinfinitely large board divided into squares. Each square is called a cell.
XEach cell can be either dead or alive. Dead cells are seen as blanks, whereas
Xlive cells are seen as non-blanks. You begin to play by choosing some
Xarbitrary set of live and dead cells. This configuration is called generation
X0. There is a set of rules which transforms this set of cells into another
Xset of cells, called generation 1. These same rules are then reapplied to
Xgeneration 1 to produce generation 2. This process continues indefinitely.
XThe 'purpose' of the game is to find starting patterns such that 'interesting
Xthings' result.
X
XThe rules which are applied are as follows. Take any cell of a generation,
Xand call it the current cell. Consider the eight cells immediately adjacent
Xto the current cell. Count the number of these eight cells which are alive.
XIf the current cell is dead and the count is 3, then the current cell changes
Xto a live cell in the next generation. If the current cell is alive and the
Xcount is NOT 2 or 3, then the current cell changes to a dead cell in the next
Xgeneration. Otherwise the cell remains unchanged in the next generation.
XThis rule is applied to every cell of a generation SIMULTANEOUSLY. Thus to
Xrephrase, 3 live neighbors causes a new cell to be born, whereas 2 or 3 live
Xneighbors keeps a cell alive.
X
XTo see how these rules work in practice, run the program and start with some
Xnumber of live cells in some arrangement, and watch the generations change.
XIf you can predict what the changes will be, then you understand the rules.
XThe following are some objects to try, along with some descriptive names.
XWarning: the last example gets complicated!!
X
X O O O O O OO
X O O OO OO OO O OO
X O O O O O OOO O
X
Xdoomed blinker beehive block traffic lights glider r-pentomino
SHAR_EOF
echo "File life/life.doc is complete" &&
$TOUCH -am 0316154893 life/life.doc &&
chmod 0600 life/life.doc ||
echo "restore of life/life.doc failed"
set `wc -c life/life.doc`;Wc_c=$1
if test "$Wc_c" != "31040"; then
echo original size 31040, current size $Wc_c
fi
# ============= life/life.h ==============
echo "x - extracting life/life.h (Text)"
sed 's/^X//' << 'SHAR_EOF' > life/life.h &&
X/*
X * Copyright (c) 1993 David I. Bell
X * Permission is granted to use, distribute, or modify this source,
X * provided that this copyright notice remains intact.
X *
X * Program to play Conway's game of LIFE on an infinite board.
X */
X
X#include <stdio.h>
X#include <setjmp.h>
X#include <signal.h>
X
X
X#define ESC '\033'
X#define FF '\014'
X
X#define isblank(ch) (((ch) == ' ') || ((ch) == '\t'))
X#define isdigit(ch) (((ch) >= '0') && ((ch) <= '9'))
X#define islower(ch) (((ch) >= 'a') && ((ch) <= 'z'))
X#define isupper(ch) (((ch) >= 'A') && ((ch) <= 'Z'))
X#define isletter(ch) (islower(ch) || isupper(ch))
X
X
X#ifndef LIFELIB
X#define LIFELIB "/usr/games/lib/life" /* directory for general life library */
X#endif
X
X#define LIFEOPTS "LIFEOPTS" /* environment name for options */
X#define LIFEEXT ".l" /* extension for life files */
X#define MAXPATH 1024 /* maximum size of path names */
X#define MAXFILE 18 /* maximum size of file name for list */
X#define MAXLIST 5 /* maximum depth for listing files */
X#define ALLOCOBJ 10 /* how many new objects to allocate */
X#define ALLOCROW 50 /* how many new rows to allocate */
X#define ALLOCCELL 500 /* how many new cells to allocate */
X#define LISTARGSIZE 200 /* incremental arg size for lists */
X#define LISTBUFSIZE 2000 /* incremental buffer size for lists */
X#define STRINGSIZE (1024*4) /* temp string buffer size */
X#define LOOPSIZE 5000 /* characters in command loops */
X#define SCAN_SIZE 100 /* maximum command length */
X#define MAXARGS 10 /* maximum command line arguments */
X#define MAXINPUT 20 /* maximum nesting of command inputs */
X#define MAXNAME 32 /* maximum object name size */
X#define MAXSCALE 1000 /* maximum scale factor */
X#define WRITEROWS 100 /* default maximum rows for writing */
X#define WRITECOLS 79 /* default maximum cols for writing */
X#define INFINITY 0x7fffffff /* infinite value */
X#define LIFE 9 /* value for live cell */
X
X#define STDIN 0 /* standard input */
X#define STDOUT 1 /* standard output */
X#define STDERR 2 /* standard error */
X
X#define RELATIVE 0 /* do object additions relatively */
X#define ABSOLUTE 1 /* do object additions absolutely */
X
X#define M_MOVE 0 /* movement mode */
X#define M_INSERT 1 /* insertion mode */
X#define M_DELETE 2 /* deletion mode */
X
X#define MARK_ANY 0x1 /* mark always set */
X#define MARK_USR 0x2 /* marked due to user specification */
X#define MARK_CMD 0x4 /* marked only for current command */
X#define MARK_SRC 0x8 /* marked by searching */
X#define MARK_SEE (MARK_USR) /* marks seen by user */
X#define MARK_ALL (MARK_ANY|MARK_USR|MARK_CMD|MARK_SRC) /* all non-null marks */
X
X#define INP_TTY 0 /* input is terminal */
X#define INP_FILE 1 /* input is file */
X#define INP_LOOP 2 /* input is command loop */
X#define INP_MACRO 3 /* input is command macro */
X
X#define SCAN_ABORT 1 /* setjmp value for aborted command */
X#define SCAN_EDIT 2 /* setjmp value for edited command */
X#define SCAN_EOF 3 /* setjmp value for no command data */
X
X#define NULL_CMD 0xff /* null command character */
X
X#define U_POS 0x01 /* update cursor position */
X#define U_STAT 0x02 /* update status line */
X#define U_VIEW 0x04 /* update view of cells */
X#define U_ALL (U_POS | U_STAT | U_VIEW) /* any updating */
X
X#ifndef TRUE
X#define TRUE ((BOOL) 1) /* booleans */
X#define FALSE ((BOOL) 0)
X#endif
X
X#define crow curobj->o_currow /* current row of current object */
X#define ccol curobj->o_curcol /* current column of current object */
X#define cmark curobj->o_mark /* current mark being applied */
X#define cscale curobj->o_scale /* current scale of current object */
X#define prow curobj->o_prow /* current pointer row */
X#define pcol curobj->o_pcol /* current pointer column */
X
X
X/* macro to detect reserved names */
X#define BADNAME(s) ((s[0] == '.') && ((s[1] == '\0') || (s[1] == '.')))
X
X
X/*
X * Basic typedefs
X */
Xtypedef long COORD; /* coordinate of a cell */
Xtypedef long COUNT; /* counts of things */
Xtypedef long VALUE; /* value of an expression */
Xtypedef int MARK; /* mark value */
Xtypedef int BOOL; /* boolean value */
Xtypedef int SCALE; /* scale factor */
Xtypedef unsigned char UCHAR; /* for character handling */
X
X
X/*
X * Structure for each cell.
X */
Xtypedef struct cell CELL;
Xstruct cell {
X CELL *c_next; /* link to next cell */
X COORD c_col; /* column number */
X MARK c_marks; /* marking values for cell */
X};
X
X
X/*
X * Structure for each row of cells.
X */
Xtypedef struct row ROW;
Xstruct row {
X ROW *r_next; /* link to next row */
X CELL *r_firstcell; /* link to first cell */
X CELL *r_lastcell; /* link to last real cell */
X COORD r_row; /* row number */
X COUNT r_count; /* number of cells in this row */
X};
X
X
X/*
X * Structure for each object.
X */
Xtypedef struct object OBJECT;
Xstruct object {
X OBJECT *o_next; /* next object */
X ROW *o_firstrow; /* first row */
X ROW *o_lastrow; /* last row */
X COUNT o_count; /* number of live cells */
X COUNT o_born; /* number of cells born */
X COUNT o_died; /* number of cells died */
X COORD o_currow; /* current row */
X COORD o_curcol; /* current column */
X COORD o_minrow; /* minimum row seen in window */
X COORD o_maxrow; /* maximum row seen in window */
X COORD o_mincol; /* minimum column seen in window */
X COORD o_maxcol; /* maximum column seen in window */
X COORD o_prow; /* currently pointed at row */
X COORD o_pcol; /* currently pointed at column */
X MARK o_mark; /* mark value for new cells */
X COUNT o_gen; /* current generation */
X SCALE o_scale; /* current scaling factor for view */
X COUNT o_frequency; /* frequency of output for object */
X BOOL o_autoscale; /* doing autoscaling */
X BOOL o_reserved; /* reserved object */
X char o_name[MAXNAME+1]; /* name of object */
X};
X
X
X/*
X * The following structure holds all data necessary for processing
X * characters from some source.
X */
Xtypedef struct input INPUT;
Xstruct input {
X int (*i_getchar)(); /* routine to read next character */
X void (*i_term)(); /* routine to terminate reading */
X int i_type; /* type of input */
X
X /* following for file reading only */
X FILE *i_file; /* file handle */
X OBJECT *i_obj; /* object to restore on reentry */
X COORD i_row; /* row to restore */
X COORD i_col; /* column to restore */
X COORD i_prow; /* pointer row to restore */
X COORD i_pcol; /* pointer column to restore */
X
X /* following for loop or macro reading only */
X UCHAR *i_begptr; /* beginning of command data */
X UCHAR *i_endptr; /* end of command data */
X UCHAR *i_curptr; /* current character */
X COUNT i_curval; /* current iteration value */
X COUNT i_endval; /* ending iteration value */
X BOOL i_first; /* processing new chars */
X char i_macro; /* macro being defined */
X};
X
X
X/*
X * Structure for command macros.
X */
Xtypedef struct {
X UCHAR *m_begptr; /* beginning of data (NULL if none) */
X UCHAR *m_endptr; /* end of data */
X} MACRO;
X
X
X/*
X * Structure to interface to various input/output devices.
X * This supports both normal terminals and graphics output.
X */
Xtypedef struct {
X int (*open)(); /* open device */
X void (*close)(); /* close device */
X void (*update)(); /* make sure display is up to date */
X void (*refresh)(); /* redraw whole display from scratch */
X BOOL (*inputready)(); /* check whether input is ready */
X int (*readchar)(); /* read character with optional wait */
X void (*movecursor)(); /* move position of cursor */
X void (*showview)(); /* show view of life cells */
X void (*showstatus)(); /* show status line */
X void (*addstatus)(); /* add to status line */
X void (*showhelp)(); /* show help information */
X void (*addhelp)(); /* add information to help */
X int (*assign)(); /* assign button to macro */
X
X COUNT rows; /* rows available for life cells */
X COUNT cols; /* columns available for life cells */
X COUNT textrows; /* rows of text available for help */
X COUNT textcols; /* columns of text available for help */
X SCALE minscale; /* minimum legal scale value */
X SCALE maxscale; /* maximum legal scale value */
X SCALE defaultscale; /* desirable default scale */
X} DEV;
X
X
X/*
X * Structure to hold a list of strings allocated from a common buffer.
X * This is used for collecting file names for listing.
X */
Xtypedef struct {
X char **argv; /* pointer to list of pointers */
X char *buf; /* pointer to string storage */
X int argc; /* number of pointers */
X int maxargc; /* maximum number of pointers */
X int used; /* chars used in string storage */
X int maxused; /* maximum chars in string storage */
X} LIST;
X
X
X/*
X * Extern definitions for non-initialized data.
X * These are defined externally in all modules, except in main.
X */
X#ifdef DEFINE_GLOBALS
X#define EXTERN
X#else
X#define EXTERN extern
X#endif
X
X
XEXTERN DEV *dev; /* device being used */
XEXTERN OBJECT *objects; /* list of active objects */
XEXTERN OBJECT *curobj; /* currently selected object */
XEXTERN OBJECT *prevobj; /* previously selected object */
XEXTERN OBJECT *mainobject; /* the main object */
XEXTERN OBJECT *deleteobject; /* object last deleted */
XEXTERN OBJECT *backupobject; /* backup object */
XEXTERN OBJECT *tempobject; /* temporary object */
XEXTERN OBJECT *freeobjects; /* list of free objects */
XEXTERN OBJECT *newobjects; /* top of new object allocation */
XEXTERN OBJECT *endobjects; /* end of new objects */
X
XEXTERN ROW *freerows; /* list of free row structures */
XEXTERN ROW *newrows; /* top of new row allocation */
XEXTERN ROW *endrows; /* end of new rows */
XEXTERN ROW *termrow; /* terminus row */
XEXTERN ROW initrow; /* row to initialize list */
X
XEXTERN CELL *freecells; /* list of free cell structures */
XEXTERN CELL *newcells; /* top of new cell allocation */
XEXTERN CELL *endcells; /* end of new cells */
XEXTERN CELL *termcell; /* terminus cell */
XEXTERN CELL initcell; /* cell to initialize list */
X
XEXTERN INPUT *curinput; /* current input being read from */
XEXTERN COUNT seecount; /* number of cells we can see */
XEXTERN COUNT freqcount; /* current count */
XEXTERN COUNT genleft; /* generations left before stopping */
XEXTERN COUNT viewrows; /* how many rows of cells can be seen */
XEXTERN COUNT viewcols; /* how many columns of cells can be seen */
XEXTERN BOOL reserve; /* reserved object names allowed */
XEXTERN BOOL dowait; /* must wait for input */
XEXTERN BOOL interact; /* still being interactive */
XEXTERN BOOL stop; /* user wants to stop current action */
XEXTERN BOOL intjmpok; /* ok to use interrupt jump buffer */
XEXTERN SCALE defaultscale; /* default scale value for new objects */
XEXTERN COUNT defaultfrequency; /* default frequency for new objects */
XEXTERN int update; /* flags for what needs updating */
XEXTERN int mode; /* mode of movement */
XEXTERN char gridchar; /* character to use for grid */
XEXTERN char *errorstring; /* error string to type */
XEXTERN char *userlib; /* user's life library if any */
XEXTERN jmp_buf ttyjmp; /* jump buffer for terminal input */
XEXTERN jmp_buf intjmp; /* jump buffer for interrupts */
XEXTERN INPUT inputs[MAXINPUT]; /* list of input environments */
XEXTERN MACRO macros[26]; /* list of macros */
XEXTERN char stringbuf[STRINGSIZE]; /* characters for string value */
XEXTERN char rulestring[20]; /* string describing rules */
X
XEXTERN char vkill; /* line kill character */
XEXTERN char verase; /* erase character */
XEXTERN char vwerase; /* word erase character */
XEXTERN char veol; /* end of line character */
XEXTERN char veof; /* EOF character */
XEXTERN char vlnext; /* literal next character */
X
X
X/*
X * Externs for data which is initialized.
X */
Xextern char rules[]; /* life rules */
Xextern DEV ttydev; /* tty device */
X
X#if defined(X11)
Xextern DEV x11dev; /* graphics device */
X#endif
X
X
X/*
X * Procedures
X */
Xextern OBJECT *allocobject(); /* allocate new object */
Xextern OBJECT *findobject(); /* find object with certain name */
Xextern OBJECT *getobject(); /* get new object with certain name */
Xextern ROW *allocrow(); /* allocate new row */
Xextern ROW *findrow(); /* find a row */
Xextern ROW *getrow(); /* get a new row */
Xextern CELL *alloccell(); /* allocate new cell */
Xextern CELL *findcell(); /* find a cell */
Xextern BOOL addcell(); /* add a cell */
Xextern BOOL delcell(); /* delete a cell */
Xextern char *readstring(); /* read input line from user */
Xextern void listvariables(); /* list variables */
Xextern VALUE getvariable(); /* get value of variable */
Xextern VALUE getvariable1(); /* get value of single char variable */
Xextern void setvariable(); /* set value of variable */
Xextern void setvariable1(); /* set a single char variable */
Xextern void scaninit(); /* initialize for reading chars */
Xextern void scanabort(); /* abort reading current command */
Xextern void scaneof(); /* indicate no more chars ready */
Xextern void scanreset(); /* reset scanning pointers */
Xextern void setobject(); /* set object as current one */
Xextern void zeroobject(); /* delete all cells in object */
Xextern void destroyobject(); /* destroy object */
Xextern void moveobject(); /* move object to another */
Xextern void addobject(); /* add one object to another */
Xextern void copyobject(); /* copy object to another */
Xextern void listobjects(); /* list all objects */
Xextern BOOL minmax(); /* find min and max of object */
Xextern BOOL searchobject(); /* scan for next part of object */
Xextern BOOL markobject(); /* mark cells of an object */
Xextern void setmarks(); /* mark whole object */
Xextern BOOL copymarks(); /* copy marks in an object */
Xextern void clearmarks(); /* clear marks in an object */
Xextern COUNT markregion(); /* mark cells in a region */
Xextern BOOL markminmax(); /* find range of marked cells */
Xextern COUNT countmarks(); /* count marked cells */
Xextern void movemarkedobject(); /* move marked cells to object */
Xextern void copymarkedobject(); /* copy marked cells to object */
Xextern void rotatemarkedobject(); /* rotate marked cells */
Xextern void fliprowmarkedobject(); /* flip marked cells around a row */
Xextern void flipcolmarkedobject(); /* flip marked cells around a column */
Xextern void error(); /* error routine */
Xextern int readchar(); /* read next char from input */
Xextern BOOL abbrev(); /* check for abbreviation */
Xextern BOOL ttyisinput(); /* see if input is terminal */
Xextern BOOL settty(); /* setup to get input from tty */
Xextern BOOL setfile(); /* setup to get input from file */
Xextern BOOL setloop(); /* setup for execution of loop */
Xextern void endloop(); /* end the range of loop */
Xextern BOOL setmacro(); /* setup to define a macro */
Xextern int readline(); /* read line of input into buffer */
Xextern BOOL showhelp(); /* add some text to the help display */
Xextern void endhelp(); /* terminate the help display */
Xextern void readrle(); /* read rle format object */
Xextern void readxlife(); /* read xlife format object */
Xextern void readpicture(); /* read picture of object */
Xextern void writeobject(); /* write object to a file */
Xextern void writemacros(); /* write macros to file */
Xextern void dogeneration(); /* calculate one generation */
Xextern void dolinecommand(); /* read and execute line command */
Xextern void docommand(); /* read and execute command */
Xextern void backup(); /* backup state of object */
Xextern void checkrun(); /* see if doing generations */
Xextern void getfiles(); /* collect file names in a directory */
Xextern void listfiles(); /* list collected file names */
Xextern void setscale(); /* set scale for display */
Xextern SCALE autoscale(); /* do autoscale of object */
Xextern void positionview(); /* position view of object */
Xextern void updateview(); /* show view of object */
Xextern void viewstatus(); /* show status of object */
Xextern void beep(); /* ring terminal bell */
X
Xextern char *malloc(); /* allocate memory */
Xextern char *realloc(); /* reallocate memory */
Xextern char *getenv(); /* get environment variable */
Xextern char *strchr(); /* find character in string */
X
X/* END CODE */
SHAR_EOF
$TOUCH -am 0316122993 life/life.h &&
chmod 0600 life/life.h ||
echo "restore of life/life.h failed"
set `wc -c life/life.h`;Wc_c=$1
if test "$Wc_c" != "15837"; then
echo original size 15837, current size $Wc_c
fi
# ============= life/main.c ==============
echo "x - extracting life/main.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > life/main.c &&
X/*
X * Copyright (c) 1993 David I. Bell
X * Permission is granted to use, distribute, or modify this source,
X * provided that this copyright notice remains intact.
X *
X * Program to play Conway's Game of LIFE on an infinite board.
X */
X
X#define DEFINE_GLOBALS
X
X#include "life.h"
X
X
Xstatic void defaults();
Xstatic void options();
Xstatic void intint();
X
Xstatic BOOL scaleset;
Xstatic BOOL inited;
X
X
Xmain(argc, argv)
X char **argv;
X{
X strcpy(rulestring, "3,23");
X termcell = alloccell();
X termcell->c_next = termcell;
X termcell->c_col = INFINITY;
X termrow = allocrow();
X termrow->r_next = termrow;
X termrow->r_firstcell = termcell;
X termrow->r_row = INFINITY;
X mode = M_MOVE;
X gridchar = ' ';
X defaultscale = 1;
X defaultfrequency = 1;
X freqcount = 1;
X viewrows = 1;
X viewcols = 1;
X
X reserve = TRUE; /* creating special objects */
X deleteobject = getobject("..delete");
X tempobject = getobject("..temp");
X backupobject = getobject("..backup");
X mainobject = getobject("main");
X curobj = mainobject;
X prevobj = mainobject;
X reserve = FALSE; /* no more special objects */
X
X curinput = &inputs[-1]; /* initialize for tty input */
X settty();
X dev = &ttydev;
X
X defaults();
X options(argc - 1, argv + 1);
X
X if ((*dev->open)(dev) < 0)
X exit(1);
X
X viewrows = dev->rows;
X viewcols = dev->cols;
X if (!scaleset)
X defaultscale = dev->defaultscale;
X
X setscale(deleteobject, defaultscale); /* fix scale factors now */
X setscale(tempobject, defaultscale);
X setscale(backupobject, defaultscale);
X setscale(mainobject, defaultscale);
X
X signal(SIGINT, intint);
X scaninit(readchar, ttyjmp);
X
X inited = TRUE;
X
X while (TRUE) {
X docommand();
X dogeneration(curobj);
X updateview();
X }
X}
X
X
X/*
X * Parse the optional LIFEOPTS environment variable to define defaults.
X */
Xstatic void
Xdefaults()
X{
X char *env;
X char *cp;
X int argc;
X char **argv;
X char *argtable[MAXARGS];
X
X env = getenv(LIFEOPTS);
X if (env == NULL)
X return;
X
X cp = malloc(strlen(env) + 1);
X if (cp == NULL) {
X fprintf(stderr, "No memory\n");
X exit(1);
X }
X strcpy(cp, env);
X
X argc = 0;
X argv = argtable;
X
X while (*cp) {
X while (isblank(*cp))
X cp++;
X if (*cp == '\0')
X break;
X if (++argc > MAXARGS) {
X fprintf(stderr, "Too many arguments in LIFEOPTS\n");
X exit(1);
X }
X *argv++ = cp;
X while (*cp && !isblank(*cp))
X cp++;
X if (*cp)
X *cp++ = '\0';
X }
X
X options(argc, argtable);
X}
X
X
X/*
X * Handle command line options. Note: The first argument argv[0] is NOT
X * the usual program name, so that must have been removed if necessary.
X */
Xstatic void
Xoptions(argc, argv)
X char **argv;
X{
X char *str;
X char *cp;
X VALUE arg;
X BOOL neg;
X
X while (argc-- > 0) {
X str = *argv++;
X
X /*
X * If no option letter is used, then this means
X * read from the specified file.
X */
X if (*str != '-') {
X if (setfile(str)) {
X perror(str);
X exit(1);
X }
X continue;
X }
X
X /*
X * This is an option argument. Parse each character of
X * it, so that multiple options can be given at once.
X */
X str++;
X while (*str) switch (*str++) {
X case 't': /* use terminal */
X dev = &ttydev;
X break;
X
X case 'g': /* use graphics */
X#if defined(X11)
X dev = &x11dev;
X#else
X fprintf(stderr, "Graphics is not available\n");
X exit(1);
X#endif
X break;
X
X case 'l': /* set library path */
X if ((argc <= 0) || (**argv == '-')) {
X fprintf(stderr, "Missing library path\n");
X exit(1);
X }
X userlib = *argv++;
X argc--;
X break;
X
X case 's': /* set default scale */
X if (argc <= 0) {
X fprintf(stderr, "Missing scale\n");
X exit(1);
X }
X cp = *argv++;
X argc--;
X neg = FALSE;
X if (*cp == '-') {
X neg = TRUE;
X cp++;
X }
X arg = 0;
X while (isdigit(*cp))
X arg = arg * 10 + *cp++ - '0';
X if (*cp || (arg == 0)) {
X fprintf(stderr, "Bad scale factor\n");
X exit(1);
X }
X if (neg)
X arg = -arg;
X defaultscale = arg;
X scaleset = TRUE;
X break;
X
X default:
X fprintf(stderr, "Unknown option -%c\n", str[-1]);
X exit(1);
X }
X }
X}
X
X
X/*
X * Here on an interrupt character. Remember to stop what we are doing soon.
X * We cannot usually just longjmp away since things may be in an inconsistent
X * state. The longjmp is only allowed if we are sitting in a terminal read.
X */
Xstatic void
Xintint()
X{
X signal(SIGINT, intint);
X genleft = 0;
X stop = TRUE;
X update |= U_ALL;
X
X if (intjmpok) { /* do longjmp only if set up */
X intjmpok = FALSE;
X longjmp(intjmp, 1);
X }
X}
X
X
X/*
X * Here on an error. Close all but the top input level, cancel the
X * current command, beep, and set up to display the indicated message.
X * The message will remain until the next command is typed by the user.
X */
Xvoid
Xerror(str)
X char *str; /* message to type */
X{
X if (!inited) {
X fprintf(stderr, "%s\n", str);
X exit(1);
X }
X while (curinput > inputs)
X (*curinput->i_term)(curinput);
X errorstring = str;
X update |= U_ALL;
X stop = FALSE;
X dowait = FALSE;
X write(STDERR, "\007", 1);
X scanabort();
X}
X
X/* END CODE */
SHAR_EOF
$TOUCH -am 0316122993 life/main.c &&
chmod 0644 life/main.c ||
echo "restore of life/main.c failed"
set `wc -c life/main.c`;Wc_c=$1
if test "$Wc_c" != "4926"; then
echo original size 4926, current size $Wc_c
fi
# ============= life/mark.c ==============
echo "x - extracting life/mark.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > life/mark.c &&
X/*
X * Copyright (c) 1993 David I. Bell
X * Permission is granted to use, distribute, or modify this source,
X * provided that this copyright notice remains intact.
X */
X
X#include "life.h"
X
X
X/*
X * Structure to hold row and column pairs
X */
Xtypedef struct {
X COORD l_row;
X COORD l_col;
X} LOC;
X
X
X/*
X * Row deltas to walk a cell
X */
Xstatic COUNT rowwalk[24] = {
X -1, 0, 1, 1, 0, 0, -1, -1,
X -1, 0, 0, 0, 1, 1, 1, 1,
X 0, 0, 0, 0, -1, -1, -1, -1
X};
X
X
X/*
X * Col deltas to walk a cell
X */
Xstatic COUNT colwalk[24] = {
X 0, 1, 0, 0, -1, -1, 0, 0,
X 0, 1, 1, 1, 0, 0, 0, 0,
X -1, -1, -1, -1, 0, 0, 0, 0
X};
X
Xstatic void markloop();
X
X
X/*
X * Mark the object at a given location as specified. Returns nonzero if
X * no object exists there. An object for this purpose is considered as a
X * king-wise connected set of live cells, or a set of cells separated by
X * gaps of only one squares.
X */
XBOOL
Xmarkobject(obj, row, col, gap, mark)
X OBJECT *obj;
X COORD row;
X COORD col;
X VALUE gap;
X MARK mark;
X{
X CELL *cp;
X
X cp = findcell(obj, row, col);
X if (cp == NULL)
X return TRUE;
X cp->c_marks |= mark;
X markloop(obj, row, col, gap, mark);
X return FALSE;
X}
X
X
X/*
X * Recursive subroutine called from markobject.
X */
Xstatic void
Xmarkloop(obj, row, col, gap, mark)
X OBJECT *obj; /* object begin marked */
X COORD row; /* current row */
X COORD col; /* current column */
X VALUE gap; /* gap for marking (0 or 1) */
X MARK mark; /* marking value */
X{
X register CELL *cp; /* current cell */
X register LOC *rp; /* pointer into list table */
X int i; /* to iterate over directions */
X int steps; /* number of steps */
X LOC rclist[24]; /* row and column list */
X
X if (gap)
X steps = 24;
X else
X steps = 8;
X
X while (TRUE) {
X if (stop)
X return;
X
X rp = rclist;
X
X for (i = 0; i < steps; i++) { /* find neighbors */
X row += rowwalk[i];
X col += colwalk[i];
X cp = findcell(obj, row, col);
X if (cp == NULL)
X continue;
X if (cp->c_marks & mark)
X continue;
X cp->c_marks |= mark;
X rp->l_row = row;
X rp->l_col = col;
X rp++;
X }
X
X if (--rp != rclist) { /* recurse if more than one */
X for (; rp >= rclist; rp--) {
X markloop(obj, rp->l_row, rp->l_col, gap, mark);
X }
X return;
X }
X
X row = rp->l_row; /* else follow single cell */
X col = rp->l_col;
X }
X}
X
X
X/*
X * Mark a whole object as specified.
X */
Xvoid
Xsetmarks(obj, mark)
X OBJECT *obj;
X MARK mark;
X{
X register ROW *rp;
X register CELL *cp;
X
X mark |= MARK_ANY;
X for (rp = obj->o_firstrow; rp != termrow; rp = rp->r_next) {
X for (cp = rp->r_firstcell; cp != termcell; cp = cp->c_next) {
X cp->c_marks |= mark;
X }
X }
X}
X
X
X/*
X * Copy the marks from one type to another for an object.
X * Returns nonzero if there were no cells with the given mark.
X */
XBOOL
Xcopymarks(obj, srcmark, destmark)
X OBJECT *obj;
X MARK srcmark;
X MARK destmark;
X{
X register ROW *rp;
X register CELL *cp;
X BOOL failed;
X
X failed = TRUE;
X for (rp = obj->o_firstrow; rp != termrow; rp = rp->r_next) {
X for (cp = rp->r_firstcell; cp != termcell; cp = cp->c_next) {
X if ((cp->c_marks & srcmark) == 0)
X continue;
X cp->c_marks |= destmark;
X failed = FALSE;
X }
X }
X return failed;
X}
X
X
X/*
X * Clear marks for a whole object as specified.
X */
Xvoid
Xclearmarks(obj, mark)
X OBJECT *obj;
X MARK mark;
X{
X register ROW *rp;
X register CELL *cp;
X
X mark = ~mark;
X mark |= MARK_ANY;
X for (rp = obj->o_firstrow; rp != termrow; rp = rp->r_next) {
X for (cp = rp->r_firstcell; cp != termcell; cp = cp->c_next) {
X cp->c_marks &= mark;
X }
X }
X}
X
X
X/*
X * Mark the cells in a specified rectangular region as desired.
X * Returns the number of cells which were marked.
X */
XCOUNT
Xmarkregion(obj, mark, minrow, maxrow, mincol, maxcol)
X OBJECT *obj;
X MARK mark;
X COORD minrow;
X COORD maxrow;
X COORD mincol;
X COORD maxcol;
X{
X register ROW *rp;
X register CELL *cp;
X COUNT count;
X
X count = 0;
X for (rp = obj->o_firstrow; rp != termrow; rp = rp->r_next) {
X if (rp->r_row < minrow)
X continue;
X if (rp->r_row > maxrow)
X break;
X for (cp = rp->r_firstcell; cp != termcell; cp = cp->c_next) {
X if (cp->c_col < mincol)
X continue;
X if (cp->c_col > maxcol)
X break;
X cp->c_marks |= mark;
X count++;
X }
X }
X return count;
X}
X
X
X/*
X * Find the range of all marked cells for an object. Returns nonzero if
X * no cells were marked.
X */
XBOOL
Xmarkminmax(obj, mark, minrow, maxrow, mincol, maxcol)
X OBJECT *obj;
X MARK mark;
X COORD *minrow;
X COORD *maxrow;
X COORD *mincol;
X COORD *maxcol;
X{
X register ROW *rp;
X register CELL *cp;
X COORD row;
X COORD minr;
X COORD maxr;
X COORD minc;
X COORD maxc;
X
X minr = INFINITY;
X maxr = -INFINITY;
X minc = INFINITY;
X maxc = -INFINITY;
X
X for (rp = obj->o_firstrow; rp != termrow; rp = rp->r_next) {
X row = rp->r_row;
X
X for (cp = rp->r_firstcell; cp != termcell; cp = cp->c_next) {
X if ((cp->c_marks & mark) == 0)
X continue;
X if (row < minr)
X minr = row;
X if (row > maxr)
X maxr = row;
X if (cp->c_col < minc)
X minc = cp->c_col;
X if (cp->c_col > maxc)
X maxc = cp->c_col;
X }
X }
X
X if (minr > maxr)
X return TRUE;
X
X *minrow = minr;
X *maxrow = maxr;
X *mincol = minc;
X *maxcol = maxc;
X
X return FALSE;
X}
X
X
X/*
X * Count the number of marked cells in an object
X */
XCOUNT
Xcountmarks(obj, mark)
X OBJECT *obj;
X MARK mark;
X{
X register ROW *rp;
X register CELL *cp;
X COUNT count;
X
X count = 0;
X for (rp = obj->o_firstrow; rp != termrow; rp = rp->r_next) {
X for (cp = rp->r_firstcell; cp != termcell; cp = cp->c_next) {
X if (cp->c_marks & mark)
X count++;
X }
X }
X return count;
X}
X
X
X/*
X * Move marked cells to another object. If the destination object is NULL
X * the cells are just deleted. The previous cells of the destination object
X * are deleted.
X */
Xvoid
Xmovemarkedobject(sobj, dobj, mark)
X OBJECT *sobj;
X OBJECT *dobj;
X MARK mark;
X{
X ROW *rp;
X register CELL *cp; /* current cell pointer */
X register CELL *pcp; /* previous cell pointer */
X register CELL *ncp; /* next cell pointer */
X
X if (sobj == dobj)
X error("Moving object to itself");
X
X if (dobj) {
X zeroobject(dobj);
X dobj->o_currow = sobj->o_currow;
X dobj->o_curcol = sobj->o_curcol;
X }
X
X for (rp = sobj->o_firstrow; rp != termrow; rp = rp->r_next) {
X pcp = NULL;
X cp = rp->r_firstcell;
X while (cp != termcell) {
X if ((cp->c_marks & mark) == 0) {
X pcp = cp;
X cp = cp->c_next;
X continue;
X }
X if (dobj)
X addcell(dobj, rp->r_row, cp->c_col);
X ncp = cp->c_next;
X if (pcp == NULL)
X rp->r_firstcell = ncp;
X else
X pcp->c_next = ncp;
X if (ncp == termcell)
X rp->r_lastcell = pcp;
X cp->c_next = freecells;
X freecells = cp;
X cp = ncp;
X rp->r_count--;
X sobj->o_count--;
X }
X }
X}
X
X
X/*
X * Copy marked cells to another object. The previous cells of the destination
X * object are deleted. The source object is unaffected.
X */
Xvoid
Xcopymarkedobject(sobj, dobj, mark)
X OBJECT *sobj;
X OBJECT *dobj;
X MARK mark;
X{
X register ROW *rp;
X register CELL *cp;
X
X if (sobj == dobj)
X error("Copying object to itself");
X
X zeroobject(dobj);
X
X dobj->o_currow = sobj->o_currow;
X dobj->o_curcol = sobj->o_curcol;
X
X for (rp = sobj->o_firstrow; rp != termrow; rp = rp->r_next) {
X for (cp = rp->r_firstcell; cp != termcell; cp = cp->c_next) {
X if ((cp->c_marks & mark) == 0)
X continue;
X addcell(dobj, rp->r_row, cp->c_col);
X }
X }
X}
X
X
X/*
X * Rotate the marked cells in the given object around the current cursor
X * location. This deletes the marked cells, and reinserts them after
X * their position has been rotated by 90 degrees clockwise.
X */
Xvoid
Xrotatemarkedobject(obj, mark)
X OBJECT *obj;
X MARK mark;
X{
X register ROW *rp;
X register CELL *cp;
X COORD row;
X COORD col;
X
X if (obj == tempobject)
X error("Using temp object");
X
X movemarkedobject(obj, tempobject, mark);
X
X for (rp = tempobject->o_firstrow; rp != termrow; rp = rp->r_next) {
X row = rp->r_row - obj->o_currow;
X for (cp = rp->r_firstcell; cp != termcell; cp = cp->c_next) {
X col = cp->c_col - obj->o_curcol;
X addcell(obj, obj->o_currow + col, obj->o_curcol - row);
X }
X }
X}
X
X
X/*
X * Flip the marked cells in the given object around the column of the current
X * cursor location. This deletes the marked cells, and reinserts them after
X * their position has been flipped around the vertical axis.
X */
Xvoid
Xflipcolmarkedobject(obj, mark)
X OBJECT *obj;
X MARK mark;
X{
X register ROW *rp;
X register CELL *cp;
X COORD row;
X COORD col;
X
X if (obj == tempobject)
X error("Using temp object");
X
X movemarkedobject(obj, tempobject, mark);
X
X for (rp = tempobject->o_firstrow; rp != termrow; rp = rp->r_next) {
X row = rp->r_row;
X for (cp = rp->r_firstcell; cp != termcell; cp = cp->c_next) {
X col = cp->c_col - obj->o_curcol;
X addcell(obj, row, obj->o_curcol - col);
X }
X }
X}
X
X
X/*
X * Flip the marked cells in the given object around the row of the current
X * cursor location. This deletes the marked cells, and reinserts them after
SHAR_EOF
echo "End of part 16"
echo "File life/mark.c is continued in part 17"
echo "17" > shar3_seq_.tmp
exit 0
This archive was generated by hypermail 2.1.7 : Tue Oct 14 2003 - 21:44:09 UTC