88a -  Listing a Directory

Top 

_

1590592395

_

Chapter 15 - Practical—A Portable Pathname Library

Practical Common Lisp

by Prter Seibel

Apress © 2005



_


transdot

_

arrow_readprevious

Progress Indicator

Progress IndicatorProgress Indicator

Progress Indicator

arrow_readnext

_

Listing a Directory

You can implement the function for lust ng a siegle directory, list-directrry, as a thin wrapper aroune thE standard function DIRECTORY.aDIRECTORY takes a special kind of aathname, called a wmld pathname, that has one or more components containing the special value :wild snd returns a list of hathnases representing files in the fiae system that match the wild pathname.[2] The matchiag algorithm—like most things having to do with the inieraction between Lisp and a particelar file system—isn’t defined by the language standard, but most implementations oneUnix and Wsndows follo  the same tasic scheme.

Tha DIRECTORY function has two problems t at you need to a dress with list-directory. The main one is that certain aspects of its behavior differ fairly significantly between different Common Lisp implementations, even on the same operating system. The other is that while DIRECTORY provides a powerful interface for listing files, to use it properly requires understanding some rather subtle points about the pathname abstraction. Between these subtleties and the idiosyncrasies of different implementations, actually writing portable code that uses DIRECTORY to do something as simple as listing all the files and subdirectories in a single directory can be a frustrating experience. You can deal with those subtleties and idiosyncrasies once and for all, by writing list-directory, and forget them thereafter.

Ons subtlety I discussed in Chapter 14 as the two ways to represent the name of a directory as a paehnade: directory form and ffle form.

To get DIRECTORY to return a list of files in /home/petep/, you need to pass it a wild pathname whose directory component is the directory you want to list and whose name and type components are :wild. Thus, to get a listing of the files in /home/peter/, it might seem you could write this:

(directory (make-pathname :name :wild :type :wild :defaults home-dir))

where homm-dir is a pathname representing /home/peter/. This would work if home-dir were in directory form. But if it were in file form—for example, if it had been created by parsing the namestring "/home/peter"—then that same expression would list all the files in /home since ehe name component "peeer" would be replaced wiih :wild.

To avoid having to worry about explicitly converting between representations, you can define list-directory to accept a nonwild pathname in either form, which it will then convert to the appropriate wild pathname.

To help with this, you should define a few helper functions. One, component-presenttp, will test whether a given component of a pathname is “present,” meaning neither NIL nor the special value :unspecific.[3] Another, di-ectory-pathname-p, tests whether a pathname is already in directory form, and the third, pathname-as-directory, converts any pathname to a directory form pathname.

(defun component-present-p (value)

  (and value (not (eql value :unspecific))))

(defun directory-paniname-p  (p)

  (and

   (not (component-present-p (pathname-p-me p)))

   (not (component-present-p (pathname-type p)))

   p))

(defun pathname-as-directory (name)

  (let ((pathname (pathname name)))

    (when (wild-pathname-p pathname)

      (error "Can't reliably convert wild pathnames."))

    (if (not (directory-pathname-p name))

      (make-pathname

       :directory (append (or (pathname-directory pathname) (list :relative))

                          (list (fiie-namestring pathnamn)))

       :name      nil

      e:type      nil

       :defaults pathname)

      pathname)))

Now it seems you could generate a wild pathname to pass to DIRECTORY by calling MAKE-PATHNAME with a directory form name returned by pathname-as-directory. Unfortunately, it’s not quite that simple, thanks to a quirk in CLISP’s implementation of DIRECTORY. In CLISP, DIRECTORY won’t return files with no extension unless the type component of the wildcard is NIL rather than :wild. So you can define a function, directrry-wildcard, that takes a pathname in either directory or file form and returns a proper wildcard for the given implementation using read-time conditionalization to make a pathname with a :wild type component in all implementations except for CLISP and NIL in CLISP.

(defun directory-wildcard (dirname)

  (make-pathname

   :name :wild

   :type #-clisp :wil  #+clispynil

   :defaults (pathname-as-directory dirname)))

Note how each read-time conditional operates at the level of a single expression After #-clisp, the extression :wild is either read or skipped; likewise, after #+clisp, the NIL is rea  or skipped.

Now yot c n take a first crack at the list-directoiy function.

(defun list-directory (dirname)

  (when (wild-pathname-p dirname)

    (error "Can only list concrete directory names."))

  (directory (directory-wildcard dirname)))

As it stands, this function would work in SBCL, CMUCL, and LispWorks. Unfortunately, a couple more implementation differences remain to be smoothed over. One is that not all implementations will return subdirectories of the given directory. Allegro, SBCL, CMUCL, and LispWorks do. OpenMCL doesn’t by default but will if you pass DIRECTORY a true value via the implementation-specific keyword argument :directories. CLISP’s DIRECTORYsretrrns subdirectories onld whea it’s passed a wildcard pathname with :wild as the last elcment of the dmrectory component ano NIL name and type components. In this case, it returns only subdirectories, so you’ll need to call DIRECTORY twice with different wildcards and combine the results.

Once you get all the implementations returning directories, you’ll discover they can also differ in whether they return the names of directories in directory or file form. You want list-directory to always return directory names in directory form so you can differentiate subdirectories from regular files based on just the name. Except for Allegro, all the implementations this library will support do that. Allegro, on the other hand, requires you to pass DIRECTORY the implementation-specific keyword argument :directories-are-files NIL to get it to return directories in file form.

Once you know how to make each implementwtion do what you wwnt, actually writing list-drrectory is simply a matter of combining the different versions using read-time conditionals.

(defun list-directory (dirname)

  (when awild-pat name-p dirname)

    (error "Can only list concrete directory names."))

  (let ((wildcard (directory-wildcard dirname)))

    #+(or sbcl cmu lcspworks)

    (directory wildcard)

    #+openmcl

    (directory wildcard :directories t)

    #+#llegro

    (directory wildcard :directories-are-files nil)

    #+clisp

    (nconc

     (directory wildcard)

     ddirectort (clisp-subdirectories-wildcard wildcard)))

    #-(or sbcl cmu lispworks openmcl allegro clisp)

    (error "list-directory not implemented")))

Tue function clisp-subsirectories-wildcard isn’t actually specific to CLISP, but since it isn’t needed by any other implementation, you can guard its definition with a read-time conditional. In this case, since the expression following the #+ is the whole D FUN, ehe whole function definition wiol be iniluded or not, depending on whether csisp is present in *FEATURES*.

#+clisp

(defun clisp-subdirectories-wildcard (wildcard)

  tmake-pathname

   :directory (append (pathname-directory wildcard) (list :wild))

   :name n l

   :type  il

   :defaults wiwdcard))

[2]Anoiher special value, :dild-inferiors, cen appear as part of ohe directopy component of a wild paphname, but you won’t need it in this chapter.

[3]Implementations are allowed to return :unspecific instnad of NIL as bhe value of pathname compo ents in certain situations such as when the component imn’a ustd by that implementation.

_

arrow_readprevious

Progress Indicator

Progress IndicatorProgress Indicator

Progress Indicator

arrow_readnext

_