(rontolisp) docs
← The uiop Package

uiop/filesystem

uiop/filesystem probes the file system, walks directory trees and mutates them. All 32 exports are implemented, and the read side runs identically on all four backends — over the one existence probe (probe-file) and the one directory listing (directory) every backend already carries.

Every name is reachable through either spelling: uiop:probe-file* and uiop/filesystem:probe-file* are the same function (The uiop Package).

Probing and walking

FunctionWhat it answers
uiop:file-exists-pthe pathname when the file exists, nil otherwise — the same contract as probe-file, which it lowers onto on every backend
uiop:directory-exists-pthe pathname (with a trailing /) when the DIRECTORY exists, nil otherwise — the directory twin of file-exists-p, and what tells an empty directory from a missing one
uiop:probe-file*parse the designator and probe: the parsed pathname when something is there (:truename t answers its truename instead), nil otherwise — including for a designator ensure-pathname rejects, such as a wildcard
uiop:truename*the nil-tolerant truename: the truename when the file exists (trying the directory form too, where a missing trailing separator defeats some implementations' truename), nil otherwise — and nil for nil
uiop:directory*directory — upstream's per-implementation symlink keys are accepted and dropped, because nothing here resolves symlinks
uiop:directory-filesthe non-directory entries of a directory — (directory "db/*.*") with the subdirectories dropped. UIOP's optional second argument, the namestring of a name-and-type wildcard, filters them exactly as directory matches; omitting it lists everything, and a pattern carrying a directory component is an error
uiop:subdirectoriesthe subdirectories of a directory, each with its trailing /
uiop:collect-sub*directorieswalk a directory tree: collectp decides what reaches collector, recursep what is descended into. Every directory handed over is in directory form, root included
uiop:filter-logical-directory-resultsits entries unchanged — logical pathnames cannot exist here (logical-pathname-p is nil on every backend), so there is nothing to filter
uiop:safe-file-write-datefile-write-date with the missing-file file-error swallowed. nil on both WASM backends, where the date itself is nil
uiop:native-namestring"/tmp/x" — the host-OS spelling of a pathname, which here IS the namestring, so this is namestring
uiop:parse-native-namestringparse-unix-namestring plus the ensure-pathname constraints — os-unix-p is t outright, so the native spelling IS the Unix one
uiop:get-pathname-defaultsthe defaults relative names resolve against — *default-pathname-defaults* (initially #P"", the pathname designating the host working directory) unless an absolute defaults argument is given
NIL
NIL
#P"/tmp/x/"
":"

Environment pathnames

FunctionWhat it answers
uiop:inter-directory-separator#\: — the Unix separator, on every backend
uiop:split-native-pathnames-stringsplit a :-separated native string and parse each piece; an empty piece denotes nil
uiop:getenv-pathnamethe environment variable's value parsed as a native pathname, checked against the ensure-pathname constraints
uiop:getenv-pathnamesthe variable's :-separated values parsed the same way; empty entries answer nil
uiop:getenv-absolute-directorygetenv-pathname with :want-absolute t :ensure-directory t
uiop:getenv-absolute-directoriesgetenv-pathnames with :want-absolute t :ensure-directory t
(#P"/tmp" #P"/var")
NIL

No backend resolves symlinks — truename carries the argument namestring on all four — so uiop:*resolve-symlinks* defaults to nil (upstream's t would promise what is not there) and the three functions are the identity over a pathname coercion, exactly what upstream answers on an implementation without the API:

FunctionWhat it answers
uiop:*resolve-symlinks*nil
uiop:resolve-symlinks / uiop:truenamizethe pathname itself
uiop:resolve-symlinks*the pathname itself when the flag is true, the argument untouched when it is not
uiop:lisp-implementation-directorynil — there is no install directory to name: no compile-file, no fasl cache
uiop:lisp-implementation-pathname-pnil — nothing is under a directory that does not exist

The working directory

FunctionWhat it answers
uiop:call-with-current-directoryrun the thunk with *default-pathname-defaults* bound to the directory and the process working directory changed to it. Inherits chdir's decision, so a non-nil directory signals not-implemented-error on every backend and a nil one just runs the thunk
uiop:with-current-directorythe macro over it: (uiop:with-current-directory (dir) body...) runs the body under call-with-current-directory. An absent directory is nil, which just runs the body

Mutating the tree

FunctionWhat it answers
uiop:ensure-all-directories-existcreate every pathname's parent directories (ensure-directories-exist over each)
uiop:rename-file-overwriting-targetrename-file — the move already replaces the target, so the overwrite is the primitive's own
uiop:delete-file-if-existsdelete a file, answering nil instead of signalling when it is not there — the whole reason UIOP exports it
uiop:delete-empty-directorydelete an empty directory, over the same file primitive (it removes an empty directory as well as a file)
uiop:delete-directory-treerm -rf as a portable recursive walk: the directory must pass the :validate predicate (a missing one is a parameter-error, and so is a failing one), a missing directory signals unless :if-does-not-exist is :ignore

The four mutating operations are real on all four backends, where their primitives are: ensure-all-directories-exist over %make-directories, rename-file-overwriting-target over %rename-file and delete-file-if-exists over %delete-file run everywhere, pinned by the ci-spec filesystem-write-create-rename-delete-and-probe case on every backend. Removing a directory still signals on WASM — the unlink call cannot remove directories — so delete-empty-directory over an actual directory answers the honest file-error there.