The Scheme standard provides a simple mechanism for reading and writing files: file ports. MIT Scheme provides additional tools for dealing with other aspects of the file system:
MIT Scheme programs need to use names to designate files. The main difficulty in dealing with names of files is that different file systems have different naming formats for files. For example, here is a table of several file systems (actually, operating systems that provide file systems) and what equivalent file names might look like for each one:
System File Name ------ --------- TOPS-20 <LISPIO>FORMAT.FASL.13 TOPS-10 FORMAT.FAS[1,4] ITS LISPIO;FORMAT FASL MULTICS >udd>LispIO>format.fasl TENEX <LISPIO>FORMAT.FASL;13 VAX/VMS [LISPIO]FORMAT.FAS;13 UNIX /usr/lispio/format.fasl MS-DOS C:\USR\LISPIO\FORMAT.FAS
It would be impossible for each program that deals with file names to know about each different file name format that exists; a new operating system to which Scheme was ported might use a format different from any of its predecessors. Therefore, MIT Scheme provides two ways to represent file names: filenames (also called namestrings), which are strings in the implementation-dependent form customary for the file system, and pathnames, which are special abstract data objects that represent file names in an implementation-independent way. Procedures are provided to convert between these two representations, and all manipulations of files can be expressed in machine-independent terms by using pathnames.
In order to allow MIT Scheme programs to operate in a network environment that may have more than one kind of file system, the pathname facility allows a file name to specify which file system is to be used. In this context, each file system is called a host, in keeping with the usual networking terminology.(26)
Note that the examples given in this section are specific to unix pathnames. Pathnames for other operating systems have different external representations.
Pathname objects are usually created by parsing filenames (character strings) into component parts. MIT Scheme provides operations that convert filenames into pathnames and vice versa.
(parse-namestring object #f #f)
.
(->pathname "foo") => #[pathname 65 "foo"] (->pathname "/usr/morris") => #[pathname 66 "/usr/morris"]
This procedure does not do defaulting of pathname components.
The optional arguments are used to determine what syntax should be used
for parsing the string. In general this is only really useful if your
implementation of MIT Scheme supports more than one file system,
otherwise you would use ->pathname
. If given, host must be
a host object or #f
, and defaults must be a pathname.
Host specifies the syntax used to parse the string. If host
is not given or #f
, the host component from defaults is
used instead; if defaults is not given, the host component from
*default-pathname-defaults*
is used.
->namestring
returns a newly allocated string that is the
filename corresponding to pathname.
(->namestring (->pathname "/usr/morris/minor.van")) => "/usr/morris/minor.van"
pathname-simplify
might not always be able to simplify the
pathname, e.g. on unix with symbolic links the directory
`/usr/morris/../' need not be the same as `/usr/'. In cases
of uncertainty the behavior is conservative, returning the original or a
partly simplified pathname.
(pathname-simplify "/usr/morris/../morris/dance") => #[pathname "/usr/morris/dance"]
A pathname object always has six components, described below. These components are the common interface that allows programs to work the same way with different file systems; the mapping of the pathname components into the concepts peculiar to each file system is taken care of by the Scheme implementation.
Note that a pathname is not necessarily the name of a specific file.
Rather, it is a specification (possibly only a partial specification) of
how to access a file. A pathname need not correspond to any file that
actually exists, and more than one pathname can refer to the same file.
For example, the pathname with a version of newest
may refer to
the same file as a pathname with the same components except a certain
number as the version. Indeed, a pathname with version newest
may refer to different files as time passes, because the meaning of such
a pathname depends on the state of the file system. In file systems
with such facilities as "links", multiple file names, logical devices,
and so on, two pathnames that look quite different may turn out to
address the same file. To access a file given a pathname, one must do a
file-system operation such as open-input-file
.
Two important operations involving pathnames are parsing and merging. Parsing is the conversion of a filename (which might be something supplied interactively by the users when asked to supply the name of a file) into a pathname object. This operation is implementation-dependent, because the format of filenames is implementation-dependent. Merging takes a pathname with missing components and supplies values for those components from a source of default values.
Not all of the components of a pathname need to be specified. If a
component of a pathname is missing, its value is #f
.
Before the file system interface can do anything interesting with a
file, such as opening the file, all the missing components of a pathname
must be filled in. Pathnames with missing components are used
internally for various purposes; in particular, parsing a namestring
that does not specify certain components will result in a pathname with
missing components.
Any component of a pathname may be the symbol unspecific
, meaning
that the component simply does not exist, for file systems in which such
a value makes no sense. For example, unix and MS-DOS file systems
usually do not support version numbers, so the version component for a
unix or MS-DOS host might be unspecific
.(27)
Each component in a pathname is typically one of the following (with some exceptions that will be described below):
#f
wild
unspecific
The host, directory, and version pathname components are exceptions to
these rules in that they may never be strings, although the values
#f
, wild
, and unspecific
are allowed with their
usual meanings. Here are the other values allowed for these components:
host?
predicate.
absolute
or the symbol relative
. If the first
element in the list is the symbol absolute
, then the directory
component (and subsequently the pathname) is absolute; the first
component in the sequence is to be found at the "root" of the file
system. If the directory is relative then the
first component is to be found in some as yet unspecified directory;
typically this is later specified to be the current working
directory.
Aside from absolute
and relative
, which may only appear as
the first element of the list, each subsequent element in the list is
either a string or the symbol wild
(each with the same meaning as
described above), or up
, which means the next directory is the
"parent" of the previous one. up
corresponds to the file
`..' in unix file systems.
In file systems that do not have "hierarchical" structure, a specified
directory component will always be a list whose first element is
absolute
. If the system does not support directories other than a
single global directory, the list will have no other elements. If the
system supports "flat" directories, i.e. a global set of directories
with no subdirectories, then the list will contain a second element,
which is either a string or wild
. In other words, a
non-hierarchical file system is treated as if it were hierarchical, but
the hierarchical features are unused. This representation is somewhat
inconvenient for such file systems, but it discourages programmers from
making code depend on the lack of a file hierarchy. Fortunately few
such file systems are in common use today.
newest
, which
means to choose the largest available version number for that file; or
the symbol oldest
, which means to choose the smallest version
number. In the future some other possible values may be added, e.g.
installed
. Note that in the current implementation there are no
file systems that support version numbers; thus this component is not
used and should be specified as #f
.
(make-pathname #f #f '(absolute "usr" "morris") "foo" "scm" #f) => #[pathname 67 "/usr/morris/foo.scm"]
(define x (->pathname "/usr/morris/foo.scm")) (pathname-host x) => #[host 1] (pathname-device x) => unspecific (pathname-directory x) => (absolute "usr" "morris") (pathname-name x) => "foo" (pathname-type x) => "scm" (pathname-version x) => unspecific
unspecific
because this might not be permitted in some
situations.
(define p (->pathname "/usr/blisp/rel15")) p => #[pathname 71 "/usr/blisp/rel15"] (pathname-new-name p "rel100") => #[pathname 72 "/usr/blisp/rel100"] (pathname-new-directory p '(relative "test" "morris")) => #[pathname 73 "test/morris/rel15"] p => #[pathname 71 "/usr/blisp/rel15"]
pathname-new-component
operations, except that they only change the specified component
if it has the value #f
in pathname.
#t
if object is a pathname; otherwise returns
#f
.
#t
if pathname1 is equivalent to pathname2;
otherwise returns #f
.
Pathnames are equivalent if all of their components are equivalent,
hence two pathnames that are equivalent must identify the same file or
equivalent partial pathnames.
However, the converse is not true: non-equivalent pathnames may specify
the same file (e.g. via absolute and relative directory components),
and pathnames that specify no file at all (e.g. name and directory
components unspecified) may be equivalent.
#t
if pathname is an absolute rather than relative
pathname object; otherwise returns #f
. All pathnames are either
absolute or relative, so if this procedure returns #f
, the
argument is a relative pathname.
#t
if pathname contains any wildcard components;
otherwise returns #f
.
*default-pathname-defaults*
and default-version defaults
to newest
.
The pathnames are combined by components: if pathname has a
non-missing component, that is the resulting component, otherwise the
component from defaults is used.
The default version can be #f
to preserve the information that
the component was missing from pathname.
The directory component is handled specially: if both pathnames have
directory components that are lists, and the directory component from
pathname is relative (i.e. starts with relative
), then the
resulting directory component is formed by appending pathname's
component to defaults's component.
For example:
(define path1 (->pathname "scheme/foo.scm")) (define path2 (->pathname "/usr/morris")) path1 => #[pathname 74 "scheme/foo.scm"] path2 => #[pathname 75 "/usr/morris"] (merge-pathnames path1 path2) => #[pathname 76 "/usr/scheme/foo.scm"] (merge-pathnames path2 path1) => #[pathname 77 "/usr/morris.scm"]
The merging rules for the version are more complex and depend on whether pathname specifies a name. If pathname does not specify a name, then the version, if not provided, will come from defaults. However, if pathname does specify a name then the version is not affected by defaults. The reason is that the version "belongs to" some other file name and is unlikely to have anything to do with the new one. Finally, if this process leaves the version missing, then default-version is used.
The net effect is that if the user supplies just a name, then the host, device, directory and type will come from defaults, but the version will come from default-version. If the user supplies nothing, or just a directory, the name, type and version will come over from defaults together.
set-working-directory-pathname!
sets this variable to a new
value, computed by merging the new working directory with the variable's
old value.
(define (pathname-default pathname device directory name type version) (make-pathname (pathname-host pathname) (or (pathname-device pathname) device) (or (pathname-directory pathname) directory) (or (pathname-name pathname) name) (or (pathname-type pathname) type) (or (pathname-version pathname) version)))
file-namestring
returns a string
representing just the name, type and version
components of pathname; the result of directory-namestring
represents just the host, device, and directory
components; and host-namestring
returns a string for just the
host portion.
enough-namestring
takes another argument, defaults.
It returns an abbreviated namestring that is just sufficient to identify
the file named by pathname when considered relative to the
defaults (which defaults to *default-pathname-defaults*
).
(file-namestring "/usr/morris/minor.van") => "minor.van" (directory-namestring "/usr/morris/minor.van") => "/usr/morris/" (enough-namestring "/usr/morris/men") => "men" ;perhaps
file-pathname
returns a pathname with just the
name, type and version components of pathname.
The result of directory-pathname
is a pathname containing the
host, device and directory components of pathname.
enough-pathname
takes another argument, defaults.
It returns an abbreviated pathname that is just sufficient to identify
the file named by pathname when considered relative to the
defaults (which defaults to *default-pathname-defaults*
).
These procedures are similar to file-namestring
,
directory-namestring
and enough-namestring
, but they
return pathnames instead of strings.
pathname-as-directory
.
(directory-pathname-as-file (->pathname "/usr/blisp/")) => #[pathname "/usr/blisp"]
directory-pathname-as-file
.
(pathname-as-directory (->pathname "/usr/blisp/rel5")) => #[pathname "/usr/blisp/rel5/"]
This section gives some standard operations on host objects, and some procedures that return some useful pathnames.
#t
if object is a pathname host; otherwise returns
#f
.
#t
if host1 and host2 denote the same
pathname host; otherwise returns #f
.
local-host
. If
the initialization file does not exist this procedure returns #f
.
local-host
. The
concept of a "home directory" is itself somewhat
implementation-dependent, but it should be the place where the user
keeps personal files, such as initialization files and mail. For
example, on unix this is the user's unix home directory, whereas on
MS-DOS the home directory is determined from the HOME
,
USER
and USERDIR
environment variables.
condition-type:file-operation-error
is signalled if
pathname cannot be located on the library search path.
(system-library-pathname "compiler.com") => #[pathname 45 "/usr/local/lib/mit-scheme/compiler.com"]
condition-type:file-operation-error
is signalled if
pathname cannot be located on the library search path.
(system-library-directory-pathname "options") => #[pathname 44 "/usr/local/lib/mit-scheme/options/"]
When MIT Scheme is started, the current working
directory (or simply, working directory) is initialized in an
operating-system dependent manner; usually, it is the directory in which
Scheme was invoked. The working directory can be determined from within
Scheme by calling the pwd
procedure, and changed by calling the
cd
procedure. Each REP loop has its own working directory,
and inferior REP loops initialize their working directory from the
value in effect in their superior at the time they are created.
pwd
is an alias for
working-directory-pathname
; the long name is intended for
programs and the short name for interactive use.
pathname-as-directory
. cd
is an alias for
set-working-directory-pathname!
; the long name is intended for
programs and the short name for interactive use.
Additionally, set-working-directory-pathname!
modifies the value
of *default-pathname-defaults*
by merging the new working
directory into it.
In the unix implementation, when this procedure is executed in the top-level REP loop, it changes the working directory of the running Scheme executable.
(set-working-directory-pathname! "/usr/morris/blisp") => #[pathname "/usr/morris/blisp/"] (set-working-directory-pathname! "~") => #[pathname "/usr/morris/"]
This procedure signals an error if filename does not refer to an existing directory.
If filename describes a relative rather than absolute pathname, this procedure interprets it as relative to the current working directory, before changing the working directory.
(working-directory-pathname) => #[pathname "/usr/morris/"] (set-working-directory-pathname! "foo") => #[pathname "/usr/morris/foo/"]
pathname-as-directory
. In addition to binding the working
directory, with-working-directory-pathname
also binds the
variable *default-pathname-defaults*
, merging the old value of
that variable with the new working directory pathname. Both bindings
are performed in exactly the same way as fluid binding of a variable
(see section Fluid Binding).
This section describes procedures that manipulate files and directories.
Any of these procedures can signal a number of errors for many reasons.
The specifics of these errors are much too operating-system dependent to
document here. However, if such an error is signalled by one of
these procedures, it will be of type
condition-type:file-operation-error
.
#t
if filename is an existing file or directory;
otherwise returns #f
. In operating systems that support symbolic
links, if the file is a symbolic link, this procedure tests the
existence of the file linked to, not the link itself.
condition-type:file-operation-error
is signalled if the
appropriate file cannot be located within the file system.
call-with-temporary-filename
generates a temporary filename, and
calls procedure with that filename as its sole argument. The
filename is guaranteed not to refer to any existing file, and, barring
unusual circumstances, it can be used to open an output file without
error. When procedure returns, if the file referred to by the
filename exists, it is deleted; then, the value yielded by
procedure is returned. If procedure escapes from its
continuation, and the file referred to by the filename exists, it is
deleted.
#t
if the file named filename exists and is a
directory. Otherwise returns #f
. In operating systems that
support symbolic links, if filename names a symbolic link, this
examines the file linked to, not the link itself.
#f
.
#t
if filename names a file that can be opened for
input; i.e. a readable file. Otherwise returns #f
.
#t
if filename names a file that can be opened for
output; i.e. a writable file. Otherwise returns #f
.
0
and 7
inclusive; it is a bitwise-encoded predicate selector with 1
meaning "executable", 2
meaning "writable", and 4
meaning "readable". file-access
returns #t
if
filename exists and satisfies the predicates selected by
mode. For example, if mode is 5
, then filename
must be both readable and executable. If filename doesn't exist,
or if it does not satisfy the selected predicates, #f
is
returned.
file-modes
returns an
exact non-negative integer encoding the file's permissions. The
encoding of this integer is operating-system dependent, but typically it
contains bits that indicate what users and processes are allowed to
read, write, or execute the file. If filename does not name an
existing file, #f
is returned.
file-modes
. set-file-modes!
modifies the file's
permissions to be those encoded by modes.
file-modification-time
returns #f
.
In operating systems that support symbolic links, if filename
names a symbolic link, file-modification-time
returns the
modification time of the file linked to. An alternate procedure,
file-modification-time-direct
, returns the modification time of
the link itself; in all other respects it is identical to
file-modification-time
. For symmetry,
file-modification-time-indirect
is a synonym of
file-modification-time
.
file-access-time
returns #f
.
Some operating systems don't implement access times; in those systems
file-access-time
returns an unspecified value.
In operating systems that support symbolic links, if filename
names a symbolic link, file-access-time
returns the access time
of the file linked to. An alternate procedure,
file-access-time-direct
, returns the access time of the link
itself; in all other respects it is identical to
file-access-time
. For symmetry, file-access-time-indirect
is a synonym of file-access-time
.
file-access-time
and file-modification-time
,
respectively. set-file-times!
alters the access and modification
times of the file specified by filename to the values given by
access-time and modification-time, respectively. For
convenience, either of the time arguments may be specified as #f
;
in this case the corresponding time is not changed.
set-file-times!
returns an unspecified value.
#f
. The information returned is a vector of 10 items:
#t
if the file is a directory, a character string
(the name linked to) if a symbolic link, or #f
for all other
types of file.
In operating systems that support symbolic links, if filename
names a symbolic link, file-attributes
returns the attributes of
the link itself. An alternate procedure,
file-attributes-indirect
, returns the attributes of the file
linked to; in all other respects it is identical to
file-attributes
. For symmetry, file-attributes-direct
is
a synonym of file-attributes
.
->pathname
. The directory specified by directory is
read, and the contents of the directory is returned as a newly allocated
list of absolute pathnames. The result is sorted according to the usual
sorting conventions for directories, unless sort? is specified as
#f
. If directory has name, type, or version components,
the returned list contains only those pathnames whose name, type, and
version components match those of directory; wild
or
#f
as one of these components means "match anything".