Constructing New Pathnames
You can construct arbitrary pathnames using the **MAKE-PATHNAME**
function. It takes one keyword argument for each pathname component and returns a pathname with any supplied components filled in and the rest **NIL**
.9
(make-pathname
:directory '(:absolute "foo" "bar")
:name "baz"
:type "txt") ==> #p"/foo/bar/baz.txt"
However, if you want your programs to be portable, you probably don’t want to make pathnames completely from scratch: even though the pathname abstraction protects you from unportable filename syntax, filenames can be unportable in other ways. For instance, the filename /home/peter/foo.txt
is no good on an OS X box where /home/
is called /Users/
.
Another reason not to make pathnames completely from scratch is that different implementations use the pathname components slightly differently. For instance, as mentioned previously, some Windows-based Lisp implementations store the drive letter in the device component while others store it in the host component. If you write code like this:
(make-pathname :device "c" :directory '(:absolute "foo" "bar") :name "baz")
it will be correct on some implementations but not on others.
Rather than making names from scratch, you can build a new pathname based on an existing pathname with **MAKE-PATHNAME**
‘s keyword parameter :defaults
. With this parameter you can provide a pathname designator, which will supply the values for any components not specified by other arguments. For example, the following expression creates a pathname with an .html
extension and all other components the same as the pathname in the variable input-file
:
(make-pathname :type "html" :defaults input-file)
Assuming the value in input-file
was a user-provided name, this code will be robust in the face of operating system and implementation differences such as whether filenames have drive letters in them and where they’re stored in a pathname if they do.10
You can use the same technique to create a pathname with a different directory component.
(make-pathname :directory '(:relative "backups") :defaults input-file)
However, this will create a pathname whose whole directory component is the relative directory backups/
, regardless of any directory component input-file
may have had. For example:
(make-pathname :directory '(:relative "backups")
:defaults #p"/foo/bar/baz.txt") ==> #p"backups/baz.txt"
Sometimes, though, you want to combine two pathnames, at least one of which has a relative directory component, by combining their directory components. For instance, suppose you have a relative pathname such as #p"foo/bar.html"
that you want to combine with an absolute pathname such as #p"/www/html/"
to get #p"/www/html/foo/bar.html"
. In that case, **MAKE-PATHNAME**
won’t do; instead, you want **MERGE-PATHNAMES**
.
**MERGE-PATHNAMES**
takes two pathnames and merges them, filling in any **NIL**
components in the first pathname with the corresponding value from the second pathname, much like **MAKE-PATHNAME**
fills in any unspecified components with components from the :defaults
argument. However, **MERGE-PATHNAMES**
treats the directory component specially: if the first pathname’s directory is relative, the directory component of the resulting pathname will be the first pathname’s directory relative to the second pathname’s directory. Thus:
(merge-pathnames #p"foo/bar.html" #p"/www/html/") ==> #p"/www/html/foo/bar.html"
The second pathname can also be relative, in which case the resulting pathname will also be relative.
(merge-pathnames #p"foo/bar.html" #p"html/") ==> #p"html/foo/bar.html"
To reverse this process and obtain a filename relative to a particular root directory, you can use the handy function **ENOUGH-NAMESTRING**
.
(enough-namestring #p"/www/html/foo/bar.html" #p"/www/") ==> "html/foo/bar.html"
You can then combine **ENOUGH-NAMESTRING**
with **MERGE-PATHNAMES**
to create a pathname representing the same name but in a different root.
(merge-pathnames
(enough-namestring #p"/www/html/foo/bar/baz.html" #p"/www/")
#p"/www-backups/") ==> #p"/www-backups/html/foo/bar/baz.html"
**MERGE-PATHNAMES**
is also used internally by the standard functions that actually access files in the file system to fill in incomplete pathnames. For instance, suppose you make a pathname with just a name and a type.
(make-pathname :name "foo" :type "txt") ==> #p"foo.txt"
If you try to use this pathname as an argument to **OPEN**
, the missing components, such as the directory, must be filled in before Lisp will be able to translate the pathname to an actual filename. Common Lisp will obtain values for the missing components by merging the given pathname with the value of the variable ***DEFAULT-PATHNAME-DEFAULTS***
. The initial value of this variable is determined by the implementation but is usually a pathname with a directory component representing the directory where Lisp was started and appropriate values for the host and device components, if needed. If invoked with just one argument, **MERGE-PATHNAMES**
will merge the argument with the value of ***DEFAULT-PATHNAME-DEFAULTS***
. For instance, if ***DEFAULT-PATHNAME-DEFAULTS***
is #p"/home/peter/"
, then you’d get the following:
(merge-pathnames #p"foo.txt") ==> #p"/home/peter/foo.txt"