{Begin SubSec File Attributes}
{Title File Attributes}
{Text


{index *PRIMARY* 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 {fn GETFILEINFO} and {fn SETFILEINFO} allow the user to conveniently access file attributes:

{note {fn GETFILEINFO} and {fn SETFILEINFO} for Interlisp-10 were written by R. M. Kaplan and J. J. Vittal.}


{FnDef {FnName GETFILEINFO} {FnArgs FILE ATTRIB}
{Text
Returns the current setting of the {arg ATTRIB} attribute of {arg FILE}.
}}

{FnDef {FnName SETFILEINFO} {FnArgs FILE ATTRIB VALUE}
{Text
Sets the attribute {arg ATTRIB} of {arg FILE} to be {arg VALUE}.  {fn SETFILEINFO} returns {lisp T} if it is able to change the attribute {arg ATTRIB}, and {lisp NIL} if unsuccessful, either because the file device does not recognize {arg ATTRIB} or because the file device does not permit the attribute to be modified.
}}

The {arg FILE} argument to {fn GETFILEINFO} and {fn SETFILEINFO} can be an open stream (or an argument designating an open stream, see {PageRef fn GETSTREAM}), or the name of a closed file.  {fn SETFILEINFO} in general requires write access to the file.

The attributes recognized by {fn GETFILEINFO} and {fn SETFILEINFO} fall into two categories: {it permanent} attributes, which are properties of the file, and {it temporary} attributes, which are properties only of an open stream to the file.  The temporary attributes are only recognized when {arg FILE} designates an open stream; the permanent attributes are usually equally accessible for open and closed files.  However, some devices are willing to change the value of certain attributes of an open stream only when specified in the {arg PARAMETERS} argument to {fn OPENSTREAM} ({PageRef fn OPENSTREAM}), not on a later call to {fn SETFILEINFO}.

The following are currently recognized as permanent attributes of a file:

{Begin LabeledList Permanent File Attributes}

{Label {lisp BYTESIZE}{index BYTESIZE (File Attribute)}}
{Text
The byte size of the file.  Interlisp-D currently only supports byte size 8.
}

{Label {lisp LENGTH}{index LENGTH (File Attribute)}}
{Text
The number of bytes in the file.  Alternatively, the byte position of the end-of-file.  Like {lisp (GETEOFPTR {arg FILE})}, but {arg FILE} does not have to be open.
}

{Label {lisp SIZE}{index SIZE (File Attribute)}}
{Text
The size of {arg FILE} in pages.
}

{Label {lisp CREATIONDATE}{index CREATIONDATE (File Attribute)}}
{Text
The date and time, as a string, that the content of {arg FILE} was "created".  The creation date changes whenever the content of the file is modified, but remains unchanged when a file is transported, unmodified, across file systems.  Specifically, {fn COPYFILE} and {fn RENAMEFILE} ({PageRef Fn COPYFILE}) preserve the file's creation date.  Note that this is different from the concept of "creation date" used by some operating systems (e.g., Tops20).
}

{Label {lisp WRITEDATE}{index WRITEDATE (File Attribute)}}
{Text
The date and time, as a string, that the content of {arg FILE} was last written to this particular file system.  When a file is copied, its creation date does not change, but its write date becomes the time at which the copy is made.
}
  
{Label {lisp READDATE}{index READDATE (File Attribute)}}
{Text
The date and time, as a string, that {arg FILE} was last read, or {lisp NIL} if it has never been read.
}


{Label {lisp ICREATIONDATE}{index ICREATIONDATE (File Attribute)}}
{Label {lisp IWRITEDATE}{index IWRITEDATE (File Attribute)}}
{Label {lisp IREADDATE}{index IREADDATE (File Attribute)}}
{Text
The {lisp CREATIONDATE}, {lisp WRITEDATE} and {lisp READDATE}, respectively, in integer form, as {fn IDATE} ({PageRef Fn IDATE}) would return.  This form is useful for comparing dates.
}

{Label {lisp AUTHOR}{index AUTHOR (File Attribute)}}
{Text
The name of the user who last wrote the file.
}

{Label {lisp TYPE}{index TYPE (File Attribute)}}
{Text
The "type" of the file, some indication of the nature of the file's content.  The "types" of files allowed depends on the file device.  Most devices recognize the litatom {lisp TEXT} to mean that the file contains just characters, or {lisp BINARY} to mean that the file contains arbitrary data.

Some devices support a wider range of file types that distinguish among the various sorts of files one might create whose content is "binary".  All devices interpret any value of {lisp TYPE} that they do not support to be {lisp BINARY}.  Thus, {fn GETFILEINFO} may return the more general value {lisp BINARY} instead of the original type that was passed to {fn SETFILEINFO} or {fn OPENSTREAM}.  Similarly, {fn COPYFILE}, while attempting to preserve the {lisp TYPE} of the file it is copying, may turn, say, an {lisp INTERPRESS} file into a mere {lisp BINARY} file.

The way in which some file devices (e.g., Xerox file servers) support a wide range of file types is by representing the type as an integer, whose interpretation is known by the client.  The variable {var FILING.TYPES}{index *PRIMARY* FILING.TYPES Var} is used to associate symbolic types with numbers for these devices.  This list initially contains some of the well-known assignments of type name to number; the user can add additional elements to handle any private file types.  For example, suppose there existed an NS file type {lisp MAZEFILE} with numeric value 5678.  You could add the element {lisp (MAZEFILE 5678)} to {var FILING.TYPES} and then use {lisp MAZEFILE} as a value for the {lisp TYPE} attribute to {fn SETFILEINFO} or {lisp OPENSTREAM}.  Other devices are, of course, free to store {lisp TYPE} attributes in whatever manner they wish, be it numeric or symbolic.  {var FILING.TYPES} is merely considered the official registry for Xerox file types.

For most file devices, the {lisp TYPE} of a newly created file, if not specified in the {arg PARAMETERS} argument to {fn OPENSTREAM}, defaults to the value of {var DEFAULTFILETYPE}{index DEFAULTFILETYPE Var}, initially {lisp TEXT}.

{note The following devices recognize general types: CORE, the Dorado DSK, NS File Servers.  Currently, no non-Xerox servers support other than the default types}
}

{End LabeledList Permanent File Attributes}


The following are currently recognized as temporary attributes of an open stream:

{Begin LabeledList Temporary File Attributes}

{Label {lisp ACCESS}{index ACCESS (File Attribute)}}
{Text
The current access rights of the stream (see {PageRef Term File Access Rights}).  Can be one of {lisp INPUT}, {lisp OUTPUT}, {lisp BOTH}, {lisp APPEND}; or {lisp NIL} if the stream is not open.
}

{Label {lisp ENDOFSTREAMOP}{index ENDOFSTREAMOP (File Attribute)}}
{Text
The action to be taken when a stream is at "end of file" and an attempt is made to take input from it.  The value of this attribute is a function of one argument, the stream.  The function can examine the stream and its calling context and take any action it wishes.  If the function returns normally, its should return either {lisp T}, meaning to try the input operation again, or the byte that {fn BIN} would have returned had there been more bytes to read.  Ordinarily, one should not let the {lisp ENDOFSTREAMOP} function return unless one is only performing binary input from the file, since there is no way in general of knowing in what state the reader was at the time the end of file occurred, and hence how it will interpret a single byte returned to it.

The default {lisp ENDOFSTREAMOP} is a system function that causes the error {lisp END OF FILE}.{index END OF FILE (Error)}  The behavior of that error can be further modified for a particular stream by using the {lisp EOF} option of {fn WHENCLOSE} ({PageRef fn WHENCLOSE}).
}

{Label {lisp EOL}{index EOL (File Attribute)}}
{Text
The end-of-line convention for the stream.{index *PRIMARY* End-of-line character}  This can be {lisp CR}, {lisp LF}, or {lisp CRLF}, indicating with what byte or sequence of bytes the "End Of Line" character is represented on the stream.  On input, that sequence of bytes on the stream is read as {lisp (CHARCODE EOL)} by {fn READCCODE} or the string reader.  On output, {lisp (TERPRI)} and {lisp (PRINTCCODE (CHARCODE EOL))} cause that sequence of bytes to be placed on the stream.

The end of line convention is usually not apparent to the user.  The file system is usually aware of the convention used by a particular remote operating system, and sets this attribute accordingly.  If you believe a file actually is stored with a different convention than the default, it is possible to modify the default behavior by including the {lisp EOL} attribute in the {arg PARAMETERS} argument to {fn OPENSTREAM}.
}


{Label {lisp BUFFERS}{index BUFFERS (File Attribute)}}
{Text
Value is the number of 512-byte buffers that the stream maintains at one time.  This attribute is only used by certain random-access devices (currently, the local disk, floppy, and Leaf servers); all others ignore it.

Streams open to files generally maintain some portion of the file buffered in memory, so that each call to an I/O function does not require accessing the actual file on disk or a file server.  For files being read or written sequentially, not much buffer space is needed, since once a byte is read or written, it will never need to be seen again.  In the case of random access streams, buffering is more complicated, since a program may jump around in the file, using {fn SETFILEPTR} ({PageRef Fn SETFILEPTR}).  In this case, the more buffer space the stream has, the more likely it is that after a {fn SETFILEPTR} to a place in the file that has already been accessed, the stream still has that part of the file buffered and need not go out to the device again.  This benefit must, of course, be traded off against the amount of memory consumed by the buffers.
}

{End LabeledList Temporary File Attributes}

}{End SubSec File Attributes}


{Begin SubSec Closing and Reopening Files}
{Title Closing and Reopening Files}
{Text

{index *PRIMARY* Closing and reopening files}
{index *PRIMARY* Reopening files}


The function {fn WHENCLOSE} permits the user to associate certain operations with open streams that govern how and when the stream will be closed.  The user can specify that certain functions will be executed before {fn CLOSEF} closes the stream and/or after {fn CLOSEF} closes the stream.  The user can make a particular stream be invisible to {fn CLOSEALL}, so that it will remain open across user invocations of {fn CLOSEALL}.


{FnDef {FnName WHENCLOSE} {FnArgs FILE PROP{SUB 1} VAL{SUB 1} {ellipsis} PROP{SUB N} VAL{SUB N}}
{Type NOSPREAD}
{Text
{arg FILE} must designate an open stream other than {lisp T} ({lisp NIL} defaults to the primary input stream, if other than {lisp T}, or primary output stream if other than {lisp T}).  The remaining arguments specify properties to be associated with the full name of {arg FILE}.  {fn WHENCLOSE} returns the full name of {arg FILE} as its value.
}}


{fn WHENCLOSE} recognizes the following property names:

{Begin LabeledList WHENCLOSE property names}

{Label {lisp BEFORE}}
{Text
{arg VAL} is a function that {fn CLOSEF} will apply to the stream 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.
}

{Label {lisp AFTER}}
{Text
{arg VAL} is a function that {fn CLOSEF} will apply to the stream just after it is closed.  This capability permits in-core data structures that know about the stream to be cleaned up when the stream is closed.  
}


{Label {lisp CLOSEALL}}
{Text
{arg VAL} is either {lisp YES} or {lisp NO} and determines whether {arg FILE} will be closed by {index CLOSEALL FN}{fn CLOSEALL} ({lisp YES}) or whether {fn CLOSEALL} will ignore it ({lisp NO}).  {fn CLOSEALL} uses {fn CLOSEF}, so that any {lisp AFTER} functions will be executed if the stream is in fact closed.  Files are initialized with {lisp CLOSEALL} set to {lisp YES}.
}

{Label {lisp EOF}}
{Text
{arg VAL} is a function that will be applied to the stream when an end-of-file error occurs, and the {var ERRORTYPELST} entry for that error, if any, returns {lisp NIL}.  The function can examine the context of the error, and can decide whether to close the stream, {fn RETFROM} some function, or perform some other computation. If the function supplied returns normally (i.e., does not {fn RETFROM} some function), the normal error machinery will be invoked.

The default {lisp EOF} behavior, unless overridden by this {fn WHENCLOSE} option, is to call the value of {var DEFAULTEOFCLOSE} (below).

For some applications, the {lisp ENDOFSTREAMOP} attribute ({PageRef (File attribute) ENDOFSTREAMOP}) is a more useful way to intercept the end-of-file error.  The {lisp ENDOFSTREAMOP} attribute comes into effect before the error machinery is ever activated.

{Note there should be a more elegent way to have no error other than using a RETFROM.  Perhaps if the EOF fn returns non-NIL, there should be no error.}
}

{End LabeledList WHENCLOSE property names}


Multiple {lisp AFTER} and {lisp BEFORE} functions may be associated with a file; they are executed in sequence with the most recently associated function executed first.  The {lisp CLOSEALL} and {lisp EOF} values, however, will override earlier values, so only the last value specified will have an effect.

{VarDef {Name DEFAULTEOFCLOSE}
{Text
Value is the name of a function that is called by default when an end of file error occurs and no {lisp EOF} option has been specified for the stream by {fn WHENCLOSE}.  The initial value of {var DEFAULTEOFCLOSE} is {fn NILL}, meaning take no special action (go ahead and cause the error).  Setting it to {fn CLOSEF} would cause the stream to be closed before the rest of the error machinery is invoked.
}}

{begin Comment Interlisp-10 specific}
{lisp BEFORE} and {lisp AFTER} differ in their behavior with respect to {fn SYSOUT}.  If a stream that was open before {fn SYSOUT} does not have a {lisp STATUS} function associated with it that causes the stream to be successfully restored after the {fn SYSOUT} is started, then the stream is considered to have been "closed" by the {fn SYSOUT}, and its {lisp AFTER} function will be executed after the {fn SYSOUT} starts. 

{lisp STATUS}
This property provides a way of restoring the status of files when a {fn SYSOUT} is resumed.  {arg VAL} is a function that will be applied to the stream just before a {fn SYSOUT}.  {arg VAL} is expected to return a list, {fn CAR} of which  is a function which will be {fn APPLY}'d to the {fn CDR} when the {fn SYSOUT} is started up and which will restore the status of {arg FILE}.  If the value of the {fn APPLY} is {lisp NIL}, it is assumed the file could not be successfully restored, a warning message is printed, and then any {lisp AFTER} functions associated with the file are executed.  A second {lisp STATUS} specification will supercede an earlier one.  
{end Comment Interlisp-10 specific}


}{End SubSec Closing and Reopening Files}



{Begin SubSec Local Hard Disk Device}
{Title Local Hard Disk Device}
{Text

{index *PRIMARY* Local hard disk device}
{index *PRIMARY* Hard disk device}
{index *PRIMARY* DSK (file device)}

{it Warning:  This section describes the Interlisp-D functions that control the local hard disk drive available on some computers.  All of these functions may not work on all computers running Interlisp-D.  For more information on using the local hard disk facilities, see the users guide for your computer.}


This section describes the local file system currently supported on the Xerox 1108 and 1186 computers.  The Xerox 1132 supports a simpler local file system.  The functions below are no-ops on the Xerox 1132, except for {index DISKPARTITION Fn}{fn DISKPARTITION} (which returns a disk partition number), and {index DISKFREEPAGES Fn}{fn DISKFREEPAGES}.  On the Xerox 1132, different numbered partitions are referenced by using devices such as {lisp {bracket DSK1}}, {lisp {bracket DSK2}}, etc.  {lisp {bracket DSK}} always refers to the disk partition that Interlisp is running on.  The 1132 local file system does not support the use of directories.



The hard disk used with the Xerox 1108 or 1186 may be partitioned into a number of named {index Logical volumes}"logical volumes."  Logical volumes may be used to hold the Interlisp {index Virtual memory file}virtual memory file (see {PageRef Term Virtual memory file}), or Interlisp files.  For information on intializing and partitioning the hard disk, see the users guide for your computer.  In order to store Interlisp files on a logical volume, it is necessary to create a lisp file directory on that volume (see {fn CREATEDSKDIRECTORY}, below).  


So long as there exists a logical volume with a Lisp directory on it, files on this volume can be accessed by using the file device called {lisp {bracket DSK}}.  Interlisp-D can be used to read, write, and otherwise interact with files on local disk disks through standard Interlisp input/output functions.  All I/O functions such as {fn LOAD}, {fn OPENSTREAM}, {fn READ}, {fn PRINT}, {fn GETFILEINFO}, {fn COPYFILE}, etc., work with files on the local disk.

If you do not have a logical volume with a Lisp directory on it, Interlisp emulates the {lisp {bracket DSK}} device by a core device, a file device whose backing store is entirely within the Lisp virtual memory.  However, this is not recommended because the core device only provides limited scratch space, and since the core device is contained in virtual memory, it (and the files stored on it) will be erased when the virtual memory file is reloaded.


Each logical volume with a Lisp directory on it serves as a directory of the device {lisp {bracket DSK}}.  Files are referred to by forms such as

{lispcode
{bracket DSK}<{arg VOLUMENAME}>{arg FILENAME}}

Thus, the file {lisp INIT.LISP} on the volume {lisp LISPFILES} would be called {lisp {bracket DSK}<LISPFILES>INIT.LISP}.  

Subdirectories within a logical volume are supported, using the > character in file names to delimit subdirectory names.  For example, the file name {lisp {bracket DSK}<LISPFILES>DOC>DESIGN.TEDIT} designates the file names {lisp DESIGN.TEDIT} on the subdirectory {lisp DOC} on the logical volume {lisp LISPFILES}.

If a logical volume name is not specified, it defaults in an unusual but simple way: the logical volume defaults to the next logical volume that has a lisp file directory on it including or after the volume containing the currently running virtual memory.  For example, if the local disk has the logical volumes {lisp LISP}, {lisp TEMP}, and {lisp LISPFILES}, the {lisp LISP} volume contains the running virtual memory, and only the {lisp LISP} volume has a Lisp file directory on it, then {lisp {bracket DSK}INIT.LISP} refers to the file {lisp {bracket DSK}<LispFiles>INIT.LISP}.  All the functions below default logical volume names in a similar way, except for those such as {fn CREATEDSKDIRECTORY}.  To determine the current default lisp file directory, evaluate {lisp (DIRECTORYNAME '{bracket DSK})}.



{FnDef {Name CREATEDSKDIRECTORY} {Args VOLUMENAME {anonarg}}
{Text
Creates a lisp file directory on the logical volume {arg VOLUMENAME}, and returns the name of the directory created.  It is only necessary to create a lisp file directory the first time the logical volume is used.  After that, the system automatically recognizes and opens access to the logical volumes that have lisp file directories on them.
}}


{FnDef {Name PURGEDSKDIRECTORY} {Args VOLUMENAME {anonarg}}
{Text
Erases all lisp files on the volume {arg VOLUMENAME}, and deletes the lisp file directory.
}}



{FnDef {Name LISPDIRECTORYP} {Args VOLUMENAME}
{Text
Returns {lisp T} if the logical volume {arg VOLUMENAME} has a lisp file directory on it.
}}


{FnDef {Name VOLUMES} {Args }
{Text
Returns a list of the names of all of the logical volumes on the local hard disk (whether they have lisp file directories or not).
}}


{FnDef {Name VOLUMESIZE} {Args VOLUMENAME {anonarg}}
{Text
Returns the total size of the logical volume {arg VOLUMENAME} in disk pages.
}}

{FnDef {Name DISKFREEPAGES} {Args VOLUMENAME {anonarg}}
{Text
Returns the total number of free disk pages left on the logical volume {arg VOLUMENAME}.
}}


{FnDef {Name DISKPARTITION} {Args }
{Text
Returns the name of the logical volume containing the {index Virtual memory file}virtual memory file that Interlisp is currently running in (see {PageRef Term Virtual memory file}).
}}


{FnDef {Name DSKDISPLAY} {Args NEWSTATE}
{Text
Controls a display window that displays information about the logical volumes on the local hard disk (logical volume names, sizes, free pages, etc.).  {fn DSKDISPLAY} opens or closes this display window depending on the value of {arg NEWSTATE} (one of {lisp ON}, {lisp OFF}, or {lisp CLOSED}), and returns the previous state of the display window.

If {arg NEWSTATE} is {lisp ON}, the display window is opened, and it is automatically updated whenever the file system state changes (this can slow file operations significantly).  If {arg NEWSTATE} is {lisp OFF}, the display window is opened, but it is not automatically updated.  If {arg NEWSTATE} is {lisp CLOSED}, the display window is closed.  The display mode is initially set to {lisp CLOSED}.

Once the display window is open, the user can update it or change its state with the mouse.  Left-buttoning the display window updates it, and middle-buttoning the window brings up a menu that allows you to change the display state.

Note:  {fn DSKDISPLAY} uses the value of the variable {index *PRIMARY* DSKDISPLAY.POSITION Var}{var DSKDISPLAY.POSITION} for the position of the lower-left corner of the disk display window when it is opened.  This variable is changed if the disk display window is moved.
}}


{FnDef {Name SCAVENGEDSKDIRECTORY} {Args VOLUMENAME SILENT}
{Text
Rebuilds the lisp file directory for the logical volume {arg VOLUMENAME}.  This may repair damage in the unlikely event of file system failure, signified by symptoms such as infinite looping or other strange behavior while the system is doing a directory search.  Calling {fn SCAVENGEDSKDIRECTORY} will not harm an intact volume.

Normally, {fn SCAVENGEDSKDIRECTORY} prints out messages as it scavenges the directory.  If {arg SILENT} is non-{lisp NIL}, these messages are not printed.

Note:  Some low-level disk failures may cause {index HARD DISK ERROR Error}"{lisp HARD DISK ERROR}" errors to occur.  To fix such a failure, it may be necessary to log out of Interlisp, scavenge the logical volume in question using Pilot tools, and then call {fn SCAVENGEDSKDIRECTORY} from within Interlisp.  See the users guide for your computer for more information. 
}}



}{End SubSec Local Hard Disk Device}



{Begin SubSec Floppy Disk Device}
{Title Floppy Disk Device}
{Text

{index *PRIMARY* Floppy disk drive}
{index *PRIMARY* FLOPPY (file device)}

{it Warning:  This section describes the Interlisp-D functions that control the floppy disk drive available on some computers.  All of these functions may not work on all computers running Interlisp-D.  For more information on using the floppy disk facilities, see the users guide for your computer.}

The floppy disk drive is accessed through the device {lisp {bracket FLOPPY}}.  Interlisp-D can be used to read, write, and otherwise interact with files on floppy disks through standard Interlisp input/output functions.  All I/O functions such as {fn LOAD}, {fn OPENSTREAM}, {fn READ}, {fn PRINT}, {fn GETFILEINFO}, {fn COPYFILE}, etc., work with files on floppies.

Note that floppy disks are a removable storage medium.  Therefore, it is only meaningful to perform i/o operations to the floppy disk drive, rather than to a given floppy disk.  In this section, the phrase "the floppy" is used to mean "the floppy that is currently in the floppy disk drive."

For example, the following sequence could be used to open a file {lisp XXX.TXT} on the floppy, print "Hello" on it, and close it:

{lispcode
(SETQ XXX (OPENSTREAM '{bracket FLOPPY}XXX.TXT 'OUTPUT 'NEW)
(PRINT "Hello" XXX)
(CLOSEF XXX)}

{FnDef {Name FLOPPY.MODE} {Args MODE}
{Text
Interlisp-D can currently read and write files on floppies stored in a number of different formats.  At any point, the floppy is considered to be in one of four "modes," which determines how it reads and writes files on the floppy.  {fn FLOPPY.MODE} sets the {index Floppy disk modes}floppy mode to the value of {arg MODE}, one of {lisp PILOT}, {lisp HUGEPILOT}, {lisp SYSOUT}, or {lisp CPM}, and returns the previous floppy mode.  The floppy modes are interpreted as follows:

{Begin Labeledlist FLOPPY.MODE modes}

{Label {lisp PILOT}}
{Text
This is the normal floppy mode, using floppies in the Xerox {index Pilot floppy disk format}Pilot floppy disk format.  This file format allows all of the normal Interlisp-D I/O operations.  This format also supports file names with arbitrary levels of subdirectories.  For example, it is possible to create a file named {lisp {bracket FLOPPY}<Lisp>Project>FOO.TXT}. 
}

{Label {lisp HUGEPILOT}}
{Text
This floppy mode is used to access files that are larger than a single floppy, stored on multiple floppies.  There are some restrictions with using "huge" files.  Some I/O operations are not meaningful for "huge" files.  When a stream is created for output in this mode, the {lisp LENGTH} file attribute ({PageRef (File Attribute) LENGTH}) must be specified when the file is opened, so that it is known how many floppies will be needed.  When an output file is created, the floppy (or floppies) are automatically erased and reformatted (after confirmation from the user).

{lisp HUGEPILOT} mode is primarily useful for saving big files to and from floppies.  For example, the following could be used to copy the file {lisp {bracket ERIS}<Lisp>Bigfile.txt} onto the huge Pilot file {lisp {bracket FLOPPY}BigFile.save}:

{lispcode
(FLOPPY.MODE 'HUGEPILOT)
(COPYFILE '{bracket ERIS}<Lisp>Bigfile.txt '{bracket FLOPPY}BigFile.save)}

and the following would restore the file:

{lispcode
(FLOPPY.MODE 'HUGEPILOT)
(COPYFILE '{bracket FLOPPY}BigFile.save '{bracket ERIS}<Lisp>Bigfile.txt)}

During each copying operation, the user will be prompted to insert "the next floppy" if {lisp {bracket ERIS}<Lisp>Bigfile.txt} takes multiple floppies.
}

{Label {lisp SYSOUT}}
{Text
Similar to {lisp HUGEPILOT} mode, {lisp SYSOUT} mode is used for storing {index Sysout files}sysout files ({PageRef Term Sysout files}) on multiple floppy disks.  The user is prompted to insert new floppies as they are needed.

This mode is set automatically when {fn SYSOUT} or {fn MAKESYS} is done to the floppy device:  {lisp (SYSOUT '{bracket FLOPPY})} or {lisp (MAKESYS '{bracket FLOPPY})}.  Notice that the file name does not need to be specifed in {lisp SYSOUT} mode; unlike {lisp HUGEPILOT} mode, the file name {lisp Lisp.sysout} is always used.

Note:  The procedure for loading sysout files from floppies depends on the particular computer being used.  For information on loading sysout files from floppies, see the users guide for your computer.

Explicitly setting the mode to {lisp SYSOUT} is useful when copying a sysout file to or from floppies.  For example, the following can be used to copy the sysout file {lisp {bracket ERIS}<Lisp>Lisp.sysout} onto floppies (it is important to set the floppy mode back when done):

{lispcode
(FLOPPY.MODE 'SYSOUT)
(COPYFILE '{bracket ERIS}<Lisp>Lisp.sysout '{bracket FLOPPY})
(FLOPPY.MODE 'PILOT)}
}

{Label {lisp CPM}}
{Text
Interlisp-D supports the single-density single-sided (SDSS) CPM floppy format (a standard used by many computers).  CPM-formatted floppies are totally different than Pilot floppies, so the user should call {fn FLOPPY.MODE} to switch to {lisp CPM} mode when planning to use CPM floppies.  After switching to {lisp CPM} mode, {fn FLOPPY.FORMAT} can be used to create CPM-formatted floppies, and the usual input/output operations work with CPM floppy files.

Note:  There are a few limitations on CPM floppy format files:  (1) CPM file names are limited to eight or fewer characters, with extensions of three or fewer characters;  (2) CPM floppies do not have directories or version numbers; and (3) CPM files are padded out with blanks to make the file lengths multiples of 128.
}

{End Labeledlist FLOPPY.MODE modes}

}}




{FnDef {Name FLOPPY.FORMAT} {Args NAME AUTOCONFIRMFLG SLOWFLG}
{Text
{fn FLOPPY.FORMAT} erases and initializes the track information on a floppy disk.  This must be done when new floppy disks are to be used for the first time.  This can also be used to erase the information on used floppy disks.

{arg NAME} should be a string that is used as the name of the floppy (106 characters max).  This name can be read and set using {fn FLOPPY.NAME} (below).

If {arg AUTOCONFIRMFLG} is {lisp NIL}, the user will be prompted to confirm erasing the floppy, if it appears to contain valid information.  If {arg AUTOCONFIRMFLG} is {lisp T}, the user is not prompted to confirm.

If {arg SLOWFLG} is {lisp NIL}, only the Pilot records needed to give your floppy an empty directory are written.  If {arg SLOWFLG} is {lisp T}, {fn FLOPPY.FORMAT} will completely erase the floppy, writing track information and critical Pilot records on it.  {arg SLOWFLG} should be set to {lisp T} when formatting a brand-new floppy.

Note:  Formatting a floppy is a very compute-intensive operation for the I/O hardware.  Therefore, the cursor may stop tracking the mouse and keystrokes may be lost while formatting a floppy.  This behavior goes away when the formatting is finished.

Warning:  The floppy mode set by {fn FLOPPY.MODE} (above) affects how {fn FLOPPY.FORMAT} formats the floppy.  If the floppy is going to be used in Pilot mode, it should be formatted under {lisp (FLOPPY.MODE 'PILOT)}.  If it is to be used as a CMP floppy, it should be formatted under {lisp (FLOPPY.MODE 'CPM)}.  The two types of formatting are incompatible.
}}


{FnDef {Name FLOPPY.NAME} {Args NAME}
{Text
If {arg NAME} is {lisp NIL}, returns the name stored on the floppy disk.  If {arg NAME} is non-{lisp NIL}, then the name of the floppy disk is set to {arg NAME}.
}}

{FnDef {Name FLOPPY.FREE.PAGES} {Args }
{Text
Returns the number of unallocated free pages on the floppy disk in the floppy disk drive.

Note:  Pilot floppy files are represented by contiguous pages on a floppy disk.  If the user is creating and deleting a lot of files on a floppy, it is advisable to keep such a floppy less than 75 percent full.
}}

{FnDef {Name FLOPPY.CAN.READP} {Args }
{Text
Returns non-{lisp NIL} if there is a floppy in the floppy drive.

Note:  {fn FLOPPY.CAN.READP} does not provide any debouncing (protection against not fully closing the floppy drive door).  It may be more useful to use {fn FLOPPY.WAIT.FOR.FLOPPY} (below).
}}

{FnDef {Name FLOPPY.CAN.WRITEP} {Args }
{Text
Returns non-{lisp NIL} if there is a floppy in the floppy drive and the floppy drive can write on this floppy.

It is not possible to write on a floppy disk if the "write-protect notch" on the floppy disk is punched out.
}}


{FnDef {Name FLOPPY.WAIT.FOR.FLOPPY} {Args NEWFLG}
{Text
If {arg NEWFLG} is {lisp NIL}, waits until a floppy is in the floppy drive before returning.

If {arg NEWFLG} is {lisp T}, waits until the existing floppy in the floppy drive, if any, is removed, then waits for a floppy to be inserted into the drive before returning.
}}


{FnDef {Name FLOPPY.SCAVENGE} {Args }
{Text
Attempts to repair a floppy whose critical records have become confused (causing errors when file operations are attempted).  May also retrieve accidently-deleted files, provided they haven't been overwritten by new files.  
}}


{FnDef {Name FLOPPY.TO.FILE} {Args TOFILE}
{Text
Copies the entire contents of the floppy to the {index *PRIMARY* Floppy image file}"floppy image" file {arg TOFILE}, which can be on a file server, local disk, etc.  This can be used to create a centralized copy of a floppy, that different users can copy to their own floppy disks (using {fn FLOPPY.FROM.FILE}).

Note:  A floppy image file for an 8-inch floppy is about 2500 pages long, regardless of the number of pages in use on the floppy.
}}

{FnDef {Name FLOPPY.FROM.FILE} {Args FROMFILE}
{Text
Copies the "floppy image" file {arg FROMFILE} to the floppy.  {arg FROMFILE} must be a file produced by {fn FLOPPY.TO.FILE}.
}}


{FnDef {Name FLOPPY.ARCHIVE} {Args FILES NAME}
{Text
{fn FLOPPY.ARCHIVE} formats a floppy inserted into the floppy drive, giving the floppy the name {lisp {arg NAME}#1}.  {fn FLOPPY.ARCHIVE} then copies each file in {arg FILES} to the freshly formatted floppy.  If the first floppy fills up, {fn FLOPPY.ARCHIVE} uses multiple floppies (named {lisp {arg NAME}#2}, {lisp {arg NAME}#3}, etc.), each time prompting the user to insert a new floppy.

The function {fn DIRECTORY} ({PageRef Fn DIRECTORY}) is convenient for generating a list of files to archive.  For example,

{lispcode
(FLOPPY.ARCHIVE
   (DIRECTORY '{bracket ERIS}<Lisp>Project>*)
   'Project)}

will archive all files on the directory {lisp {bracket ERIS}<Lisp>Project>} to floppies (named {lisp Project#1}, {lisp Project#2}, etc.).
}}


{FnDef {Name FLOPPY.UNARCHIVE} {Args HOST/DIRECTORY}
{Text
{fn FLOPPY.UNARCHIVE} copies all files on the current floppy to the directory {arg HOST/DIRECTORY}.  For example, {lisp (FLOPPY.UNARCHIVE '{bracket ERIS}<Lisp>Project>)} will copy each file on the current floppy to the directory {lisp {bracket ERIS}<Lisp>Project>}.  If there is more than one floppy to restore from archive, {fn FLOPPY.UNARCHIVE} should be called on each floppy disk.
}}



}{End SubSec Floppy Disk Device}





{Begin SubSec I/O Operations to and from Strings}
{Title I/O Operations to and from Strings}
{Text

It is possible to treat a string as if it were the contents of a file by using the following function:

{FnDef {Name OPENSTRINGSTREAM} {Args STR ACCESS}
{Text 
Returns a stream that can be used to access the characters of the string {arg STR}.  {arg ACCESS} may be either {lisp INPUT}, {lisp OUTPUT}, or {lisp BOTH}; {lisp NIL} defaults to {lisp 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 {lisp (OPENP)}.

For example, after performing

{lispcode
(SETQ STRM (OPENSTRINGSTREAM "THIS 2 (IS A LIST)"))}

the following succession of reads could occur:

{lispcode
   (READ STRM)  => THIS
   (RATOM STRM) => 2
   (READ STRM)  => (IS A LIST)
   (EOFP STRM)  => T}

}}

Compatibility Note: In Interlisp-10 it was possible to take input from a string simply by passing the string as the {arg FILE} argument to an input function.  In order to maintain compatibility with this feature, Interlisp-D provides the same capability.  This not terribly clean feature persists in the present implementation to give users time to convert old code.  This means that strings are {it not} equivalent to litatoms when specifying a file name as a stream argument (see {PageRef Tag StreamFileNames}).  In a future release, the old Interlisp-10 string-reading feature will be decommissioned, and {fn OPENSTRINGSTREAM} will be the only way to perform I/O on a string.

}{End SubSec I/O Operations to and from Strings}



{Begin SubSec Temporary Files and the CORE Device}
{Title Temporary Files and the CORE Device}
{Text

Many operating systems have a notion of "scratch file", a file typically used as temporary storage for data most naturally maintained in the form of a file, rather than some other data structure.  A scratch file can be used as a normal file in most respects, but is automatically deleted from the file system after its useful life is up, e.g., when the job terminates, or the user logs out.  In normal operation, the user need never explicitly delete such files, since they are guaranteed to disappear soon.

A similar functionality is provided in Interlisp-D by core-resident files.  Core-resident files are on the device {lisp CORE}{index CORE (file device)}.  The directory structure 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.

Core files are opened and closed by name the same as any other file, e.g., {lisp (OPENSTREAM '{bracket CORE}<FOO>FIE.DCOM 'OUTPUT)}.  Directory names are completely optional, so files can also have names of the form {lisp {bracket CORE}NAME.EXT}.  Core files can be enumerated by {fn DIRECTORY} ({PageRef fn DIRECTORY}).  While open, they are registered in {lisp (OPENP)}.  They do consume virtual memory space, which is only reclaimed when the file is deleted.  Some caution should thus be used when creating large {lisp CORE} files.  Since the virtual memory of an Interlisp-D workstation usually persists far longer than the typical process on a mainframe computer, it is still important to delete {lisp CORE} files after they are no longer in use.

For many applications, the name of the scratch file is irrelevant, and there is no need for anyone to have access to the file independent of the program that created it.  For such applications, {lisp NODIRCORE} files are preferable.  Files created on the device lisp {lisp NODIRCORE}{index NODIRCORE (file device)} are core-resident files that have no name and are registered in no directory.  These files "disappear", and the resources they consume are reclaimed, when all pointers to the file are dropped.  Hence, such files need never be explicitly deleted or, for that matter, closed.  The "name" of such a file is simply the stream object returned from {lisp (OPENSTREAM '{bracket NODIRCORE} 'OUTPUT)}, and it is this stream object that must be passed to all input/output operations, including {fn CLOSEF} and any calls to {fn OPENSTREAM} to reopen the file.   


{FnDef {Name COREDEVICE} {Args NAME NODIRFLG}
{Text
Creates a new device for core-resident files and assigns {arg NAME} as its device name.  Thus, after performing {lisp (COREDEVICE 'FOO)}, one can execute {lisp (OPENSTREAM '{bracket FOO}BAR 'OUTPUT)} to open a file on that device.  Interlisp-D is initialized with the single core-resident device named {lisp CORE}, but {fn COREDEVICE} may be used to create any number of logically distinct core devices.

If {arg NODIRFLG} is non-{lisp NIL}, a core device that acts like {lisp {bracket NODIRCORE}} is created.
}}


Compatibility note: In Interlisp-10, it was possible to create scratch files by using file names with suffixes {lisp ;S} or {lisp ;T}.  In Interlisp-D, these suffixes in file names are simply ignored when output is directed to a particular host or device.  However, the function {fn PACKFILENAME.STRING} is defined to default the device name to {lisp CORE} if the file has the {lisp TEMPORARY} attribute and no explicit host is provided.


{note how to create new device structures?  document?}

}{End SubSec Temporary Files and the CORE Device}


{Begin SubSec NULL Device}
{Title NULL Device}
{Text

{index NULL (file device)}

The {lisp NULL} device provides a source of content-free "files".  {lisp (OPENSTREAM '{bracket NULL} 'OUTPUT)} creates a stream that discards all output directed at it.  {lisp (OPENSTREAM '{bracket NULL} 'INPUT)} creates a stream that is perpetually at end-of-file (i.e., has no input).  

}{End SubSec NULL Device}



{Begin Comment End Of Line Convention}
I don't think this belongs here.  Perhaps with CHARCODE?  It's only an issue for Interlisp-10 programmers moving into Interlisp-D --bvm

{Begin SubSec End Of Line Convention}
{Title End Of Line Convention}
{Text

While all versions of Interlisp use ASCII for internal representation of alphabetic and numeric characters, there are still some differences in the {it 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 {lisp EOL} character (37Q).  The {fn CHARCODE} macro ({PageRef Fn CHARCODE}) is the appropriate way to code programs to be independent of the {lisp EOL} convention: in all systems {lisp (CHARCODE EOL)} is always the appropriate end-of-line character.  {lisp (CHARCODE CR)} and {lisp (CHARCODE TENEXEOL)} provide the system-dependent character codes.  

}{End SubSec End Of Line Convention}
{End Comment End Of Line Convention}