Subject: bug#10489: 24.0.92; dired-do-copy may create infinite directory hierarchy

From: Thierry Volpiatto <[email-masked]>
Date: Mon, 27 Feb 2012 07:45:49 +0100

Stefan Monnier <[email-masked]> writes:

>>> if both FILE1 and FILE2 do not exist. Either it is a bug, or it must
>>> be documented.
>
> Since file-equal-p checks equality of *files* as opposed to equality of
> file *names*, all we need to do is to remind (in the docstring) that
> this predicate is only defined on existing files.
I have modified files-equal-p and file-subdir-of-p to work with
non--existing files, this allow to remove one call to file-subdir-of-p
in copy-directory and not create the initial top directory in this case
(not in this patch yet).

--8<---------------cut here---------------start------------->8---
diff --git a/lisp/files.el b/lisp/files.el
--- a/lisp/files.el
+++ b/lisp/files.el
@@ -4986,25 +4986,33 @@
       (delete-directory-internal directory)))))
 
 (defun files-equal-p (file1 file2)
-  "Return non-nil if FILE1 and FILE2 name the same file."
+  "Return non-nil if FILE1 and FILE2 name the same file.
+This function works even on non--existing files."
   (let ((handler (or (find-file-name-handler file1 'files-equal-p)
                      (find-file-name-handler file2 'files-equal-p))))
     (if handler
         (funcall handler 'files-equal-p file1 file2)
-      (equal (file-attributes (file-truename file1))
-             (file-attributes (file-truename file2))))))
-
-(defun file-subdir-of-p (dir1 dir2)
+      (let ((f1-attr (file-attributes (file-truename file1)))
+            (f2-attr (file-attributes (file-truename file2))))
+        (if (and f1-attr f2-attr)
+            (equal f1-attr f2-attr)
+          (string= (file-truename
+                    (file-name-as-directory file1))
+                   (file-truename
+                    (file-name-as-directory file2))))))))
+
+(defun file-subdir-of-p (dir1 dir2 &optional noexist)
   "Return non-nil if DIR1 is a subdirectory of DIR2.
 Note that a directory is treated by this function as a subdirectory of itself.
-This function only works when its two arguments already exist,
-when they don't, it returns nil."
+This function only works when its two arguments already exist unless
+optional argument NOEXIST is non--nil, otherwise it returns nil."
   (let ((handler (or (find-file-name-handler dir1 'file-subdir-of-p)
                      (find-file-name-handler dir2 'file-subdir-of-p))))
     (if handler
-        (funcall handler 'file-subdir-of-p dir1 dir2)
-      (when (and (file-directory-p dir1)
-                 (file-directory-p dir2))
+        (funcall handler 'file-subdir-of-p dir1 dir2 noexist)
+      (when (or noexist
+                (and (file-directory-p dir1)
+                     (file-directory-p dir2)))
         (loop with f1 = (file-truename dir1)
               with f2 = (file-truename dir2)
               with ls1 = (or (split-string f1 "/" t) (list "/"))
--8<---------------cut here---------------end--------------->8---

I will apply it today if no objections.

-- 
  Thierry
Get my Gnupg key:
gpg --keyserver pgp.mit.edu --recv-keys 59F29997