The following works, and I think should work in most implementations (I have tried this on LW on MacOS, SBCL on Linux and Clojure on MacOS).
(defun directory-pathname (p)
;; Everything but the file part: this should be called
;; non-file-pathname or something
(make-pathname :name nil :type nil :version nil
:defaults (pathname p)))
(defconstant +wild-file+
;; a completely wildcarded filename with no other components
(make-pathname :name ':wild
:type ':wild
:version ':wild))
(defun wild-directory (d)
;; Take the directory parts of D and return a pathname which matches
;; all files in tat directory.
;;
;; Actually we could just use D for the defaults since it's only
;; used for defaults and that's less consy, but never mind
(merge-pathnames +wild-file+ (directory-pathname d)))
Then, for instance (directory (wild-directory "/tmp/"))
will return everything in the /tmp/
directory, and (directory (wild-directory (user-homedir-pathname)))
will return everything in your home directory.
However note that (this is clearly very implementation-dependent but it looks like all the implementations I use agree on this and I agree with their interpretation): (directory (wild-directory "/tmp"))
will return everything in /
, because "/tmp"
as a pathname means 'the file called tmp
in the directory called /
'. As I said I think this is a reasonable interpretation: pathnames which refer to directories end in /
.
Also note that +wild-file+
really is wild: it doesn't treat files whose names begin with .
in any special way. I think there is fairly obvious ambiguity about how filenames like /foo/bar/.x
should be parsed, although all three implementations I've tried take what I think is the right interpretation: the name is ".x"
and the type is nil
(/foo/bar/.x.y
seems to parse with a type of "y"
which I think is also reasonable).
In any case where you need to do any serious pathname manipulation the trick is to define a bunch of wrapper functions and constants which you then use, and behind which you can hide implementation-dependence (in this case the implementation-dependence is how to get all the files in a directory, everything else seems to be common between the implementations).
I suspect such an abstraction layer exists, but I don't know what it is unfortunately.
After writing this I tried CLISP, and it does something interestingly different: it looks like directory
doesn't return subdirectory names, just filenames. That's a perfectly reasonable implementation I think, but it would require some more abstraction.