25. STREAMS AND FILES Interlisp-D can perform input/output operations on a large variety of physical devices, including local disk drives, floppy disk drives, the keyboard and display screen, and remote file server computers accessed over a network. Often, it is desirable to create functions that can perform I/O operations to an arbitrary device, working either with files on either a local disk or a remote file server, for example. If each of these devices had its own separate set of I/O functions, it would be necessary to have a separate version of the function for each device. In order to allow user to access arbitrary I/O devices in a consistant way, Interlisp uses the idea of streams. A stream is a data object that encapsulates all of the information about a particular I/O device. Passing a stream to general I/O functions will cause the appropriate action for that particular device. Not every device knows how to implement every I/O operation, and some devices have special functions specific to that device. The specific operations implemented for each device is described in its documentation. Most of the I/O devices that can be accessed from Interlisp-D are "file devices". A file device is an I/O device that is used for storing and accessing blocks of data, called "files". Floppy disks, local disk drives, and remote file servers are all used as file devices. In order to create a new file, or access an already-existing file, you need to supply a "file name", an object that indicates the device and the specific file on the device. Interlisp uses a consistant format for file names on all devices, and provides functions for manipulating file names (see page X.XX). Output devices which implement graphics operations, such as the bitmap display and graphics printers, are accessed by using special streams called "image streams". Image streams implement a much larger set of output operations, such as drawing lines and displaying characters in multiple fonts. Image streams are described on page X.XX. This chapter describes the creation and manipulation of streams to file devices. 25.1 Terminal Input/Output Streams The litatom T is used to designate the terminal input and output stream, controlling input from the keyboard and output to the display screen. All I/O functions accept T as meaning the terminal streams. The T stream is always open. The terminal display stream can be set to a given window or display stream by using TTYDISPLAYSTREAM (page X.XX). For more information on terminal I/O, see page X.XX. 25.2 Primary Input/Output Streams The litatom NIL is used to designate the "primary" input and output streams. All I/O functions accept NIL as meaning the primary input/output streams. Initially, these streams are set to be the same as the terminal input/output streams, but they can be changed using the following functions: (INPUT FILE) [Function] Sets FILE as the primary input stream; returns the full file name of the old primary input stream. FILE must be open for input. (INPUT) returns the current primary input stream, which is not changed. (OUTPUT FILE) [Function] Sets FILE as the primary output stream; returns the full file name of the old primary output stream. FILE must be open for output. (OUTPUT) returns the current primary output stream, which is not changed. (INFILE FILE) [Function] Opens FILE for input, and sets it as the primary input stream. Equivalent to (INPUT (OPENSTREAM FILE 'INPUT 'OLD)) (OUTFILE FILE) [Function] Opens FILE for output, and sets it as the primary output stream. Equivalent to (OUTPUT (OPENSTREAM FILE 'OUTPUT 'NEW)). 25.3 Opening and Closing File Streams The basic handle on an open file is the STREAM datatype. The following functions provide user access to file streams: (OPENSTREAM FILE ACCESS RECOG PARAMETERS ---) [Function] Opens and returns a STREAM for the file specified by FILE. Causes error FILE NOT FOUND if FILE is not recognized by the file system, or other errors if FILE is recognized but cannot be opened, e.g. FILE WON'T OPEN if the file is already opened by someone else or is protected against the operation, FILE SYSTEM RESOURCES EXCEEDED if there is no more room in the file system. FILE is a file name, interpreted as described on page X.XX. ACCESS specifies the "access rights" to be used when opening the file. This should be one of the following: INPUT Only input operations are permitted on the file. Initially starts reading at the beginning of the file. OUTPUT Only output operations are permitted on the file. Initially starts writing at the beginning of the file. BOTH Both input and output operations are permitted on the file. Initially starts reading or writing at the beginning of the file. ACCESS=BOTH may not be allowed for files on some devices. APPEND Only output operations are permitted on the file. Initially starts writing at the end of the file. ACCESS=APPEND may not be allowed for files on some devices. Note: ACCESS=OUTPUT implies that one intends to write a new or different file, even if a version number was specified and the corresponding file already exists. Thus any previous contents of the file are discarded, and the file is empty immediately after the OPENSTREAM. If it is desired to write on an already existing file while preserving the old contents, the file must be opened for access BOTH or APPEND. RECOG specifies the recognition mode of FILE, as described on page X.XX. If RECOG=NIL, it defaults according to the value of ACCESS: for ACCESS=INPUT, RECOG=OLD is used; for ACCESS=OUTPUT, RECOG=NEW is used; for the other values of ACCESS, RECOG=OLD/NEW is used. PARAMETERS is a list of pairs (ATTRIB VALUE), where ATTRIB is any file attribute that the file system is willing to allow the user to set (see SETFILEINFO, page X.XX). A non-list ATTRIB in PARAMETERS is treated as the pair (ATTRIB T). OPENSTREAM also accepts the following as elements of PARAMETERS: DON'T.CHANGE.DATE If this is an element of PARAMETERS, the file's creation date is not changed when the file is opened. This is meaningful only for files being opened for output. SEQUENTIAL If this is an element of PARAMETERS, this indicates that the file is only going to be accesses sequentially. For some devices, sequential access to files is much more efficient than random access. Warning: Currently, Interlisp-D only supports having one stream open to a given file at a given time. If OPENSTREAM is called to open a file that is already open, the exact same stream will be returned. This restriction will eventually be changed (see page X.XX). (GETSTREAM FILE ACCESS) [Function] Returns a stream corresponding to an open file. FILE should be a full/partial file name. If ACCESS=NIL, returns the stream for FILE if FILE is open either for input or for output. If ACCESS is INPUT, OUTPUT or BOTH, returns the stream for FILE if it is open in that access mode. If FILE is not open in the specified mode, causes an error FILE NOT OPEN. If FILE is a stream, it is simply returned if it is open with the specified ACCESS. (STREAMP X) [Function] Returns X if X is a STREAM, otherwise NIL. (OPENP FILE ACCESS) [Function] If ACCESS=NIL, returns the full file name of FILE if FILE is open either for input or for output; otherwise NIL. If ACCESS is INPUT, OUTPUT or BOTH, returns the full file name of FILE if it is open in that access mode; otherwise NIL. Note: If FILE is not recognized, OPENP returns NIL without generating an error. (OPENP) returns a list of all files open for input or output, excluding the terminal stream (T) and the current typescript (dribble) file, if any (page X.XX). (CLOSEF FILE) [Function] Closes FILE, and returns the full file name of FILE. Generates an error, FILE NOT OPEN, if FILE is not open. If FILE is NIL, it attempts to close the primary input stream if it is not the terminal stream. Failing that, it attempts to close the primary output stream if it is not the terminal stream. Failing both, it returns NIL. If it closes either the primary input stream or the primary output stream, it resets that stream to the terminal stream. See page X.XX for information on the primary input/output streams. WHENCLOSE (page X.XX) allows the user to "advise" CLOSEF to perform various operations when a file is closed. (CLOSEF? FILE) [Function] Closes FILE if it is open, otherwise does nothing. Returns FILE. (CLOSEALL ALLFLG) [Function] Closes all open files, except the terminal stream and the current typescript file, if any. Returns a list of the files closed. WHENCLOSE (page X.XX) allows certain files to be "protected" from CLOSEALL. If ALLFLG is T, all files, including those protected by WHENCLOSE, are closed. 25.4 File Names Different file devices use different conventions for naming files. However, user programs want to handle all file names in the same way. Therefore, Interlisp supports a consistant format for file names, which is translated to the appropriate implementation-specific format when communicating to the file device. All programs that need to reference parts of a filename, or construct new file names from existing ones, should use the functions described below. Every file name is composed of a collection of fields which have different semantic interpretations. A field name is a literal atom which is the name of a file-name field. Allowable field names are: HOST, DEVICE, DIRECTORY, NAME, EXTENSION, VERSION, and TEMPORARY. (HOSTNAMEP NAME) [Function] Returns T if NAME is recognized as a valid device or remote file server name at the moment HOSTNAMEP is called. (FILENAMEFIELD FILENAME FIELDNAME) [Function] Returns the contents of the FIELDNAME field of FILENAME. (UNPACKFILENAME FILENAME ---) [Function] Returns a list of alternating field names and field contents. Examples: _ (UNPACKFILENAME 'FOO.BAR) (NAME FOO EXTENSION BAR) _ (UNPACKFILENAME '{ERIS}CURRENT>IMTRAN.DCOM;21) (HOST ERIS DIRECTORY LISP>CURRENT NAME IMTRAN EXTENSION DCOM VERSION 21) Note: (UNPACKFILENAME 'DSK:FOO) returns (DEVICE DSK: NAME FOO), i.e. the : is left in. This is so (DEVICE NIL:) may be distinguished from (DEVICE NIL). (PACKFILENAME FIELDNAME1 FIELDCONTENTS1 ... FIELDNAMEN FIELDCONTENTSN) [NoSpread Function] Takes a list of alternating field names and field contents (atoms or strings), and returns the corresponding file name. For example, (PACKFILENAME 'DIRECTORY 'LISP 'NAME 'NET) returns NET. If the same field name is given twice, the first occurrence is used. If the "field name" BODY is given, this means that the operand to BODY should itself be unpacked and spliced into the argument list at that point. This is useful for providing default field names, or to change just one field in an existing name. For example, to take a file name FILE and change the DIRECTORY field, perform (PACKFILENAME 'DIRECTORY NEWDIRECTORY 'BODY FILE). Alternatively, to provide a default for the EXTENSION field, perform (PACKFILENAME 'BODY FILE 'EXTENSION DEFAULT). This uses DEFAULT as the extension unless one is already specified in FILE. Note that a null field is a field that has been specified, e.g., if FILE=FOO;1 in the above example, the default extension will be used, but if FILE=FOO.;1, it will not, because a null extension has been specified. If the first argument to PACKFILENAME is a list, PACKFILENAME is called on that argument. Thus PACKFILENAME and UNPACKFILENAME operate as inverses. Note: PACKFILENAME interpretes the DIRECTORY field as a "directory specification", which can either be a directory name, or include host, device and/or directory with appropriate punctuation. Therefore, (PACKFILENAME 'DIRECTORY '{DSK}) returns {DSK}. 25.5 File Naming and Recognition In Interlisp, a file name is a literal atom composed of one or more fields, separated by suitable punctuation. The precise fields and their interpretation is dependent on the implementation; the functions PACKFILENAME and UNPACKFILENAME (page X.XX) are used to construct and take apart filenames in an implementation-independent way. Depending on the file system implementation, file names given to input/output functions may be incompletely specified, with the file system handling the task of obtaining a specific file from a partial name, or recognizing the file. For example, in file systems that support version numbers, one can call OPENSTREAM giving a file name without a version number, and the file system will supply a default version number based on the context (opening a new file for output vs. an old file for input). When a file is opened for input and no version number is given, the highest existing version number is used. Similarly, when a file is opened for output and no version number is given, a new file is created with a version number one higher than the highest one currently in use with that file name. The full filename consists of device or host name, directory, name, extension, and version, i.e, {ERIS}FOO.;3. Warning: The functions below can be used to perform file recognition without opening a file. Note that for some file devices (e.g., on a remote host), it isn't possible to determine the full name of a file without trying to open it. In this case, OUTFILEP and FULLNAME may not always return the correct value. For example, a remote host may not support the operation. There is also a possibility that the file system will change between the call to OUTFILEP and any actual opening of the file. Thus, OUTFILEP and FULLNAME can only be treated as a "best guess". (INFILEP FILE) [Function] Returns the full file name of FILE if FILE is recognized as specifying the name of an existing file that could potentially be opened for input, NIL otherwise. Recognition is in input context, i.e., if no version number is given, the highest existing version number is returned. (OUTFILEP FILE) [Function] Similar to INFILEP, except recognition is in output context, i.e., if no version number is given, a version number one higher than the highest existing version number is returned. Roughly speaking, OUTFILEP returns the full name of the file that would be created if OUTFILE were called with the same argument. A more general version of INFILEP and OUTFILEP is provided by the function FULLNAME: (FULLNAME X RECOG) [Function] If X is recognized in the recognition mode specified by RECOG as an abbreviation for some file, returns the file's full name, otherwise NIL. RECOG can be OLD, meaning choose the (newest) existing version of the file; NEW, meaning make the full file name one which does not yet exist (version number one higher than highest existing version); OLDEST, meaning choose the existing file with the lowest version number; or OLD/NEW, meaning to recognize an existing version if possible, otherwise a new version (useful only for writing a file). RECOG=NIL defaults to OLD. For all other values of RECOG, generates an error ILLEGAL ARG. If X is not a literal atom, generates an error, ARG NOT LITATOM. For example, INFILEP could be defined as (FULLNAME FILE 'OLD) and OUTFILEP as (FULLNAME FILE 'NEW). The RECOG argument is used only for defaulting the version, not to pass judgment on the specified parts. In particular, RECOG=NEW does not require that the file be new. For example, (FULLNAME 'FOO.;2 'NEW) may return FOO.;2 if that file already exists, even though (FULLNAME 'FOO 'NEW) would default the version to a new number, perhaps returning FOO.;5. Note that INFILEP, OUTFILEP and FULLNAME do not open any files, or change the primary files; they are pure predicates. In general they are also only hints, as they do not necessarily imply that the caller has access rights to the file. For example, INFILEP might return non-NIL, but OPENFILE might fail for the same file because the file is read-protected against the user, or the file happens to be open for output by another user at the time. Similarly, OUTFILEP could return non-NIL, but OPENFILE could fail with a FILE SYSTEM RESOURCES EXCEEDED error. Note also that in a multi-user file system, intervening file operations by another user could contradict the information returned by recognition. For example, a file that was INFILEP might be deleted, or between an OUTFILEP and the subsequent OPENFILE, another user might create a new version or delete the highest version, causing the names returned by OUTFILEP and OPENFILE to have different version numbers. Thus, in general, the "truth" about a file can only be obtained by actually opening the file; in particular, creators of files should rely on the name returned from OPENFILE, not from OUTFILEP. If the file system does not successfully recognize an incomplete file name, a FILE NOT FOUND error is generated (except for INFILEP, OUTFILEP, FULLNAME and OPENP, which in this case return NIL). As described on page X.XX, before a FILE NOT FOUND error occurs, it is intercepted via an entry on ERRORTYPELST, which causes SPELLFILE (page X.XX) to be called. SPELLFILE will search alternate directories and possibly attempt spelling correction on the file name. Only if SPELLFILE is unsuccessful will the error actually occur. Note that recognition is performed on the user's entire directory, not just the open files, which can result in certain anomalies. It is possible for a file name that was previously recognized to become ambiguous. For example, a program performs (INFILE 'FOO), opening FOO.;1, and reads several expressions from FOO. Then the user interrupts the program, creates a FOO.;2 and reenters his program. Now a call to READ giving it FOO as its FILE argument will generate a FILE NOT OPEN error, because FOO will be recognized as FOO.;2. 25.6 File Attributes Any file has a number of "file attributes", such as the read date, protection, and bytesize. The exact attributes that a file can have is dependent on the file device. The functions GETFILEINFO and SETFILEINFO allow the user to conveniently access file attributes: (GETFILEINFO FILE ATTRIB) [Function] Returns the current setting of the ATTRIB attribute of FILE. (SETFILEINFO FILE ATTRIB VALUE) [Function] Sets the attribute ATTRIB of FILE to be VALUE. SETFILEINFO returns T if it is able to change the attribute ATTRIB, and NIL if unsuccessful (some attributes cannot be changed with SETFILEINFO, such as ACCESS, CREATIONDATE, etc). GETFILEINFO and SETFILEINFO currently recognize the following values for ATTRIB: ACCESS The current access rights of FILE (see page X.XX). Can be one of INPUT, OUTPUT, BOTH, APPEND, or NIL if the file is not open. BYTESIZE The byte size of the file. Note: Currently, only 8 or NIL are accepted as the BYTESIZE file attribute. LENGTH The byte position of the end-of-file. Like (GETEOFPTR FILE), but FILE does not have to be open. SIZE The size of FILE in pages. WRITEDATE READDATE CREATIONDATE The date (and time) as a string that FILE was respectively last written, last read, and originally created. Note: CREATIONDATE could be described as "date contents last modified." It is changed when a file is initially created, but also when an existing file is opened for write, and modified. COPYFILE (page X.XX) preserves the creation date of the original file when it creates a copy. IWRITEDATE IREADDATE ICREATIONDATE The respective date in integer form, as IDATE (page X.XX) would return. AUTHOR The user who last wrote the file. TYPE The type of file; usually either TEXT or BINARY, but can include arbitrary file types for devices/hosts that understand more specific types. Devices that do not "understand" arbitrary values for TYPE treat unknown types as BINARY. Thus, GETFILEINFO may return the more general value BINARY instead of the original type that you passed to SETFILEINFO or OPENSTREAM. The variable FILING.TYPES is used to associate symbolic types with numbers for devices that actually store the TYPE attribute as a number (such as NS file servers). For example, suppose there existed an NS file type MAZEFILE with numeric value 5678. You could add the element (MAZEFILE 5678) to FILING.TYPES and then use MAZEFILE as a value for the TYPE attribute to SETFILEINFO or OPENSTREAM. Other devices are, of course, free to store TYPE attributes in whatever manner they wish, be it numeric or symbolic. EOL The end-of-line convention for the file. This can be CR, LF, or CRLF, indicating what sequences of characters on a file are converted to the implementation-dependent EOL character when atoms and strings are read or printed. 25.7 Closing and Reopening Files The function WHENCLOSE permits the user to associate certain operations with open files that govern how and when the file will be closed, and how the file's status will be restored when a SYSOUT is started up. The user can specify that certain functions will be executed before CLOSEF closes the file and/or after CLOSEF closes the file. The user can make a particular file be invisible to CLOSEALL, so that it will remain open across user invocations of CLOSEALL. Finally, the user can associate a status-saving function with a file which will be called before SYSOUT and which can specify what to do when a SYSOUT is restarted. (WHENCLOSE FILE PROP1 VAL1 ... PROPN VALN) [NoSpread Function] FILE must specify the name of an open file other than T (NIL defaults to the primary input file, if other than T, or primary output file if other than T). The remaining arguments specify properties to be associated with the full name of FILE. WHENCLOSE returns the full name of FILE as its value. WHENCLOSE recognizes the following property names: BEFORE VAL is a list of functions that CLOSEF will apply to the full name of FILE just before it is closed. This might be used, for example, to copy information about the file from an in-core data structure to the file just before it is closed. AFTER VAL is a list of functions that CLOSEF will apply to the full name of FILE just after it is closed. This capability permits in-core data structures that know about the file to be cleaned up when the file is closed. BEFORE and AFTER differ in their behavior with respect to SYSOUT. If a file that was open before SYSOUT does not have a STATUS function associated with it that causes the file to be successfully restored after the SYSOUT is started, then the file is considered to have been "closed" by the SYSOUT, and its AFTER function will be executed after the SYSOUT starts. STATUS This property provides a way of restoring the status of files when a SYSOUT is resumed. VAL is a function that will be applied to the full name of FILE just before a SYSOUT. VAL is expected to return a list, CAR of which is a function which will be APPLY'd to the CDR when the SYSOUT is started up and which will restore the status of FILE. If the value of the APPLY is NIL, it is assumed the file could not be successfully restored, a warning message is printed, and then any AFTER functions associated with the file are executed. CLOSEALL VAL is either YES or NO and determines whether FILE will be closed by CLOSEALL (YES) or whether CLOSEALL will ignore it (NO). CLOSEALL uses CLOSEF, so that any AFTER functions will be executed if the file is in fact closed. EOF VAL is a function that will be applied to the full name of FILE when an end-of-file error occurs, and the ERRORTYPELST entry for that error, if any, returns NIL. The function can examine the context of the error, and can decide whether to close the file, RETFROM some function, or perform some other computation. If the function supplied returns normally (i.e. does not RETFROM some function), the normal error machinery will be invoked (but FILE will not be automatically closed if the EOF function did not close it). Note that multiple AFTER and BEFORE functions may be associated with a file; they are executed in sequence with the most recently associated function executed first. However, a second STATUS specification will supercede an earlier one. The CLOSEALL and EOF values will also override earlier values, so only the last value specified will have an effect. Files are initialized with CLOSEALL - YES, EOF - CLOSEF. 25.8 I/O Operations With Strings Interlisp-D inherited a feature from Interlisp-10 such that if a string was given as the file argument to an input function (READ, READC, etc.), that the function would interpret the string as the contents of a file and read the characters of the string. However, this never was a very clean design, and it interferes with the desire to use strings as file names. The following function has been created to handle I/O operations from/to strings more cleanly: (OPENSTRINGSTREAM STR ACCESS) [Function] Returns a stream that can be used to access the characters of the string STR. ACCESS may be either INPUT, OUTPUT, or BOTH; NIL defaults to INPUT. The stream returned may be used exactly like a file opened with the same access, except that output operations may not extend past the end of the original string. Also, string streams do not appear in the value of (OPENP). Note: In a future release, I/O operations directly from strings will be decommissioned, and OPENSTRINGSTREAM will be the only way to treat a string as a file. 25.9 Temporary Files and the CORE Device The local DSK device and most file servers do not support the temporary or scratch files that are available in Interlisp-10. Files that are created do not disappear when some later event such as logout occurs and instead must be deleted by specific action on the part of the user. For this reason, the ;S and ;T suffixes in file names are simply ignored when output is directed to a particular host or device. However, Interlisp-D does support a notion of core-resident files, and in many cases these provide a reasonable substitute for Interlisp-10 scratch files. Core-resident files are on the device CORE (e.g. {CORE}FIE.DCOM;5). The directory for this device and all files on it are represented completely within the user's virtual memory. These files are treated as ordinary files by all file operations; their only distinguishing feature is that all trace of them disappears when the virtual memory is abandoned. In Interlisp-D, the function PACKFILENAME is defined to default the device name to CORE if the file has the TEMPORARY attribute and no explicit host is provided. If the directory information associated with CORE devices is not needed, the device NODIRCORE can be used to open core-resident files which "disappear" when they are closed. Note that {NODIRCORE} files do not have names, so the only way to manipulate them is to pass around the value that OPENFILE returned when the file was opened. Another useful device is the null device, {NULL}. Input and output streams can be opened on the null device, and passed to I/O functions. Any input operations from a null device input stream cause a end-of-file error. Any output operations to a null device output stream are no-ops. (COREDEVICE NAME NODIRFLG) [Function] Creates a new device for core-resident files and assigns NAME as its device name. Thus, after performing (COREDEVICE 'FOO), one can execute (OUTFILE '{FOO}BAR) to open a file on that device. Interlisp-D is initialized with the single core-resident device CORE, but this function may be used to create any number of logically distinct core devices. If NODIRFLG is non-NIL, a core device that acts like {NODIRCORE} is created. 25.10 End Of Line Convention While all versions of Interlisp use ASCII for internal representation of alphabetic and numeric characters, there are still some differences in the end of line conventions. In Interlisp-D, end of line is represented internally by the carriage return character (15Q), whereas the internal representation in Interlisp-10 is the EOL character (37Q). The CHARCODE macro (page X.XX) is the appropriate way to code programs to be independent of the EOL convention: in all systems (CHARCODE EOL) is always the appropriate end-of-line character. (CHARCODE CR) and (CHARCODE TENEXEOL) provide the system-dependent character codes. 25.11 Using Files with Processes Currently, Interlisp-D does not provide interlocks to keep multiple processes from trying to access the same file. Therefore, the user has to be careful not to have two processes manipulating the same file at the same time. For example, it will not work to have one process TCOMPL a file while another process is running LISTFILES on it. 25.12 Using File Names Instead of Streams In earlier implementations of Interlisp, the "handle" used to refer to an open file was the file name, represented as an atom, instead of a stream. When the file name was passed to any I/O function, it was mapped to a stream by looking it up in a list of open files. This scheme had the advantage that file names were easy to use, type in and display. However, as the system developed it appeared that this was a bad design. First, it was inefficient to map file names to files repeatedly. Second, using file names as "handles" meant that each file could only have one stream. Interlisp-D still supports the use of file names instead of streams, but it is not recommended for use in new code. The following functions are provided for backwards compatibility: (OPENFILE FILE ACCESS RECOG PARAMETERS ---) [Function] Opens FILE with access rights as specified by ACCESS, and recognition mode RECOG. Similar to OPENSTREAM (page X.XX), except that it returns the full file name, instead of the stream. (IOFILE FILE) [Function] Equivalent to (OPENFILE FILE 'BOTH 'OLD); opens FILE for both input and output. If a program is using a file name as the handle on a file, the following efficiency issue should be kept in mind: Each open file is associated with completely-specified file name that uniquely identifies the file to the file system in any context. It is this "full" file name that is returned from OPENFILE and other functions that return names of open files. For example, (OPENFILE 'FOO 'OUTPUT) might return {ERIS}FOO.;3. Any time that an input/output function is called with a file name other than the full file name, Interlisp must perform recognition on the partial file name in order to determine which open file is intended. Thus if repeated operations are to be performed, it is considerably more efficient to use the full file name returned from OPENFILE than to repeatedly use the possibly incomplete name that was used to open the file. 25.13 Future Changes and Efficiency Issues At some point in the future, the Interlisp-D i/o system will change so that each call to OPENFILE returns a distinct stream. This differs from the current behavior, inherited from Interlisp-10, that there can only be one stream open on any file, and that a second OPENFILE (assuming both are for input) will return the same "opening". This change is required in order to deal rationally with files in a multiprocessing environment. This change will of necessity produce the following incompatibilities: 1) OPENFILE will return a STREAM, not a full file name. To make this less confusing, STREAMs will have a print format that reveals the underlying file's actual name, and the functions UNPACKFILENAME and FILENAMEFIELD, when given a STREAM, will operate on the stream's name. 2) A greater penalty will ensue for passing as the FILE argument to i/o operations anything other than the object returned from OPENFILE. Passing the file's name will be significantly slower than passing the stream (even when passing the "full" file name), and in the case where there is more than one stream open on the file it might even act on the wrong one. Advice for planning for this change: Users are encouraged to write code which binds a variable to the result of OPENFILE and passes that variable to all i/o operations; such code will likely continue to work. Similar code that will work less well, if at all, is that which binds a variable to the result of an INFILEP and passes that to OPENFILE and all i/o operations; such code works well now, but implicitly assumes that INFILEP and OPENFILE return the same thing, an invalid assumption in this future world. (Code that passes incomplete file names to i/o operations is incurring a substantial performance penalty even now, and should have been changed long ago to use the result of the OPENFILE.) To see more directly the effects of passing around STREAMs instead of file names, replace your calls to OPENFILE with calls to OPENSTREAM. OPENSTREAM is called in exactly the same way, but returns a STREAM. Streams can be passed to READ, PRINT, CLOSEF, etc just as the file's full name can be currently, but using them is more efficient. The function FULLNAME, when applied to a stream, returns its full file name. 25.14 Deleting, Copying, and Renaming Files (DELFILE FILE) [Function] Deletes FILE if possible. Returns FILE if deleted, else NIL. (COPYFILE FROMFILE TOFILE) [Function] Copies a file to a new file. The source and destination may be any servers/devices. COPYFILE attempts to preserve the TYPE and CREATIONDATE where possible. COPYFILE always tries to create the new file with the same file type as the original file. If the original file's file type is unknown, COPYFILE infers the type (file type is BINARY if any of its 8-bit bytes have their high bit on). Note: COPYFILE uses the FTP file transfer protocol whenever possible. Using the FTP protocol for such file transfers is much more efficient for some file servers. (RENAMEFILE OLDFILE NEWFILE) [Function] Renames OLDFILE to be NEWFILE. Causes an error, FILE NOT FOUND if FILE does not exist. Returns the full name of the new file, if successful, else NIL if the rename cannot be performed. If OLDFILE and NEWFILE are on the same device, and the device implements the RENAMEFILE operation, RENAMEFILE can be very fast. However, if the device does not know how to rename files directly, or if OLDFILE and NEWFILE are on different devices, RENAMEFILE works by copying OLDFILE to NEWFILE and then deleting OLDFILE. 25.15 Connecting to Directories Interlisp has a notion of a "connected" directory, which is used as the default when you give a filename lacking an explicit device/host (and directory). The default is changed by using the programmer's assistant command CONN or the function CNDIR. CONN directory [Prog. Asst. Command] In Interlisp-D, directory can be of the form {host}. Either part of the argument is optional; if the directory is omitted, the default for devices that have directories is the value of (USERNAME); if the host is omitted, connection will be made to another directory on the same host as before. If CONN is given with no arguments, connects to the value of LOGINHOST/DIR. Note that CONN does not require or provide any directory access privileges. Access privileges are checked when a file is opened. (CNDIR HOST/DIR) [Function] Programmatic form of CONN. Connects to the directory HOST/DIR. Returns the fullname of the now-connected directory. LOGINHOST/DIR [Variable] CONN with no argument connects to the value of the variable LOGINHOST/DIR, initially {DSK}, but usually reset in the user's greeting file. (DIRECTORYNAME DIRNAME STRPTR ---) [Function] If DIRNAME is T, returns the currently connected host and directory name. If FLG is DIRNAME, returns the value of LOGINHOST/DIR. If STRPTR is T, the value is returned as an atom, otherwise it is returned as a string. (DIRECTORYNAMEP DIRNAME HOSTNAME) [Function] Returns T if DIRNAME is recognized as a valid directory. DIRNAME may include an explicit hostname. DIRECTORIES [Variable] Global variable containing the list of directories searched (in order) by SPELLFILE and FINDFILE (page X.XX) when not given an explicit DIRLST argument. In this list, the atom NIL stands for the login directory (LOGINHOST/DIR), and the atom T stands for the currently connected directory. LISPUSERSDIRECTORIES [Variable] Global variable containing a list of directories to search for "library" package files. Used by the FILES file package command (page X.XX). 25.16 Manipulating File Directories The function DIRECTORY allows the user to conveniently specify and/or program a variety of directory operations: (DIRECTORY FILES COMMANDS DEFAULTEXT DEFAULTVERS) [Function] Returns, lists, or performs arbitrary operations on all files specified by the "file group" FILES. A file group has the form of a regular file name, except that the character * can be used to match any number of characters in the file name. For example, the file group A*B matches all file names beginning with the character A and ending with the character B. The file group *.DCOM matches all files with an extension of DCOM. Unspecified fields in the file group default to *, except when the preceding field delimiter is included, in which case the field is explicitly null. Null version is interpreted as "highest". Thus: FILES = * or *.* or *.*;* enumerates all files; FILES = *. or *.;* enumerates all versions of files with null extension; FILES = *.; enumerates the highest version of files with null extension; and FILES = *.*; enumerates the highest version of all files. If FILES is NIL, it defaults to *.*;*. Note: Some hosts/devices are not capable of supporting "highest version" in enumeration. Such hosts instead enumerate ALL versions. For each file that matches the file group FILES, the "file commands" in COMMANDS are executed in order. Some of the file commands allow aborting the command processing for a given file. This can be used to further screen the files. The interpretation of the different file commands is described below. If COMMANDS is NIL, it defaults to (COLLECT), which collects the matching file names in a list and returns it as the value of DIRECTORY. The "file commands" in COMANDS are interpreted as follows: P Print file name. PP Print file name (except for version number). a string Prints the string. READDATE, WRITEDATE CREATIONDATE, SIZE LENGTH, BYTESIZE PROTECTION, AUTHOR TYPE Prints the appropriate information returned by GETFILEINFO (page X.XX). COLLECT The value of DIRECTORY will be a list of file names; add the complete file name of this file to that list. COUNTSIZE The value of DIRECTORY will be a sum; add the size of this file to that sum. PAUSE Wait until the user types any char before proceeding with the rest of the commands (good for display if you want to ponder). PROMPT MESS Prompts with MESS; if user responds with No, abort command processing for this file. OLDERTHAN N Continue command processing if the file hasn't been referenced (read or written) in N days. BY USER Continue command processing if the file was last written by the given user. @ X X is either a function of one argument (FILENAME), or an arbitrary expression which uses the variable FILENAME freely. If X returns NIL, abort command processing for this file. OUT FILE Directs output to FILE. COLUMNS N Attempt to format output in N columns (rather than just 1). DELETE Deletes file. If this is specified, the value of DIRECTORY is NIL if no COLLECT command is specified, otherwise the list of files deleted. DELVER Deletes all but the highest version of the file. DIRECTORY uses DIRCOMMANDS to correct spelling, which also provides a way of defining abbreviations and synonyms (page X.XX). Currently the following abbreviations are recognized: AU => AUTHOR - => PAUSE COLLECT? => PROMPT " ? " COLLECT DA TI => WRITEDATE DEL => DELETE DEL? DELETE? => PROMPT " delete? " DELETE OLD => OLDERTHAN 90 PR => PROTECTION SI => SIZE (FILDIR FILEGROUP) [Function] FILEGROUP is a file group descriptor, i.e., it can contain the character * to match any number of characters. FILDIR returns a list of the files which match FILEGROUP, like the DIRECTORY function, e.g., (FILDIR '*.DCOM). There is also a programmer's assistant command DIR which calls the function DIRECTORY: DIR FILES . COMMANDS [Prog. Asst. Command] Calls the function DIRECTORY with (P . COMMANDS) as the command list and * and * as the default extension and default version respectively. NDIR FILES . COMMANDS [Prog. Asst. Command] Version of DIR which lists the file names in a multi-column format. Also, by default only lists the most recent version of files. 25.17 File Servers A file server is a shared resource on a local communications network which provides large amounts of file storage. Different file servers honor a variety of access protocols. Interlisp-D supports three such protocols, known respectively as PUP-FTP, PUP-Leaf, and NS Filing. Other communications protocols that support file server access may be available as library packages, such as TCP/IP, RS232-based protocols, etc. Leaf is a random access protocol. It has been integrated into the Interlisp-D file system to allow files on a file server to be treated in much the same way files are accessed on the local disk. Except where noted in this section, the standard file operations (OPENFILE, INFILEP, CLOSEF, etc.) all work for remote files. This section explains how to make use of remote files and what differences exist between them and other files. 25.17.1 File Server File Names The full name of a file on a file server host includes the name of the host in braces, and a directory specification in angle brackets, e.g., {ERIS}FOO.DCOM;3. These names are not necessarily the syntax by which the actual device/server knows the files (e.g. some file servers use "!" instead of ";"), but Lisp presents a uniform set of naming conventions. The user can "connect" to a directory on a file server using the CONN command (page X.XX), after which any filename supplied that does not include the host name and/or directory will use the "connected" host and/or directory. Specifically, if the host is omitted, then the connected host is used, and if the directory is also omitted, the connected directory is used as well. If an explicit host is supplied, no defaulting of the directory occurs. Interlisp supports file access to Xerox 803x file servers, using the Filing Protocol built on Xerox Network Systems protocols. From inside Interlisp-D, one accesses files on NS servers similarly to PUP servers: any device with a colon in its name is presumed to be accessible with NS protocols rather than PUP, e.g., {PHYLEX:}. The general format of NS fileserver device names is {SERVERNAME:DOMAIN:ORGANIZATION}; the device specification for an 8000-series product must contain the ClearingHouse domain and organization. If domain and organization are not supplied directly, then they are obtained from the defaults, which themselves are found by a search for the nearest ClearingHouse. The Interlisp-D software that supports access to NS fileservers uses IFS-style pathnames and does the appropriate mapping in software. One important difference, however, is that fileserver, directory, and file names may have spaces in them. NS file servers are modeled after the Star world, and have "File Drawers" rather than directories; "File Folders" are like sub-directories. The functions DIRECTORY, FILEBROWSER, INFILE, COPYFILE, LOAD, and MAKEFILE are working now with NS file servers. NETWORKOSTYPES [Variable] Files servers on different machines have different login protocols, file name formats, etc. For proper service from file servers other than Xerox file servers, the user should add entries to the association-list NETWORKOSTYPES associating the host name (all uppercase) with its operating system type, currently one of TENEX, TOPS20, UNIX, or VMS. For example (ADDTOVAR NETWORKOSTYPES (MAXC2 . TENEX)) will inform Interlisp that the file server MAXC2 is a TENEX file server. 25.17.2 Logging In Most file servers require a user name and password for access. When a file server requests this information, Interlisp-D first uses the default user name and password. If the file server doesn't recognize that name/password, Interlisp-D prompts the user for a name and password to use. It suggests a default name, which the user can accept by typing a space, or replace by typing a new name or backspacing over it. Interlisp-D saves names and passwords for each host, so the user can login to different file servers using different names. Interlisp-D also prompts for password information when a protection violation occurs on accessing a directory, since many file servers allow setting additional protection on directories. Interlisp-D saves names and passwords for protected directories, so the user is only prompted once. (LOGIN HOSTNAME --- --- ---) [Function] Forces Interlisp-D to ask for the user name and password to be used when accessing host HOSTNAME. Any previous login information for HOSTNAME is overriden. If HOSTNAME is NIL, it overrides login information for all hosts. Password information vanishes when LOGOUT, SYSOUT, or MAKESYS is executed. Returns the user name. (SETPASSWORD HOST USER PASSWORD DIRECTORY) [Function] Sets the values in the internal password database as if typed in via LOGIN. (SETUSERNAME NAME) [Function] Sets the default user name to NAME. (USERNAME FLG STRPTR PRESERVECASE) [Function] If FLG=NIL, returns the default user name; if FLG=T, returns the connected directory name. USERNAME returns the value as a string. If STRPTR is a string pointer, it is modified and returned. If STRPTR is T, USERNAME returns the value as an atom. Usually, the value returned is converted to upper case. If PRESERVECASE is non-NIL, the case is preserved. 25.17.3 Abnormal Conditions If Interlisp-D tries to access a file and does not get a response from the file server in a reasonable period of time, it prints a message that the file server is not responding, and keeps trying. If the file server has actually crashed, this may continue indefinitely. A control-E or similar interrupt aborts out of this state. If the file server crashes but is restarted before the user attempts to do anything, file operations will usually proceed normally, except for a brief pause while Interlisp-D tries to reestablish any connections it had open before the crash. It will inform the user of any problems that arise in so doing. The most likely problem occurs when a file has been opened for output but has not yet been written to (or not enough has been written so that Interlisp-D has written to the file server). In this case the file server will think the file is not there when Interlisp-D tries to reestablish the connection. A similar situation arises if the system has been idle (or at least has not accessed the file server) for a sufficiently long period. In this case, the file server will time out the connection. Normally, Interlisp-D will attempt to recover gracefully as described above. LOGOUT closes any Leaf connections that are currently open. On return, it attempts to reestablish connections for any files that were open before logging out. If a file has disappeared or been modified, Interlisp-D reports this fact. If it is desired to break the Leaf connection without logging out, call (BREAKCONNECTION HOST). Any subsequent reference to files on that host will reestablish the connection. The main reason for doing this occurs if Interlisp-D is interrupted while a file is being opened, leaving the file server thinking the file is open and Lisp thinking it is closed, and then getting a file busy when Interlisp-D next tries to open it. On rare occasions, the Ethernet may appear completely unresponsive, due to Interlisp having gotten into a bad state. Typing (RESTART.ETHER) will reinitialize Lisp's Ethernet driver(s), just as when the Lisp system is started up following a LOGOUT, SYSOUT, etc (see page X.XX) 25.17.4 Caveats Leaf does not currently support directory enumeration except for one minor case (in the version field). Hence, DIRECTORY or FILDIR cannot be used on a Leaf file server to get a list of files. INFILEP and GETFILEINFO currently have to open the file for input in order to obtain their information, and hence the file's read date will change, even though the semantics of these functions do not imply it. This differs from the operation of DSK. Interlisp supports simultaneous access to the same server from different processes and permits overlapping of Lisp computation with file server operations, allowing for improved performance. However, as a corollary of this, a file is not closed the instant that CLOSEF returns; Interlisp closes the file "in the background". It is therefore very important that the user exits Interlisp via (LOGOUT), or (LOGOUT T), rather than boot the machine or exit via Raid. 25.17.5 Sequential vs. Random Access Files Interlisp-D includes an implementation of the PupFtp protocol, which supports transferring files sequentially only. In those cases where sequential access (as opposed to random access) to a file is appropriate, the use of PupFtp generally results in considerable speed improvement over Leaf, particularly for writing files on a Xerox IFS. The system tries to use PupFtp where possible for SYSOUT and for the destination file of a COPYFILE. One can indicate that a file is going to be accessed only sequentially by including the keyword SEQUENTIAL in the list of PARAMETERS passed to OPENSTREAM (page X.XX); the PupFtp will be used, if possible. If for some reason your file server supports PupFtp but you do not wish COPYFILE or SYSOUT to use it, you can set the internal variable \FTPAVAILABLE to NIL. Copyright (c) 1985 Xerox Corporation. All rights reserved.