I) Introduction.

	There are many instances when a MacLISP programmer wishes to
simulate an arbitrary I/O device.  That is, he wishes to use the primitives
provided in LISP for doing I/O (such as READ and PRINT) yet wishes the
target/source of the characters to be completely arbitrary.  I propose a
mechanism called an "SFA" (Software File Array) to handle this situation.

	An SFA consists of a function and some associated local storage
bound up in an "SFA-object".  In order to satisfy the above goals, an
SFA-object may be used in almost all places that a file-object can be used.
Note that the existence of SFAs will not obviate the need for the current
NEWIO implementation of files.  SFAs are strictly a user entity, and the
files are still needed in order to communicate with the operating system.

	The SFA-function is a user-defined EXPR/SUBR which accepts 3
arguments.  Its first argument is the SFA-object on which it is to act, and
the second argument is a symbol indicating the operation to be performed.
The third argument is operation specific.  The SFA-object needs to be
supplied to the function because one SFA-function may be associated with
many different SFA-objects.  There are some operations used by the system
(TYI, TYO, etc...).   The user is free to define new operations and to use
them by calling the SFA directly.

	As there are predefined uses for many of the system I/O functions
that currently exist, when these functions are translated to SFA operations
they must retain the semantics they have when applied to file-objects.
Since this is the case, any SFA that expects to interface directly to the
standard I/O routines must know about the difference between binary and
character-oriented operations.  Generally, an SFA will only handle one type
of I/O (i.e. most SFAs will not handle both the IN and TYI operation), yet
the structure of SFAs will not preclude them handling both.


II) System defined operations

1) Operations applicable to both input and output SFAs

A) WHICH-OPERATIONS

   The SFA must return a list of operations which it can perform.
Every SFA *must* handle the WHICH-OPERATIONS operation.  

B) OPEN

   This operation is invoked if an OPEN is done with a SFA as its first
argument.  The SFA should return either a file-object or a
SFA-object.  The third argument to the SFA is the second argument to
the OPEN, and it defaults to NIL.


C) CLOSE

   This operation is invoked when a CLOSE is done with a SFA as its
argument.  There is never a third arg, and the SFA should  return either 
T if the close was successful and NIL if not (e.g. if the SFA was already
"closed".)


D) DELETEF

   No arguments will be passed to the SFA.  There is no clear global
semantic meaning of this.


E) FILEPOS

    If an argument is given to FILEPOS, it is NCONS'ed and handed to the
SFA, else the SFA is given NIL.  For a NIL argument, a fixnum should be
returned.  For one argument, the SFA should interpret the argument as a
"position" to set the "file" to, and perform appropriate actions.


F) (STATUS FILEMODE) [operation: FILEMODE]

   The SFA should return a list of adjectives describing itself.  If the
SFA cannot support the FILEMODE operation (as determined by a
system-performed WHICH-OPERATIONS check), then a list of the form:

	((SFA) {results of a WHICH-OPERATIONS call})

is returned.

2) Functions applicable to SFAs which can do input

   If the SFA is to support normal LISP reading other than TYI, it must
support at least TYI and UNTYI, and preferably TYIPEEK also.

A) TYI

   The SFA should return a fixnum representing a character.  This operation
may be called from a READ, READLINE, or a straight TYI (the SFA cannot
depend upon being able to determine the context).  The argument is the
value to return when EOF is reached (this is true for all input functions
including IN).


B) UNTYI

   The SFA should put the argument into its input stream and on the next
TYI should spew forth the saved character rather than the next one in the
"stream".  Note that an arbitrary number of UNTYI's may be done in a row.


C) TYIPEEK

   The SFA should return a fixnum (as in TYI) but should not remove the
character from the input flow.  Therefore, subsequent TYIPEEK's will read
the same character.  If the SFA cannot handle TYIPEEK, it will be simulated
by a TYI/UNTYI pair of operations.


D) IN

   The value returned should be a fixnum that is the next binary value in
the input stream.  The argument is the EOF value.


E) READLINE

   The value returned should be a symbol that represents one "line" of the
input stream.  If the SFA cannot handle this operation, it is simulated in
terms of TYI.  The argument is the value to return upon EOF.


F) READ

   The value returned should be the next "object" in the input stream  If
the SFA cannot handle this operation, it is simulated in terms of
TYI/UNTYI.  The argument is the value to return upon EOF.


G) LISTEN
   CLEAR-INPUT

    For LISTEN, the total number of items (or "characters") currently
held in the input buffer is returned;  for CLEAR-INPUT, the input buffer
is just thrown away.

3) Functions applicable to SFAs which can do output

A) TYO

   The argument is a fixnum representing a character.  The value returned
should be T if the SFA succeeds, and NIL if the output was unsuccessful.


B) OUT

   The argument is a fixnum and is output as such.  The TYO/OUT pair of
functions is the analog to the TYI/IN pair.


C) FORCE-OUTPUT
   CLEAR-OUTPUT

   For FORCE-OUTPUT, the SFA should empty its output buffer to the logical 
"device" to which it is connected; for CLEAR-OUTPUT, any buffer is just
thrown away.

D) PRIN1

   The SFA should print the arg in its slashified form.  If the SFA
cannot perform this operation, it is simulated in terms of TYO.


E) PRINT

   The SFA should print the arg in its slashified form preceeded by a
<NEWLINE> and terminated by a <SPACE>.  If the SFA cannot perform this
operation, it is simulated in terms of TYO.


F) PRINC

   The SFA should print the arg in its unslashified form.  If the SFA
cannot perform this operation, it is simulated in terms of TYO.


G) CURSORPOS

   The SFA will receive a list of the data args given to CURSORPOS. There
may be 0, 1 or 2 of these.  If given the horizontal and vertical position,
there will be two elements in the list (<Vertical> <Horizontal>) which are
zero-indexed fixnums (or NIL for either/both meaning use the current value).
If given a single ascii character object, a relative cursor movement will
be expected (eg, C=Clear Screen, U=Move Up, ... [See .INFO.;LISP CURSOR])
If an empty data list is received, the SFA-handler should return the current
cursor position as a dotted pair (<Vertical> . <Horizontal>). There is
no clear global semantic meaning of this operations when
performed on non-tty's.


H) RUBOUT 

    The sfa should try to delete the last character output (either
by main output, or by echo output).  See documentation on the MacLISP
RUBOUT function.


III) System functions for manipulating SFAs

A) (SFA-CREATE <old-SFA or SFA-function>
	       <amount-of-local-user-storage>
	       <printed-representation>)

   SFA-CREATE returns an SFA-object which represents a function and
associated local storage.  If the first arg is a SYMBOL, then that symbol
is taken as the SFA-function;  if the first arg is an SFA-object, then the
function associated with it is used as the SFA-function, and the local
storage is possibly reclaimed (the first arg being an SFA is not yet
implemented).  The second arg to this function should be the number of user
locations to allocate in the SFA-object.

   When the SFA-CREATE is done, the SFA is immediatly invoked with a
WHICH-OPERATIONS call.  A mask is then created corresponding to the
internal functions that the SFA knows how to do.  This mask is used for
fast error-checking when a predefined operation is handed an SFA (e.g.
TYO).


B) (SFA-CALL <SFA-object> <operation> [<argument to SFA>])

   SFA-CALL is used to perform an arbitrary operation on an arbitrary
SFA-object.  For example, (TYO 1 <SFA>) is equivalent to
(SFA-CALL <SFA> 'TYO 1).


C) (SFAP <lisp-object>)

   Returns T iff <lisp-object> is a SFA-object else returns NIL.


D) (SFA-GET <SFA-object> <fixnum or system-location-name>)

   SFA-GET reads the local storage of <SFA-object>.   If the second
arg is a fixnum, then location <fixnum> of the user's local storage is
accessed.  This number may range from 0 to the maximum as specified when
the SFA-object is created {see SFA-CREATE}.  If the second arg is a
symbol, then one of the "named" locations is accessed.  The names are as
follows:

	FUNCTION		The SFA-function
	WHICH-OPERATIONS	A list of operations of interest to the
				 system that the SFA can perform.  This is a
				 subset of the SFAs WHICH-OPERATIONS list.
	PNAME			The object to print as the "name" of an SFA
	PLIST 			A general property list, for use by the user.
	XCONS 			A "correspondent" for a bi-directional SFA;
				 akin to the TTYCONS concept for TTY's.

E) (SFA-STORE <SFA-object> <fixnum or system-location-name> <value>)

   <value> is stored in the location specified by the second arg to
SFA-STORE.  Locations are as for SFA-GET.  It is strongly recommended that
the SFA-function NEVER be changed.




IV) Higher level tools.

A) DEFSFA -- Define a SFA

   [requires some runtime support, which will be autoloaded in]

(DEFSFA <sfa-name> (<self-var> <op-var>) <slot-specs> <options>
	&rest <clauses>)

<sfa-name> -- The name of this type of SFA.

<self-var> -- a variable to be bound to the SFA itself when it is sent a
message

<op-var> -- a variable to be bound to the name of the message.

<slot-specs> -- a list of slot names, or (<slot-name> <initial-value>)
each slot will have an accessor macro defined for it, with a name formed by
(SYMBOLCONC <sfa-name> '- <slot-name>)

<options> -- a list of option specs.  An option spec is either a bare
symbol, denoting that the specified option is TRUE, or a list of an option
name and a value.  The only option defined currently is :INIT, which
suppresses newly consed SFA's of this type getting a :INIT message if no
initial values are supplied.  (If initial values are supplied, the :INIT
message must be sent in order to store them).

<clauses> -- a list of clauses.  Each clause has the form
  (<op or list of ops> <argspec> . <body>)
  <op or list of ops> -- the ops are message keys, ala the second argument
			 to SEND or SFA-CALL.
  <argspec> -- This is either a symbol, in which case it is bound to the
	       3rd argument, or a list of two or more symbols, in which
	       case it is bound to the 3rd-<n+2>th arguments.  No error
	       checking on number of arguments will be done.  If the symbol
	       is atomic, it will be reachable via either SFA-CALL or SEND,
	       otherwise it will be reachable only by SEND.  This is
	       because SFA-CALL does not allow other than 3 arguments to be
	       passed.
  <body> -- list of forms to be evaluated.  The last one's value will be
	    returned.

This defines accessor macros for each slot in the SFA, plus a macro for
creating the SFA, named by prepending CONS-A- to the sfa-name.  The
CONS-A-mumble macro does the SFA-CREATE, and immediately sends it a :INIT
message with a list of alternating slot-names and values.

All this works via the following lower-level mechanisms:

B) (SFA-UNCLAIMED-MESSAGE <sfa> <operation> <data>) Basically, this reports
the error.  But first, it handles several special cases.  The first is
turning a SEND into an SFA-CALL.  When a SEND is performed on a SFA, it is
simulated by doing an SFA-CALL with an operation of :SEND, and a 3rd
argument of (LIST <old operation> <3rd argument> ...  <nth argument>).  If
:SEND is the operation to SFA-UNCLAIMED-MESSAGE, AND the SFA claims to
implement the <old operation> (in response to a WHICH-OPERATIONS-INTERNAL
query), it will turn it back into an SFA-CALL of the <old operation>, with
any extra arguments being thrown away.  If it DOESN'T claim to handle it
the message is passed off to the superclass via SEND-AS

A subcase of the :SEND case is when the <old operation> is
WHICH-OPERATIONS.  This implies a (SEND <sfa> 'WHICH-OPERATIONS) was done.
So this is simulated by mergeing any operations obtainable from the
superclass of SFA-CLASS.  (If the EXTEND package isn't loaded, it doesn't
try to get them though.)

If the message is WHICH-OPERATIONS-INTERNAL, then the handler did not
implement WHICH-OPERATIONS-INTERNAL.  We assume WHICH-OPERATIONS will
do the job and SFA-CALL that.

If the message is something other than :SEND, it just passes the message on
up to the superclass with SEND-AS.

It is recommended that any SFA's which for one reason or another do not use
DEFSFA should use SFA-UNCLAIMED-MESSAGE.  Essentially the correct behaviour
with respect to SEND should result.

;;; Local modes: !
;;; Mode: Text !
;;; Auto fill mode:1 !
;;; Fill column:75 !
;;; End: !
