Node:Files List, Next:Counting function definitions, Previous:Sorting, Up:Prepare the data
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:
.el; and if so, add its name to a list.
.  or ..; and if
so skip it.
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'
(see Recursive Pattern: accumulate),
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)