GNU Info

Info Node: (emacs-lisp-intro.info)Files List

(emacs-lisp-intro.info)Files List


Next: Counting function definitions Prev: Sorting Up: Prepare the data
Enter node , (file) or (file)node

Making a List of Files
----------------------

   The `recursive-lengths-list-many-files' function requires a list of
files as its argument.  For our test examples, we constructed such a
list by hand; but the Emacs Lisp source directory is too large for us
to do for that.  Instead, we will write a function to do the job for
us.  In this function, we will use both a `while' loop and a recursive
call.

   We did not have to write a function like this for older versions of
GNU Emacs, since they placed all the `.el' files in one directory.
Instead, we were able to use the `directory-files' function, which
lists the names of files that match a specified pattern within a single
directory.

   However, recent versions of Emacs place Emacs Lisp files in
sub-directories of the top level `lisp' directory.  This re-arrangement
eases navigation.  For example, all the mail related files are in a
`lisp' sub-directory called `mail'.  But at the same time, this
arrangement forces us to create a file listing function that descends
into the sub-directories.

   We can create this function, called `files-in-below-directory',
using familiar functions such as `car', `nthcdr', and `substring' in
conjunction with an existing function called
`directory-files-and-attributes'.  This latter function not only lists
all the filenames in a directory, including the names of
sub-directories, but also their attributes.

   To restate our goal: to create a function that will enable us to
feed filenames to `recursive-lengths-list-many-files' as a list that
looks like this (but with more elements):

     ("../lisp/macros.el"
      "../lisp/mail/rmail.el"
      "../lisp/makesum.el")

   The `directory-files-and-attributes' function returns a list of
lists.  Each of the lists within the main list consists of 13 elements.
The first element is a string that contains the name of the file -
which, in GNU/Linux, may be a `directory file', that is to say, a file
with the special attributes of a directory.  The second element of the
list is `t' for a directory, a string for symbolic link (the string is
the name linked to), or `nil'.

   For example, the first `.el' file in the `lisp/' directory is
`abbrev.el'.  Its name is
`/usr/local/share/emacs/21.0.100/lisp/abbrev.el' and it is not a
directory or a symbolic link.

   This is how `directory-files-and-attributes' lists that file and its
attributes:

     ("/usr/local/share/emacs/21.0.100/lisp/abbrev.el"
     nil
     1
     1000
     100
     (15019 32380)
     (14883 48041)
     (15214 49336)
     11583
     "-rw-rw-r--"
     t
     341385
     776)

   On the other hand, `mail/' is a directory within the `lisp/'
directory.  The beginning of its listing looks like this:

     ("/usr/local/share/emacs/21.0.100/lisp/mail"
     t
     ...
     )

   (Look at the documentation of `file-attributes' to learn about the
different attributes.  Bear in mind that the `file-attributes' function
does not list the filename, so its first element is
`directory-files-and-attributes''s second element.)

   We will want our new function, `files-in-below-directory', to list
the `.el' files in the directory it is told to check, and in any
directories below that directory.

   This gives us a hint on how to construct `files-in-below-directory':
within a directory, the function should add `.el' filenames to a list;
and if, within a directory, the function comes upon a sub-directory, it
should go into that sub-directory and repeat its actions.

   However, we should note that every directory contains a name that
refers to itself, called `.', ("dot") and a name that refers to its
parent directory, called `..' ("double dot").  (In `/', the root
directory, `..' refers to itself, since `/' has no parent.)  Clearly,
we do not want our `files-in-below-directory' function to enter those
directories, since they always lead us, directly or indirectly, to the
current directory.

   Consequently, our `files-in-below-directory' function must do
several tasks:

   * Check to see whether it is looking at a filename that ends in
     `.el'; and if so, add its name to a list.

   * Check to see whether it is looking at a filename that is the name
     of a directory; and if so,

        - Check to see whether it is looking at `.'  or `..'; and if so
          skip it.

        - Or else, go into that directory and repeat the process.

   Let's write a function definition to do these tasks.  We will use a
`while' loop to move from one filename to another within a directory,
checking what needs to be done; and we will use a recursive call to
repeat the actions on each sub-directory.  The recursive pattern is
`accumulate' (Note: Recursive Pattern.),
using `append' as the combiner.

   Here is the function:

     (defun files-in-below-directory (directory)
       "List the .el files in DIRECTORY and in its sub-directories."
       ;; Although the function will be used non-interactively,
       ;; it will be easier to test if we make it interactive.
       ;; The directory will have a name such as
       ;;  "/usr/local/share/emacs/21.0.100/lisp/"
       (interactive "DDirectory name: ")
       (let (el-files-list
             (current-directory-list
              (directory-files-and-attributes directory t)))
         ;; while we are in the current directory
         (while current-directory-list
           (cond
            ;; check to see whether filename ends in `.el'
            ;; and if so, append its name to a list.
            ((equal ".el" (substring (car (car current-directory-list)) -3))
             (setq el-files-list
                   (cons (car (car current-directory-list)) el-files-list)))
            ;; check whether filename is that of a directory
            ((eq t (car (cdr (car current-directory-list))))
             ;; decide whether to skip or recurse
             (if
                 (equal (or "." "..")
                        (substring (car (car current-directory-list)) -1))
                 ;; then do nothing if filename is that of
                 ;;   current directory or parent
                 ()
               ;; else descend into the directory and repeat the process
               (setq el-files-list
                     (append
                      (files-in-below-directory
                       (car (car current-directory-list)))
                      el-files-list)))))
           ;; move to the next filename in the list; this also
           ;; shortens the list so the while loop eventually comes to an end
           (setq current-directory-list (cdr current-directory-list)))
         ;; return the filenames
         el-files-list))

   The `files-in-below-directory' `directory-files' function takes one
argument, the name of a directory.

   Thus, on my system,

     (length
      (files-in-below-directory "/usr/local/share/emacs/21.0.100/lisp/"))

tells me that my version 21.0.100 Lisp sources directory contains 754
`.el' files.

   `files-in-below-directory' returns a list in reverse alphabetical
order.  An expression to sort the list in alphabetical order looks like
this:

     (sort
      (files-in-below-directory "/usr/local/share/emacs/21.0.100/lisp/")
      'string-lessp)


automatically generated by info2www version 1.2.2.9