uiop/pathname
uiop/pathname is the pathname algebra — the portable layer libraries use to
build, take apart and compare pathnames without touching the file system.
All 50 exports are implemented, as pure computation over the pathname value,
so every one runs identically on all four backends — the interpreter, the JVM,
and both WASM outputs.
Every name is reachable through either spelling: uiop:subpathname and
uiop/pathname:subpathname are the same function
(The uiop Package).
A rontolisp pathname carries one flat namestring, so the component-wise algebra collapses onto namestring computation — with two consequences worth knowing:
- Logical pathnames do not exist (no logical host can be defined), so
uiop:logical-pathname-panswersnilfor everything,uiop:physical-pathname-pispathnamep,uiop:physicalize-pathnameis the coercing identity, anduiop:make-pathname-logicalsignalsuiop:not-implemented-error. - Nothing is absolutized:
uiop:ensure-absolute-pathnameanswers a relative path as itself where upstream signals — rontolisp resolves relative paths against the host working directory for the whole run, so the path as given is already the file's identity.
Building and merging
| Function | What it does |
|---|---|
uiop:merge-pathnames* | the defaults-aware merge — an absolute specified wins, a relative one is appended to the defaults' directory |
uiop:subpathname | a relative subpath merged under a base pathname's directory |
uiop:subpathname* | nil when the base is nil, otherwise subpathname with the base first put in directory form |
uiop:ensure-directory-pathname | the pathname in directory form (a trailing /) |
uiop:ensure-absolute-pathname | an absolute path passes through; a relative one is merged against the defaults (a pathname, or a function answering one) |
uiop:nil-pathname / uiop:*nil-pathname* | the neutral defaults — the empty pathname #P"" |
uiop:pathname-root | the root of the pathname's host and device — #P"/", the only root here |
uiop:pathname-host-pathname | a pathname carrying only the host — #P"", since no host is modeled |
uiop:make-pathname* | make-pathname, kept for callers of the deprecated spelling |
uiop:make-pathname-component-logical | :unspecific becomes nil; everything else passes through |
uiop:normalize-pathname-directory-component | a directory component in CLHS list form ("foo" → (:absolute "foo")) |
uiop:denormalize-pathname-directory-component | the identity — the normalized form is the native one |
uiop:merge-pathname-directory-components | the directory-list half of the merge, :back handling included |
uiop:*unspecific-pathname-type* | nil — a component that is not present is nil here |
#P"/tmp/foo/bar/baz.txt"
#P"/tmp/foo/x.txt"
#P"/tmp/b.txt"
(:ABSOLUTE "a" "x")
Predicates
absolute-pathname-p, relative-pathname-p and file-pathname-p answer the
parsed pathname when true (a generalized boolean, as upstream); the rest
answer t/nil. None of them touches the file system.
| Function | True when |
|---|---|
uiop:absolute-pathname-p | the namestring starts with / |
uiop:relative-pathname-p | it does not (the empty pathname included) |
uiop:directory-pathname-p | non-wild, with no name and no type — empty or ending in / |
uiop:file-pathname-p | a name or type component is present |
uiop:hidden-pathname-p | the name starts with a dot |
uiop:pathname-equal | the two designators carry the same namestring |
uiop:logical-pathname-p | never (no logical pathnames exist) |
uiop:physical-pathname-p | the argument is a pathname |
(#P"/a/b" #P"a/b")
(T #P"/a/b")
(T T)
Directories
| Function | What it does |
|---|---|
uiop:pathname-directory-pathname | the pathname's directory, name and type dropped |
uiop:pathname-parent-directory-pathname | one directory level up (the root's parent is the root) |
#P"/a/b/"
#P"/a/"
Parsing
| Function | What it does |
|---|---|
uiop:parse-unix-namestring | a Unix-syntax string as a pathname: "" and "." components dropped, :type appended, :ensure-directory forcing directory form |
uiop:unix-namestring | the Unix-style namestring — which here is the namestring |
uiop:split-name-type | two values, NAME and TYPE of a filename (the last dot separates them; a lone leading dot belongs to the name) |
uiop:split-unix-namestring-directory-components | four values: :absolute/:relative, the directory components, the last component, and whether the string was a bare filename |
#P"a/b/c.txt"
#P"foo/bar.lisp"
("foo" "lisp")
(:ABSOLUTE ("a" "b") "c.txt" NIL)
Relative to a base
| Function | What it does |
|---|---|
uiop:subpathp | when the first pathname sits under the second, the relative remainder that merges back onto it; nil otherwise |
uiop:enough-pathname | that remainder when there is one, the pathname itself otherwise |
uiop:call-with-enough-pathname | calls a function on enough-pathname, with *default-pathname-defaults* bound to the base |
uiop:with-enough-pathname | macro shorthand for the above — (uiop:with-enough-pathname (p :defaults d) ...) rebinds p |
uiop:with-pathname-defaults | macro: run the body with *default-pathname-defaults* bound to the given form, or to *nil-pathname* when none is given |
#P"foo/bar.txt"
#P"/x/a.txt"
#P"a/b.txt"
#P"/wpd/"
Checking constraints
uiop:ensure-pathname is the constraint
machine the rest of uiop routes through: it coerces a designator (a string goes
through parse-unix-namestring), then applies the :want-* checks and
:ensure-* transforms in upstream's order. A failed check signals, or calls a
custom :on-error function.
#P"a/b/"
:ERR
Lite next to upstream, deliberately: a failed check reports
Invalid pathname ~S: ~A (not upstream's ~? chain), :want-logical always
fails, and :resolve-symlinks / :truenamize are accepted and ignored (no
backend resolves a symlink); :truename answers what probe-file answers.
Wildcards and translation
The *wild* family are namestring literals over the two wildcards the
directory matcher reads (* and ?), so a wild
constant and the matcher can never disagree.
| Name | Value / what it does |
|---|---|
uiop:*wild* | "*" |
uiop:*wild-file* / uiop:*wild-file-for-directory* | #P"*.*" |
uiop:*wild-directory* | #P"*/" |
uiop:*wild-inferiors* | #P"**/" |
uiop:*wild-path* | #P"**/*.*" |
uiop:wilden | any file in any subdirectory of the pathname's directory |
uiop:translate-pathname* | the output-translations wrapper over translate-pathname: a function destination is called, t answers the path, a relative destination is first merged with the root |
uiop:relativize-directory-component | (:absolute ...) becomes (:relative ...) |
uiop:relativize-pathname-directory | the pathname with its leading / dropped |
uiop:directory-separator-for-host | #\/ |
uiop:directorize-pathname-host-device | the identity — a Unix-shaped physical pathname is already in that form |
uiop:*output-translation-function* | 'identity — no output translations run here |
#P"/tmp/**/*.*"
#P"/out/a/b.lisp"
#P"a/b/c.txt"
Compile-time folding
Like uiop:merge-pathnames*, a uiop:subpathname whose arguments are literals
(or references to a top-level defparameter bound to one) is folded to a
pathname literal by the compile paths, so a bundled library's data-file path
costs nothing at run time.