GNU bug report logs - #28834
25.1; dired-do-copy: allow to copy into a nonexistent directory

Previous Next

Package: emacs;

Reported by: "alexei28" <alexei28 <at> gmail.com>

Date: Sat, 14 Oct 2017 14:53:01 UTC

Severity: wishlist

Tags: patch

Found in version 25.1

Done: Tino Calancha <tino.calancha <at> gmail.com>

Bug is archived. No further changes may be made.

To add a comment to this bug, you must first unarchive it, by sending
a message to control AT debbugs.gnu.org, with unarchive 28834 in the body.
You can then email your comments to 28834 AT debbugs.gnu.org in the normal way.

Toggle the display of automated, internal messages from the tracker.

View this report as an mbox folder, status mbox, maintainer mbox


Report forwarded to bug-gnu-emacs <at> gnu.org:
bug#28834; Package emacs. (Sat, 14 Oct 2017 14:53:01 GMT) Full text and rfc822 format available.

Acknowledgement sent to "alexei28" <alexei28 <at> gmail.com>:
New bug report received and forwarded. Copy sent to bug-gnu-emacs <at> gnu.org. (Sat, 14 Oct 2017 14:53:02 GMT) Full text and rfc822 format available.

Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: "alexei28" <alexei28 <at> gmail.com>
To: <bug-gnu-emacs <at> gnu.org>
Subject: 25.1; Dired+ not copy file to nonexistent directory.
Date: Sat, 14 Oct 2017 13:54:45 +0300
[Message part 1 (text/plain, inline)]
Hi!

 

Windows 10, Emacs 25.1, Dired+

 

I want to copy file from "d:/TEMP/test/test.txt" to nonexistent directory
"d:/TEMP/test/new/" in tby package Dired+

 

My steps in Dired+ buffer:

1. Run command "split-window-vertically"

2. In top and bottom buffers has directory "d:/TEMP/test/"

3. Press "Shift c" (copy file)

4. In minibuffer show message: "Copy text.txt to : d:/TEMP/test/"

5. I replace target path by: "d:/TEMP/test/new/test.txt" and press return

6. I get the next error message:

 

Copy 'd:/TEMP/test/test.txt' to 'd:/TEMP/test/new/test.txt' failed:

(file-error Copying file Operation not permitted d:/TEMP/test/test.txt
d:/TEMP/test/new/test.txt)

 

It would be good if Emacs can autocreate not exist directory when copy/move
files.

Thank you.

 

Alex

[Message part 2 (text/html, inline)]

Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#28834; Package emacs. (Sat, 14 Oct 2017 15:51:01 GMT) Full text and rfc822 format available.

Message #8 received at 28834 <at> debbugs.gnu.org (full text, mbox):

From: Drew Adams <drew.adams <at> oracle.com>
To: alexei28 <alexei28 <at> gmail.com>, 28834 <at> debbugs.gnu.org
Subject: RE: bug#28834: 25.1; Dired+ not copy file to nonexistent directory.
Date: Sat, 14 Oct 2017 08:50:46 -0700 (PDT)
To be clear, this is really about vanilla Dired, not Dired+.
The title should perhaps be changed.




Severity set to 'wishlist' from 'minor' Request was from Tino Calancha <tino.calancha <at> gmail.com> to control <at> debbugs.gnu.org. (Sun, 15 Oct 2017 02:38:02 GMT) Full text and rfc822 format available.

Changed bug title to '25.1; dired-do-copy: allow to copy into a nonexistent directory' from '25.1; Dired+ not copy file to nonexistent directory.' Request was from Tino Calancha <tino.calancha <at> gmail.com> to control <at> debbugs.gnu.org. (Sun, 15 Oct 2017 02:41:01 GMT) Full text and rfc822 format available.

Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#28834; Package emacs. (Sun, 15 Oct 2017 04:44:02 GMT) Full text and rfc822 format available.

Message #15 received at 28834 <at> debbugs.gnu.org (full text, mbox):

From: Tino Calancha <tino.calancha <at> gmail.com>
To: 28834 <at> debbugs.gnu.org
Cc: Eli Zaretskii <eliz <at> gnu.org>, Drew Adams <drew.adams <at> oracle.com>
Subject: Re: bug#28834: 25.1;
 dired-do-copy: allow to copy into a nonexistent directory
Date: Sun, 15 Oct 2017 13:42:48 +0900
"alexei28" <alexei28 <at> gmail.com> writes:

> I want to copy file from "d:/TEMP/test/test.txt" to nonexistent directory "d:/TEMP/test/new/" with Dired
> Copy ‘d:/TEMP/test/test.txt’ to ‘d:/TEMP/test/new/test.txt’ failed:
>
> (file-error Copying file Operation not permitted d:/TEMP/test/test.txt d:/TEMP/test/new/test.txt)
>
> It would be good if Emacs can autocreate not exist directory when copy/move files.


It might has sense that Dired handle the creation of those destination dirs.
WDOT?

--8<-----------------------------cut here---------------start------------->8---
commit 1325ea21a069b5c539157390fb5df2eca76adecb
Author: Tino Calancha <tino.calancha <at> gmail.com>
Date:   Sun Oct 15 13:34:08 2017 +0900

    Dired: Allow to copy/rename files into a non-existent dir
    
    * lisp/dired-aux.el (dired--create-dirs): New defun.
    (dired-copy-file-recursive, dired-rename-file): Use it (Bug#28834).
    * doc/emacs/dired.texi (Operating on Files): Update manual.
    * etc/NEWS (Changes in Specialized Modes and Packages in Emacs 27.1)
    Announce feature.
    * lisp/dired-aux-tests.el (dired-test-bug28834): Add test.

diff --git a/doc/emacs/dired.texi b/doc/emacs/dired.texi
index db5dea329b..48013a3221 100644
--- a/doc/emacs/dired.texi
+++ b/doc/emacs/dired.texi
@@ -645,7 +645,8 @@ Operating on Files
 @item C @var{new} @key{RET}
 Copy the specified files (@code{dired-do-copy}).  The argument @var{new}
 is the directory to copy into, or (if copying a single file) the new
-name.  This is like the shell command @code{cp}.
+name.  Dired creates any non-existent directories in @var{new}.  This is
+like the shell command @code{cp}.
 
 @vindex dired-copy-preserve-time
 If @code{dired-copy-preserve-time} is non-@code{nil}, then copying
@@ -674,7 +675,8 @@ Operating on Files
 @cindex moving files (in Dired)
 @item R @var{new} @key{RET}
 Rename the specified files (@code{dired-do-rename}).  If you rename a
-single file, the argument @var{new} is the new name of the file.  If
+single file, the argument @var{new} is the new name of the file.  Dired
+creates any non-existent directories in @var{new}.  If
 you rename several files, the argument @var{new} is the directory into
 which to move the files (this is like the shell command @command{mv}).
 
diff --git a/etc/NEWS b/etc/NEWS
index 716b0309a5..08291fc39f 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -56,6 +56,12 @@ whether '"' is also replaced in 'electric-quote-mode'.  If non-nil,
 
 * Changes in Specialized Modes and Packages in Emacs 27.1
 
+** Dired
+
+---
+*** dired-do-copy and dired-rename-file now create non-existent
+directories in the destination.
+
 ** Edebug
 
 +++
diff --git a/lisp/dired-aux.el b/lisp/dired-aux.el
index 7e2252fcf1..fcea8f1b13 100644
--- a/lisp/dired-aux.el
+++ b/lisp/dired-aux.el
@@ -1548,6 +1548,10 @@ dired-copy-file
 
 (declare-function make-symbolic-link "fileio.c")
 
+(defun dired--create-dirs (dir)
+  (unless (file-exists-p dir)
+    (dired-create-directory dir)))
+
 (defun dired-copy-file-recursive (from to ok-flag &optional
 				       preserve-time top recursive)
   (when (and (eq t (car (file-attributes from)))
@@ -1564,6 +1568,7 @@ dired-copy-file-recursive
 	  (if (stringp (car attrs))
 	      ;; It is a symlink
 	      (make-symbolic-link (car attrs) to ok-flag)
+            (dired--create-dirs (file-name-directory to))
 	    (copy-file from to ok-flag preserve-time))
 	(file-date-error
 	 (push (dired-make-relative from)
@@ -1573,6 +1578,7 @@ dired-copy-file-recursive
 ;;;###autoload
 (defun dired-rename-file (file newname ok-if-already-exists)
   (dired-handle-overwrite newname)
+  (dired--create-dirs (file-name-directory newname))
   (rename-file file newname ok-if-already-exists) ; error is caught in -create-files
   ;; Silently rename the visited file of any buffer visiting this file.
   (and (get-file-buffer file)
diff --git a/test/lisp/dired-aux-tests.el b/test/lisp/dired-aux-tests.el
index d41feb1592..d8114d8401 100644
--- a/test/lisp/dired-aux-tests.el
+++ b/test/lisp/dired-aux-tests.el
@@ -40,5 +40,25 @@
           (should-not (dired-do-shell-command "ls ? ./`?`" nil files)))
       (delete-file foo))))
 
+(ert-deftest dired-test-bug28834 ()
+  "test for https://debbugs.gnu.org/28834 ."
+  (let* ((from (make-temp-file "from"))
+         (foo (make-temp-file "foo" 'dir))
+         (bar (file-name-as-directory (expand-file-name "bar" foo)))
+         (qux (file-name-as-directory (expand-file-name "qux" foo)))
+         (tmpdir temporary-file-directory)
+         (to-cp (expand-file-name "foo-cp" bar))
+         (to-mv (expand-file-name "foo-mv" qux)))
+    (unwind-protect
+        (progn
+          (dired-copy-file-recursive from to-cp nil)
+          (should (file-exists-p to-cp))
+          (dired-rename-file from to-mv nil)
+          (should (file-exists-p to-mv)))
+      ;; clean up
+      (delete-directory foo 'recursive)
+      (delete-file from))))
+
+
 (provide 'dired-aux-tests)
 ;; dired-aux-tests.el ends here

--8<-----------------------------cut here---------------end--------------->8---
In GNU Emacs 27.0.50 (build 1, x86_64-pc-linux-gnu, GTK+ Version 3.22.11)
 of 2017-10-15
Repository revision: eed3a3d9e95d2c5346a23c9d92ca4e5848330183




Added tag(s) patch. Request was from Tino Calancha <tino.calancha <at> gmail.com> to control <at> debbugs.gnu.org. (Sun, 15 Oct 2017 04:44:02 GMT) Full text and rfc822 format available.

Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#28834; Package emacs. (Sun, 15 Oct 2017 14:22:01 GMT) Full text and rfc822 format available.

Message #20 received at 28834 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Tino Calancha <tino.calancha <at> gmail.com>
Cc: 28834 <at> debbugs.gnu.org, drew.adams <at> oracle.com
Subject: Re: bug#28834: 25.1;
 dired-do-copy: allow to copy into a nonexistent directory
Date: Sun, 15 Oct 2017 17:21:21 +0300
> From: Tino Calancha <tino.calancha <at> gmail.com>
> Cc: Eli Zaretskii <eliz <at> gnu.org>, Drew Adams <drew.adams <at> oracle.com>
> Date: Sun, 15 Oct 2017 13:42:48 +0900
> 
> It might has sense that Dired handle the creation of those destination dirs.
> WDOT?

I think, if we accept this, it must be an optional feature, by default
off.

Thanks.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#28834; Package emacs. (Sun, 15 Oct 2017 14:48:01 GMT) Full text and rfc822 format available.

Message #23 received at 28834 <at> debbugs.gnu.org (full text, mbox):

From: Tino Calancha <tino.calancha <at> gmail.com>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: 28834 <at> debbugs.gnu.org, drew.adams <at> oracle.com,
 Tino Calancha <tino.calancha <at> gmail.com>
Subject: Re: bug#28834: 25.1; dired-do-copy: allow to copy into a nonexistent
 directory
Date: Sun, 15 Oct 2017 23:47:45 +0900 (JST)

On Sun, 15 Oct 2017, Eli Zaretskii wrote:

>> From: Tino Calancha <tino.calancha <at> gmail.com>
>> Cc: Eli Zaretskii <eliz <at> gnu.org>, Drew Adams <drew.adams <at> oracle.com>
>> Date: Sun, 15 Oct 2017 13:42:48 +0900
>>
>> It might has sense that Dired handle the creation of those destination dirs.
>> WDOT?
>
> I think, if we accept this, it must be an optional feature, by default
> off.
Sounds good to me.
I will prepare a new patch tomorrow.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#28834; Package emacs. (Sun, 15 Oct 2017 15:44:02 GMT) Full text and rfc822 format available.

Message #26 received at 28834 <at> debbugs.gnu.org (full text, mbox):

From: Drew Adams <drew.adams <at> oracle.com>
To: Tino Calancha <tino.calancha <at> gmail.com>, 28834 <at> debbugs.gnu.org
Cc: Eli Zaretskii <eliz <at> gnu.org>
Subject: RE: bug#28834: 25.1; dired-do-copy: allow to copy into a nonexistent
 directory
Date: Sun, 15 Oct 2017 08:43:32 -0700 (PDT)
> > I want to copy file from "d:/TEMP/test/test.txt" to nonexistent
> > directory "d:/TEMP/test/new/" with Dired
> > Copy ‘d:/TEMP/test/test.txt’ to ‘d:/TEMP/test/new/test.txt’ failed:
> >
> > (file-error Copying file Operation not permitted d:/TEMP/test/test.txt
> d:/TEMP/test/new/test.txt)
> >
> > It would be good if Emacs can autocreate not exist directory when
> > copy/move files.
> 
> It might has sense that Dired handle the creation of those destination
> dirs.  WDOT?

The enhancement request is "It would be good if Emacs CAN
autocreate not exist directory when copy/move files."

The operative word here is "CAN".  A change to _always_
create missing dirs would be inappropriate.  (I have
not looked at your patch in detail.)

It is important that Dired not just create dirs on its
own, especially since that would be an incompatible
behavior change, and also because it would mean also
creating "missing dirs" as a result of a user making
a typing mistake.  At the very least, users should be
able to control this by way of confirming.

What should be done is to offer users the POSSIBILITY
of having Dired create the missing directories.

This can be done in various ways:

1. Have a different command, which users can bind in
   place of the existing command.

2. Have a user option that controls whether Dired creates
   missing dirs by default, i.e., without confirmation.

3. Have Dired ask whether to create missing directories.

I think probably 2+3 would be good: have Dired prompt
when dirs are missing, but have an option that lets
users who never want to be prompted bypass prompting.
The option could be 3-valued: `always-prompt',
`always-create', `never-create' (or other names).




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#28834; Package emacs. (Mon, 16 Oct 2017 03:48:02 GMT) Full text and rfc822 format available.

Message #29 received at 28834 <at> debbugs.gnu.org (full text, mbox):

From: Tino Calancha <tino.calancha <at> gmail.com>
To: Drew Adams <drew.adams <at> oracle.com>
Cc: Eli Zaretskii <eliz <at> gnu.org>, 28834 <at> debbugs.gnu.org
Subject: Re: bug#28834: 25.1;
 dired-do-copy: allow to copy into a nonexistent directory
Date: Mon, 16 Oct 2017 12:47:06 +0900
Drew Adams <drew.adams <at> oracle.com> writes:

> The enhancement request is "It would be good if Emacs CAN
> autocreate not exist directory when copy/move files."
>
> The operative word here is "CAN".
> This can be done in various ways:
>
> 1. Have a different command, which users can bind in
>    place of the existing command.
>
> 2. Have a user option that controls whether Dired creates
>    missing dirs by default, i.e., without confirmation.
>
> 3. Have Dired ask whether to create missing directories.
>
> I think probably 2+3 would be good: have Dired prompt
> when dirs are missing, but have an option that lets
> users who never want to be prompted bypass prompting.
> The option could be 3-valued: `always-prompt',
> `always-create', `never-create' (or other names).

Thank you for the explanation.  Let's discuss the updated patch:
--8<-----------------------------cut here---------------start------------->8---
commit b656651e9268f5dd646933b992bd37771c3e99ca
Author: Tino Calancha <tino.calancha <at> gmail.com>
Date:   Mon Oct 16 12:41:41 2017 +0900

    Allow to copy/rename file into a non-existent dir
    
    * lisp/dired-aux.el (dired-create-destination-dirs): New option.
    (dired--create-nonexistent-dirs): New defun.
    (dired-copy-file-recursive, dired-rename-file): Use it (Bug#28834).
    * lisp/dired-aux-tests.el (dired-test-bug28834): Add test.
    * doc/emacs/dired.texi (Operating on Files): Update manual.
    * etc/NEWS (Changes in Specialized Modes and Packages in Emacs 27.1)
    Announce this change.

diff --git a/doc/emacs/dired.texi b/doc/emacs/dired.texi
index db5dea329b..0f37ac60ac 100644
--- a/doc/emacs/dired.texi
+++ b/doc/emacs/dired.texi
@@ -646,6 +646,16 @@ Operating on Files
 Copy the specified files (@code{dired-do-copy}).  The argument @var{new}
 is the directory to copy into, or (if copying a single file) the new
 name.  This is like the shell command @code{cp}.
+The option @var{dired-create-destination-dirs} controls whether Dired
+should create non-existent directories in @var{new}.
+
+@videnx dired-create-destination-dirs
+The option @code{dired-create-destination-dirs} controls whether Dired
+should create non-existent directories in the destination while
+copying/renaming files.  The default value @code{never} means Dired
+never creates such missing directories;  the value @code{always},
+means Dired automatically creates them; the value @code{prompt}
+means Dired asks you for confirmation before creating them.
 
 @vindex dired-copy-preserve-time
 If @code{dired-copy-preserve-time} is non-@code{nil}, then copying
@@ -678,6 +688,9 @@ Operating on Files
 you rename several files, the argument @var{new} is the directory into
 which to move the files (this is like the shell command @command{mv}).
 
+The option @var{dired-create-destination-dirs} controls whether Dired
+should create non-existent directories in @var{new}.
+
 Dired automatically changes the visited file name of buffers associated
 with renamed files so that they refer to the new names.
 
diff --git a/etc/NEWS b/etc/NEWS
index 716b0309a5..e5cec45426 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -56,6 +56,13 @@ whether '"' is also replaced in 'electric-quote-mode'.  If non-nil,
 
 * Changes in Specialized Modes and Packages in Emacs 27.1
 
+** Dired
+
+---
+*** The new user option 'dired-create-destination-dirs' controls whether
+'dired-do-copy' and 'dired-rename-file' must create non-existent
+directories in the destination.
+
 ** Edebug
 
 +++
diff --git a/lisp/dired-aux.el b/lisp/dired-aux.el
index 7e2252fcf1..0e415b7738 100644
--- a/lisp/dired-aux.el
+++ b/lisp/dired-aux.el
@@ -1548,6 +1548,26 @@ dired-copy-file
 
 (declare-function make-symbolic-link "fileio.c")
 
+(defcustom dired-create-destination-dirs 'never
+  "Whether Dired should create destination dirs when copying/removing files.
+If never, don't create them.
+If always, create them without ask.
+If prompt, ask for user confirmation."
+  :type '(choice (const :tag "Never create non-existent dirs" never)
+		 (const :tag "Always create non-existent dirs" always)
+		 (const :tag "Ask for user confirmation" prompt))
+  :group 'dired
+  :version "27.1")
+
+(defun dired--create-dirs (dir)
+  (unless (file-exists-p dir)
+    (pcase dired-create-destination-dirs
+      ('never nil)
+      ('always (dired-create-directory dir))
+      ('prompt
+       (when (yes-or-no-p (format "Create destination dir '%s'? " dir))
+         (dired-create-directory dir))))))
+
 (defun dired-copy-file-recursive (from to ok-flag &optional
 				       preserve-time top recursive)
   (when (and (eq t (car (file-attributes from)))
@@ -1564,6 +1584,7 @@ dired-copy-file-recursive
 	  (if (stringp (car attrs))
 	      ;; It is a symlink
 	      (make-symbolic-link (car attrs) to ok-flag)
+            (dired--create-dirs (file-name-directory to))
 	    (copy-file from to ok-flag preserve-time))
 	(file-date-error
 	 (push (dired-make-relative from)
@@ -1573,6 +1594,7 @@ dired-copy-file-recursive
 ;;;###autoload
 (defun dired-rename-file (file newname ok-if-already-exists)
   (dired-handle-overwrite newname)
+  (dired--create-dirs (file-name-directory newname))
   (rename-file file newname ok-if-already-exists) ; error is caught in -create-files
   ;; Silently rename the visited file of any buffer visiting this file.
   (and (get-file-buffer file)
diff --git a/test/lisp/dired-aux-tests.el b/test/lisp/dired-aux-tests.el
index d41feb1592..7778db0ea4 100644
--- a/test/lisp/dired-aux-tests.el
+++ b/test/lisp/dired-aux-tests.el
@@ -40,5 +40,34 @@
           (should-not (dired-do-shell-command "ls ? ./`?`" nil files)))
       (delete-file foo))))
 
+(ert-deftest dired-test-bug28834 ()
+  "test for https://debbugs.gnu.org/28834 ."
+  (let* ((from (make-temp-file "from"))
+         (foo (make-temp-file "foo" 'dir))
+         (bar (file-name-as-directory (expand-file-name "bar" foo)))
+         (qux (file-name-as-directory (expand-file-name "qux" foo)))
+         (tmpdir temporary-file-directory)
+         (to-cp (expand-file-name "foo-cp" bar))
+         (to-mv (expand-file-name "foo-mv" qux))
+         (dired-create-destination-dirs 'always))
+    (unwind-protect
+        (progn
+          (dired-copy-file-recursive from to-cp nil)
+          (should (file-exists-p to-cp))
+          (dired-rename-file from to-mv nil)
+          (should (file-exists-p to-mv))
+          ;; Repeat the same with `dired-create-destination-dirs' set to 'never.
+          (dired-rename-file to-mv from nil)
+          (delete-file to-cp)
+          (delete-directory bar)
+          (delete-directory qux)
+          (let ((dired-create-destination-dirs 'never))
+            (should-error (dired-copy-file-recursive from to-cp nil))
+            (should-error (dired-rename-file from to-mv nil))))
+      ;; clean up
+      (delete-directory foo 'recursive)
+      (delete-file from))))
+
+
 (provide 'dired-aux-tests)
 ;; dired-aux-tests.el ends here

--8<-----------------------------cut here---------------end--------------->8---
In GNU Emacs 27.0.50 (build 11, x86_64-pc-linux-gnu, GTK+ Version 3.22.11)
 of 2017-10-16
Repository revision: eed3a3d9e95d2c5346a23c9d92ca4e5848330183




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#28834; Package emacs. (Mon, 16 Oct 2017 16:13:01 GMT) Full text and rfc822 format available.

Message #32 received at 28834 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Tino Calancha <tino.calancha <at> gmail.com>
Cc: 28834 <at> debbugs.gnu.org, drew.adams <at> oracle.com
Subject: Re: bug#28834: 25.1;
 dired-do-copy: allow to copy into a nonexistent directory
Date: Mon, 16 Oct 2017 19:12:12 +0300
> From: Tino Calancha <tino.calancha <at> gmail.com>
> Cc: 28834 <at> debbugs.gnu.org,  Eli Zaretskii <eliz <at> gnu.org>
> Date: Mon, 16 Oct 2017 12:47:06 +0900
> 
> Thank you for the explanation.  Let's discuss the updated patch:

Thanks, I have a few comments:

> diff --git a/doc/emacs/dired.texi b/doc/emacs/dired.texi
> index db5dea329b..0f37ac60ac 100644
> --- a/doc/emacs/dired.texi
> +++ b/doc/emacs/dired.texi
> @@ -646,6 +646,16 @@ Operating on Files
>  Copy the specified files (@code{dired-do-copy}).  The argument @var{new}
>  is the directory to copy into, or (if copying a single file) the new
>  name.  This is like the shell command @code{cp}.
> +The option @var{dired-create-destination-dirs} controls whether Dired
> +should create non-existent directories in @var{new}.

This sentence is redundant (repeated right after it), and also uses
@var incorrectly.

> +@videnx dired-create-destination-dirs
   ^^^^^^^
A typo.

> +The option @code{dired-create-destination-dirs} controls whether Dired
> +should create non-existent directories in the destination while
> +copying/renaming files.  The default value @code{never} means Dired
> +never creates such missing directories;  the value @code{always},
> +means Dired automatically creates them; the value @code{prompt}
> +means Dired asks you for confirmation before creating them.

I think we generally use 'ask', not 'prompt' in these cases.

> diff --git a/etc/NEWS b/etc/NEWS
> index 716b0309a5..e5cec45426 100644
> --- a/etc/NEWS
> +++ b/etc/NEWS
> @@ -56,6 +56,13 @@ whether '"' is also replaced in 'electric-quote-mode'.  If non-nil,
>  
>  * Changes in Specialized Modes and Packages in Emacs 27.1
>  
> +** Dired
> +
> +---

Since you've updated the manual, this should be "+++", not "---".

> +*** The new user option 'dired-create-destination-dirs' controls whether
> +'dired-do-copy' and 'dired-rename-file' must create non-existent
                                           ^^^^
"Should", not "must".

> +directories in the destination.
> +
>  ** Edebug
>  
>  +++
> diff --git a/lisp/dired-aux.el b/lisp/dired-aux.el
> index 7e2252fcf1..0e415b7738 100644
> --- a/lisp/dired-aux.el
> +++ b/lisp/dired-aux.el
> @@ -1548,6 +1548,26 @@ dired-copy-file
>  
>  (declare-function make-symbolic-link "fileio.c")
>  
> +(defcustom dired-create-destination-dirs 'never
> +  "Whether Dired should create destination dirs when copying/removing files.
> +If never, don't create them.
> +If always, create them without ask.
> +If prompt, ask for user confirmation."

The symbols should be quoted: `never', `always', etc.

Btw, perhaps it's better to use nil instead of 'never'.

> +(defun dired--create-dirs (dir)
> +  (unless (file-exists-p dir)
> +    (pcase dired-create-destination-dirs
> +      ('never nil)
> +      ('always (dired-create-directory dir))
> +      ('prompt
> +       (when (yes-or-no-p (format "Create destination dir '%s'? " dir))
> +         (dired-create-directory dir))))))

Is use of pcase really justified here?

> +(ert-deftest dired-test-bug28834 ()
> +  "test for https://debbugs.gnu.org/28834 ."
> +  (let* ((from (make-temp-file "from"))
> +         (foo (make-temp-file "foo" 'dir))
> +         (bar (file-name-as-directory (expand-file-name "bar" foo)))
> +         (qux (file-name-as-directory (expand-file-name "qux" foo)))
> +         (tmpdir temporary-file-directory)
> +         (to-cp (expand-file-name "foo-cp" bar))
> +         (to-mv (expand-file-name "foo-mv" qux))
> +         (dired-create-destination-dirs 'always))
> +    (unwind-protect
> +        (progn
> +          (dired-copy-file-recursive from to-cp nil)
> +          (should (file-exists-p to-cp))
> +          (dired-rename-file from to-mv nil)
> +          (should (file-exists-p to-mv))
> +          ;; Repeat the same with `dired-create-destination-dirs' set to 'never.
> +          (dired-rename-file to-mv from nil)
> +          (delete-file to-cp)
> +          (delete-directory bar)
> +          (delete-directory qux)
> +          (let ((dired-create-destination-dirs 'never))
> +            (should-error (dired-copy-file-recursive from to-cp nil))
> +            (should-error (dired-rename-file from to-mv nil))))

This doesn't test the 3rd value.  Why is that?




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#28834; Package emacs. (Tue, 17 Oct 2017 01:50:02 GMT) Full text and rfc822 format available.

Message #35 received at 28834 <at> debbugs.gnu.org (full text, mbox):

From: Drew Adams <drew.adams <at> oracle.com>
To: Tino Calancha <tino.calancha <at> gmail.com>
Cc: Eli Zaretskii <eliz <at> gnu.org>, 28834 <at> debbugs.gnu.org
Subject: RE: bug#28834: 25.1; dired-do-copy: allow to copy into a nonexistent
 directory
Date: Mon, 16 Oct 2017 18:49:17 -0700 (PDT)
Thanks for working on this, Tino.

I haven't looked at it in detail.

1. Maybe name the function `dired-maybe-create-dirs'
   (and I don't see why its name should suggest that
   it is internal).

2. I think this should use `%s', no?

  (format "Create destination dir '%s'? " dir))

(I'm also surprised that we apparently now use
'...' instead of `...' in News, but whatever...)




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#28834; Package emacs. (Tue, 17 Oct 2017 04:50:02 GMT) Full text and rfc822 format available.

Message #38 received at 28834 <at> debbugs.gnu.org (full text, mbox):

From: Tino Calancha <tino.calancha <at> gmail.com>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: 28834 <at> debbugs.gnu.org, drew.adams <at> oracle.com
Subject: Re: bug#28834: 25.1;
 dired-do-copy: allow to copy into a nonexistent directory
Date: Tue, 17 Oct 2017 13:49:34 +0900
Eli Zaretskii <eliz <at> gnu.org> writes:

Thank you for the comments.
* I have changed the new function name to `dired-maybe-create-dirs'
  as Drew suggested.
* I think I have addressed all your comments as well.

>> +The option @var{dired-create-destination-dirs} controls whether Dired
>> +should create non-existent directories in @var{new}.
>
> This sentence is redundant (repeated right after it), and also uses
> @var incorrectly.
Removed that entence.

>> +@videnx dired-create-destination-dirs
>    ^^^^^^^
> A typo.
Fixed.

>> +means Dired automatically creates them; the value @code{prompt}
>> +means Dired asks you for confirmation before creating them.
>
> I think we generally use 'ask', not 'prompt' in these cases.
Changed to 'ask.

>> +** Dired
>> +
>> +---
>
> Since you've updated the manual, this should be "+++", not "---".
OK.

>> +'dired-do-copy' and 'dired-rename-file' must create non-existent
>                                            ^^^^
> "Should", not "must".
OK.

>> +If never, don't create them.
>> +If always, create them without ask.
>> +If prompt, ask for user confirmation."
>
> The symbols should be quoted: `never', `always', etc.
OK.
> Btw, perhaps it's better to use nil instead of 'never'.
Changed never ---> nil.

>> +(defun dired--create-dirs (dir)
>> +  (unless (file-exists-p dir)
>> +    (pcase dired-create-destination-dirs
>> +      ('never nil)
>> +      ('always (dired-create-directory dir))
>> +      ('prompt
>> +       (when (yes-or-no-p (format "Create destination dir '%s'? " dir))
>> +         (dired-create-directory dir))))))
>
> Is use of pcase really justified here?
I rewrote the function: now it loooks more simple.

>> +(ert-deftest dired-test-bug28834 ()
>> +  "test for https://debbugs.gnu.org/28834 ."
> This doesn't test the 3rd value.  Why is that?

Laziness? Bad memory? Busy? Stomach problems? Other not listed here?
Updated the test: now it run all cases.

--8<-----------------------------cut here---------------start------------->8---
commit fb91d159f241a6eeecc41c4101472f145a00c0d1
Author: Tino Calancha <tino.calancha <at> gmail.com>
Date:   Tue Oct 17 13:48:59 2017 +0900

    Allow to copy/rename file into a non-existent dir
    
    * lisp/dired-aux.el (dired-create-destination-dirs): New option.
    (dired-maybe-create-dirs): New defun.
    (dired-copy-file-recursive, dired-rename-file): Use it (Bug#28834).
    * lisp/dired-aux-tests.el (dired-test-bug28834): Add test.
    * doc/emacs/dired.texi (Operating on Files): Update manual.
    * etc/NEWS (Changes in Specialized Modes and Packages in Emacs 27.1)
    Announce this change.

diff --git a/doc/emacs/dired.texi b/doc/emacs/dired.texi
index db5dea329b..9348ef5042 100644
--- a/doc/emacs/dired.texi
+++ b/doc/emacs/dired.texi
@@ -647,6 +647,14 @@ Operating on Files
 is the directory to copy into, or (if copying a single file) the new
 name.  This is like the shell command @code{cp}.
 
+@vindex dired-create-destination-dirs
+The option @code{dired-create-destination-dirs} controls whether Dired
+should create non-existent directories in the destination while
+copying/renaming files.  The default value @code{nil} means Dired
+never creates such missing directories;  the value @code{always},
+means Dired automatically creates them; the value @code{ask}
+means Dired asks you for confirmation before creating them.
+
 @vindex dired-copy-preserve-time
 If @code{dired-copy-preserve-time} is non-@code{nil}, then copying
 with this command preserves the modification time of the old file in
@@ -678,6 +686,9 @@ Operating on Files
 you rename several files, the argument @var{new} is the directory into
 which to move the files (this is like the shell command @command{mv}).
 
+The option @code{dired-create-destination-dirs} controls whether Dired
+should create non-existent directories in @var{new}.
+
 Dired automatically changes the visited file name of buffers associated
 with renamed files so that they refer to the new names.
 
diff --git a/etc/NEWS b/etc/NEWS
index 716b0309a5..acfc52ab52 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -56,6 +56,13 @@ whether '"' is also replaced in 'electric-quote-mode'.  If non-nil,
 
 * Changes in Specialized Modes and Packages in Emacs 27.1
 
+** Dired
+
++++
+*** The new user option 'dired-create-destination-dirs' controls whether
+'dired-do-copy' and 'dired-rename-file' should create non-existent
+directories in the destination.
+
 ** Edebug
 
 +++
diff --git a/lisp/dired-aux.el b/lisp/dired-aux.el
index 7e2252fcf1..5e92c58d12 100644
--- a/lisp/dired-aux.el
+++ b/lisp/dired-aux.el
@@ -1548,6 +1548,24 @@ dired-copy-file
 
 (declare-function make-symbolic-link "fileio.c")
 
+(defcustom dired-create-destination-dirs nil
+  "Whether Dired should create destination dirs when copying/removing files.
+If nil, don't create them.
+If `always', create them without ask.
+If `ask', ask for user confirmation."
+  :type '(choice (const :tag "Never create non-existent dirs" nil)
+		 (const :tag "Always create non-existent dirs" always)
+		 (const :tag "Ask for user confirmation" ask))
+  :group 'dired
+  :version "27.1")
+
+(defun dired-maybe-create-dirs (dir)
+  "Create DIR if doesn't exist according with `dired-create-destination-dirs'."
+  (when (and dired-create-destination-dirs (not (file-exists-p dir)))
+    (if (or (eq dired-create-destination-dirs 'always)
+            (yes-or-no-p (format "Create destination dir `%s'? " dir)))
+        (dired-create-directory dir))))
+
 (defun dired-copy-file-recursive (from to ok-flag &optional
 				       preserve-time top recursive)
   (when (and (eq t (car (file-attributes from)))
@@ -1564,6 +1582,7 @@ dired-copy-file-recursive
 	  (if (stringp (car attrs))
 	      ;; It is a symlink
 	      (make-symbolic-link (car attrs) to ok-flag)
+            (dired-maybe-create-dirs (file-name-directory to))
 	    (copy-file from to ok-flag preserve-time))
 	(file-date-error
 	 (push (dired-make-relative from)
@@ -1573,6 +1592,7 @@ dired-copy-file-recursive
 ;;;###autoload
 (defun dired-rename-file (file newname ok-if-already-exists)
   (dired-handle-overwrite newname)
+  (dired-maybe-create-dirs (file-name-directory newname))
   (rename-file file newname ok-if-already-exists) ; error is caught in -create-files
   ;; Silently rename the visited file of any buffer visiting this file.
   (and (get-file-buffer file)
diff --git a/test/lisp/dired-aux-tests.el b/test/lisp/dired-aux-tests.el
index d41feb1592..9316217dd2 100644
--- a/test/lisp/dired-aux-tests.el
+++ b/test/lisp/dired-aux-tests.el
@@ -20,7 +20,7 @@
 ;;; Code:
 (require 'ert)
 (require 'dired-aux)
-
+(eval-when-compile (require 'cl-lib))
 
 (ert-deftest dired-test-bug27496 ()
   "Test for https://debbugs.gnu.org/27496 ."
@@ -40,5 +40,59 @@
           (should-not (dired-do-shell-command "ls ? ./`?`" nil files)))
       (delete-file foo))))
 
+;; Auxiliar macro for `dired-test-bug28834': it binds
+;; `dired-create-destination-dirs' to CREATE-DIRS and execute BODY.
+;; If YES-OR-NO is non-nil, it binds `yes-or-no-p' to
+;; to avoid the prompt.
+(defmacro with-dired-bug28834-test (create-dirs yes-or-no &rest body)
+  (declare ((debug form symbolp body)))
+  (let ((foo (make-symbol "foo")))
+    `(let* ((,foo (make-temp-file "foo" 'dir))
+            (dired-create-destination-dirs ,create-dirs))
+       (setq from (make-temp-file "from"))
+       (setq to-cp
+             (expand-file-name
+              "foo-cp" (file-name-as-directory (expand-file-name "bar" ,foo))))
+       (setq to-mv
+             (expand-file-name
+              "foo-mv" (file-name-as-directory (expand-file-name "qux" ,foo))))
+       (unwind-protect
+           (if ,yes-or-no
+               (cl-letf (((symbol-function 'yes-or-no-p)
+                          (lambda (prompt) (eq ,yes-or-no 'yes))))
+                 ,@body)
+             ,@body)
+         ;; clean up
+         (delete-directory ,foo 'recursive)
+         (delete-file from)))))
+
+(ert-deftest dired-test-bug28834 ()
+  "test for https://debbugs.gnu.org/28834 ."
+  (let (from to-cp to-mv)
+    ;; `dired-create-destination-dirs' set to 'always.
+    (with-dired-bug28834-test
+     'always nil
+     (dired-copy-file-recursive from to-cp nil)
+     (should (file-exists-p to-cp))
+     (dired-rename-file from to-mv nil)
+     (should (file-exists-p to-mv)))
+    ;; `dired-create-destination-dirs' set to nil.
+    (with-dired-bug28834-test
+     nil nil
+     (should-error (dired-copy-file-recursive from to-cp nil))
+     (should-error (dired-rename-file from to-mv nil)))
+    ;; `dired-create-destination-dirs' set to 'ask.
+    (with-dired-bug28834-test
+     'ask 'yes ; Answer `yes'
+     (dired-copy-file-recursive from to-cp nil)
+     (should (file-exists-p to-cp))
+     (dired-rename-file from to-mv nil)
+     (should (file-exists-p to-mv)))
+    (with-dired-bug28834-test
+     'ask 'no ; Answer `no'
+     (should-error (dired-copy-file-recursive from to-cp nil))
+     (should-error (dired-rename-file from to-mv nil)))))
+
+
 (provide 'dired-aux-tests)
 ;; dired-aux-tests.el ends here
--8<-----------------------------cut here---------------end--------------->8---
In GNU Emacs 27.0.50 (build 1, x86_64-pc-linux-gnu, GTK+ Version 3.22.11)
 of 2017-10-17
Repository revision: 94281c9a1cc0f756841fdc9b266657853df94a29




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#28834; Package emacs. (Tue, 17 Oct 2017 16:38:01 GMT) Full text and rfc822 format available.

Message #41 received at 28834 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Tino Calancha <tino.calancha <at> gmail.com>
Cc: 28834 <at> debbugs.gnu.org, drew.adams <at> oracle.com
Subject: Re: bug#28834: 25.1;
 dired-do-copy: allow to copy into a nonexistent directory
Date: Tue, 17 Oct 2017 19:36:33 +0300
> From: Tino Calancha <tino.calancha <at> gmail.com>
> Cc: 28834 <at> debbugs.gnu.org,  drew.adams <at> oracle.com
> Date: Tue, 17 Oct 2017 13:49:34 +0900
> 
> * I have changed the new function name to `dired-maybe-create-dirs'
>   as Drew suggested.
> * I think I have addressed all your comments as well.

Thanks, this LGTM.  One minor comment:

> +(defun dired-maybe-create-dirs (dir)
> +  "Create DIR if doesn't exist according with `dired-create-destination-dirs'."
                                  ^^^^^^^^^^^^^^
"according to"

Please push to master in a few days, if no one raises any issues.




Reply sent to Tino Calancha <tino.calancha <at> gmail.com>:
You have taken responsibility. (Sat, 21 Oct 2017 04:14:01 GMT) Full text and rfc822 format available.

Notification sent to "alexei28" <alexei28 <at> gmail.com>:
bug acknowledged by developer. (Sat, 21 Oct 2017 04:14:02 GMT) Full text and rfc822 format available.

Message #46 received at 28834-done <at> debbugs.gnu.org (full text, mbox):

From: Tino Calancha <tino.calancha <at> gmail.com>
To: 28834-done <at> debbugs.gnu.org
Subject: Re: bug#28834: 25.1;
 dired-do-copy: allow to copy into a nonexistent directory
Date: Sat, 21 Oct 2017 13:13:34 +0900
Eli Zaretskii <eliz <at> gnu.org> writes:

>> From: Tino Calancha <tino.calancha <at> gmail.com>
>> Cc: 28834 <at> debbugs.gnu.org,  drew.adams <at> oracle.com
>> Date: Tue, 17 Oct 2017 13:49:34 +0900
>> 
>> * I have changed the new function name to `dired-maybe-create-dirs'
>>   as Drew suggested.
>> * I think I have addressed all your comments as well.
>
> Thanks, this LGTM.
Added feature into master branch as commit
"Allow to copy/rename file into a non-existent dir"
(cb29f41624e5163a0aea4bfc98591e683807a2f8)




bug archived. Request was from Debbugs Internal Request <help-debbugs <at> gnu.org> to internal_control <at> debbugs.gnu.org. (Sat, 18 Nov 2017 12:24:06 GMT) Full text and rfc822 format available.

This bug report was last modified 6 years and 158 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.