Next: Counting function definitions, Previous: Sorting, Up: Prepare the data [Contents][Index]
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/22.1.1/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:
("abbrev.el" nil 1 1000 100
(20615 27034 579989 697000) (17905 55681 0 0) (20615 26327 734791 805000) 13188 "-rw-r--r--"
t 2971624 773)
On the other hand, mail/ is a directory within the lisp/ directory. The beginning of its listing looks like this:
("mail" t … )
(To learn about the different attributes, look at the documentation of
file-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 .. (“dot 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:
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 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/22.1.1/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, add 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 "." (substring (car (car current-directory-list)) -1)) ;; then do nothing since filename is that of ;; current directory or parent, "." or ".." ()
;; 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/22.1.1/lisp/"))
tells me that in and below my Lisp sources directory are 1031 ‘.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/22.1.1/lisp/") 'string-lessp)
Next: Counting function definitions, Previous: Sorting, Up: Prepare the data [Contents][Index]