		Using the reference/communications package


The package keeps a collection of "handles" to named objects, each
bearing a name, data type, and a pointer to the current value of the object.

By maintaining the data type we keep name spaces separate; a Geom called "fred"
won't be confused with a Camera called "fred", preventing wildly inappropriate
assignments from being made.

The data type is determined by an array of function pointers (HandleOps)
performing various operations on that type of object (import from stream,
delete, etc.).

Operations on Handles include:

	Create/assign a new value:
	    Handle *HandleAssign(char *name, HandleOps *ops, Ref *object);
		Finds and returns the appropriate Handle, creating it if not
		present, and sets the Handle's current object.
		All registered references to the Handle's value are updated.

	Find a Handle, given its name and data-type:
	    Handle *HandleByName(char *name, HandleOps *ops)
		Returns NULL if no such handle exists.

	Get the object currently held by a Handle:
	    Ref *HandleObject(Handle *handle);
		Note that the object may be NULL.

	Set the current object of a Handle:
	    void HandleSetObject(Handle *handle, Ref *object)

	Register a Ref * as one we should update when a Handle's object is set
	    void HandleRegister(Handle **hp, Ref *parent, void *info,
							  void (*update)())
		Whenever the Handle's object changes, we automatically call
		   (*update)(hp, parent, info)

	    void HandleUnregister(Handle **)
		Remember to HandleUnregister() when deleting an object or
		changing its Handle pointer!

	Do everything needed by XXXFLoad() routines to set up an object ref:
	    Handle *HandleReferringTo(int prefixch,
				   char *refstring,
				   HandleOps *ops);


Operations on stream-type Pools (each associated with a file I/O stream)
include:

	Opening a Pool:

	    Pool *PoolStreamOpen(char *fname, int rw, HandleOps *ops)

		Opens a Pool.  PoolStreamFuncs is a collection of function
		pointers:
			int (*import)(Handle **, Ref **, Pool *);
			int (*export)(Handle *, Ref *, Pool *);
			int (*close)(Pool *);
			int (*resync)(Pool *);

	Main-loop operations:

	    int PoolInputFDs( fd_set *fds, int *max );
		To know whether there is input available on any open pool,
		applications need the union of all pool file descriptors.
		This call returns that set, which can be fed to select()
		after adding any application-specific file descriptors.
		It also returns (in max) the number + 1 of the highest
		file descriptor.

		Pools might also have pending input in buffers which will
		not be visible to select().  In this case we want to ensure
		that select() will not block, so the value of PoolInputFDs() is
		   1 if some input is buffered (select should be given
			a short or zero timeout) or
		   0 otherwise.

	    int PoolReadStreams( fd_set *fds, int count );
		After select() returns, the set of input-ready file descriptors
		should be passed to PoolReadStreams(), with select()'s return
		value (upper bound on the number of ready streams) passed in
		count.  PoolReadStreams() removes from fds the file descriptors
		it uses, and returns the decremented count.

Stream syntax:

    Defining a named object:

    name "fred"

	assigns the name "fred" to the object in which it appears.

	Also, when an object with no "name" keyword is read from a file,
	it should be entered under a handle with the same name as the file.

    < "file"

	XXXFLoad() routines should, if asked to load from a plain file
	(as opposed to a named pipe), check for a Handle by that name.
	If it exists and has a non-NULL associated object, use that
	rather than opening and reading the file.  In combination with the
	previous rule, this avoids the overhead of re-reading files.


    Embedded references

    < "file"
    < ":name"
    < "file:name"

	These sequences allow objects in streams to refer to other
	named objects.  The first form refers to a named object from an
	unspecified source; the second indicates that the object should
	be read from "file".  The syntax implies that filenames can't contain
	colons, but this doesn't seem too bad.

    
Typical code snippets might be:

    In an XXXFLoad() routine:
	char *myname = filename;
	...
	    case '<':
		w = ftoken(infile);
		HandleReferringTo('<', w, XXXstreamfuncs);
		break;

	    case "name":
		myname = ftoken(infile);
	...

	if(object != NULL && myname != NULL)
	    HandleAssign(myname, XXXDelete, (Ref *)object);

	return object;
