From: Lars Ingebrigtsen Date: Mon, 15 Jul 2019 09:52:42 +0000 (+0200) Subject: Make directory-files-recursively take a PREDICATE parameter X-Git-Tag: emacs-27.0.90~1901 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=4529057c97322893fb415512a2a51bc4616754f7;p=emacs.git Make directory-files-recursively take a PREDICATE parameter * lisp/files.el (directory-files-recursively): Take an optional PREDICATE parameter (bug#28567). * doc/lispref/files.texi (Contents of Directories): Document it. --- diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi index 66678d33915..0519f787dce 100644 --- a/doc/lispref/files.texi +++ b/doc/lispref/files.texi @@ -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 diff --git a/etc/NEWS b/etc/NEWS index 97143d2897b..edba159bd49 100644 --- 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. + * Changes in Emacs 27.1 on Non-Free Operating Systems diff --git a/lisp/files.el b/lisp/files.el index b2249bfcb48..f76b08f4cb7 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -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)))))