{Begin SubSec File Names} {Title File Names} {Text {index *PRIMARY* File names} {index *PRIMARY* Manipulating file names} {note Should have a more complete explanation of file name format, with examples!!} A file name in Interlisp-D is a string or litatom whose characters specify a "path" to the actual file: on what host or device the file resides, in which directory, and so forth. Because Interlisp-D supports a variety of non-local file devices, parts of the path could be very device-dependent. However, it is desirable for programs to be able to manipulate file names in a device-independent manner. To this end, Interlisp-D specifies a uniform file name syntax over all devices; the functions that perform the actual file manipulation for a particular device are responsible for any translation to that device's naming conventions. A file name is composed of a collection of {it fields}, some of which have specific semantic interpretations. The functions described below refer to each field by a {it field name}, a literal atom from among the following: {lisp HOST}, {lisp DEVICE}, {lisp DIRECTORY}, {lisp NAME}, {lisp EXTENSION}, and {lisp VERSION}. The standard syntax for a file name that contains all of those fields is {Lisp {bracket HOST}DEVICE:NAME.EXTENSION;VERSION}. Some host's file systems do not use all of those fields in their file names. {Begin LabeledList File Name Fields} {Label {index HOST (File name field)}{lisp HOST}} {Text Specifies the host whose file system contains the file. In the case of local file devices, the "host" is the name of the device, e.g., {lisp DSK} or {lisp FLOPPY}. } {Label {index DEVICE (File name field)}{lisp DEVICE}} {Text Specifies, for those hosts that divide their file system's name space among mutiple physical devices, the device or logical structure on which the file resides. This should not be confused with Interlisp-D's abstract "file device", which denotes either a host or a local physical device and is specified by the {lisp HOST} field. } {Label {index DIRECTORY (File name field)}{lisp DIRECTORY}} {Text Specifies the "directory" containing the file. A directory usually is a grouping of a possibly large set of loosely related files, e.g., the personal files of a particular user, or the files belonging to some project. The {lisp DIRECTORY} field usually consists of a principal directory and zero or more subdirectories that together describe a path through a file system's hierarchy. Each subdirectory name is set off from the previous directory or subdirectory by the character "{lisp >}"; e.g., "{lisp LISP>LIBRARY>NEW}". } {Label {index NAME (File name field)}{lisp NAME}} {Text This field carries no specific meaning, but generally names a set of files thought of as being different renditions of the "same" abstract file. } {Label {index EXTENSION (File name field)}{lisp EXTENSION}} {Text This field also carries no specific meaning, but generally distinguishes the form of files having the same name. Most files systems have some "conventional" extensions that denote something about the content of the file. E.g., in Interlisp-D, the extension {lisp DCOM} standardly denotes a file containing compiled function definitions. } {Label {index VERSION (File name field)}{lisp VERSION}} {Text A number used to distinguish the versions or "generations" of the files having a common name and extension. The version number is incremented each time a new file by the same name is created. } {End LabeledList File Name Fields} Most functions that take as input "a directory" accept either a directory name (the contents of the {lisp DIRECTORY} field of a file name) or a "full" directory specification{emdash}a file name fragment consisting of only the fields {lisp HOST}, {lisp DEVICE}, and {lisp DIRECTORY}. In particular, the "connected directory" ({pageRef FN CNDIR}) consists, in general, of all three fields. {index UNIX file names} For convenience in dealing with certain operating systems, Interlisp-D also recognizes {lisp []} and {lisp ()} as host delimiters (synonymous with {lisp {bracket}}), and {lisp /} as a directory delimiter (synonymous with {lisp <} at the beginning of a directory specification and {lisp >} to terminate directory or subdirectory specification). For example, a file on a Unix file server {lisp UNX} with the name {lisp /usr/foo/bar/stuff.tedit}, whose {lisp DIRECTORY} field is thus {lisp usr/foo/bar}, could be specified as {lisp {bracket UNX}/usr/foo/bar/stuff.tedit}, or {lisp (UNX)stuff.tedit}, or several other variations. Note that when using {lisp []} or {lisp ()} as host delimiters, they usually must be escaped with the reader's {lisp %} escape character if the file name is expressed as a litatom rather than a string. {index Quoting file names} {Tag QuotingFileNames} Different hosts have different requirements regarding which characters are valid in file names. From Interlisp-D's point of view, any characters are valid. However, in order to be able to parse a file name into its component fields, it is necessary that those characters that are conventionally used as file name delimiters be quoted when they appear inside of fields where there could be ambiguity. The file name quoting character is "{lisp '}" (single quote). Thus, the following characters must be quoted when not used as delimeters: {lisp :}, {lisp >}, {lisp ;}, {lisp /}, and {lisp '} itself. The character {lisp .} (period) need only be quoted if it is to be considered a part of the {lisp EXTENSION} field. The characters {lisp {rbracket}}, {lisp ]}, and {lisp )} need only be quoted in a file name when the host field of the name is introduced by {lisp {lbracket}}, {lisp [}, and {lisp (}, respectively. The characters {lisp {lbracket}}, {lisp [}, {lisp (}, and {lisp <} need only be quoted if they appear as the first character of a file name fragment, where they would otherwise be assumed to introduce the {lisp HOST} or {lisp DIRECTORY} fields. The following functions are the standard way to manipulate file names in Interlisp. Their operation is purely syntactic{emdash}they perform no file system operations themselves. {FnDef {FnName UNPACKFILENAME.STRING} {FnArgs FILENAME {anonarg} {anonarg} {anonarg}} {Text Parses {arg FILENAME}, returning a list in property list format of alternating field names and field contents. The field contents are returned as strings. If {arg FILENAME} is a stream, its full name is used. Only those fields actually present in {arg FILENAME} are returned. A field is considered present if its delimiting punctuation (in the case of {lisp EXTENSION} and {lisp VERSION}, the preceding period or semicolon, respectively) is present, even if the field itself is empty. Empty fields are denoted by {lisp ""} (the empty string). Examples: {lispcode (UNPACKFILENAME.STRING "FOO.BAR") => (NAME "FOO" EXTENSION "BAR")} {lispcode (UNPACKFILENAME.STRING "FOO.;2") => (NAME "FOO" EXTENSION "" VERSION "2")} {lispcode (UNPACKFILENAME.STRING "FOO;") => (NAME "FOO" VERSION "")} {lispcode (UNPACKFILENAME.STRING "{bracket ERIS}CURRENT>IMTRAN.DCOM;21") => (HOST "ERIS" DIRECTORY "LISP>CURRENT" NAME "IMTRAN" EXTENSION "DCOM" VERSION "21")} }} {FnDef {FnName UNPACKFILENAME} {FnArgs FILE {anonarg}} {Text Old version of {fn UNPACKFILENAME.STRING} that returns the field values as atoms, rather than as strings. {fn UNPACKFILENAME.STRING} is now considered the "correct" way of unpacking file names, because it does not lose information when the contents of a field are numeric. For example, {lispcode (UNPACKFILENAME 'STUFF.TXT) => (NAME STUFF EXTENSION TXT)} but {lispcode (UNPACKFILENAME 'STUFF.029) => (NAME STUFF EXTENSION 29)} Explicitly omitted fields are denoted by the atom {lisp NIL}, rather than the empty string. }} Note: Both {fn UNPACKFILENAME} and {fn UNPACKFILENAME.STRING} leave the trailing colon on the device field, so that the Tenex device {lisp NIL:} can be distinguished from the absence of a device. Although {fn UNPACKFILENAME.STRING} is capable of making the distinction, it retains this behavior for backward compatibility. Thus, {lispcode (UNPACKFILENAME.STRING '{bracket TOAST}DSK:FOO) => (HOST "TOAST" DEVICE "DSK:" NAME "FOO")} {note currently, UNPACKFILENAME is the same on Interlisp-D and -10, so it will unpack ;T, etc, without giving an error, even in systems where such files cannot exist. bug or feature?} {FnDef {FnName FILENAMEFIELD} {FnArgs FILENAME FIELDNAME} {Text Returns, as an atom, the contents of the {arg FIELDNAME} field of {arg FILENAME}. If {arg FILENAME} is a stream, its full name is used. }} {FnDef {FnName PACKFILENAME.STRING} {FnArgs FIELD{SUB 1} CONTENTS{SUB 1} {ellipsis} FIELD{SUB N} CONTENTS{SUB N}} {Type NOSPREAD} {Text Takes a sequence of alternating field names and field contents (atoms or strings), and returns the corresponding file name, as a string. If {fn PACKFILENAME.STRING} is given a single argument, it is interpreted as a list of alternating field names and field contents. Thus {fn PACKFILENAME.STRING} and {fn UNPACKFILENAME.STRING} operate as inverses. If the same field name is given twice, the {it first} occurrence is used. The contents of the field name {lisp DIRECTORY} may be either a directory name or a full directory specification as described above. {fn PACKFILENAME.STRING} also accepts the "field name" {lisp BODY} to mean that its contents should itself be unpacked and spliced into the argument list at that point. This feature, in conjunction with the rule that fields early in the argument list override later duplicates, is useful for altering existing file names. For example, to provide a default field, place {lisp BODY} first in the argument list, then the default fields. To override a field, place the new fields first and {lisp BODY} last. If the value of the {lisp BODY} field is a stream, its full name is used. Examples: {lispcode (PACKFILENAME.STRING 'DIRECTORY "LISP" 'NAME "NET") => "NET"} {lispcode (PACKFILENAME.STRING 'NAME "NET" 'DIRECTORY "{bracket DSK}") => "{bracket DSK}NET"} {lispcode (PACKFILENAME.STRING 'DIRECTORY "{bracket DSK}" 'BODY "{bracket TOAST}BAR") => "{bracket DSK}BAR"} {lispcode (PACKFILENAME.STRING 'DIRECTORY "FRED" 'BODY "{bracket TOAST}BAR") => "{bracket TOAST}BAR"} {lispcode (PACKFILENAME.STRING 'BODY "{bracket TOAST}BAR" 'DIRECTORY "FRED") => "{bracket TOAST}BAR"} {lispcode (PACKFILENAME.STRING 'VERSION NIL 'BODY "{bracket TOAST}BAR.DCOM;2") => "{bracket TOAST}BAR.DCOM"} {lispcode (PACKFILENAME.STRING 'BODY "{bracket TOAST}BAR.DCOM" 'VERSION 1) => "{bracket TOAST}BAR.DCOM;1"} {lispcode (PACKFILENAME.STRING 'BODY "{bracket TOAST}BAR.DCOM;" 'VERSION 1) => "{bracket TOAST}BAR.DCOM;"} {lispcode (PACKFILENAME.STRING 'BODY "BAR.;1" 'EXTENSION "DCOM") => "BAR.;1"} {lispcode (PACKFILENAME.STRING 'BODY "BAR;1" 'EXTENSION "DCOM") => "BAR.DCOM;1"} In the last two examples, note that in one case the extension is explicitly present in the body (as indicated by the preceding period), while in the other there is no indication of an extension, so the default is used. }} {FnDef {FnName PACKFILENAME} {FnArgs FIELD{SUB 1} CONTENTS{SUB 1} {ellipsis} FIELD{SUB N} CONTENTS{SUB N}} {Type NOSPREAD} {Text The same as {fn PACKFILENAME.STRING}, except that it returns the file name as a litatom, instead of a string. }} {note document UNIX-style file names From Intermezzo release notes: The following directory commands to the UNIX file server {bracket UN} are equivalent: DIR {bracket UN}* DIR {bracket UN}* DIR {bracket UN}/user/jones/* The file names printed will all have the same structure: {bracket UN}xxx. ----- UNPACKFILENAME treats a UNIX file pathname between slashes as a directory specification. For example, _(UNPACKFILENAME '/a/rosie/rosie2.4/ELLIE.MAY;3) (DIRECTORY a/rosie/rosie2.4 NAME ELLIE EXTENSION MAY VERSION 3).} }{End SubSec File Names} {Begin SubSec Incomplete File Names} {Title Incomplete File Names} {Text {index *PRIMARY* Incomplete file names} {index *PRIMARY* Connected directory} {index File names} In general, it is not necessary to pass a complete file name (one containing all the fields listed above) to functions that take a file name as argument. Interlisp supplies suitable defaults for certain fields, as described below. Functions that return names of actual files, however, always return the fully specified name. If the version field is omitted from a file name, Interlisp performs version recognition, as described on {PageRef Term Version Recognition of files}. If the host, device and/or directory field are omitted from a file name, Interlisp defaults them with respect to the currently connected directory. The connected directory is changed by calling the function {lisp CNDIR} or using the programmer's assistant command {lisp CONN}. Defaults are added to the partially specified name "left to right" until a host, device or directory field is encountered. Thus, if the connected directory is {lisp {bracket TWENTY}PS:}, then {lispcode BAR.DCOM {rm means} {bracket TWENTY}PS:BAR.DCOM} {lispcode BAR.DCOM {rm means} {bracket TWENTY}PS:BAR.DCOM} {lispcode MTA0:BAR.DCOM {rm means} {bracket TWENTY}MTA0:BAR.DCOM} {lispcode {bracket THIRTY}BAR.DCOM {rm means} {bracket THIRTY}BAR.DCOM} In addition, if the partially specified name contains a subdirectory, but no principal directory, then the subdirectory is appended to the connected directory. For example, {lispcode ISO>BAR.DCOM {rm means} {bracket TWENTY}PS:ISO>BAR.DCOM} Or, if the connected directory is the Unix directory {lisp {bracket UNX}/usr/fred/}, then {lisp iso/bar.dcom} means {lisp {bracket UNX}/usr/fred/iso/bar.dcom}, but {lisp /other/bar.dcom} means {lisp {bracket UNX}/other/bar.dcom}. {FnDef {Name CNDIR} {Args HOST/DIR} {Text Connects to the directory {arg HOST/DIR}, which can either be a directory name or a full directory specification including host and/or device. If the specification includes just a host, and the host supports directories, the directory is defaulted to the value of {lisp (USERNAME)}; if the host is omitted, connection is made to another directory on the same host as before. If {arg HOST/DIR} is {lisp NIL}, connects to the value of {var LOGINHOST/DIR}. {fn CNDIR} returns the full name of the now-connected directory. Causes an error, {index Non-existent directory Error}{lisp Non-existent directory}, if {arg HOST/DIR} is not recognized as a valid directory. Note that {fn CNDIR} does not necessarily require or provide any directory access privileges. Access privileges are checked when a file is opened. }} {Def {Type PACom} {Name CONN} {Args HOST/DIR} {NoParens} {Text Convenient command form of {fn CNDIR} for use at the executive. Connects to {arg HOST/DIR}, or to the value of {var LOGINHOST/DIR} if {arg HOST/DIR} is omitted. This command is undoable{emdash}undoing it causes the system to connect to the previously connected directory. }} {VarDef {Name LOGINHOST/DIR} {Text {lisp CONN} with no argument connects to the value of the variable {var LOGINHOST/DIR}, initially {lisp {bracket DSK}}, but usually reset in the user's greeting file ({PageRef Term Greeting}). }} {FnDef {Name DIRECTORYNAME} {Args DIRNAME STRPTR {anonarg}} {Text If {arg DIRNAME} is {lisp T}, returns the full specification of the currently connected directory. If {arg DIRNAME} is {lisp NIL}, returns the "login" directory specification (the value of {var LOGINHOST/DIR}). For any other value of {arg DIRNAME}, returns a full directory specification if {arg DIRNAME} designates an existing directory (satisfies {fn DIRECTORYNAMEP}), otherwise {lisp NIL}. If {arg STRPTR} is {lisp T}, the value is returned as an atom, otherwise it is returned as a string. {Note STRPTR ought to be renamed, since its only meaning nowadays is "make atom"! --bvm} }} {FnDef {Name DIRECTORYNAMEP} {Args DIRNAME HOSTNAME} {Text Returns {lisp T} if {arg DIRNAME} is recognized as a valid directory on host {arg HOSTNAME}, or on the host of the currently connected directory if {arg HOSTNAME} is {lisp NIL}. {arg DIRNAME} may be either a directory name or a full directory specification containing host and/or device as well. If {arg DIRNAME} includes subdirectories, this function may or may not pass judgment on their validity. Some hosts support "true" subdirectories, distinct entities manipulable by the file system, while others only provide them as a syntactic convenience. }} {FnDef {Name HOSTNAMEP} {Args NAME} {Text Returns {lisp T} if {arg NAME} is recognized as a valid host or file device name at the moment {fn HOSTNAMEP} is called. }} }{End SubSec Incomplete File Names} {Begin SubSec Version Recognition} {Title Version Recognition} {Text {index *PRIMARY* Version recognition of files} {Index *PRIMARY* Recognition of file versions} Most of the file devices in Interlisp support file version numbers. That is, it is possible to have several files of the exact same name, differing only in their {lisp VERSION} field, which is incremented for each new "version" of the file that is created. When a file name lacking a version number is presented to the file system, it is necessary to determine which version number is intended. This process is known as {it version recognition}. When {fn OPENSTREAM} opens a file 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 version number defaulting for {fn OPENSTREAM} can be changed by specifying a different value for its {arg RECOG} argument, as described under {fn FULLNAME}, below. Other functions that accept file names as arguments generally perform the default version recognition, which is newest version for existing files, or a new version if using the file name to create a new file. The one exception is {fn DELFILE}, which defaults to the oldest existing version of the file. The functions below can be used to perform version recognition without actually calling {fn OPENSTREAM} to open the file. Note that these functions only tell the truth about the moment at which they are called, and thus cannot in general be used to anticipate the name of the file opened by a comparable {fn OPENSTREAM}. They are sometimes, however, helpful hints. {index File names} {index Full file names} {FnDef {FnName FULLNAME} {FnArgs X RECOG} {Text If {arg X} is an open stream, simply returns the full file name of the stream. Otherwise, if {arg X} is a file name given as a string or litatom, performs version recognition, as follows: If {arg X} is recognized in the recognition mode specified by {arg RECOG} as an abbreviation for some file, returns the file's full name, otherwise {lisp NIL}. {arg RECOG} is one of the following: {Begin LabeledList Recognition modes} {Label {lisp OLD}} {Text Choose the newest existing version of the file. Return {Lisp NIL} if no file named {arg X} exists. } {Label {lisp OLDEST}} {Text Choose the oldest existing version of the file. Return {Lisp NIL} if no file named {arg X} exists. } {Label {lisp NEW}} {Text Choose a new (not yet existing) version of the file. That is, if versions of {arg X} already exist, then choose a version number one higher than highest existing version; else choose version 1. For some file systems, {fn FULLNAME} returns {lisp NIL} if the user does not have the access rights necessary for creating a new file named {arg X}. } {Label {lisp OLD/NEW}} {Text Try {lisp OLD}, then {lisp NEW}. That is, choose the newest existing version of the file, if any; else choose version 1. This usually only makes sense if you are intending to open {arg X} for access {lisp BOTH}. } {End LabeledList Recognition modes} {arg RECOG}={lisp NIL} defaults to {lisp OLD}. For all other values of {arg RECOG}, generates an error {lisp ILLEGAL ARG}.{index ILLEGAL ARG Error} If {arg X} already contains a version number, the {arg RECOG} argument will never change it. In particular, {arg RECOG}={lisp NEW} does not require that the file actually be new. For example, {lisp (FULLNAME 'FOO.;2 'NEW)} may return {lisp {bracket ERIS}FOO.;2} if that file already exists, even though {lisp (FULLNAME 'FOO 'NEW)} would default the version to a new number, perhaps returning {lisp {bracket ERIS}FOO.;5}. }} {FnDef {FnName INFILEP} {FnArgs FILE} {Text Equivalent to {lisp (FULLNAME {arg FILE} 'OLD)}. That is, returns the full file name of the newest version of {arg FILE} if {arg FILE} is recognized as specifying the name of an existing file that could potentially be opened for input, {lisp NIL} otherwise. }} {FnDef {FnName OUTFILEP} {FnArgs FILE} {Text Equivalent to {lisp (FULLNAME {arg FILE} 'NEW)}. }} Note that {fn INFILEP}, {fn OUTFILEP} and {fn FULLNAME} do not open any 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, {fn INFILEP} might return non-{lisp NIL}, but {fn OPENSTREAM} 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, {fn OUTFILEP} could return non-{lisp NIL}, but {fn OPENSTREAM} could fail with a {index FILE SYSTEM RESOURCES EXCEEDED Error}{lisp FILE SYSTEM RESOURCES EXCEEDED} error. Note also that in a shared file system, such as a remote file server, intervening file operations by another user could contradict the information returned by recognition. For example, a file that was {fn INFILEP} might be deleted, or between an {fn OUTFILEP} and the subsequent {fn OPENSTREAM}, another user might create a new version or delete the highest version, causing {fn OPENSTREAM} to open a different version of the file than the one returned by {fn OUTFILEP}. In addition, some file servers do not well support recognition of files in output context. Thus, in general, the "truth" about a file can only be obtained by actually opening the file; creators of files should rely on the name of the stream opened by {fn OPENSTREAM}, not the value returned from these recognition functions. In particular, for the reasons described earlier, programmers are discouraged from using {fn OUTFILEP} or {lisp (FULLNAME {arg NAME} 'NEW)}. }{End SubSec Version Recognition} {Begin SubSec Using File Names Instead of Streams} {Title Using File Names Instead of Streams} {Text {index File names} {tag StreamFileNames} In earlier implementations of Interlisp, from the days of Interlisp-10 onward, the "handle" used to refer to an open file was not a stream, but rather the file's full name, represented as a litatom. 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 was sometimes convenient for typing in file commands at the executive, but was very poor for serious programming in two major ways. First, the mapping from file name to stream on every input/output operation is inefficient. Second, and more importantly, using the file name as the handle on an open stream means that it is not possible to have more than one stream open on a given file at once. As of this writing, Interlisp-D is in a transition period, where it still supports the use of litatom file names as synonymous with open streams, but this use is not recommended. The remainder of this section discusses this usage of file names for the benefit of those reading older programs and wishing to convert them as necessary to work properly when this compatibility feature is removed. {Begin SubSec File Name Efficiency Considerations} {Title File Name Efficiency Considerations} {Text {index Incomplete File Names} It is possible for a program to be seriously inefficient using a file name as a stream if the program is not using the file's full name, the name returned by {fn OPENFILE} (below). 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 {fn OPENFILE} than to repeatedly use the possibly incomplete name that was used to open the file. There is a more subtle problem with partial file names, in that recognition is performed on the user's entire directory, not just the open files. It is possible for a file name that was previously recognized to denote one file to suddenly denote a different file. For example, suppose a program performs {lisp (INFILE 'FOO)}, opening {lisp FOO.;1}, and reads several expressions from {lisp FOO}. Then the user interrupts the program, creates a {lisp FOO.;2} and resumes the program (or a user at another workstation creates a {lisp FOO.;2}). Now a call to {fn READ} giving it {lisp FOO} as its {arg FILE} argument will generate a {lisp FILE NOT OPEN}{index FILE NOT OPEN Error} error, because {lisp FOO} will be recognized as {lisp FOO.;2}. }{End SubSec File Name Efficiency Considerations} {Begin SubSec Obsolete File Opening Functions} {Title Obsolete File Opening Functions} {Text The following functions are now considered obsolete, but are provided for backwards compatibility: {FnDef {FnName OPENFILE} {FnArgs FILE ACCESS RECOG PARAMETERS {anonarg}} {Text Opens {arg FILE} with access rights as specified by {arg ACCESS}, and recognition mode {arg RECOG}, and returns the full name of the resulting stream. Equivalent to {lisp (FULLNAME (OPENSTREAM {arg FILE} {arg ACCESS} {arg RECOG} {arg PARAMETERS}))}. {note do we need to explain the old interpretation of PARAMETERS and following arg (used to be BYTESIZE and PARAMETERS)?} }} {FnDef {FnName INFILE} {FnArgs FILE} {Text Opens {arg FILE} for input, and sets it as the primary input stream. Equivalent to {lisp (INPUT (OPENSTREAM {arg FILE} 'INPUT 'OLD))} }} {FnDef {FnName OUTFILE} {FnArgs FILE} {Text Opens {arg FILE} for output, and sets it as the primary output stream. Equivalent to {lisp (OUTPUT (OPENSTREAM {arg FILE} 'OUTPUT 'NEW))}. }} {FnDef {FnName IOFILE} {FnArgs FILE} {Text Equivalent to {lisp (OPENFILE {arg FILE} 'BOTH 'OLD)}; opens {arg FILE} for both input and output. Does not affect the primary input or output stream. }} }{End SubSec Obsolete File Opening Functions} {Begin SubSec Converting Old Programs} {Title Converting Old Programs} {Text {index *PRIMARY* Multiple streams to a file} At some point in the future, the Interlisp-D file system will change so that each call to {fn OPENSTREAM} returns a distinct stream, even if a stream is already open to the specified file. 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) The functions {fn OPENFILE}, {fn INPUT}, and {fn OUTPUT} will return a {lisp STREAM}, not a full file name. To make this less confusing in interactive situations, {lisp STREAM}s will have a print format that reveals the underlying file's actual name, 2) A greater penalty will ensue for passing as the {arg FILE} argument to i/o operations anything other than the object returned from {fn 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. 3) {fn OPENP} will return {lisp NIL} when passed the name of a file rather than a stream (the value of {fn OPENFILE} or {fn OPENSTREAM}). Users should consider the following advice when writing new programs and editing existing programs, in order that they will continue to operate well when this change is made: Because of the efficiency and ambiguity considerations described earlier, users have long been encouraged to use only full file names as {arg FILE} arguments to i/o operations. The "proper" way to have done this was to bind a variable to the value returned from {fn OPENFILE} and pass that variable to all i/o operations; such code will continue to work. A less proper way to obtain the full file name, but one which has to date not incurred any obvious penalty, is that which binds a variable to the result of an {fn INFILEP} and passes that to {fn OPENFILE} and all i/o operations. This has worked because {fn INFILEP} and {fn OPENFILE} both return a full file name, an invalid assumption in this future world. Such code should be changed to pass around the value of the {fn OPENFILE}, not the {fn INFILEP}. Code that calls {fn OPENP} to test whether a possibly incomplete file name is already open should be recoded to pass to {fn OPENP} only the value returned from {fn OPENFILE} or {fn OPENSTREAM}. Code that uses ordinary string functions to manipulate file names, and in particular the value returned from {fn OPENFILE}, should be changed to use the the functions {fn UNPACKFILENAME.STRING} and {fn PACKFILENAME.STRING}. Those functions work both on file names (strings) and streams (coercing the stream to the name of its file). Code that tests the value of {fn OUTPUT} for equality to some known file name or {lisp T} should be examined carefully and, if possible, recoded. {note Sure would be nice if there were functions INPUTSTREAM and OUTPUTSTREAM -bvm} To see more directly the effects of passing around {lisp STREAM}s instead of file names, replace your calls to {fn OPENFILE} with calls to {fn OPENSTREAM}. {fn OPENSTREAM} is called in exactly the same way, but returns a {lisp STREAM}. Streams can be passed to {fn READ}, {fn PRINT}, {fn CLOSEF}, etc just as the file's full name can be currently, but using them is more efficient. The function {fn FULLNAME}, when applied to a stream, returns its full file name. }{End SubSec Converting Old Programs} }{End SubSec Using File Names Instead of Streams} {Begin SubSec Using Files with Processes} {Title Using Files with Processes} {Text Because Interlisp-D does not yet support multiple streams per file, problems can arise if different processes attempt to access the same file. The user has to be careful not to have two processes manipulating the same file at the same time, since the two processes will be sharing a single input stream and file pointer. For example, it will not work to have one process {fn TCOMPL} a file while another process is running {fn LISTFILES} on it. }{End SubSec Using Files with Processes} ?1(DEFAULTFONT 1 (GACHA 10) (GACHA 8) (TERMINAL 8)) ?1(DEFAULTFONT 1 (GACHA 10) (GACHA 8) (TERMINAL 8)) y§y§zº