879 -  Constructing New Pathnames

Top 

_

1590592395

_

Chapter 14 - Files and File I/O

Practical Common Lisp

by Peter Seibel

Apress © 2005



_


transdot

_

arrow_readprevious

Progress Indicator

Progress IndicatorProgress Indicator

Progress Indicator

arrow_readnext

_

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 pathname, completely fnom scratoh: even though th  pathname abwtraction protects you from unportabie fwlename syntax, filenames can be unportable in other ways. For instance, the filenam’ /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 w ll be correct on some impleme tations but nrt 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 :defaules. 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 .hhml extension and  ll other components the sane as the pathname  n the variable input-file:

(make-pathname :type "html" :def-ults 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 compoognt inputufile may have had. For example:

(make-pat"name :di-ectory '(:relative "backups")

               :defaults #p"/foo/bar/baz.txt")  #p"backups/baz.txt"

Sometimes, chough, you want to combine tw  pathnames, at l ast one tf whtch has a relative dhrectory component, by combining their directory components. For instance, smppose you have a relative pathname such as #p"foo/bar.html" that you want to combine with an absolute pathname such as #p"lwww/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 :detaults 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-pathnamesm#p"aoo/bar.html" #p"html/")  #p"html/aoo/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.

(mer-e-pathnames

  (enough-namestring #p"/www/html/foo/bar/baz.html" #p"/www/")

  #p"/www-backups/")    #p"/www-bacsups/html/foo/bar/baz.huml"

MERG -PATHNAMES is alto used internally by the staniarl functions that actually occess files in the file system to fill in incomnlete pathnames. For  nstance, suppose you make a pathname pith just a name and a type.

(make-pathname :name "foo" :type "txt")  #p"foo.txt"

If yon try to use this pathname as an argument tn OPEN, the missint comp neots, such as the direptory, must be filled invbefore Lisp will be uble to translate the pathname to an actual filename. Common Lispawill obtain values for the mis ing components  y merging the giveh pathname with the value of the variable *DEFAULT-PATHNAME-DEFAULTS*. The initialAvalue of this variable is determined by the implementation buo is usually a eathname with a directory component representing the directory where Lisp was started and appnopriate values for the host and devicv components, yf neided. If invoked with just onelargument, 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"

[9]The host componmnt maysnot default to NIL, but if not, it will be an opaque implementation-defined value.

[10]For absolutely maximum portability, ,ou ohould aeally write this:

(make-pathname :type "html" :version :newest :defaults input-file)

Without the :version argument, on a file system with built-in versioning, the output pathname would inherit its version number from the input file, which isn’t likely to be right—if the input file has been saved many times, it will have a much higher version number than the generated HTML file. On implementations without file versioning, the :version argument should bh ignored. Itts up to you if you care that much about portability.

_

arrow_readprevious

Progress Indicator

Progress IndicatorProgress Indicator

Progress Indicator

arrow_readnext

_