]> git.eshelyaron.com Git - emacs.git/commitdiff
Make directory-files-recursively take a PREDICATE parameter
authorLars Ingebrigtsen <larsi@gnus.org>
Mon, 15 Jul 2019 09:52:42 +0000 (11:52 +0200)
committerLars Ingebrigtsen <larsi@gnus.org>
Mon, 15 Jul 2019 09:52:42 +0000 (11:52 +0200)
* lisp/files.el (directory-files-recursively): Take an optional
PREDICATE parameter (bug#28567).

* doc/lispref/files.texi (Contents of Directories): Document it.

doc/lispref/files.texi
etc/NEWS
lisp/files.el

index 66678d33915c40e227d1e6df8036c6c3bb1d14bd..0519f787dce3029b208d213c188ce8b16bbb6355 100644 (file)
@@ -2911,7 +2911,7 @@ An error is signaled if @var{directory} is not the name of a directory
 that can be read.
 @end defun
 
-@defun directory-files-recursively directory regexp &optional include-directories
+@defun directory-files-recursively directory regexp &optional include-directories predicate
 Return all files under @var{directory} whose names match @var{regexp}.
 This function searches the specified @var{directory} and its
 sub-directories, recursively, for files whose basenames (i.e., without
@@ -2925,6 +2925,13 @@ alphabetically by their basenames.  By default, directories whose
 names match @var{regexp} are omitted from the list, but if the
 optional argument @var{include-directories} is non-@code{nil}, they
 are included.
+
+By default, all subdirectories are descended into.  If @var{predicate}
+is @code{t}, errors when trying to descend into a subdirectory (for
+instance, if it's not readable by this user) are ignored.  If it's
+neither @code{nil} nor @code{t}, it should be a function that takes
+one parameter (the subdirectory name) and should return non-@code{nil}
+if the directory is to be descended into.
 @end defun
 
 @defun directory-files-and-attributes directory &optional full-name match-regexp nosort id-format
index 97143d2897bfc177f4fe2915c823b7df1eb293af..edba159bd499399c4e9bb5584672ccf30a13a51b 100644 (file)
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -2326,6 +2326,10 @@ zero-padding, upper- and lower-casing, and limiting the length of the
 interpolated strings.  The function has now also been documented in
 the Emacs Lisp manual.
 
++++
+** `directory-files-recursively' can now take an optional PREDICATE
+parameter to control descending into subdirectories.
+
 \f
 * Changes in Emacs 27.1 on Non-Free Operating Systems
 
index b2249bfcb48ede75915765e6f866d21b8e9d8228..f76b08f4cb7ee56315719ba1406b58a167131de0 100644 (file)
@@ -812,13 +812,22 @@ The path separator is colon in GNU and GNU-like systems."
                  (lambda (f) (and (file-directory-p f) 'dir-ok)))
     (error "No such directory found via CDPATH environment variable"))))
 
-(defun directory-files-recursively (dir regexp &optional include-directories)
+(defun directory-files-recursively (dir regexp
+                                        &optional include-directories predicate)
   "Return list of all files under DIR that have file names matching REGEXP.
-This function works recursively.  Files are returned in \"depth first\"
-order, and files from each directory are sorted in alphabetical order.
-Each file name appears in the returned list in its absolute form.
-Optional argument INCLUDE-DIRECTORIES non-nil means also include in the
-output directories whose names match REGEXP."
+This function works recursively.  Files are returned in \"depth
+first\" order, and files from each directory are sorted in
+alphabetical order.  Each file name appears in the returned list
+in its absolute form.
+
+Optional argument INCLUDE-DIRECTORIES non-nil means also include
+in the output directories whose names match REGEXP.
+
+PREDICATE can be either nil (which means that all subdirectories
+are descended into), t (which means that subdirectories that
+can't be read are ignored), or a function (which is called with
+name name of the subdirectory and should return non-nil if the
+subdirectory is to be descended into)."
   (let* ((result nil)
         (files nil)
          (dir (directory-file-name dir))
@@ -832,10 +841,20 @@ output directories whose names match REGEXP."
            (let* ((leaf (substring file 0 (1- (length file))))
                   (full-file (concat dir "/" leaf)))
              ;; Don't follow symlinks to other directories.
-             (unless (file-symlink-p full-file)
-               (setq result
-                     (nconc result (directory-files-recursively
-                                    full-file regexp include-directories))))
+             (when (and (not (file-symlink-p full-file))
+                         ;; Allow filtering subdirectories.
+                         (or (eq predicate nil)
+                             (eq predicate t)
+                             (funcall predicate full-file)))
+                (let ((sub-files
+                       (if (eq predicate t)
+                           (condition-case _
+                               (directory-files-recursively
+                               full-file regexp include-directories)
+                             (file-error nil))
+                         (directory-files-recursively
+                         full-file regexp include-directories))))
+                 (setq result (nconc result sub-files))))
              (when (and include-directories
                         (string-match regexp leaf))
                (setq result (nconc result (list full-file)))))