]> code.delx.au - gnu-emacs/blobdiff - lisp/dired-aux.el
Copy in some minor bug fixes from the trunk.
[gnu-emacs] / lisp / dired-aux.el
index 963866b355431755742fcbdcb03136b0db7be8d8..a786ed1c90352d0b0f4f82ab20e08ee2e5dcb98b 100644 (file)
@@ -1,7 +1,7 @@
 ;;; dired-aux.el --- less commonly used parts of dired  -*-byte-compile-dynamic: t;-*-
 
-;; Copyright (C) 1985, 1986, 1992, 1994, 1998, 2000, 2001, 2004
-;;   Free Software Foundation, Inc.
+;; Copyright (C) 1985, 1986, 1992, 1994, 1998, 2000, 2001, 2002, 2003,
+;;   2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
 
 ;; Author: Sebastian Kremer <sk@thp.uni-koeln.de>.
 ;; Maintainer: FSF
@@ -11,7 +11,7 @@
 
 ;; GNU Emacs is free software; you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 2, or (at your option)
+;; the Free Software Foundation; either version 3, or (at your option)
 ;; any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
@@ -21,8 +21,8 @@
 
 ;; You should have received a copy of the GNU General Public License
 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
-;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-;; Boston, MA 02111-1307, USA.
+;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
 
 ;;; Commentary:
 
 ;;; Code:
 
 ;; We need macros in dired.el to compile properly.
-(eval-when-compile (require 'dired))
+(require 'dired)
+
+(defvar dired-create-files-failures nil
+  "Variable where `dired-create-files' records failing file names.
+Functions that operate recursively can store additional names
+into this list; they also should call `dired-log' to log the errors.")
 
 ;;; 15K
 ;;;###begin dired-cmd.el
@@ -53,16 +58,22 @@ FILE defaults to the file at the mark.  (That's the mark set by
 \\[set-mark-command], not by Dired's \\[dired-mark] command.)
 The prompted-for file is the first file given to `diff'.
 With prefix arg, prompt for second argument SWITCHES,
- which is options for `diff'."
+which is options for `diff'."
   (interactive
-   (let ((default (if (mark t)
+   (let ((current (dired-get-filename t))
+        (default (if (mark t)
                      (save-excursion (goto-char (mark t))
                                      (dired-get-filename t t)))))
+     (if (or (equal default current)
+            (and (not (equal (dired-dwim-target-directory)
+                             (dired-current-directory)))
+                 (not mark-active)))
+        (setq default nil))
      (require 'diff)
-     (list (read-file-name (format "Diff %s with: %s"
-                                  (dired-get-filename t)
+     (list (read-file-name (format "Diff %s with%s: "
+                                  current
                                   (if default
-                                      (concat "(default " default ") ")
+                                      (concat " (default " default ")")
                                     ""))
                           (if default
                               (dired-current-directory)
@@ -126,16 +137,21 @@ Examples of PREDICATE:
   (let* ((dir1 (dired-current-directory))
          (file-alist1 (dired-files-attributes dir1))
          (file-alist2 (dired-files-attributes dir2))
-        (file-list1 (mapcar
+        file-list1 file-list2)
+    (setq file-alist1 (delq (assoc "." file-alist1) file-alist1))
+    (setq file-alist1 (delq (assoc ".." file-alist1) file-alist1))
+    (setq file-alist2 (delq (assoc "." file-alist2) file-alist2))
+    (setq file-alist2 (delq (assoc ".." file-alist2) file-alist2))
+    (setq file-list1 (mapcar
                      'cadr
                       (dired-file-set-difference
                        file-alist1 file-alist2
-                      predicate)))
-        (file-list2 (mapcar
+                      predicate))
+         file-list2 (mapcar
                      'cadr
                       (dired-file-set-difference
                        file-alist2 file-alist1
-                      predicate))))
+                      predicate)))
     (dired-fun-in-all-buffers
      dir1 nil
      (lambda ()
@@ -482,7 +498,8 @@ the Dired buffer, so output files usually are created there instead of
 in a subdir.
 
 In a noninteractive call (from Lisp code), you must specify
-the list of file names explicitly with the FILE-LIST argument."
+the list of file names explicitly with the FILE-LIST argument, which
+can be produced by `dired-get-marked-files', for example."
 ;;Functions dired-run-shell-command and dired-shell-stuff-it do the
 ;;actual work and can be redefined for customization.
   (interactive
@@ -733,19 +750,22 @@ Otherwise, the rule is a compression rule, and compression is done with gzip.")
           ;;; We don't recognize the file as compressed, so compress it.
           ;;; Try gzip; if we don't have that, use compress.
           (condition-case nil
-              (if (not (dired-check-process (concat "Compressing " file)
-                                            "gzip" "-f" file))
-                  (let ((out-name
-                         (if (file-exists-p (concat file ".gz"))
-                             (concat file ".gz")
-                           (concat file ".z"))))
-                    ;; Rename the compressed file to NEWNAME
-                    ;; if it hasn't got that name already.
-                    (if (and newname (not (equal newname out-name)))
-                        (progn
-                          (rename-file out-name newname t)
-                          newname)
-                      out-name)))
+              (let ((out-name (concat file ".gz")))
+                (and (or (not (file-exists-p out-name))
+                         (y-or-n-p
+                          (format "File %s already exists.  Really compress? "
+                                  out-name)))
+                     (not (dired-check-process (concat "Compressing " file)
+                                               "gzip" "-f" file))
+                     (or (file-exists-p out-name)
+                         (setq out-name (concat file ".z")))
+                     ;; Rename the compressed file to NEWNAME
+                     ;; if it hasn't got that name already.
+                     (if (and newname (not (equal newname out-name)))
+                         (progn
+                           (rename-file out-name newname t)
+                           newname)
+                       out-name)))
             (file-error
              (if (not (dired-check-process (concat "Compressing " file)
                                            "compress" "-f" file))
@@ -839,6 +859,9 @@ Otherwise, the rule is a compression rule, and compression is done with gzip.")
               (sit-for 1)
               (apply 'message qprompt qs-args)
               (setq char (set qs-var (read-char))))
+            ;; Display the question with the answer.
+            (message "%s" (concat (apply 'format qprompt qs-args)
+                             (char-to-string char)))
             (memq (cdr elt) '(t y yes)))))))
 \f
 ;;;###autoload
@@ -904,7 +927,7 @@ or delete subdirectories can bypass this machinery.  Hence, you sometimes
 may have to reset some subdirectory switches after a `dired-undo'.
 You can reset all subdirectory switches to the default using
 \\<dired-mode-map>\\[dired-reset-subdir-switches].
-See Info node `(emacs-xtra)Subdir switches' for more details."
+See Info node `(emacs)Subdir switches' for more details."
   ;; Moves point if the next ARG files are redisplayed.
   (interactive "P\np")
   (if (and test-for-subdir (dired-get-subdir))
@@ -1106,6 +1129,9 @@ Special value `always' suppresses confirmation."
                 (other :tag "ask" t))
   :group 'dired)
 
+;; This is a fluid var used in dired-handle-overwrite.  It should be
+;; let-bound whenever dired-copy-file etc are called.  See
+;; dired-create-files for an example.
 (defvar dired-overwrite-confirmed)
 
 (defun dired-handle-overwrite (to)
@@ -1118,8 +1144,8 @@ Special value `always' suppresses confirmation."
             (setq backup (car (find-backup-file-name to)))
             (or (eq 'always dired-backup-overwrite)
                 (dired-query 'overwrite-backup-query
-                             (format "Make backup for existing file `%s'? "
-                                     to))))
+                             "Make backup for existing file `%s'? "
+                             to)))
        (progn
          (rename-file to backup 0)     ; confirm overwrite of old backup
          (dired-relist-entry backup)))))
@@ -1127,37 +1153,74 @@ Special value `always' suppresses confirmation."
 ;;;###autoload
 (defun dired-copy-file (from to ok-flag)
   (dired-handle-overwrite to)
-  (condition-case ()
-      (dired-copy-file-recursive from to ok-flag dired-copy-preserve-time t
-                                dired-recursive-copies)
-    (file-date-error (message "Can't set date")
-                    (sit-for 1))))
+  (dired-copy-file-recursive from to ok-flag dired-copy-preserve-time t
+                            dired-recursive-copies))
 
 (defun dired-copy-file-recursive (from to ok-flag &optional
                                       preserve-time top recursive)
-  (let ((attrs (file-attributes from)))
+  (let ((attrs (file-attributes from))
+       dirfailed)
     (if (and recursive
             (eq t (car attrs))
             (or (eq recursive 'always)
-                (yes-or-no-p (format "Recursive copies of %s " from))))
+                (yes-or-no-p (format "Recursive copies of %s? " from))))
        ;; This is a directory.
-       (let ((files (directory-files from nil dired-re-no-dot)))
+       (let ((mode (file-modes from))
+             (files
+              (condition-case err
+                  (directory-files from nil dired-re-no-dot)
+                (file-error
+                 (push (dired-make-relative from)
+                       dired-create-files-failures)
+                 (dired-log "Copying error for %s:\n%s\n" from err)
+                 (setq dirfailed t)
+                 nil))))
          (if (eq recursive 'top) (setq recursive 'always)) ; Don't ask any more.
-         (if (file-exists-p to)
-             (or top (dired-handle-overwrite to))
-           (make-directory to))
-         (while files
-           (dired-copy-file-recursive
-            (expand-file-name (car files) from)
-            (expand-file-name (car files) to)
-            ok-flag preserve-time nil recursive)
-           (setq files (cdr files))))
+         (unless dirfailed
+           (if (file-exists-p to)
+               (or top (dired-handle-overwrite to))
+             (condition-case err
+                 ;; We used to call set-file-modes here, but on some
+                 ;; Linux kernels, that returns an error on vfat
+                 ;; filesystems
+                 (let ((default-mode (default-file-modes)))
+                   (unwind-protect
+                       (progn
+                         (set-default-file-modes #o700)
+                         (make-directory to))
+                     (set-default-file-modes default-mode)))
+               (file-error
+                (push (dired-make-relative from)
+                      dired-create-files-failures)
+                (setq files nil)
+                (dired-log "Copying error for %s:\n%s\n" from err)))))
+         (dolist (file files)
+           (let ((thisfrom (expand-file-name file from))
+                 (thisto (expand-file-name file to)))
+             ;; Catch errors copying within a directory,
+             ;; and report them through the dired log mechanism
+             ;; just as our caller will do for the top level files.
+             (condition-case err
+                 (dired-copy-file-recursive
+                  thisfrom thisto
+                  ok-flag preserve-time nil recursive)
+               (file-error
+                (push (dired-make-relative thisfrom)
+                      dired-create-files-failures)
+                (dired-log "Copying error for %s:\n%s\n" thisfrom err)))))
+         (when (file-directory-p to)
+           (set-file-modes to mode)))
       ;; Not a directory.
       (or top (dired-handle-overwrite to))
-      (if (stringp (car attrs))
-         ;; It is a symlink
-         (make-symbolic-link (car attrs) to ok-flag)
-       (copy-file from to ok-flag dired-copy-preserve-time)))))
+      (condition-case err
+         (if (stringp (car attrs))
+             ;; It is a symlink
+             (make-symbolic-link (car attrs) to ok-flag)
+           (copy-file from to ok-flag dired-copy-preserve-time))
+       (file-date-error
+        (push (dired-make-relative from)
+              dired-create-files-failures)
+        (dired-log "Can't set date on %s:\n%s\n" from err))))))
 
 ;;;###autoload
 (defun dired-rename-file (file newname ok-if-already-exists)
@@ -1279,7 +1342,8 @@ Special value `always' suppresses confirmation."
 ;; newfile's entry, or t to use the current marker character if the
 ;; oldfile was marked.
 
-  (let (failures skipped (success-count 0) (total (length fn-list)))
+  (let (dired-create-files-failures failures
+       skipped (success-count 0) (total (length fn-list)))
     (let (to overwrite-query
             overwrite-backup-query)    ; for dired-handle-overwrite
       (mapcar
@@ -1322,16 +1386,25 @@ ESC or `q' to not overwrite any of the remaining files,
                    (dired-add-file to actual-marker-char))
                (file-error             ; FILE-CREATOR aborted
                 (progn
-                  (setq failures (cons (dired-make-relative from) failures))
+                  (push (dired-make-relative from)
+                        failures)
                   (dired-log "%s `%s' to `%s' failed:\n%s\n"
                              operation from to err))))))))
        fn-list))
     (cond
+     (dired-create-files-failures
+      (setq failures (nconc failures dired-create-files-failures))
+      (dired-log-summary
+       (format "%s failed for %d file%s in %d requests"
+               operation (length failures)
+               (dired-plural-s (length failures))
+               total)
+       failures))
      (failures
       (dired-log-summary
        (format "%s failed for %d of %d file%s"
-               operation (length failures) total
-               (dired-plural-s total))
+               operation (length failures)
+               total (dired-plural-s total))
        failures))
      (skipped
       (dired-log-summary
@@ -1505,7 +1578,10 @@ When operating on multiple or marked files, you specify a directory,
 and new copies of these files are made in that directory
 with the same names that the files currently have.  The default
 suggested for the target directory depends on the value of
-`dired-dwim-target', which see."
+`dired-dwim-target', which see.
+
+This command copies symbolic links by creating new ones,
+like `cp -d'."
   (interactive "P")
   (let ((dired-recursive-copies dired-recursive-copies))
     (dired-do-create-files 'copy (function dired-copy-file)
@@ -1521,7 +1597,9 @@ When operating on multiple or marked files, you specify a directory
 and new symbolic links are made in that directory
 with the same names that the files currently have.  The default
 suggested for the target directory depends on the value of
-`dired-dwim-target', which see."
+`dired-dwim-target', which see.
+
+For relative symlinks, use \\[dired-do-relsymlink]."
   (interactive "P")
   (dired-do-create-files 'symlink (function make-symbolic-link)
                           "Symlink" arg dired-keep-marker-symlink))
@@ -1747,7 +1825,7 @@ or delete subdirectories can bypass this machinery.  Hence, you sometimes
 may have to reset some subdirectory switches after a `dired-undo'.
 You can reset all subdirectory switches to the default using
 \\<dired-mode-map>\\[dired-reset-subdir-switches].
-See Info node `(emacs-xtra)Subdir switches' for more details."
+See Info node `(emacs)Subdir switches' for more details."
   (interactive
    (list (dired-get-filename)
         (if current-prefix-arg
@@ -1942,8 +2020,8 @@ of marked files.  If KILL-ROOT is non-nil, kill DIRNAME as well."
 
 (defun dired-tree-lessp (dir1 dir2)
   ;; Lexicographic order on file name components, like `ls -lR':
-  ;; DIR1 < DIR2 iff DIR1 comes *before* DIR2 in an `ls -lR' listing,
-  ;;   i.e., iff DIR1 is a (grand)parent dir of DIR2,
+  ;; DIR1 < DIR2 if DIR1 comes *before* DIR2 in an `ls -lR' listing,
+  ;;   i.e., if DIR1 is a (grand)parent dir of DIR2,
   ;;   or DIR1 and DIR2 are in the same parentdir and their last
   ;;   components are string-lessp.
   ;; Thus ("/usr/" "/usr/bin") and ("/usr/a/" "/usr/b/") are tree-lessp.
@@ -2200,7 +2278,10 @@ Third arg DELIMITED (prefix arg) means replace only word-delimited matches.
 If you exit (\\[keyboard-quit], RET or q), you can resume the query replace
 with the command \\[tags-loop-continue]."
   (interactive
-   "sQuery replace in marked files (regexp): \nsQuery replace %s by: \nP")
+   (let ((common
+         (query-replace-read-args
+          "Query replace regexp in marked files" t t)))
+     (list (nth 0 common) (nth 1 common) (nth 2 common))))
   (dolist (file (dired-get-marked-files nil nil 'dired-nondirectory-p))
     (let ((buffer (get-file-buffer file)))
       (if (and buffer (with-current-buffer buffer