May be it is a really dumb question, but after playing around with all built-in pathname-family functions and cl-fad
/pathname-utils
packages I still can't figure out how to convert a relative path to absolute (with respect to $PWD):
; let PWD be "/very/long/way"
(abspath "../road/home"); -> "/very/long/road/home"
Where the hypothetical function abspath
works just like os.path.abspath()
in Python.
Here's the final solution (based on the previous two answers):
(defun abspath
(path-string)
(uiop:unix-namestring
(uiop:merge-pathnames*
(uiop:parse-unix-namestring path-string))))
uiop:parse-unix-namestring
converts the string argument to a pathname, replacing .
and ..
references; uiop:merge-pathnames*
translates a relative pathname to absolute; uiop:unix-namestring
converts the pathname back to a string.
Also, if you know for sure what kind of file the path points to, you can use either:
(uiop:unix-namestring (uiop:file-exists-p path))
or
(uiop:unix-namestring (uiop:directory-exists-p path))
because both file-exists-p
and directory-exists-p
return absolute pathnames (or nil
, if file does not exist).
UPDATE:
Apparently in some implementations (like ManKai Common Lisp) uiop:merge-pathnames*
does not prepend the directory part if the given pathname lacks ./
prefix (for example if you feed it #P"main.c"
rather than #P"./main.c"
). So the safer solution is:
(defun abspath
(path-string &optional (dir-name (uiop:getcwd)))
(uiop:unix-namestring
(uiop:ensure-absolute-pathname
(uiop:merge-pathnames*
(uiop:parse-unix-namestring path-string))
dir-name)))