(sh-get-kw): Remove '()' from the list of
[bpt/emacs.git] / lisp / dired-aux.el
index 8661df0..bf87ce7 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, 2002, 2003,
-;;   2004, 2005, 2006, 2007 Free Software Foundation, Inc.
+;;   2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
 
 ;; Author: Sebastian Kremer <sk@thp.uni-koeln.de>.
 ;; Maintainer: FSF
@@ -9,10 +9,10 @@
 
 ;; This file is part of GNU Emacs.
 
-;; GNU Emacs is free software; you can redistribute it and/or modify
+;; 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)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -20,9 +20,7 @@
 ;; GNU General Public License for more details.
 
 ;; 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., 51 Franklin Street, Fifth Floor,
-;; Boston, MA 02110-1301, USA.
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
 
 ;;; Commentary:
 
@@ -36,8 +34,9 @@
 
 ;;; Code:
 
-;; We need macros in dired.el to compile properly.
-(eval-when-compile (require 'dired))
+;; We need macros in dired.el to compile properly,
+;; and we call subroutines in it too.
+(require 'dired)
 
 (defvar dired-create-files-failures nil
   "Variable where `dired-create-files' records failing file names.
@@ -253,9 +252,31 @@ List has a form of (file-name full-file-name (attribute-list))"
 ;;;###autoload
 (defun dired-do-chmod (&optional arg)
   "Change the mode of the marked (or next ARG) files.
-This calls chmod, thus symbolic modes like `g+w' are allowed."
+Symbolic modes like `g+w' are allowed."
   (interactive "P")
-  (dired-do-chxxx "Mode" dired-chmod-program 'chmod arg))
+  (let* ((files (dired-get-marked-files t arg))
+        (modestr (and (stringp (car files))
+                      (nth 8 (file-attributes (car files)))))
+        (default
+          (and (stringp modestr)
+               (string-match "^.\\(...\\)\\(...\\)\\(...\\)$" modestr)
+               (replace-regexp-in-string
+                "-" ""
+                (format "u=%s,g=%s,o=%s"
+                        (match-string 1 modestr)
+                        (match-string 2 modestr)
+                        (match-string 3 modestr)))))
+        (modes (dired-mark-read-string
+                "Change mode of %s to: " nil
+                'chmod arg files default))
+        (num-modes (if (string-match "^[0-7]+" modes)
+                       (string-to-number modes 8))))
+    (dolist (file files)
+      (set-file-modes
+       file
+       (if num-modes num-modes
+        (file-modes-symbolic-to-number modes (file-modes file)))))
+    (dired-do-redisplay arg)))
 
 ;;;###autoload
 (defun dired-do-chgrp (&optional arg)
@@ -348,14 +369,14 @@ Uses the shell command coming from variables `lpr-command' and
 ;; If the current file was used, the list has but one element and ARG
 ;; does not matter. (It is non-nil, non-integer in that case, namely '(4)).
 
-(defun dired-mark-read-string (prompt initial op-symbol arg files)
-  ;; PROMPT for a string, with INITIAL input.
+(defun dired-mark-read-string (prompt initial op-symbol arg files &optional default)
+  ;; PROMPT for a string, with INITIAL input and DEFAULT value.
   ;; Other args are used to give user feedback and pop-up:
   ;; OP-SYMBOL of command, prefix ARG, marked FILES.
   (dired-mark-pop-up
    nil op-symbol files
    (function read-string)
-   (format prompt (dired-mark-prompt arg files)) initial))
+   (format prompt (dired-mark-prompt arg files)) initial nil default))
 \f
 ;;; Cleaning a directory: flagging some backups for deletion.
 
@@ -452,16 +473,53 @@ with a prefix argument."
 \f
 ;;; Shell commands
 
+(declare-function mailcap-file-default-commands "mailcap" (files))
+
+(defun minibuffer-default-add-dired-shell-commands ()
+  "Return a list of all commands associted with current dired files.
+This function is used to add all related commands retieved by `mailcap'
+to the end of the list of defaults just after the default value."
+  (interactive)
+  (let ((commands (and (boundp 'files) (require 'mailcap nil t)
+                      (mailcap-file-default-commands files))))
+    (if (listp minibuffer-default)
+       (append minibuffer-default commands)
+      (cons minibuffer-default commands))))
+
+;; This is an extra function so that you can redefine it, e.g., to use gmhist.
 (defun dired-read-shell-command (prompt arg files)
-;;  "Read a dired shell command prompting with PROMPT (using read-string).
-;;ARG is the prefix arg and may be used to indicate in the prompt which
-;;  files are affected.
-;;This is an extra function so that you can redefine it, e.g., to use gmhist."
-  (dired-mark-pop-up
-   nil 'shell files
-   (function read-string)
-   (format prompt (dired-mark-prompt arg files))
-   nil 'shell-command-history))
+  "Read a dired shell command prompting with PROMPT (using read-shell-command).
+ARG is the prefix arg and may be used to indicate in the prompt which
+FILES are affected."
+  (minibuffer-with-setup-hook
+      (lambda ()
+       (set (make-local-variable 'minibuffer-default-add-function)
+            'minibuffer-default-add-dired-shell-commands))
+    (dired-mark-pop-up
+     nil 'shell files
+     #'read-shell-command
+     (format prompt (dired-mark-prompt arg files))
+     nil nil)))
+
+;;;###autoload
+(defun dired-do-async-shell-command (command &optional arg file-list)
+  "Run a shell command COMMAND on the marked files asynchronously.
+
+Like `dired-do-shell-command' but if COMMAND doesn't end in ampersand,
+adds `* &' surrounded by whitespace and executes the command asynchronously.
+The output appears in the buffer `*Async Shell Command*'."
+  (interactive
+   (let ((files (dired-get-marked-files t current-prefix-arg)))
+     (list
+      ;; Want to give feedback whether this file or marked files are used:
+      (dired-read-shell-command "& on %s: " current-prefix-arg files)
+      current-prefix-arg
+      files)))
+  (unless (string-match "[*?][ \t]*\\'" command)
+    (setq command (concat command " *")))
+  (unless (string-match "&[ \t]*\\'" command)
+    (setq command (concat command " &")))
+  (dired-do-shell-command command arg file-list))
 
 ;; The in-background argument is only needed in Emacs 18 where
 ;; shell-command doesn't understand an appended ampersand `&'.
@@ -1061,7 +1119,6 @@ See Info node `(emacs)Subdir switches' for more details."
   ;; or wildcard lines.
   ;; Important: never moves into the next subdir.
   ;; DIR is assumed to be unhidden.
-  ;; Will probably be redefined for VMS etc.
   (save-excursion
     (or (dired-goto-subdir dir) (error "This cannot happen"))
     (forward-line 1)
@@ -1116,6 +1173,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)
@@ -1123,16 +1183,15 @@ Special value `always' suppresses confirmation."
   ;; `dired-overwrite-confirmed' and `overwrite-backup-query' are fluid vars
   ;; from dired-create-files.
   (let (backup)
-    (if (and dired-backup-overwrite
-            dired-overwrite-confirmed
-            (setq backup (car (find-backup-file-name to)))
-            (or (eq 'always dired-backup-overwrite)
-                (dired-query 'overwrite-backup-query
-                             "Make backup for existing file `%s'? "
-                             to)))
-       (progn
-         (rename-file to backup 0)     ; confirm overwrite of old backup
-         (dired-relist-entry backup)))))
+    (when (and dired-backup-overwrite
+              dired-overwrite-confirmed
+              (setq backup (car (find-backup-file-name to)))
+              (or (eq 'always dired-backup-overwrite)
+                  (dired-query 'overwrite-backup-query
+                               "Make backup for existing file `%s'? "
+                               to)))
+      (rename-file to backup 0)        ; confirm overwrite of old backup
+      (dired-relist-entry backup))))
 
 ;;;###autoload
 (defun dired-copy-file (from to ok-flag)
@@ -1140,6 +1199,8 @@ Special value `always' suppresses confirmation."
   (dired-copy-file-recursive from to ok-flag dired-copy-preserve-time t
                             dired-recursive-copies))
 
+(declare-function make-symbolic-link "fileio.c")
+
 (defun dired-copy-file-recursive (from to ok-flag &optional
                                       preserve-time top recursive)
   (let ((attrs (file-attributes from))
@@ -1149,7 +1210,8 @@ Special value `always' suppresses confirmation."
             (or (eq recursive 'always)
                 (yes-or-no-p (format "Recursive copies of %s? " from))))
        ;; This is a directory.
-       (let ((files
+       (let ((mode (file-modes from))
+             (files
               (condition-case err
                   (directory-files from nil dired-re-no-dot)
                 (file-error
@@ -1163,7 +1225,15 @@ Special value `always' suppresses confirmation."
            (if (file-exists-p to)
                (or top (dired-handle-overwrite to))
              (condition-case err
-                 (make-directory to)
+                 ;; 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)
@@ -1182,7 +1252,9 @@ Special value `always' suppresses confirmation."
                (file-error
                 (push (dired-make-relative thisfrom)
                       dired-create-files-failures)
-                (dired-log "Copying error for %s:\n%s\n" thisfrom err))))))
+                (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))
       (condition-case err
@@ -1319,51 +1391,48 @@ Special value `always' suppresses confirmation."
        skipped (success-count 0) (total (length fn-list)))
     (let (to overwrite-query
             overwrite-backup-query)    ; for dired-handle-overwrite
-      (mapcar
-       (function
-       (lambda (from)
-         (setq to (funcall name-constructor from))
-         (if (equal to from)
-             (progn
-               (setq to nil)
-               (dired-log "Cannot %s to same file: %s\n"
-                          (downcase operation) from)))
-         (if (not to)
-             (setq skipped (cons (dired-make-relative from) skipped))
-           (let* ((overwrite (file-exists-p to))
-                  (dired-overwrite-confirmed   ; for dired-handle-overwrite
-                   (and overwrite
-                        (let ((help-form '(format "\
+      (dolist (from fn-list)
+        (setq to (funcall name-constructor from))
+        (if (equal to from)
+            (progn
+              (setq to nil)
+              (dired-log "Cannot %s to same file: %s\n"
+                         (downcase operation) from)))
+        (if (not to)
+            (setq skipped (cons (dired-make-relative from) skipped))
+          (let* ((overwrite (file-exists-p to))
+                 (dired-overwrite-confirmed ; for dired-handle-overwrite
+                  (and overwrite
+                       (let ((help-form '(format "\
 Type SPC or `y' to overwrite file `%s',
 DEL or `n' to skip to next,
 ESC or `q' to not overwrite any of the remaining files,
 `!' to overwrite all remaining files with no more questions." to)))
-                          (dired-query 'overwrite-query
-                                       "Overwrite `%s'?" to))))
-                  ;; must determine if FROM is marked before file-creator
-                  ;; gets a chance to delete it (in case of a move).
-                  (actual-marker-char
-                   (cond  ((integerp marker-char) marker-char)
-                          (marker-char (dired-file-marker from)) ; slow
-                          (t nil))))
-             (condition-case err
-                 (progn
-                   (funcall file-creator from to dired-overwrite-confirmed)
-                   (if overwrite
-                       ;; If we get here, file-creator hasn't been aborted
-                       ;; and the old entry (if any) has to be deleted
-                       ;; before adding the new entry.
-                       (dired-remove-file to))
-                   (setq success-count (1+ success-count))
-                   (message "%s: %d of %d" operation success-count total)
-                   (dired-add-file to actual-marker-char))
-               (file-error             ; FILE-CREATOR aborted
-                (progn
-                  (push (dired-make-relative from)
-                        failures)
-                  (dired-log "%s `%s' to `%s' failed:\n%s\n"
-                             operation from to err))))))))
-       fn-list))
+                         (dired-query 'overwrite-query
+                                      "Overwrite `%s'?" to))))
+                 ;; must determine if FROM is marked before file-creator
+                 ;; gets a chance to delete it (in case of a move).
+                 (actual-marker-char
+                  (cond  ((integerp marker-char) marker-char)
+                         (marker-char (dired-file-marker from)) ; slow
+                         (t nil))))
+            (condition-case err
+                (progn
+                  (funcall file-creator from to dired-overwrite-confirmed)
+                  (if overwrite
+                      ;; If we get here, file-creator hasn't been aborted
+                      ;; and the old entry (if any) has to be deleted
+                      ;; before adding the new entry.
+                      (dired-remove-file to))
+                  (setq success-count (1+ success-count))
+                  (message "%s: %d of %d" operation success-count total)
+                  (dired-add-file to actual-marker-char))
+              (file-error              ; FILE-CREATOR aborted
+               (progn
+                 (push (dired-make-relative from)
+                       failures)
+                 (dired-log "%s `%s' to `%s' failed:\n%s\n"
+                            operation from to err))))))))
     (cond
      (dired-create-files-failures
       (setq failures (nconc failures dired-create-files-failures))
@@ -1517,10 +1586,16 @@ Optional arg HOW-TO is used to set the value of the into-dir variable
   "Create a directory called DIRECTORY."
   (interactive
    (list (read-file-name "Create directory: " (dired-current-directory))))
-  (let ((expanded (directory-file-name (expand-file-name directory))))
-    (make-directory expanded)
-    (dired-add-file expanded)
-    (dired-move-to-filename)))
+  (let* ((expanded (directory-file-name (expand-file-name directory)))
+        (try expanded) new)
+    ;; Find the topmost nonexistent parent dir (variable `new')
+    (while (and try (not (file-exists-p try)) (not (equal new try)))
+      (setq new try
+           try (directory-file-name (file-name-directory try))))
+    (make-directory expanded t)
+    (when new
+      (dired-add-file new)
+      (dired-move-to-filename))))
 
 (defun dired-into-dir-with-symlinks (target)
   (and (file-directory-p target)
@@ -1871,7 +1946,6 @@ This function takes some pains to conform to `ls -lR' output."
     (save-excursion (dired-mark-remembered mark-alist))
     (restore-buffer-modified-p modflag)))
 
-;; This is a separate function for dired-vms.
 (defun dired-insert-subdir-validate (dirname &optional switches)
   ;; Check that it is valid to insert DIRNAME with SWITCHES.
   ;; Signal an error if invalid (e.g. user typed `i' on `..').
@@ -1993,8 +2067,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.
@@ -2234,8 +2308,87 @@ Use \\[dired-hide-subdir] to (un)hide a particular subdirectory."
 ;;;###end dired-ins.el
 
 \f
+;; Search only in file names in the Dired buffer.
+
+(defcustom dired-isearch-filenames nil
+  "*If non-nil, Isearch in Dired matches only file names."
+  :type '(choice (const :tag "No restrictions" nil)
+                (const :tag "Isearch only in file names" dired-filename))
+  :group 'dired
+  :version "23.1")
+
+(defvar dired-isearch-orig-success-function nil)
+
+(defun dired-isearch-filenames-toggle ()
+  "Toggle file names searching on or off.
+When on, Isearch checks the success of the current matching point
+using the function `dired-isearch-success-function' that matches only
+at file names.  When off, it uses the default function
+`isearch-success-function-default'."
+  (interactive)
+  (setq isearch-success-function
+       (if (eq isearch-success-function 'dired-isearch-success-function)
+           'isearch-success-function-default
+         'dired-isearch-success-function))
+  (setq isearch-success t isearch-adjusted t)
+  (isearch-update))
+
+;;;###autoload
+(defun dired-isearch-filenames-setup ()
+  "Set up isearch to search in Dired file names.
+Intended to be added to `isearch-mode-hook'."
+  (when dired-isearch-filenames
+    (define-key isearch-mode-map "\M-sf" 'dired-isearch-filenames-toggle)
+    (setq dired-isearch-orig-success-function
+         (default-value 'isearch-success-function))
+    (setq-default isearch-success-function 'dired-isearch-success-function)
+    (add-hook 'isearch-mode-end-hook 'dired-isearch-filenames-end nil t)))
+
+(defun dired-isearch-filenames-end ()
+  "Clean up the Dired file name search after terminating isearch."
+  (define-key isearch-mode-map "\M-sf" nil)
+  (setq-default isearch-success-function dired-isearch-orig-success-function)
+  (remove-hook 'isearch-mode-end-hook 'dired-isearch-filenames-end t))
+
+(defun dired-isearch-success-function (beg end)
+  "Match only at visible regions with the text property `dired-filename'."
+  (and (isearch-success-function-default beg end)
+       (if dired-isearch-filenames
+          (text-property-not-all (min beg end) (max beg end)
+                                 'dired-filename nil)
+        t)))
+
+;;;###autoload
+(defun dired-isearch-filenames ()
+  "Search for a string using Isearch only in file names in the Dired buffer."
+  (interactive)
+  (let ((dired-isearch-filenames t))
+    (isearch-forward)))
+
+;;;###autoload
+(defun dired-isearch-filenames-regexp ()
+  "Search for a regexp using Isearch only in file names in the Dired buffer."
+  (interactive)
+  (let ((dired-isearch-filenames t))
+    (isearch-forward-regexp)))
+
+\f
 ;; Functions for searching in tags style among marked files.
 
+;;;###autoload
+(defun dired-do-isearch ()
+  "Search for a string through all marked files using Isearch."
+  (interactive)
+  (multi-isearch-files
+   (dired-get-marked-files nil nil 'dired-nondirectory-p)))
+
+;;;###autoload
+(defun dired-do-isearch-regexp ()
+  "Search for a regexp through all marked files using Isearch."
+  (interactive)
+  (multi-isearch-files-regexp
+   (dired-get-marked-files nil nil 'dired-nondirectory-p)))
+
 ;;;###autoload
 (defun dired-do-search (regexp)
   "Search through all marked files for a match for REGEXP.
@@ -2282,5 +2435,5 @@ true then the type of the file linked to by FILE is printed instead."
 
 (provide 'dired-aux)
 
-;;; arch-tag: 4b508de9-a153-423d-8d3f-a1bbd86f4f60
+;; arch-tag: 4b508de9-a153-423d-8d3f-a1bbd86f4f60
 ;;; dired-aux.el ends here