#
Chapter 17
`file.ss`: Filesystem Utilities

To load: `(require (lib "file.ss"))`

See also section 11.3 in PLT MzScheme: Language Manual.

`(build-absolute-path``` `base path``` `···``)` P R O C E D U R E

Like `build-path` (see section 11.3 in PLT MzScheme: Language Manual), but `base` is required to be an absolute pathname. If `base` is not an absolute pathname, `error` is called.

`(build-relative-path``` `base path``` `···``)` P R O C E D U R E

Like `build-path` (see section 11.3 in PLT MzScheme: Language Manual), but `base` is required to be a relative pathname. If `base` is not a relative pathname, `error` is called.

`(call-with-input-file*``` `pathname proc flag-symbol``` `···``)` P R O C E D U R E

Like ``call-with-input-file``, except that the opened port is closed if control escapes from the body of `proc`.

`(call-with-output-file*``` `pathname proc flag-symbol``` `···``)` P R O C E D U R E

Like ``call-with-output-file``, except that the opened port is closed if control escapes from the body of `proc`.

`(copy-directory/files``` `src-path dest-path``)` P R O C E D U R E

Copies the file or directory `src-path` to `dest-path`, raising `exn:fail:filesystem` if the file or directory cannot be copied, possibly because `dest-path` exists already. If `src-path` is a directory, the copy applies recursively to the directory's content. If a source is a link, the target of the link is copied rather than the link itself.

`(delete-directory/files``` `path``)` P R O C E D U R E

Deletes the file or directory specified by `path`, raising `exn:fail:filesystem` if the file or directory cannot be deleted. If `path` is a directory, then `delete-directory/files` is first applied to each file and directory in `path` before the directory is deleted. The return value is void.

`(explode-path``` `path``)` P R O C E D U R E

Returns the list of directories that constitute `path`. The `path` argument must be normalized in the sense of `normalize-path` (see below).

`(file-name-from-path``` `path``)` P R O C E D U R E

If `path` is a file pathname, returns just the file name part without the directory path.

`(filename-extension``` `path``)` P R O C E D U R E

Returns a byte string that is the extension part of the filename in `path`. If `path` is (syntactically) a directory, `#f` is returned.

`(find-files``` `predicate``` [`start-pathname`]`)` P R O C E D U R E

Traverses the filesystem starting at `start-pathname` and creates a list of all files and directories for which `predicate` returns true. If `start-pathname` is `#f` (the default), then the traversal starts from the current directory (as determined by ``current-directory``; see section 7.9.1.1 in PLT MzScheme: Language Manual). The resulting list has directories precede their contents.

The `predicate` procedure is called with a single argument for each file or directory. If `start-pathname` is `#f`, the argument is a pathname string that is relative to the current directory. Otherwise, it is a pathname that starts with `start-pathname`. Consequently, supplying `(`current-directory`)` for `start-pathname` is different from supplying `#f`, because `predicate` receives complete paths in the former case and relative paths in the latter. Another difference is that `predicate` is not called for the current directory when `start-pathname` is `#f`.

The `find-files` traversal follows soft links. To avoid following links, use the more general `fold-files` procedure.

If `start-pathname` does not refer to an existing file or directory, then `predicate` will be called exactly once with `start-pathname` as the argument.

`(pathlist-closure``` `path-list``)` P R O C E D U R E

This procedure consumes a list of paths, either absolute or relative to the current directory. The paths in the given `path-list` are all expected to be path names of existing directories and files. The return value is a list of paths such that

- if a nested path is given, all of its ancestors are also included in the result (but the same ancestor is not added twice);

- if a path points at a directory, all of its descendants are also included in the result;

- ancestor directories come before their descendants.

`(find-library``` `name collection``)` P R O C E D U R E

Returns the path of the specified library (see Chapter 16 in PLT MzScheme: Language Manual), returning `#f` if the specified library or collection cannot be found. The `collection` argument is optional, defaulting to `"mzlib"`.

`(find-relative-path``` `basepath path``)` P R O C E D U R E

Finds a relative pathname with respect to `basepath` that names the same file or directory as `path`. Both `basepath` and `path` must be normalized in the sense of `normalize-path` (see below). If `path` is not a proper subpath of `basepath` (i.e., a subpath that is strictly longer), `path` is returned.

`(fold-files``` `proc init-val``` [`start-pathname follow-links?`]`)` P R O C E D U R E

Traverses the filesystem starting at `start-pathname`, calling `proc` on each discovered file, directory, and link. If `start-pathname` is `#f` (the default), then the traversal starts from the current directory (as determined by ``current-directory``; see section 7.9.1.1 in PLT MzScheme: Language Manual).

The `proc` procedure is called with three arguments for each file, directory, or link:

- If `start-pathname` is `#f`, the first argument is a pathname string that is relative to the current directory. Otherwise, the first argument is a pathname that starts with `start-pathname`. Consequently, supplying `(`current-directory`)` for `start-pathname` is different from supplying `#f`, because `proc` receives complete paths in the former case and relative paths in the latter. Another difference is that `proc` is not called for the current directory when `start-pathname` is `#f`.

- The second argument is a symbol, either `'file`, `'dir`, or `'link`. The second argument can be `'link` when `follow-links?` is `#f`, in which case the filesystem traversal does not follow links. If `follow-links?` is `#t` (the default), then `proc` will only get a `'link` as a second argument when it encounters a dangling symbolic link (one that does not resolve to an existing file or directory).

- The third argument is the accumulated result. For the first call to `proc`, the third argument is `init-val`. For the second call to `proc` (if any), the third argument is the result from the first call, and so on. The result of the last call to `proc` is the result of `fold-files`.

`proc` is used in an analogous way to the procedure argument of `foldl`, where its result is used as the new accumulated result. There is an exception for the case of a directory (when the second argument is `'dir`): in this case the procedure may return two values, the second indicating whether the recursive scan should include the given directory or not. If it returns a single value, the directory is scanned.

An error is signaled if the `start-pathname` is provided but no such path exists, or if paths disappear during the scan.

`(get-preference``` `name``` [`failure-thunk flush-cache? filename`]`)` P R O C E D U R E

Extracts a preference value from the file designated by `(`find-system-path` 'pref-file)` (see section 11.3 in PLT MzScheme: Language Manual), or by `filename` if it is provided and is not `#f`. In the former case, if the preference file doesn't exist, `get-preferences` attempts to read a `plt-prefs.ss` file in the `defaults` collection, instead. If neither file exists, the preference set is empty.

The preference file should contain a symbol-keyed association list (written to the file with the default parameter settings). Keys starting with `mzscheme:`, `mred:`, and `plt:` in any letter case are reserved for use by PLT.

The result of `get-preference` is the value associated with `name` if it exists in the association list, or the result of calling `failure-thunk` otherwise. The default `failure-thunk` returns `#f`.

Preference settings from the standard preference file are cached (weakly) across calls to `get-preference`; if `flush-cache?` is provided as `#f`, the cache is used instead of the re-consulting the preferences file.

See also `put-preferences`. The `framework` collection supports a more elaborate preference system; see PLT Framework: GUI Application Framework for details.

`(make-directory*``` `path``)` P R O C E D U R E

Creates directory specified by `path`, creating intermediate directories as necessary.

`(make-temporary-file``` [`format-string copy-from-filename directory`]`)` P R O C E D U R E

Creates a new temporary file and returns a pathname string for the file. Instead of merely generating a fresh file name, the file is actually created; this prevents other threads or processes from picking the same temporary name. If `copy-from-filename` is provided as path, the temporary file is created as a copy of the named file (using `copy-file`). If `copy-from-filename` is `#f` or not provided, the temporary file is created as empty. If `copy-from-filename` is `'directory`, then the temporary ``file'' is created as a directory. If `directory` is provided and is not `#f`, then the file name generated from `format-string` is combined with `directory` to obtain a full path.

When a temporary file is created, it is not opened for reading or writing when the pathname is returned. The client program calling `make-temporary-file` is expected to open the file with the desired access and flags (probably using the `'truncate` flag; see section 11.1.3 in PLT MzScheme: Language Manual) and to delete it when it is no longer needed.

If `format-string` is specified, it must be a format string suitable for use with `format` and one additional string argument (where the string contains only digits). If the resulting string is a relative path, it is combined with the result of `(`find-system-path` 'temp-dir)`, unless `directory` is provided and non-`#f`. The default `format-string` is `"mztmp~a"`.

`(normalize-path``` `path wrt``)` P R O C E D U R E

Returns a normalized, complete version of `path`, expanding the path and resolving all soft links. If `path` is relative, then the pathname `wrt` is used as the base path. The `wrt` argument is optional; if is omitted, then the current directory is used as the base path.

Letter case is not normalized by `normalize-path`. For this and other reasons, the result of `normalize-path` is not suitable for comparisons that determine whether two paths refer to the same file (i.e., the comparison may produce false negatives).

An error is signaled by `normalize-path` if the input path contains an embedded path for a non-existent directory, or if an infinite cycle of soft-links is detected.

`(path-only``` `path``)` P R O C E D U R E

If `path` is a filename, the file's path is returned. If `path` is syntactically a directory, `#f` is returned.

`(put-preferences``` `name-list val-list``` [`locked-proc filename`]`)` P R O C E D U R E

See also `get-preference`.

Installs a set of preference values and writes all current values to the preference file designated by `(`find-system-path` 'pref-file)` (see section 11.3 in PLT MzScheme: Language Manual), or `filename` if it is supplied and not `#f`. The `name-list` argument must be a list of symbols for the preference names, and `val-list` must have the same length as `name-list`. Each element of `val-list` must be an instance of a built-in data type whose `write` output is `read`able (i.e., the `print-unreadable` parameter is set to `#f` while writing preferences; see section 7.9.1.4 in PLT MzScheme: Language Manual).

Current preference values are read from the preference file before updating, and an update ``lock'' is held starting before the file read, and lasting until after the preferences file is updated. The lock is implemented by the existence of a file in the same directory as the preference file. If the directory of the preferences file does not already exist, it is created.

If the update lock is already held (i.e., the lock file exists), then `locked-proc` is called with a single argument: the path of the lock file. The default `locked-proc` reports an error; an alternative thunk might wait a while and try again, or give the user the choice to delete the lock file (in case a previous update attempt encountered disaster).

If `filename` is `#f` or not supplied, and the preference file does not already exist, then values read from the `defaults` collection (if any) are written for preferences that are not mentioned in `name-list`.