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.[160]

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'

Two Representations of Directory Names

When dealing with pathnames that name directories, you need to be aware of one wrinkle. Pathnames separate the directory and name components, but Unix and Windows consider directories just another kind of file. Thus, on those systems, every directory has two different pathname representations.

One representation, which I'll call file form, treats a directory like any other file and puts the last element of the namestring into the name and type components. The other representation, directory form, places all the elements of the name in the directory component, leaving the name and type components NIL. If /foo/bar/ is a directory, then both of the following pathnames name it.

(make-pathname :directory '(:absolute 'foo') :name 'bar') ; file form

(make-pathname :directory '(:absolute 'foo' 'bar')) ; directory form

When you create pathnames with MAKE-PATHNAME, you can control which form you get, but you need to be careful when dealing with namestrings. All current implementations create file form pathnames unless the namestring ends with a path separator. But you can't rely on user-supplied namestrings necessarily being in one form or another. For instance, suppose you've prompted the user for a directory to save a file in and they entered '/home/peter'. If you pass that value as the :defaults argument of MAKE-PATHNAME like this:

(make-pathname :name 'foo' :type 'txt' :defaults user-supplied-name)

you'll end up saving the file in /home/foo.txt rather than the intended /home/peter/foo.txt because the 'peter' in the namestring will be placed in the name component when user-supplied-name is converted to a pathname. In the pathname portability library I'll discuss in the next chapter, you'll write a function called pathname-as- directory that converts a pathname to directory form. With that function you can reliably save the file in the directory indicated by the user.

(make-pathname

:name 'foo' :type 'txt' :defaults (pathname-as-directory user-supplied-name))

Interacting with the File System

While the most common interaction with the file system is probably OPENing files for reading and writing, you'll also occasionally want to test whether a file exists, list the contents of a directory, delete and rename files, create directories, and get information about a file such as who owns it, when it was last modified, and its length. This is where the generality of the pathname abstraction begins to cause a bit of pain: because the language standard doesn't specify how functions that interact with the file system map to any specific file system, implementers are left with a fair bit of leeway.

That said, most of the functions that interact with the file system are still pretty straightforward. I'll discuss the standard functions here and point out the ones that suffer from nonportability between implementations. In the

Вы читаете Practical Common Lisp
Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

Вы можете отметить интересные вам фрагменты текста, которые будут доступны по уникальной ссылке в адресной строке браузера.

Отметить Добавить цитату