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 and terminated by a . 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 ( ) 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 ( . ). 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 ) 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-CALL is used to perform an arbitrary operation on an arbitrary SFA-object. For example, (TYO 1 ) is equivalent to (SFA-CALL 'TYO 1). C) (SFAP ) Returns T iff is a SFA-object else returns NIL. D) (SFA-GET ) SFA-GET reads the local storage of . If the second arg is a fixnum, then location 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 ) 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 ( ) &rest ) -- The name of this type of SFA. -- a variable to be bound to the SFA itself when it is sent a message -- a variable to be bound to the name of the message. -- a list of slot names, or ( ) each slot will have an accessor macro defined for it, with a name formed by (SYMBOLCONC '- ) -- 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). -- a list of clauses. Each clause has the form ( . ) -- the ops are message keys, ala the second argument to SEND or SFA-CALL. -- 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-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. -- 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 ) 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 <3rd argument> ... ). If :SEND is the operation to SFA-UNCLAIMED-MESSAGE, AND the SFA claims to implement the (in response to a WHICH-OPERATIONS-INTERNAL query), it will turn it back into an SFA-CALL of the , 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 is WHICH-OPERATIONS. This implies a (SEND '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: !