;;; files.el --- file input and output commands for Emacs -*- lexical-binding:t -*-
-;; Copyright (C) 1985-1987, 1992-2014 Free Software Foundation, Inc.
+;; Copyright (C) 1985-1987, 1992-2015 Free Software Foundation, Inc.
;; Maintainer: emacs-devel@gnu.org
;; Package: emacs
(lambda (f) (and (file-directory-p f) 'dir-ok)))
(error "No such directory found via CDPATH environment variable"))))
+(defun file-tree-walk (dir action &rest args)
+ "Walk DIR executing ACTION on each file, with ARGS as additional arguments.
+For each file, the function calls ACTION as follows:
+
+ \(ACTION DIRECTORY BASENAME ARGS\)
+
+Where DIRECTORY is the leading directory of the file,
+ BASENAME is the basename of the file,
+ and ARGS are as specified in the call to this function, or nil if omitted.
+
+The ACTION is applied to each subdirectory before descending into
+it, and if nil is returned at that point, the descent will be
+prevented. Directory entries are sorted with string-lessp."
+ (cond ((file-directory-p dir)
+ (setq dir (file-name-as-directory dir))
+ (let ((lst (directory-files dir nil nil t))
+ fullname file)
+ (while lst
+ (setq file (car lst))
+ (setq lst (cdr lst))
+ (cond ((member file '("." "..")))
+ (t
+ (and (apply action dir file args)
+ (setq fullname (concat dir file))
+ (file-directory-p fullname)
+ (apply 'file-tree-walk fullname action args)))))))
+ (t
+ (apply action
+ (file-name-directory dir)
+ (file-name-nondirectory dir)
+ args))))
+
+(defsubst directory-name-p (name)
+ "Return non-nil if NAME ends with a slash character."
+ (and (> (length name) 0)
+ (char-equal (aref name (1- (length name))) ?/)))
+
+(defun directory-files-recursively (dir match &optional include-directories)
+ "Return all files under DIR that have file names matching MATCH (a regexp).
+This function works recursively. Files are returned in \"depth first\"
+and alphabetical order.
+If INCLUDE-DIRECTORIES, also include directories that have matching names."
+ (let ((result nil)
+ (files nil))
+ (dolist (file (sort (file-name-all-completions "" dir)
+ 'string<))
+ (unless (member file '("./" "../"))
+ (if (directory-name-p file)
+ (let* ((leaf (substring file 0 (1- (length file))))
+ (path (expand-file-name leaf dir)))
+ ;; Don't follow symlinks to other directories.
+ (unless (file-symlink-p path)
+ (setq result (nconc result (directory-files-recursively
+ path match include-directories))))
+ (when (and include-directories
+ (string-match match leaf))
+ (setq result (nconc result (list path)))))
+ (when (string-match match file)
+ (push (expand-file-name file dir) files)))))
+ (nconc result (nreverse files))))
+
(defun load-file (file)
"Load the Lisp file named FILE."
;; This is a case where .elc makes a lot of sense.
(`add (concat "/:" (apply operation arguments)))
(`insert-file-contents
(let ((visit (nth 1 arguments)))
- (prog1
+ (unwind-protect
(apply operation arguments)
(when (and visit buffer-file-name)
(setq buffer-file-name (concat "/:" buffer-file-name))))))