SRFI m -*- outline -*- * Title File system interface * Author Taylor Campbell * Abstract This SRFI defines a simple interface to the file system. It is meant to be minimal and to specify the least common denominator of what facilities most file systems provide, but also to provide a useful and practical interface to the file system. It does _not_ specify a mechanism for naming objects on the file system because that is a much more complicated matter better left to another SRFI, which should define a portable pathname abstraction like that of Common Lisp or MIT Scheme. * Issues None so far. * Rationale An interface to the file system is a necessity in writing almost any practical programs. However, in the interest of ensuring that Scheme be small and that the specification be adopted easily, this interface is minimal and provides only critical functionality, but it nonetheless tries to provide enough functionality to be practical. * Specification ** Individual file operations (FILE-EXISTS? pathname) -> boolean (CREATE-DIRECTORY pathname) (DELETE-FILE pathname) FILE-EXISTS? returns true if a file or directory exists on the file system at a location specified by PATHNAME. If the operating system does not allow the Scheme program to probe at that location, then FILE-EXISTS? signals a 'file unreachable' error. CREATE-DIRECTORY creates a new directory at PATHNAME, or signals a 'file unreachable' error if the operating system does not permit the Scheme program to create a directory there. It is an error if there already exists an object at the location designated by PATHNAME. DELETE-FILE deletes the object on the file system at the location specified by PATHNAME (be it a regular file, a directory, or anything else). If there is no such object, DELETE-FILE does nothing. It is an error if PATHNAME designates a directory that is not empty or the operating system does not permit its deletion by the Scheme program. DELETE-FILE signals a 'file unreachable' error if the Scheme program cannot access the file at the location designated by PATHNAME, whether it satisfies the above conditions or not. (There is no CREATE-FILE because that operation is performed by OPEN-OUTPUT-FILE &c. automatically; and there is no DELETE-DIRECTORY to recursively delete a directory's contents because that is better left as library functionality. The sole goal of this proposal is to provide a bare minimum for realistic file system manipulation.) (RENAME-FILE source-pathname target-pathname) Renames or moves the file specified by SOURCE-PATHNAME to TARGET-PATHNAME. TARGET-PATHNAME may be a file pathname, in which case the file is given that exact name; it may also be a directory pathname, in which case the file is moved into that directory. The object on the file system specified by SOURCE-PATHNAME may be any kind of file: regular, directory, &c. It is an error if the Scheme program is not permitted by the operating system to perform this operation, SOURCE-PATHNAME does not exist, or TARGET-PATHNAME designates a file that is not in a directory that already exists & in which files are allowed by the operating system to be created by the Scheme program. If SOURCE-PATHNAME is simply not accessible by the Scheme program, whether or it exists or not, RENAME-FILE signals a 'file unreachable' error with SOURCE-PATHNAME; or, if TARGET-PATHNAME is in a directory that is not reachable by the Scheme program, it signals a 'file unreachable' error with TARGET-PATHNAME. (There is no COPY-FILE or COPY-DIRECTORY because, again, those are much better left as library functionality.) (FILE-REGULAR? pathname) -> boolean (FILE-DIRECTORY? pathname) -> boolean (FILE-READABLE? pathname) -> boolean (FILE-WRITABLE? pathname) -> boolean These all test various attributes of the file specified by PATHNAME. FILE-REGULAR? tests whether the file is a regular file, rather than a directory or other kind of object on the file system; FILE-DIRECTORY? tests whether the file is a directory; FILE-READABLE? tests whether the file can be read by the Scheme program, i.e. opening an input port over it (with any of CALL-WITH-INPUT-FILE, OPEN-INPUT-FILE, WITH-INPUT-FROM-FILE, or any other such operation) will not fail; and FILE-WRITABLE? tests whether a file specified by PATHNAME could be written to, i.e. opening an output port to it (with OPEN-INPUT-FILE &c.) would not fail. FILE-REGULAR? & FILE-DIRECTORY? return false if there is no object on the file system designated by PATHNAME or signal 'file unreachable' errors if PATHNAME names a file that is inaccessible by the Scheme program. FILE-READABLE? returns false if the file does not already exist, FILE-WRITABLE?'s result is unspecified if the file _does_ already exist, and they will both return false if the file simply cannot be accessed by the Scheme program, whether the file really exists or not. (FILE-MODIFICATION-TIME pathname) -> time Returns a SRFI 19 time object that represents the last time that the file on the file system at the location specified by PATHNAME was modified or the time that it was created. It is an error if the operating system does not allow the Scheme program to access this attribute of the file or the file does not already exist. If the Scheme program simply cannot access the file, whether it exists or not, FILE-MODIFICATION-TIME signals a 'file unreachable' error. (FILE-SIZE-IN-BYTES pathname) -> exact, non-negative integer Returns the number of bytes that the regular file designated by PATHNAME contains. The effect is unspecified if PATHNAME is not a regular file. It is an error if the file does not already exist or the Scheme program is not permitted by the operating system to probe this attribute of the file. If the Scheme program cannot access the file, whether it exists or not, this signals a 'file unreachable' error. ** Directory contents operations (DIRECTORY-FOLD* pathname combiner seed ...) -> [final-seed ...] Folds every file in the directory specified by PATHNAME by COMBINER. That is, for every file in the directory, COMBINER is passed the full pathname & the current set of state seeds. COMBINER should return N+1 values, where N is the number of seeds: a boolean that, if true, specifies that the iteration should continue, or, if false, specifies that the iteration should halt; and the next set of state seeds. When the iteration is halted, either because COMBINER returned #F as its first value or because there are no more files in the directory, the current set of state seeds is returned. There is no reliable ordering of the filenames passed to COMBINER. If the directory specified by PATHNAME is simply not accessible by the Scheme program, DIRECTORY-FOLD* signals a 'file unreachable' error. It is an error if there is no object specified by PATHNAME, the object on the file system is not a directory, or the operating system does not permit the Scheme program to read the directory's contents. ;; Return a list of the full pathnames of all files in a directory. (DIRECTORY-FOLD* directory (LAMBDA (PATHNAME LIST) (VALUES #T (CONS PATHNAME LIST))) '()) ;; Compute a list of the full pathnames of all subdirectories of a ;; directory. (DIRECTORY-FOLD* directory (LAMBDA (PATHNAME LIST) (VALUES #T (IF (FILE-DIRECTORY? PATHNAME) (CONS PATHNAME LIST) LIST))) '()) ;; Find the (shallow) sum of the number of bytes in all files in a ;; directory. (DIRECTORY-FOLD* directory (LAMBDA (PATHNAME SUM) (VALUES #T (IF (FILE-REGULAR? PATHNAME) (+ SUM (FILE-SIZE-IN-BYTES PATHNAME)) SUM))) 0) ;; Return the full pathname of the first file in a directory that ;; satisfies some predicate, or #F if no such file exists. (DIRECTORY-FOLD* directory (LAMBDA (PATHNAME FALSE) (IF (predicate? PATHNAME) (VALUES #F PATHNAME) (VALUES #T #F))) #F) (DIRECTORY-FOLD pathname combiner seed ...) -> [final-seed ...] Simplified variant of DIRECTORY-FOLD* that does not support premature termination. This is equivalent to: (DIRECTORY-FOLD* pathname (LAMBDA (DIR-ENTRY . SEEDS) (RECEIVE NEW-SEEDS (APPLY combiner DIR-ENTRY SEEDS) (APPLY VALUES #T NEW-SEEDS))) seed ...) Some of the above examples are simplified by DIRECTORY-FOLD; e.g., to produce a list of the full pathnames of all files in a directory, (DIRECTORY-FOLD directory CONS '()) (DIRECTORY-FOLD-TREE pathname file-combiner dir-combiner seed ...) -> [final-seed ...] This is like DIRECTORY-FOLD, but it walks down entire trees of directories. For each entry in the directory named by PATHNAME: if that entry is a non-directory, FILE-COMBINER is passed the full pathname of that file and the current seeds, and the walk of the directory's entries proceeds with the new seeds; if that entry is a directory, DIR-COMBINER is passed the full pathname of that directory & the current seeds, and the seeds it returns are used to recursively descend into the directory. When the recursive descent returns, the seeds it returned are used to proceed the walk of the enclosing directory's entries. This could be defined as follows: (define (directory-fold-tree pathname file-combiner dir-combiner . seeds) (apply directory-fold pathname (lambda (pathname . seeds) (if (file-directory? pathanme) (receive new-seeds (apply dir-combiner pathname seeds) (apply directory-fold-tree pathname file-combiner dir-combiner new-seeds)) (apply file-combiner pathname seeds))) seeds)) However, it is likely to be implemented much more efficiently with respect to the underlying file system. ** File unreachable errors This SRFI requires that operations reliably in certain exceptional situations signal 'file unreachable' errors. Files are considered unreachable if the operating system does not allow the Scheme program to access any attributes of them, even such attributes as whether they exist or not, usually because the Scheme program does not have the appropriate permissions access one of the ancestor directories of the file. Precisely how these errors are signalled and represented is left unspecified by this SRFI; some suggestions are made later, however. This SRFI specifies only these three procedures: (FILE-UNREACHABLE-ERROR? object) -> boolean (FILE-UNREACHABLE-ERROR-PATHNAME condition) -> pathname (FILE-UNREACHABLE-ERROR-OPERATOR condition) -> procedure FILE-UNREACHABLE-ERROR? tests whether OBJECT is a 'file unreachable' error, i.e. whether the other operations are defined or not on it. FILE-UNREACHABLE-ERROR-PATHNAME returns the pathname that designated such an unreachable file. FILE-UNREACHABLE-ERROR-OPERATOR returns the procedure that was unable to reach the file. If a Scheme system that supports this SRFI also supports SRFIs 34, 18, or 21, it should signal 'file unreachable' errors with RAISE, and such errors should be able to be caught using WITH-EXCEPTION-HANDLER. If a Scheme system that supports this SRFI also supports SRFI 35, there should be a condition type &FILE-UNREACHABLE-ERROR defined with two fields, named PATHNAME & OPERATOR. Its parent is unspecified. For example, (define-condition-type &file-unreachable-error &error file-unreachable-error? (pathname file-unreachable-error-pathname) (operator file-unreachable-error-operator)) ;;; SIGNAL-FILE-UNREACHABLE-ERROR would be used internally by the ;;; implementations of the procedures specified in this SRFI. ;;; *Note:* This SRFI does *not* specify this procedure. (define (signal-file-unreachable-error pathname operator) (raise (condition (&file-unreachable-error (pathname pathname) (operator operator))))) * Copyright Copyright (C) 2005 Taylor Campbell. All rights reserved. Don't distribute this. I'm not liable; if stuff goes wrong, it's all your fault, *nyah nyah*. This copyright notice will change in the real SRFI document to something reasonable (in particular, the official SRFI copyright notice, surprise surprise).