{Begin SubSec Opening and Closing File Streams}
{Title Opening and Closing File Streams}
{Text

{note a lot of the basic functions refer to files, rather than streams.  This documentation needs to be reworked carefully.}

In order to perform input from or output to a file, it is necessary to create a stream to the file, using {fn OPENSTREAM}:

{FnDef {FnName OPENSTREAM} {FnArgs FILE ACCESS RECOG PARAMETERS {anonarg}}
{Text
Opens and returns a stream for the file specified by {arg FILE}, a file name.  {arg FILE} can be either a string or a litatom.  The syntax and manipulation of file names is described at length on {PageRef Term File Names}.  Incomplete file names are interpreted with respect to the connected directory ({PageRef fn CNDIR}).


{arg RECOG} specifies the recognition mode of {arg FILE}, as described on {PageRef fn FULLNAME}.  If {arg RECOG}={lisp NIL}, it defaults according to the value of {arg ACCESS}.

{arg ACCESS} specifies the {index *PRIMARY* File access rights}"access rights" to be used when opening the file, one of the following:

{Begin LabeledList OPENSTREAM access rights}

{Label {lisp INPUT}{index INPUT (File access)}}
{Text
Only input operations are permitted on the file.  The file must already exist.  Starts reading at the beginning of the file.  {arg RECOG} defaults to {lisp OLD}.
}

{Label {lisp OUTPUT}{index OUTPUT (File access)}}
{Text
Only output operations are permitted on the file.  Starts writing at the beginning of the file, which is initially empty.  While the file is open, other users or processes are unable to open the file for either input or output.  {arg RECOG} defaults to {lisp NEW}.
}

{Label {lisp BOTH}{index BOTH (File access)}}
{Text
Both input and output operations are permitted on the file.  Starts reading or writing at the beginning of the file.  {arg RECOG} defaults to {lisp OLD/NEW}.  {arg ACCESS}={lisp BOTH} implies random accessibility ({PageRef Tag RandomIO}), and thus may not be possible for files on some devices.
}

{Label {lisp APPEND}{index APPEND (File access)}}
{Text
Only sequential output operations are permitted on the file.  Starts writing at the {it end} of the file.  {arg RECOG} defaults to {lisp OLD/NEW}.  {arg ACCESS}={lisp APPEND} may not be allowed for files on some devices.
}

{End LabeledList OPENSTREAM access rights}

Note: {arg ACCESS}={lisp 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 {fn OPENSTREAM}.  If it is desired to write on an already existing file while preserving the old contents, the file must be opened for access {lisp BOTH} or {lisp APPEND}.

{arg PARAMETERS} is a list of pairs {lisp ({arg ATTRIB} {arg VALUE})}, where {arg ATTRIB} is any file attribute that the file system is willing to allow the user to set (see {fn SETFILEINFO}, {PageRef Fn SETFILEINFO}).  A non-list {arg ATTRIB} in {arg PARAMETERS} is treated as the pair {lisp ({arg ATTRIB} {lisp T})}.  Generally speaking, attributes that belong to the permanent file (e.g., {lisp TYPE}) can only be set when creating a new file, while attributes that belong only to a particular opening of a file (e.g., {lisp ENDOFSTREAMOP}) can be set on any call to {fn OPENSTREAM}.  Not all devices honor all attributes; those not recognized by a particular device are simply ignored.

In addition to the attributes permitted by {fn SETFILEINFO}, the following tokens are accepted by {fn OPENSTREAM} as values of {arg ATTRIB} in its {arg PARAMETERS} argument:

{Begin LabeledList Additional elements of PARAMETERS}

{Label {index DON'T.CHANGE.DATE (OPENSTREAM parameter)}{lisp DON'T.CHANGE.DATE}}
{Text
If {arg VALUE} is non-{lisp NIL}, the file's creation date ({PageRef (File Attribute) CREATIONDATE}) is not changed when the file is opened.  This option is meaningful only for old files being opened for access {lisp BOTH}.  This should be used only for specialized applications in which the caller does not want the file system to believe the file's content has been changed.
}

{Label {index SEQUENTIAL (OPENSTREAM parameter)}{lisp SEQUENTIAL}}
{Text
If {arg VALUE} is non-{lisp NIL}, this opening of the file need support only sequential access; i.e., the caller intends never to use {fn SETFILEPTR}.  For some devices, sequential access to files is much more efficient than random access.  Note that the device may choose to ignore this attribute and still open the file in a manner that permits random access.  Also note that this attribute does not make sense with {arg ACCESS}={lisp BOTH}.
}

{End LabeledList Additional elements of PARAMETERS}
}}


If {arg FILE} is not recognized by the file system, {fn OPENSTREAM} causes the error {index FILE NOT FOUND Error}{lisp FILE NOT FOUND}.  Ordinarily, this error is intercepted via an entry on {var ERRORTYPELST}{index ERRORTYPELST Var} ({PageRef Var ERRORTYPELST}), which causes {fn SPELLFILE}{index SPELLFILE FN} ({PageRef Fn SPELLFILE}) to be called.  {fn SPELLFILE} searches alternate directories and possibly attempts spelling correction on the file name.  Only if {fn SPELLFILE} is unsuccessful will the {lisp FILE NOT FOUND} error actually occur.

If {arg FILE} exists but cannot be opened, {fn OPENSTREAM} causes one of several other errors: {lisp FILE WON'T OPEN}{index FILE WON'T OPEN Error} if the file is already opened for conflicting access by someone else; {lisp PROTECTION VIOLATION}{index PROTECTION VIOLATION Error} if the file is protected against the operation; {lisp FILE SYSTEM RESOURCES EXCEEDED}{index FILE SYSTEM RESOURCES EXCEEDED Error} if there is no more room in the file system.

{Begin Note}
Comment I think there is ample warning of this elsewhere in this chapter.  Let's not add too much confusion so early in the chapter to this already long definition. --bvm

Warning:  Currently, Interlisp-D does not support multiple simultaneous openings of the same file.  If {fn OPENSTREAM} is called to open a file that is already open, the exact same stream is returned, in effect shared by all callers.  This restriction will be changed in some future release (see {PageRef Term Multiple streams to a file}).
{End Note}


{FnDef {FnName CLOSEF} {FnArgs FILE}
{Text
Closes {arg FILE}, and returns its full file name.  Generates an error, {lisp FILE NOT OPEN}{index FILE NOT OPEN Error}, if {arg FILE} does not designate an open stream.  After closing a stream, no further input/output operations are permitted on it.

If {arg FILE} is {lisp NIL}, it is defaulted to the primary input stream{index primary input stream} if that is not the terminal stream, or else the primary output stream{index primary output stream} if that is not the terminal stream.  If both primary input and output streams are the terminal input/output streams, {fn CLOSEF} returns {lisp NIL}.  If {fn CLOSEF} closes either the primary input stream or the primary output stream (either explicitly or in the {arg FILE} = {lisp NIL} case), it resets the primary stream for that direction to be the corresponding terminal stream.  See {PageRef Term Primary Input Stream} for information on the primary input/output streams.

{fn WHENCLOSE} ({PageRef Fn WHENCLOSE}) allows the user to "advise" {fn CLOSEF} to perform various operations when a file is closed.

Because of buffering, the contents of a file open for output are not guaranteed to be written to the actual physical file device until {fn CLOSEF} is called.  Buffered data can be forced out to a file without closing the file by using the function {fn FORCEOUTPUT} ({PageRef fn FORCEOUTPUT}).

Some network file devices perform their transactions in the background.  As a result, it is possible for a file to be closed by {fn CLOSEF} and yet not be "fully" closed for some small period of time afterward, during which time the file appears to still be busy, and cannot be opened for conflicting access by other users.
}}


{FnDef {FnName CLOSEF?} {FnArgs FILE}
{Text
Closes {arg FILE} if it is open, returning the value of {fn CLOSEF}; otherwise does nothing and returns {lisp NIL}.
}}


In the present implementation of Interlisp-D, all streams to files are kept, while open, in a registry of "open files".  This registry does not include nameless streams, such as string streams ({PageRef fn OPENSTRINGSTREAM}), display streams ({PageRef fn TTYDISPLAYSTREAM}), and the terminal input and output streams; nor streams explicitly hidden from the user, such as dribble streams ({PageRef FN DRIBBLE}).  This registry may not persist in future implementations of Interlisp-D, but at the present time it is accessible by the following two functions:

{FnDef {FnName OPENP} {FnArgs FILE ACCESS}
{Text
{arg ACCESS} is an access mode for a stream opening (one of {lisp INPUT}, {lisp OUTPUT}, {lisp BOTH}, or {lisp APPEND}), or {lisp NIL}, meaning any access.

If {arg FILE} is a stream, returns its full name if it is open for the specified access, else {lisp NIL}.

If {arg FILE} is a file name (a litatom), {arg FILE} is processed according to the rules of file recognition ({PageRef fn FULLNAME}).  If a stream open to a file by that name is registered and open for the specified access, then the file's full name is returned.  If the file name is not recognized, or no stream is open to the file with the specified access, {lisp NIL} is returned.

If {arg FILE} is {lisp NIL}, returns a list of the full names of all registered streams that are open for the specified access.
}}

{FnDef {FnName CLOSEALL} {FnArgs ALLFLG}
{Text
Closes all streams in the value of {lisp (OPENP)}.  Returns a list of the files closed.

{fn WHENCLOSE} ({PageRef Fn WHENCLOSE}) allows certain files to be "protected" from {fn CLOSEALL}.  If {arg ALLFLG} is {lisp T}, all files, including those protected by {fn WHENCLOSE}, are closed.
}}



}{End SubSec Opening and Closing File Streams}