]> code.delx.au - gnu-emacs/blobdiff - lisp/dired.el
*** empty log message ***
[gnu-emacs] / lisp / dired.el
index 75cc631c0a40992a2fe2d0a9968b0ba545fdcc14..6e061dabeb7b57d42630526d3827fe38965f8c36 100644 (file)
@@ -1,6 +1,6 @@
 ;;; dired.el --- directory-browsing commands
 
-;; Copyright (C) 1985, 86, 92, 93, 94, 95, 96, 1997, 2000, 2001
+;; Copyright (C) 1985, 86, 92, 93, 94, 95, 96, 1997, 2000, 2001, 03, 2004
 ;;  Free Software Foundation, Inc.
 
 ;; Author: Sebastian Kremer <sk@thp.uni-koeln.de>
@@ -39,7 +39,8 @@
 
 (defgroup dired nil
   "Directory editing."
-  :group 'environment)
+  :link '(custom-manual "(emacs)Dired")
+  :group 'files)
 
 (defgroup dired-mark nil
   "Handling marks in Dired."
@@ -59,19 +60,22 @@ some of the `ls' switches are not supported; see the doc string of
   :type 'string
   :group 'dired)
 
-; Don't use absolute paths as /bin should be in any PATH and people
+; Don't use absolute file names as /bin should be in any PATH and people
 ; may prefer /usr/local/gnu/bin or whatever.  However, chown is
 ; usually not in PATH.
 
 ;;;###autoload
 (defvar dired-chown-program
-  (if (memq system-type '(hpux dgux usg-unix-v irix linux gnu/linux))
+  (if (memq system-type '(hpux dgux usg-unix-v irix linux gnu/linux cygwin))
       "chown"
     (if (file-exists-p "/usr/sbin/chown")
        "/usr/sbin/chown"
       "/etc/chown"))
   "Name of chown command (usually `chown' or `/etc/chown').")
 
+(defvar dired-use-ls-dired (not (not (string-match "gnu" system-configuration)))
+  "Non-nil means Dired should use `ls --dired'.")
+
 (defvar dired-chmod-program "chmod"
   "Name of chmod command (usually `chmod').")
 
@@ -189,7 +193,22 @@ with the buffer narrowed to the listing."
 ;; Note this can't simply be run inside function `dired-ls' as the hook
 ;; functions probably depend on the dired-subdir-alist to be OK.
 
-;;; Internal variables
+;; Fixme: This should use mailcap.
+(defcustom dired-view-command-alist
+  '(("[.]\\(ps\\|ps_pages\\|eps\\)\\'" . "gv -spartan -color -watch %s")
+    ("[.]pdf\\'" . "xpdf %s")
+    ("[.]\\(jpe?g\\|gif\\|png\\)\\'" . "eog %s")
+    ("[.]dvi\\'" . "xdvi -sidemargin 0.5 -topmargin 1 %s"))
+  "Alist specifying how to view special types of files.
+Each element has the form (REGEXP . SHELL-COMMAND).
+When the file name matches REGEXP, `dired-view-file'
+invokes SHELL-COMMAND to view the file, processing it through `format'.
+Use `%s' in SHELL-COMMAND to specify where to put the file name."
+  :group 'dired
+  :type '(alist :key-type regexp :value-type string)
+  :version "21.4")
+
+;; Internal variables
 
 (defvar dired-marker-char ?*           ; the answer is 42
   ;; so that you can write things like
@@ -216,10 +235,12 @@ This is what the `do' commands look for and what the `mark' commands store.")
 
 (defvar dired-file-version-alist)
 
+;;;###autoload
 (defvar dired-directory nil
-  "The directory name or shell wildcard that was used as argument to `ls'.
+  "The directory name or wildcard spec that this Dired directory lists.
 Local to each dired buffer.  May be a list, in which case the car is the
-directory name and the cdr is the actual files to list.")
+directory name and the cdr is the list of files to mention.
+The directory name must be absolute, but need not be fully expanded.")
 
 (defvar dired-actual-switches nil
   "The value of `dired-listing-switches' used to make this buffer's text.")
@@ -248,7 +269,7 @@ directory name and the cdr is the actual files to list.")
               "-[-r][-w].[-r][-w].[-r][-w][xst]")
             "\\|"))
 (defvar dired-re-perms "[-bcdlps][-r][-w].[-r][-w].[-r][-w].")
-(defvar dired-re-dot "^.* \\.\\.?$")
+(defvar dired-re-dot "^.* \\.\\.?/?$")
 
 ;; The subdirectory names in this list are expanded.
 (defvar dired-subdir-alist nil
@@ -289,32 +310,41 @@ Subexpression 2 must end right before the \\n or \\r.")
 ;;;             "\\([-d]\\(....w....\\|.......w.\\)\\)")
 ;;;     '(1 font-lock-comment-face)
 ;;;     '(".+" (dired-move-to-filename) nil (0 font-lock-comment-face)))
+   ;; However, we don't need to highlight the file name, only the
+   ;; permissions, to win generally.  -- fx.
+   ;; Fixme: we could also put text properties on the permission
+   ;; fields with keymaps to frob the permissions, somewhat a la XEmacs.
+   (list (concat dired-re-maybe-mark dired-re-inode-size
+                "[-d]....\\(w\\)..\\(w\\).")   ; group writable
+        '(1 font-lock-warning-face))
+   (list (concat dired-re-maybe-mark dired-re-inode-size
+                "[-d]....\\(w\\)....") ; world writable
+        '(1 font-lock-comment-face))
    ;;
    ;; Subdirectories.
    (list dired-re-dir
         '(".+" (dired-move-to-filename) nil (0 font-lock-function-name-face)))
    ;;
    ;; Symbolic links.
-   (list dired-re-sym 
+   (list dired-re-sym
         '(".+" (dired-move-to-filename) nil (0 font-lock-keyword-face)))
    ;;
    ;; Files suffixed with `completion-ignored-extensions'.
    '(eval .
-     (let ((extensions (mapcar 'regexp-quote completion-ignored-extensions)))
-       ;; It is quicker to first find just an extension, then go back to the
-       ;; start of that file name.  So we do this complex MATCH-ANCHORED form.
-       (list (concat "\\(" (mapconcat 'identity extensions "\\|") "\\|#\\)$")
-            '(".+" (dired-move-to-filename) nil (0 font-lock-string-face))))))
+     ;; It is quicker to first find just an extension, then go back to the
+     ;; start of that file name.  So we do this complex MATCH-ANCHORED form.
+     (list (concat "\\(" (regexp-opt completion-ignored-extensions) "\\|#\\)$")
+          '(".+" (dired-move-to-filename) nil (0 font-lock-string-face)))))
   "Additional expressions to highlight in Dired mode.")
 \f
 ;;; Macros must be defined before they are used, for the byte compiler.
 
-;; Mark all files for which CONDITION evals to non-nil.
-;; CONDITION is evaluated on each line, with point at beginning of line.
-;; MSG is a noun phrase for the type of files being marked.
-;; It should end with a noun that can be pluralized by adding `s'.
-;; Return value is the number of files marked, or nil if none were marked.
 (defmacro dired-mark-if (predicate msg)
+  "Mark all files for which PREDICATE evals to non-nil.
+PREDICATE is evaluated on each line, with point at beginning of line.
+MSG is a noun phrase for the type of files being marked.
+It should end with a noun that can be pluralized by adding `s'.
+Return value is the number of files marked, or nil if none were marked."
   `(let (buffer-read-only count)
     (save-excursion
       (setq count 0)
@@ -420,9 +450,6 @@ Optional third argument FILTER, if non-nil, is a function to select
            (push file result)))
       result)))
 \f
-;; Function dired-ls is redefinable for VMS, ange-ftp, Prospero or
-;; other special applications.
-\f
 ;; The dired command
 
 (defun dired-read-dir-and-switches (str)
@@ -498,12 +525,34 @@ If DIRNAME is already in a dired buffer, that buffer is used without refresh."
       (setq dir-or-list dirname))
     (dired-internal-noselect dir-or-list switches)))
 
+;; The following is an internal dired function.  It returns non-nil if
+;; the directory visited by the current dired buffer has changed on
+;; disk.  DIRNAME should be the directory name of that directory.
+(defun dired-directory-changed-p (dirname)
+  (not (let ((attributes (file-attributes dirname))
+                   (modtime (visited-file-modtime)))
+               (or (eq modtime 0)
+                   (not (eq (car attributes) t))
+                   (and (= (car (nth 5 attributes)) (car modtime))
+                        (= (nth 1 (nth 5 attributes)) (cdr modtime)))))))
+
+(defun dired-buffer-stale-p (&optional noconfirm)
+  "Return non-nil if current dired buffer needs updating.
+If NOCONFIRM is non-nil, then this function always returns nil
+for a remote directory.  This feature is used by Auto Revert Mode."
+  (let ((dirname
+        (if (consp dired-directory) (car dired-directory) dired-directory)))
+    (and (stringp dirname)
+        (not (when noconfirm (file-remote-p dirname)))
+        (file-readable-p dirname)
+        (dired-directory-changed-p dirname))))
+
 ;; Separate function from dired-noselect for the sake of dired-vms.el.
 (defun dired-internal-noselect (dir-or-list &optional switches mode)
   ;; If there is an existing dired buffer for DIRNAME, just leave
   ;; buffer as it is (don't even call dired-revert).
   ;; This saves time especially for deep trees or with ange-ftp.
-  ;; The user can type `g'easily, and it is more consistent with find-file.
+  ;; The user can type `g' easily, and it is more consistent with find-file.
   ;; But if SWITCHES are given they are probably different from the
   ;; buffer's old value, so call dired-sort-other, which does
   ;; revert the buffer.
@@ -511,14 +560,17 @@ If DIRNAME is already in a dired buffer, that buffer is used without refresh."
   ;; like find-file does.
   ;; Optional argument MODE is passed to dired-find-buffer-nocreate,
   ;; see there.
-  (let* ((dirname (if (consp dir-or-list) (car dir-or-list) dir-or-list))
-        ;; The following line used to use dir-or-list.
-        ;; That never found an existing buffer, in the case
-        ;; where it is a list.
-        (buffer (dired-find-buffer-nocreate dirname mode))
+  (let* (dirname
+        buffer
         ;; note that buffer already is in dired-mode, if found
-        (new-buffer-p (not buffer))
+        new-buffer-p
         (old-buf (current-buffer)))
+    (if (consp dir-or-list)
+       (setq dirname (car dir-or-list))
+      (setq dirname dir-or-list))
+    ;; Look for an existing buffer.
+    (setq buffer (dired-find-buffer-nocreate dirname mode)
+         new-buffer-p (null buffer))
     (or buffer
        (let ((default-major-mode 'fundamental-mode))
          ;; We don't want default-major-mode to run hooks and set auto-fill
@@ -526,21 +578,14 @@ If DIRNAME is already in a dired buffer, that buffer is used without refresh."
          ;; kill-all-local-variables any longer.
          (setq buffer (create-file-buffer (directory-file-name dirname)))))
     (set-buffer buffer)
-    (if (not new-buffer-p)     ; existing buffer ...
-       (cond (switches        ; ... but new switches     
+    (if (not new-buffer-p)             ; existing buffer ...
+       (cond (switches                 ; ... but new switches
               ;; file list may have changed
-              (if (consp dir-or-list) 
-                  (setq dired-directory dir-or-list))
+              (setq dired-directory dir-or-list)
               ;; this calls dired-revert
-              (dired-sort-other switches))  
+              (dired-sort-other switches))
              ;; If directory has changed on disk, offer to revert.
-             ((if (let ((attributes (file-attributes dirname))
-                        (modtime (visited-file-modtime)))
-                    (or (eq modtime 0)
-                        (not (eq (car attributes) t))
-                        (and (= (car (nth 5 attributes)) (car modtime))
-                             (= (nth 1 (nth 5 attributes)) (cdr modtime)))))
-                  nil
+             ((when (dired-directory-changed-p dirname)
                 (message "%s"
                          (substitute-command-keys
                           "Directory has changed on disk; type \\[revert-buffer] to update Dired")))))
@@ -553,21 +598,16 @@ If DIRNAME is already in a dired buffer, that buffer is used without refresh."
            (file-name-directory dirname))
       (or switches (setq switches dired-listing-switches))
       (if mode (funcall mode)
-        (dired-mode dirname switches))
+        (dired-mode dir-or-list switches))
       ;; default-directory and dired-actual-switches are set now
       ;; (buffer-local), so we can call dired-readin:
       (let ((failed t))
        (unwind-protect
-           (progn (dired-readin dir-or-list buffer)
+           (progn (dired-readin)
                   (setq failed nil))
          ;; dired-readin can fail if parent directories are inaccessible.
          ;; Don't leave an empty buffer around in that case.
          (if failed (kill-buffer buffer))))
-      ;; No need to narrow since the whole buffer contains just
-      ;; dired-readin's output, nothing else.  The hook can
-      ;; successfully use dired functions (e.g. dired-get-filename)
-      ;; as the subdir-alist has been built in dired-readin.
-      (run-hooks 'dired-after-readin-hook)
       (goto-char (point-min))
       (dired-initial-position dirname))
     (set-buffer old-buf)
@@ -583,6 +623,7 @@ If DIRNAME is already in a dired buffer, that buffer is used without refresh."
   ;; This differs from dired-buffers-for-dir in that it does not consider
   ;; subdirs of default-directory and searches for the first match only.
   ;; Also, the major mode must be MODE.
+  (setq dirname (expand-file-name dirname))
   (let (found (blist dired-buffers))    ; was (buffer-list)
     (or mode (setq mode 'dired-mode))
     (while blist
@@ -591,9 +632,12 @@ If DIRNAME is already in a dired buffer, that buffer is used without refresh."
        (save-excursion
          (set-buffer (cdr (car blist)))
          (if (and (eq major-mode mode)
-                  (if (consp dired-directory)
-                      (equal (car dired-directory) dirname)
-                    (equal dired-directory dirname)))
+                  dired-directory  ;; nil during find-alternate-file
+                  (equal dirname
+                         (expand-file-name
+                          (if (consp dired-directory)
+                              (car dired-directory)
+                            dired-directory))))
              (setq found (cdr (car blist))
                    blist nil)
            (setq blist (cdr blist))))))
@@ -602,43 +646,33 @@ If DIRNAME is already in a dired buffer, that buffer is used without refresh."
 \f
 ;; Read in a new dired buffer
 
-;; dired-readin differs from dired-insert-subdir in that it accepts
-;; wildcards, erases the buffer, and builds the subdir-alist anew
-;; (including making it buffer-local and clearing it first).
-(defun dired-readin (dir-or-list buffer)
+(defun dired-readin ()
+  "Read in a new dired buffer.
+Differs from dired-insert-subdir in that it accepts
+wildcards, erases the buffer, and builds the subdir-alist anew
+\(including making it buffer-local and clearing it first)."
+
   ;; default-directory and dired-actual-switches must be buffer-local
   ;; and initialized by now.
-  ;; Thus we can test (equal default-directory dirname) instead of
-  ;; (file-directory-p dirname) and save a filesystem transaction.
-  ;; Also, we can run this hook which may want to modify the switches
-  ;; based on default-directory, e.g. with ange-ftp to a SysV host
-  ;; where ls won't understand -Al switches.
-  (let (dirname
-       (indent-tabs-mode nil))
-    (if (consp dir-or-list)
-       (setq dirname (car dir-or-list))
-      (setq dirname dir-or-list))
+  (let (dirname)
+    (if (consp dired-directory)
+       (setq dirname (car dired-directory))
+      (setq dirname dired-directory))
     (setq dirname (expand-file-name dirname))
-    (if (consp dir-or-list)
-       (setq dir-or-list (cons dirname (cdr dir-or-list))))
-    (run-hooks 'dired-before-readin-hook)
     (save-excursion
-      (message "Reading directory %s..." dirname)
-      (set-buffer buffer)
-      (let (buffer-read-only (failed t))
+      ;; This hook which may want to modify dired-actual-switches
+      ;; based on dired-directory, e.g. with ange-ftp to a SysV host
+      ;; where ls won't understand -Al switches.
+      (run-hooks 'dired-before-readin-hook)
+      (if (consp buffer-undo-list)
+         (setq buffer-undo-list nil))
+      (let (buffer-read-only
+           ;; Don't make undo entries for readin.
+           (buffer-undo-list t))
        (widen)
        (erase-buffer)
-       (dired-readin-insert dir-or-list)
-       (indent-rigidly (point-min) (point-max) 2)
-       ;; We need this to make the root dir have a header line as all
-       ;; other subdirs have:
-       (goto-char (point-min))
-        (if (not (looking-at "^  /.*:$"))
-            (dired-insert-headerline default-directory))
-       ;; can't run dired-after-readin-hook here, it may depend on the subdir
-       ;; alist to be OK.
-       )
-      (message "Reading directory %s...done" dirname)
+       (dired-readin-insert))
+      (goto-char (point-min))
       ;; Must first make alist buffer local and set it to nil because
       ;; dired-build-subdir-alist will call dired-clear-alist first
       (set (make-local-variable 'dired-subdir-alist) nil)
@@ -646,72 +680,97 @@ If DIRNAME is already in a dired buffer, that buffer is used without refresh."
       (let ((attributes (file-attributes dirname)))
        (if (eq (car attributes) t)
            (set-visited-file-modtime (nth 5 attributes))))
-      (if (consp buffer-undo-list)
-         (setq buffer-undo-list nil))
-      (set-buffer-modified-p nil))))
+      (set-buffer-modified-p nil)
+      ;; No need to narrow since the whole buffer contains just
+      ;; dired-readin's output, nothing else.  The hook can
+      ;; successfully use dired functions (e.g. dired-get-filename)
+      ;; as the subdir-alist has been built in dired-readin.
+      (run-hooks 'dired-after-readin-hook))))
 
 ;; Subroutines of dired-readin
 
-(defun dired-readin-insert (dir-or-list)
-  ;; Just insert listing for the passed-in directory or
-  ;; directory-and-file list, assuming a clean buffer.
-  (let (dirname)
-    (if (consp dir-or-list)
-       (setq dirname (car dir-or-list))
-      (setq dirname dir-or-list))
-    ;; Expand before comparing in case one or both have been abbreviated.
-    (if (and (equal (expand-file-name default-directory)
-                   (expand-file-name dirname))
-            (not (consp dir-or-list)))
+(defun dired-readin-insert ()
+  ;; Insert listing for the specified dir (and maybe file list)
+  ;; already in dired-directory, assuming a clean buffer.
+  (let (dir file-list)
+    (if (consp dired-directory)
+       (setq dir (car dired-directory)
+             file-list (cdr dired-directory))
+      (setq dir dired-directory
+           file-list nil))
+    (setq dir (expand-file-name dir))
+    (if (and (equal "" (file-name-nondirectory dir))
+            (not file-list))
        ;; If we are reading a whole single directory...
-       (dired-insert-directory dir-or-list dired-actual-switches nil t)
+       (dired-insert-directory dir dired-actual-switches nil nil t)
       (if (not (file-readable-p
-               (directory-file-name (file-name-directory dirname))))
-         (error "Directory %s inaccessible or nonexistent" dirname)
-       ;; Else assume it contains wildcards,
-       ;; unless it is an explicit list of files.
-       (dired-insert-directory dir-or-list dired-actual-switches
-                               (not (listp dir-or-list)))
-       (or (consp dir-or-list)
-           (save-excursion     ;; insert wildcard instead of total line:
-             (goto-char (point-min))
-             (insert "wildcard " (file-name-nondirectory dirname) "\n")))))))
-
-(defun dired-insert-directory (dir-or-list switches &optional wildcard full-p)
-  ;; Do the right thing whether dir-or-list is atomic or not.  If it is,
-  ;; inset all files listed in the cdr (the car is the passed-in directory
-  ;; list).
+               (directory-file-name (file-name-directory dir))))
+         (error "Directory %s inaccessible or nonexistent" dir)
+       ;; Else treat it as a wildcard spec
+       ;; unless we have an explicit list of files.
+       (dired-insert-directory dir dired-actual-switches
+                               file-list (not file-list) t)))))
+
+(defun dired-insert-directory (dir switches &optional file-list wildcard hdr)
+  "Insert a directory listing of DIR, Dired style.
+Use SWITCHES to make the listings.
+If FILE-LIST is non-nil, list only those files.
+Otherwise, if WILDCARD is non-nil, expand wildcards;
+ in that case, DIR should be a file name that uses wildcards.
+In other cases, DIR should be a directory name or a directory filename.
+If HDR is non-nil, insert a header line with the directory name."
   (let ((opoint (point))
        (process-environment (copy-sequence process-environment))
        end)
+    (if (or dired-use-ls-dired (file-remote-p dir))
+       (setq switches (concat "--dired " switches)))
     ;; We used to specify the C locale here, to force English month names;
     ;; but this should not be necessary any more,
     ;; with the new value of dired-move-to-filename-regexp.
-    (if (consp dir-or-list)
-       ;; In this case, use the file names in the cdr
-       ;; exactly as originally given to dired-noselect.
-       (mapcar
-        (function (lambda (x) (insert-directory x switches wildcard full-p)))
-        (cdr dir-or-list))
-      ;; Expand the file name here because it may have been abbreviated
-      ;; in dired-noselect.
-      (insert-directory (expand-file-name dir-or-list) switches wildcard full-p))
+    (if file-list
+       (dolist (f file-list)
+         (insert-directory f switches nil nil))
+      (insert-directory dir switches wildcard (not wildcard)))
     ;; Quote certain characters, unless ls quoted them for us.
     (if (not (string-match "b" dired-actual-switches))
        (save-excursion
          (setq end (point-marker))
          (goto-char opoint)
          (while (search-forward "\\" end t)
-           (replace-match "\\\\" nil t))
+           (replace-match (apply #'propertize
+                                 "\\\\"
+                                 (text-properties-at (match-beginning 0)))
+                          nil t))
          (goto-char opoint)
          (while (search-forward "\^m" end t)
-           (replace-match "\\015" nil t))
+           (replace-match (apply #'propertize
+                                 "\\015"
+                                 (text-properties-at (match-beginning 0)))
+                          nil t))
          (set-marker end nil)))
-    (dired-insert-set-properties opoint (point)))
-  (setq dired-directory dir-or-list))
+    (dired-insert-set-properties opoint (point))
+    ;; If we used --dired and it worked, the lines are already indented.
+    ;; Otherwise, indent them.
+    (unless (save-excursion
+             (goto-char opoint)
+             (looking-at "  "))
+      (let ((indent-tabs-mode nil))
+       (indent-rigidly opoint (point) 2)))
+    ;; Insert text at the beginning to standardize things.
+    (save-excursion
+      (goto-char opoint)
+      (if (and (or hdr wildcard) (not (looking-at "^  /.*:$")))
+         ;; Note that dired-build-subdir-alist will replace the name
+         ;; by its expansion, so it does not matter whether what we insert
+         ;; here is fully expanded, but it should be absolute.
+         (insert "  " (directory-file-name (file-name-directory dir)) ":\n"))
+      (when wildcard
+       ;; Insert "wildcard" line where "total" line would be for a full dir.
+       (insert "  wildcard " (file-name-nondirectory dir) "\n")))))
 
 ;; Make the file names highlight when the mouse is on them.
 (defun dired-insert-set-properties (beg end)
+  "Make the file names highlight when the mouse is on them."
   (save-excursion
     (goto-char beg)
     (while (< (point) end)
@@ -726,21 +785,14 @@ If DIRNAME is already in a dired buffer, that buffer is used without refresh."
                 help-echo "mouse-2: visit this file in other window")))
        (error nil))
       (forward-line 1))))
-
-(defun dired-insert-headerline (dir);; also used by dired-insert-subdir
-  ;; Insert DIR's headerline with no trailing slash, exactly like ls
-  ;; would, and put cursor where dired-build-subdir-alist puts subdir
-  ;; boundaries.
-  (save-excursion (insert "  " (directory-file-name dir) ":\n")))
-
 \f
 ;; Reverting a dired buffer
 
 (defun dired-revert (&optional arg noconfirm)
-  ;; Reread the dired buffer.  Must also be called after
-  ;; dired-actual-switches have changed.
-  ;; Should not fail even on completely garbaged buffers.
-  ;; Preserves old cursor, marks/flags, hidden-p.
+  "Reread the dired buffer.
+Must also be called after dired-actual-switches have changed.
+Should not fail even on completely garbaged buffers.
+Preserves old cursor, marks/flags, hidden-p."
   (widen)                              ; just in case user narrowed
   (let ((opoint (point))
        (ofile (dired-get-filename nil t))
@@ -755,7 +807,7 @@ If DIRNAME is already in a dired buffer, that buffer is used without refresh."
     ;; treat top level dir extra (it may contain wildcards)
     (dired-uncache
      (if (consp dired-directory) (car dired-directory) dired-directory))
-    (dired-readin dired-directory (current-buffer))
+    (dired-readin)
     (let ((dired-after-readin-hook nil))
       ;; don't run that hook for each subdir...
       (dired-insert-old-subdirs old-subdir-alist))
@@ -767,10 +819,9 @@ If DIRNAME is already in a dired buffer, that buffer is used without refresh."
        (goto-char opoint))             ; was before
     (dired-move-to-filename)
     (save-excursion                    ; hide subdirs that were hidden
-      (mapcar (function (lambda (dir)
-                         (if (dired-goto-subdir dir)
-                             (dired-hide-subdir 1))))
-             hidden-subdirs)))
+      (dolist (dir hidden-subdirs)
+       (if (dired-goto-subdir dir)
+           (dired-hide-subdir 1)))))
   ;; outside of the let scope
 ;;; Might as well not override the user if the user changed this.
 ;;;  (setq buffer-read-only t)
@@ -780,7 +831,7 @@ If DIRNAME is already in a dired buffer, that buffer is used without refresh."
 ;; Some of these are also used when inserting subdirs.
 
 (defun dired-remember-marks (beg end)
-  ;; Return alist of files and their marks, from BEG to END.
+  "Return alist of files and their marks, from BEG to END."
   (if selective-display                        ; must unhide to make this work.
       (let (buffer-read-only)
        (subst-char-in-region beg end ?\r ?\n)))
@@ -793,9 +844,9 @@ If DIRNAME is already in a dired buffer, that buffer is used without refresh."
                  alist (cons (cons fil chr) alist)))))
     alist))
 
-;; Mark all files remembered in ALIST.
-;; Each element of ALIST looks like (FILE . MARKERCHAR).
 (defun dired-mark-remembered (alist)
+  "Mark all files remembered in ALIST.
+Each element of ALIST looks like (FILE . MARKERCHAR)."
   (let (elt fil chr)
     (while alist
       (setq elt (car alist)
@@ -808,8 +859,8 @@ If DIRNAME is already in a dired buffer, that buffer is used without refresh."
            (delete-char 1)
            (insert chr))))))
 
-;; Return a list of names of subdirs currently hidden.
 (defun dired-remember-hidden ()
+  "Return a list of names of subdirs currently hidden."
   (let ((l dired-subdir-alist) dir pos result)
     (while l
       (setq dir (car (car l))
@@ -821,9 +872,9 @@ If DIRNAME is already in a dired buffer, that buffer is used without refresh."
          (setq result (cons dir result))))
     result))
 
-;; Try to insert all subdirs that were displayed before,
-;; according to the former subdir alist OLD-SUBDIR-ALIST.
 (defun dired-insert-old-subdirs (old-subdir-alist)
+  "Try to insert all subdirs that were displayed before.
+Do so according to the former subdir alist OLD-SUBDIR-ALIST."
   (or (string-match "R" dired-actual-switches)
       (let (elt dir)
        (while old-subdir-alist
@@ -836,20 +887,17 @@ If DIRNAME is already in a dired buffer, that buffer is used without refresh."
                (dired-insert-subdir dir))
            (error nil))))))
 
-;; Remove directory DIR from any directory cache.
 (defun dired-uncache (dir)
+  "Remove directory DIR from any directory cache."
   (let ((handler (find-file-name-handler dir 'dired-uncache)))
     (if handler
        (funcall handler 'dired-uncache dir))))
 \f
 ;; dired mode key bindings and initialization
 
-(defvar dired-mode-map nil "Local keymap for dired-mode buffers.")
-(if dired-mode-map
-    nil
+(defvar dired-mode-map
   ;; This looks ugly when substitute-command-keys uses C-d instead d:
   ;;  (define-key dired-mode-map "\C-d" 'dired-flag-file-deletion)
-
   (let ((map (make-keymap)))
     (suppress-keymap map)
     (define-key map [mouse-2] 'dired-mouse-find-file-other-window)
@@ -914,6 +962,7 @@ If DIRNAME is already in a dired buffer, that buffer is used without refresh."
     (define-key map "*u" 'dired-unmark)
     (define-key map "*?" 'dired-unmark-all-files)
     (define-key map "*!" 'dired-unmark-all-marks)
+    (define-key map "U" 'dired-unmark-all-marks)
     (define-key map "*\177" 'dired-unmark-backward)
     (define-key map "*\C-n" 'dired-next-marked-file)
     (define-key map "*\C-p" 'dired-prev-marked-file)
@@ -925,6 +974,7 @@ If DIRNAME is already in a dired buffer, that buffer is used without refresh."
     (define-key map "f" 'dired-find-file)
     (define-key map "\C-m" 'dired-advertised-find-file)
     (define-key map "g" 'revert-buffer)
+    (define-key map "\M-g" 'dired-goto-file)
     (define-key map "h" 'describe-mode)
     (define-key map "i" 'dired-maybe-insert-subdir)
     (define-key map "k" 'dired-do-kill-lines)
@@ -1172,11 +1222,14 @@ If DIRNAME is already in a dired buffer, that buffer is used without refresh."
       '(menu-item "Copy to..." dired-do-copy
                  :help "Copy current file or all marked files"))
 
-    (setq dired-mode-map map)))
+    map)
+  "Local keymap for `dired-mode' buffers.")
 \f
 ;; Dired mode is suitable only for specially formatted data.
 (put 'dired-mode 'mode-class 'special)
 
+(defvar buffer-stale-function)
+
 (defun dired-mode (&optional dirname switches)
   "\
 Mode for \"editing\" directory listings.
@@ -1255,18 +1308,32 @@ Keybindings:
        (propertized-buffer-identification "%17b"))
   (set (make-local-variable 'revert-buffer-function)
        (function dired-revert))
+  (set (make-local-variable 'buffer-stale-function)
+       (function dired-buffer-stale-p))
   (set (make-local-variable 'page-delimiter)
        "\n\n")
   (set (make-local-variable 'dired-directory)
        (or dirname default-directory))
   ;; list-buffers uses this to display the dir being edited in this buffer.
   (set (make-local-variable 'list-buffers-directory)
-       (expand-file-name dired-directory))
+       (expand-file-name (if (listp dired-directory)
+                            (car dired-directory)
+                          dired-directory)))
   (set (make-local-variable 'dired-actual-switches)
        (or switches dired-listing-switches))
-  (set (make-local-variable 'font-lock-defaults) '(dired-font-lock-keywords t))
+  (set (make-local-variable 'font-lock-defaults)
+       '(dired-font-lock-keywords t nil nil beginning-of-line))
   (dired-sort-other dired-actual-switches t)
-  (run-hooks 'dired-mode-hook))
+  (run-hooks 'dired-mode-hook)
+  (when (featurep 'x-dnd)
+    (make-variable-buffer-local 'x-dnd-test-function)
+    (make-variable-buffer-local 'x-dnd-protocol-alist)
+    (setq x-dnd-test-function 'dired-dnd-test-function)
+    (setq x-dnd-protocol-alist
+         (append '(("^file:///" . dired-dnd-handle-local-file)
+                   ("^file://"  . dired-dnd-handle-file)
+                   ("^file:"    . dired-dnd-handle-local-file))
+                 x-dnd-protocol-alist))))
 \f
 ;; Idiosyncratic dired commands that don't deal with marks.
 
@@ -1341,7 +1408,12 @@ Creates a buffer if necessary."
 (defun dired-get-file-for-visit ()
   "Get the current line's file name, with an error if file does not exist."
   (interactive)
-  (let ((file-name (file-name-sans-versions (dired-get-filename) t)))
+  ;; We pass t for second arg so that we don't get error for `.' and `..'.
+  (let ((raw (dired-get-filename nil t))
+       file-name)
+    (if (null raw)
+       (error "No file on this line"))
+    (setq file-name (file-name-sans-versions raw t))
     (if (file-exists-p file-name)
        file-name
       (if (file-symlink-p file-name)
@@ -1353,37 +1425,72 @@ Creates a buffer if necessary."
 (defun dired-find-file ()
   "In Dired, visit the file or directory named on this line."
   (interactive)
-  (find-file (dired-get-file-for-visit)))
+  ;; Bind `find-file-run-dired' so that the command works on directories
+  ;; too, independent of the user's setting.
+  (let ((find-file-run-dired t))
+    (find-file (dired-get-file-for-visit))))
 
 (defun dired-find-alternate-file ()
   "In Dired, visit this file or directory instead of the dired buffer."
   (interactive)
   (set-buffer-modified-p nil)
   (find-alternate-file (dired-get-file-for-visit)))
+;; Don't override the setting from .emacs.
+;;;###autoload (put 'dired-find-alternate-file 'disabled t)
 
 (defun dired-mouse-find-file-other-window (event)
   "In Dired, visit the file or directory name you click on."
   (interactive "e")
-  (let (file)
+  (let (window pos file)
     (save-excursion
-      (set-buffer (window-buffer (posn-window (event-end event))))
-      (save-excursion
-       (goto-char (posn-point (event-end event)))
-       (setq file (dired-get-file-for-visit))))
-    (select-window (posn-window (event-end event)))
-    (find-file-other-window (file-name-sans-versions file t))))
+      (setq window (posn-window (event-end event))
+           pos (posn-point (event-end event)))
+      (if (not (windowp window))
+         (error "No file chosen"))
+      (set-buffer (window-buffer window))
+      (goto-char pos)
+      (setq file (dired-get-file-for-visit)))
+    (if (file-directory-p file)
+       (or (and (cdr dired-subdir-alist)
+                (dired-goto-subdir file))
+           (progn
+             (select-window window)
+             (dired-other-window file)))
+      (let (cmd)
+       ;; Look for some other way to view a certain file.
+       (dolist (elt dired-view-command-alist)
+         (if (string-match (car elt) file)
+             (setq cmd (cdr elt))))
+       (if cmd
+           (call-process shell-file-name nil 0 nil
+                         "-c"
+                         (concat (format cmd (shell-quote-argument file))
+                                 " &"))
+         (select-window window)
+         (find-file-other-window (file-name-sans-versions file t)))))))
 
 (defun dired-view-file ()
   "In Dired, examine a file in view mode, returning to dired when done.
-When file is a directory, show it in this buffer if it is inserted;
-otherwise, display it in another buffer."
+When file is a directory, show it in this buffer if it is inserted.
+Some kinds of files are displayed using external viewer programs;
+see `dired-view-command-alist'.  Otherwise, display it in another buffer."
   (interactive)
   (let ((file (dired-get-file-for-visit)))
     (if (file-directory-p file)
        (or (and (cdr dired-subdir-alist)
                 (dired-goto-subdir file))
            (dired file))
-      (view-file file))))
+      (let (cmd)
+       ;; Look for some other way to view a certain file.
+       (dolist (elt dired-view-command-alist)
+         (if (string-match (car elt) file)
+             (setq cmd (cdr elt))))
+       (if cmd
+           (call-process shell-file-name nil 0 nil
+                         "-c"
+                         (concat (format cmd (shell-quote-argument file))
+                                 " &"))
+         (view-file file))))))
 
 (defun dired-find-file-other-window ()
   "In Dired, visit this file or directory in another window."
@@ -1401,11 +1508,12 @@ otherwise, display it in another buffer."
   "In Dired, return name of file mentioned on this line.
 Value returned normally includes the directory name.
 Optional arg LOCALP with value `no-dir' means don't include directory
-  name in result.  A value of `verbatim' means to return the name exactly as
-  it occurs in the buffer, and a value of t means construct name relative to
-  `default-directory', which still may contain slashes if in a subdirectory.
-Optional arg NO-ERROR-IF-NOT-FILEP means return nil if no filename on
-  this line, otherwise an error occurs."
+name in result.  A value of `verbatim' means to return the name exactly as
+it occurs in the buffer, and a value of t means construct name relative to
+`default-directory', which still may contain slashes if in a subdirectory.
+Optional arg NO-ERROR-IF-NOT-FILEP means treat `.' and `..' as
+regular filenames and return nil if no filename on this line.
+Otherwise, an error occurs in these cases."
   (let (case-fold-search file p1 p2 already-absolute)
     (save-excursion
       (if (setq p1 (dired-move-to-filename (not no-error-if-not-filep)))
@@ -1421,44 +1529,58 @@ Optional arg NO-ERROR-IF-NOT-FILEP means return nil if no filename on
          (setq file
                (read
                 (concat "\""
-                        ;; some ls -b don't escape quotes, argh!
+                        ;; Some ls -b don't escape quotes, argh!
                         ;; This is not needed for GNU ls, though.
                         (or (dired-string-replace-match
                              "\\([^\\]\\|\\`\\)\"" file "\\1\\\\\"" nil t)
                             file)
-                        "\"")))))
+                        "\"")))
+         ;; The above `read' will return a unibyte string if FILE
+         ;; contains eight-bit-control/graphic characters.
+         (if (and enable-multibyte-characters
+                  (not (multibyte-string-p file)))
+             (setq file (string-to-multibyte file)))))
     (and file (file-name-absolute-p file)
         ;; A relative file name can start with ~.
         ;; Don't treat it as absolute in this context.
         (not (eq (aref file 0) ?~))
         (setq already-absolute t))
-    (and file buffer-file-coding-system
-        (not file-name-coding-system)
-        (not default-file-name-coding-system)
-        (setq file (encode-coding-string file buffer-file-coding-system)))
     (cond
      ((null file)
       nil)
      ((eq localp 'verbatim)
       file)
+     ((and (not no-error-if-not-filep)
+          (save-excursion
+            (beginning-of-line)
+            (looking-at dired-re-dot)))
+      (error "Cannot operate on `.' or `..'"))
      ((and (eq localp 'no-dir) already-absolute)
       (file-name-nondirectory file))
      (already-absolute
-      (if (find-file-name-handler file nil)
-         (concat "/:" file)
-       file))
+      (let ((handler (find-file-name-handler file nil)))
+       ;; check for safe-magic property so that we won't
+       ;; put /: for names that don't really need them.
+       ;; For instance, .gz files when auto-compression-mode is on.
+       (if (and handler (not (get handler 'safe-magic)))
+           (concat "/:" file)
+         file)))
      ((eq localp 'no-dir)
       file)
      ((equal (dired-current-directory) "/")
       (setq file (concat (dired-current-directory localp) file))
-      (if (find-file-name-handler file nil)
-         (concat "/:" file)
-       file))
+      (let ((handler (find-file-name-handler file nil)))
+       ;; check for safe-magic property so that we won't
+       ;; put /: for names that don't really need them.
+       ;; For instance, .gz files when auto-compression-mode is on.
+       (if (and handler (not (get handler 'safe-magic)))
+           (concat "/:" file)
+         file)))
      (t
       (concat (dired-current-directory localp) file)))))
 
 (defun dired-string-replace-match (regexp string newtext
-                                         &optional literal global)
+                                   &optional literal global)
   "Replace first match of REGEXP in STRING with NEWTEXT.
 If it does not match, nil is returned instead of the new string.
 Optional arg LITERAL means to take NEWTEXT literally.
@@ -1475,7 +1597,7 @@ Optional arg GLOBAL means to replace all matches."
       (replace-match newtext t literal string))))
 
 (defun dired-make-absolute (file &optional dir)
-  ;;"Convert FILE (a pathname relative to DIR) to an absolute pathname."
+  ;;"Convert FILE (a file name relative to DIR) to an absolute file name."
   ;; We can't always use expand-file-name as this would get rid of `.'
   ;; or expand in / instead default-directory if DIR=="".
   ;; This should be good enough for ange-ftp, but might easily be
@@ -1503,9 +1625,11 @@ DIR must be a directory name, not a file name."
 
 (defvar dired-move-to-filename-regexp
   (let* ((l "\\([A-Za-z]\\|[^\0-\177]\\)")
+        (l-or-quote "\\([A-Za-z']\\|[^\0-\177]\\)")
         ;; In some locales, month abbreviations are as short as 2 letters,
         ;; and they can be followed by ".".
-        (month (concat l l "+\\.?"))
+        ;; In Breton, a month name  can include a quote character.
+        (month (concat l-or-quote l-or-quote "+\\.?"))
         (s " ")
         (yyyy "[0-9][0-9][0-9][0-9]")
         (dd "[ 0-3][0-9]")
@@ -1529,11 +1653,11 @@ DIR must be a directory name, not a file name."
         ;; The "[0-9]" below requires the previous column to end in a digit.
         ;; This avoids recognizing `1 may 1997' as a date in the line:
         ;; -r--r--r--   1 may      1997        1168 Oct 19 16:49 README
-        ;; The "[kMGTPEZY]?" below supports "ls -alh" output.
+        ;; The "[kKMGTPEZY]?" below supports "ls -alh" output.
         ;; The ".*" below finds the last match if there are multiple matches.
         ;; This avoids recognizing `jservice  10  1024' as a date in the line:
         ;; drwxr-xr-x  3 jservice  10  1024 Jul  2  1997 esg-host
-    (concat ".*[0-9][kMGTPEZY]?" s
+    (concat ".*[0-9][kKMGTPEZY]?" s
            "\\(" western "\\|" western-comma "\\|" japanese "\\|" iso "\\)"
            s "+"))
   "Regular expression to match up to the file name in a directory listing.
@@ -1548,12 +1672,21 @@ regardless of the language.")
 ;; Returns position (point) or nil if no filename on this line."
 (defun dired-move-to-filename (&optional raise-error eol)
   ;; This is the UNIX version.
-  (or eol (setq eol (progn (end-of-line) (point))))
+  (or eol (setq eol (line-end-position)))
   (beginning-of-line)
-  (if (re-search-forward dired-move-to-filename-regexp eol t)
-      (goto-char (match-end 0))
-    (if raise-error
-       (error "No file on this line"))))
+  ;; First try assuming `ls --dired' was used.
+  (let ((change (next-single-property-change (point) 'dired-filename nil eol)))
+    (cond
+     ((and change (< change eol))
+      (goto-char change))
+     ((re-search-forward dired-move-to-filename-regexp eol t)
+      (goto-char (match-end 0)))
+     ((re-search-forward dired-permission-flags-regexp eol t)
+      ;; Ha!  There *is* a file.  Our regexp-from-hell just failed to find it.
+      (funcall (if raise-error 'error 'message)
+              "Unrecognized line!  Check dired-move-to-filename-regexp"))
+     (raise-error
+      (error "No file on this line")))))
 
 (defun dired-move-to-end-of-filename (&optional no-error)
   ;; Assumes point is at beginning of filename,
@@ -1562,63 +1695,65 @@ regardless of the language.")
   ;; (dired-move-to-filename t).
   ;; On failure, signals an error (with non-nil NO-ERROR just returns nil).
   ;; This is the UNIX version.
-  (let (opoint file-type executable symlink hidden case-fold-search used-F eol)
-    ;; case-fold-search is nil now, so we can test for capital F:
-    (setq used-F (string-match "F" dired-actual-switches)
-         opoint (point)
-          eol (save-excursion (end-of-line) (point))
-         hidden (and selective-display
-                     (save-excursion (search-forward "\r" eol t))))
-    (if hidden
-       nil
-      (save-excursion;; Find out what kind of file this is:
-       ;; Restrict perm bits to be non-blank,
-       ;; otherwise this matches one char to early (looking backward):
-       ;; "l---------" (some systems make symlinks that way)
-       ;; "----------" (plain file with zero perms)
-       (if (re-search-backward
-            dired-permission-flags-regexp nil t)
-           (setq file-type (char-after (match-beginning 1))
-                 symlink (eq file-type ?l)
-                 ;; Only with -F we need to know whether it's an executable
-                 executable (and
-                             used-F
-                             (string-match
-                              "[xst]";; execute bit set anywhere?
-                              (concat
-                               (buffer-substring (match-beginning 2)
-                                                 (match-end 2))
-                               (buffer-substring (match-beginning 3)
-                                                 (match-end 3))
-                               (buffer-substring (match-beginning 4)
-                                                 (match-end 4))))))
-         (or no-error (error "No file on this line"))))
-      ;; Move point to end of name:
-      (if symlink
-         (if (search-forward " ->" eol t)
-             (progn
-               (forward-char -3)
-               (and used-F
-                    dired-ls-F-marks-symlinks
-                    (eq (preceding-char) ?@);; did ls really mark the link?
-                    (forward-char -1))))
-       (goto-char eol);; else not a symbolic link
-       ;; ls -lF marks dirs, sockets and executables with exactly one
-       ;; trailing character. (Executable bits on symlinks ain't mean
-       ;; a thing, even to ls, but we know it's not a symlink.)
-       (and used-F
-            (or (memq file-type '(?d ?s))
-                executable)
-            (forward-char -1))))
-    (or no-error
-       (not (eq opoint (point)))
-       (error (if hidden
-                  (substitute-command-keys
-                   "File line is hidden, type \\[dired-hide-subdir] to unhide")
-                "No file on this line")))
-    (if (eq opoint (point))
-       nil
-      (point))))
+  (if (get-text-property (point) 'dired-filename)
+      (goto-char (next-single-property-change (point) 'dired-filename))
+    (let (opoint file-type executable symlink hidden case-fold-search used-F eol)
+      ;; case-fold-search is nil now, so we can test for capital F:
+      (setq used-F (string-match "F" dired-actual-switches)
+           opoint (point)
+           eol (save-excursion (end-of-line) (point))
+           hidden (and selective-display
+                       (save-excursion (search-forward "\r" eol t))))
+      (if hidden
+         nil
+       (save-excursion ;; Find out what kind of file this is:
+         ;; Restrict perm bits to be non-blank,
+         ;; otherwise this matches one char to early (looking backward):
+         ;; "l---------" (some systems make symlinks that way)
+         ;; "----------" (plain file with zero perms)
+         (if (re-search-backward
+              dired-permission-flags-regexp nil t)
+             (setq file-type (char-after (match-beginning 1))
+                   symlink (eq file-type ?l)
+                   ;; Only with -F we need to know whether it's an executable
+                   executable (and
+                               used-F
+                               (string-match
+                                "[xst]" ;; execute bit set anywhere?
+                                (concat
+                                 (buffer-substring (match-beginning 2)
+                                                   (match-end 2))
+                                 (buffer-substring (match-beginning 3)
+                                                   (match-end 3))
+                                 (buffer-substring (match-beginning 4)
+                                                   (match-end 4))))))
+           (or no-error (error "No file on this line"))))
+       ;; Move point to end of name:
+       (if symlink
+           (if (search-forward " ->" eol t)
+               (progn
+                 (forward-char -3)
+                 (and used-F
+                      dired-ls-F-marks-symlinks
+                      (eq (preceding-char) ?@) ;; did ls really mark the link?
+                      (forward-char -1))))
+         (goto-char eol) ;; else not a symbolic link
+         ;; ls -lF marks dirs, sockets and executables with exactly one
+         ;; trailing character. (Executable bits on symlinks ain't mean
+         ;; a thing, even to ls, but we know it's not a symlink.)
+         (and used-F
+              (or (memq file-type '(?d ?s))
+                  executable)
+              (forward-char -1))))
+      (or no-error
+         (not (eq opoint (point)))
+         (error (if hidden
+                    (substitute-command-keys
+                     "File line is hidden, type \\[dired-hide-subdir] to unhide")
+                  "No file on this line")))
+      (if (eq opoint (point))
+         nil
+       (point)))))
 
 \f
 ;;; COPY NAMES OF MARKED FILES INTO KILL-RING.
@@ -1626,8 +1761,8 @@ regardless of the language.")
 (defun dired-copy-filename-as-kill (&optional arg)
   "Copy names of marked (or next ARG) files into the kill ring.
 The names are separated by a space.
-With a zero prefix arg, use the complete pathname of each marked file.
-With \\[universal-argument], use the relative pathname of each marked file.
+With a zero prefix arg, use the absolute file name of each marked file.
+With \\[universal-argument], use the file name sans directory of each marked file.
 
 If on a subdir headerline, use subdirname instead; prefix arg is ignored
 in this case.
@@ -1716,7 +1851,7 @@ You can then feed the file name(s) to other commands with \\[yank]."
             (substring pattern matched-in-pattern))
            "\\'")))
 
-                
+
 
 (defun dired-advertise ()
   ;;"Advertise in variable `dired-buffers' that we dired `default-directory'."
@@ -1746,8 +1881,8 @@ You can then feed the file name(s) to other commands with \\[yank]."
     (string-match (concat "^" (regexp-quote dir)) file)))
 
 (defun dired-normalize-subdir (dir)
-  ;; Prepend default-directory to DIR if relative path name.
-  ;; dired-get-filename must be able to make a valid filename from a
+  ;; Prepend default-directory to DIR if relative file name.
+  ;; dired-get-filename must be able to make a valid file name from a
   ;; file and its directory DIR.
   (file-name-as-directory
    (if (file-name-absolute-p dir)
@@ -1891,7 +2026,7 @@ instead of `dired-actual-switches'."
 (defun dired-goto-file (file)
   "Go to file line of FILE in this dired buffer."
   ;; Return value of point on success, else nil.
-  ;; FILE must be an absolute pathname.
+  ;; FILE must be an absolute file name.
   ;; Loses if FILE contains control chars like "\007" for which ls
   ;; either inserts "?" or "\\007" into the buffer, so we won't find
   ;; it in the buffer.
@@ -1904,7 +2039,7 @@ instead of `dired-actual-switches'."
   (setq file (directory-file-name file)) ; does no harm if no directory
   (let (found case-fold-search dir)
     (setq dir (or (file-name-directory file)
-                 (error "Need absolute pathname for %s" file)))
+                 (error "File name `%s' is not absolute" file)))
     (save-excursion
       ;; The hair here is to get the result of dired-goto-subdir
       ;; without really calling it if we don't have any subdirs.
@@ -1913,11 +2048,17 @@ instead of `dired-actual-switches'."
            (and (cdr dired-subdir-alist)
                 (dired-goto-subdir dir)))
          (let ((base (file-name-nondirectory file))
+               search-string
                (boundary (dired-subdir-max)))
+           (setq search-string
+                 (replace-regexp-in-string "\^m" "\\^m" base nil t))
+           (setq search-string
+                 (replace-regexp-in-string "\\\\" "\\\\" search-string nil t))
            (while (and (not found)
                        ;; filenames are preceded by SPC, this makes
                        ;; the search faster (e.g. for the filename "-"!).
-                       (search-forward (concat " " base) boundary 'move))
+                       (search-forward (concat " " search-string)
+                                       boundary 'move))
              ;; Match could have BASE just as initial substring or
              ;; or in permission bits or date or
              ;; not be a proper filename at all:
@@ -1987,13 +2128,14 @@ nil means no recursive deletes.
 `top' means ask for each directory at top level, but delete its subdirectories
 without asking.
 Anything else means ask for each directory."
-  :type '(choice :tag "Delete not empty directory"
-                (const :tag "No. Only empty directories" nil)
-                (const :tag "Ask for each directory" t)
-                (const :tag "Ask for each top directory only" top))
+  :type '(choice :tag "Delete non-empty directories"
+                (const :tag "Yes" always)
+                (const :tag "No--only delete empty directories" nil)
+                (const :tag "Confirm for each directory" t)
+                (const :tag "Confirm for each top directory only" top))
   :group 'dired)
 
-;; Match anything but `.' and `..'. 
+;; Match anything but `.' and `..'.
 (defvar dired-re-no-dot "^\\([^.]\\|\\.\\([^.]\\|\\..\\)\\).*")
 
 ;; Delete file, possibly delete a directory and all its files.
@@ -2114,7 +2256,7 @@ if there are no flagged files."
            (setq success-list (cons (buffer-name buf) success-list)))))
     success-list))
 
-;; Delete the entry for FILE from 
+;; Delete the entry for FILE from
 (defun dired-delete-entry (file)
   (save-excursion
     (and (dired-goto-file file)
@@ -2201,12 +2343,13 @@ Command symbols are `byte-compile', `chgrp', `chmod', `chown', `compress',
 `uncompress'.")
 
 (defun dired-mark-pop-up (bufname op-symbol files function &rest args)
-  "Args BUFNAME OP-SYMBOL FILES FUNCTION &rest ARGS.
-Return FUNCTION's result on ARGS after popping up a window (in a buffer
-named BUFNAME, nil gives \" *Marked Files*\") showing the marked
-files.  Uses function `dired-pop-to-buffer' to do that.
- FUNCTION should not manipulate files.
- It should only read input (an argument or confirmation).
+  "Return FUNCTION's result on ARGS after showing which files are marked.
+Displays the file names in a buffer named BUFNAME;
+ nil gives \" *Marked Files*\".
+This uses function `dired-pop-to-buffer' to do that.
+
+FUNCTION should not manipulate files, just read input
+ (an argument or confirmation).
 The window is not shown if there is just one file or
  OP-SYMBOL is a member of the list in `dired-no-confirm'.
 FILES is the list of marked files."
@@ -2278,12 +2421,10 @@ FILES is the list of marked files."
     (dired-move-to-filename)))
 
 (defun dired-between-files ()
-  ;; Point must be at beginning of line
-  ;; Should be equivalent to (save-excursion (not (dired-move-to-filename)))
-  ;; but is about 1.5..2.0 times as fast. (Actually that's not worth it)
-  (or (looking-at "^$\\|^. *$\\|^. total\\|^. wildcard\\|^. used\\|^. find")
-      (and (looking-at dired-subdir-regexp)
-          (save-excursion (not (dired-move-to-filename))))))
+  ;; This used to be a regexp match of the `total ...' line output by
+  ;; ls, which is slightly faster, but that is not very robust; notably,
+  ;; it fails for non-english locales.
+  (save-excursion (not (dired-move-to-filename))))
 
 (defun dired-next-marked-file (arg &optional wrap opoint)
   "Move to the next marked file, wrapping around the end of the buffer."
@@ -2508,9 +2649,15 @@ A prefix argument says to unflag those files instead."
                    (file-name-nondirectory fn)))))
      "auto save file")))
 
-(defvar dired-garbage-files-regexp
-  "\\.log$\\|\\.toc$\\|\\.dvi$\\|\\.bak$\\|\\.orig$\\|\\.rej$" 
-  "*Regular expression to match \"garbage\" files for `dired-flag-garbage-files'.")
+(defcustom dired-garbage-files-regexp
+  ;; `log' here is dubious, ssince it's typically used for useful log
+  ;; files, not just TeX stuff.  -- fx
+  (concat (regexp-opt
+          '(".log" ".toc" ".dvi" ".bak" ".orig" ".rej" ".aux"))
+         "\\'")
+  "Regular expression to match \"garbage\" files for `dired-flag-garbage-files'."
+  :type 'regexp
+  :group 'dired)
 
 (defun dired-flag-garbage-files ()
   "Flag for deletion all files that match `dired-garbage-files-regexp'."
@@ -2569,7 +2716,7 @@ OLD and NEW are both characters used to mark files."
 
 (defun dired-unmark-all-files (mark &optional arg)
   "Remove a specific mark (or any mark) from every file.
-After this command, type the mark character to remove, 
+After this command, type the mark character to remove,
 or type RET to remove all marks.
 With prefix arg, query for each marked file.
 Type \\[help-command] at that time for help."
@@ -2586,8 +2733,10 @@ Type SPC or `y' to unmark one file, DEL or `n' to skip to next,
                 (re-search-forward dired-re-mark nil t)
               (search-forward string nil t))
        (if (or (not arg)
-               (dired-query 'query "Unmark file `%s'? "
-                            (dired-get-filename t)))
+               (let ((file (dired-get-filename t t)))
+                 (and file
+                      (dired-query 'query "Unmark file `%s'? "
+                                   file))))
            (progn (subst-char-in-region (1- (point)) (point)
                                         (preceding-char) ?\ )
                   (setq count (1+ count)))))
@@ -2611,38 +2760,50 @@ Thus, use \\[backward-page] to find the beginning of a group of errors."
            (progn
              (select-window window)
              (goto-char (point-max))
-             (recenter -1))
+             (forward-line -1)
+             (backward-page 1)
+             (recenter 0))
          (select-window owindow)))))
 
 (defun dired-log (log &rest args)
   ;; Log a message or the contents of a buffer.
   ;; If LOG is a string and there are more args, it is formatted with
   ;; those ARGS.  Usually the LOG string ends with a \n.
-  ;; End each bunch of errors with (dired-log t): this inserts
-  ;; current time and buffer, and a \f (formfeed).
+  ;; End each bunch of errors with (dired-log t):
+  ;; this inserts the current time and buffer at the start of the page,
+  ;; and \f (formfeed) at the end.
   (let ((obuf (current-buffer)))
-    (unwind-protect                    ; want to move point
-       (progn
-         (set-buffer (get-buffer-create dired-log-buffer))
-         (goto-char (point-max))
-         (let (buffer-read-only)
-           (cond ((stringp log)
-                  (insert (if args
-                              (apply (function format) log args)
-                            log)))
-                 ((bufferp log)
-                  (insert-buffer log))
-                 ((eq t log)
-                  (insert "\n\t" (current-time-string)
-                          "\tBuffer `" (buffer-name obuf) "'\n\f\n")))))
-      (set-buffer obuf))))
+    (with-current-buffer (get-buffer-create dired-log-buffer)
+      (goto-char (point-max))
+      (let ((inhibit-read-only t))
+       (cond ((stringp log)
+              (insert (if args
+                          (apply (function format) log args)
+                        log)))
+             ((bufferp log)
+              (insert-buffer log))
+             ((eq t log)
+              (backward-page 1)
+              (unless (bolp)
+                (insert "\n"))
+              (insert (current-time-string)
+                      "\tBuffer `" (buffer-name obuf) "'\n")
+              (goto-char (point-max))
+              (insert "\f\n")))))))
 
 (defun dired-log-summary (string failures)
-  (message (if failures "%s--type ? for details (%s)"
-            "%s--type ? for details")
-          string failures)
+  (if (= (length failures) 1)
+      (message "%s"
+              (with-current-buffer dired-log-buffer
+                (goto-char (point-max))
+                (backward-page 1)
+                (if (eolp) (forward-line 1))
+                (buffer-substring (point) (point-max))))
+    (message (if failures "%s--type ? for details (%s)"
+              "%s--type ? for details")
+            string failures))
   ;; Log a summary describing a bunch of errors.
-  (dired-log (concat "\n" string))
+  (dired-log (concat "\n" string "\n"))
   (dired-log t))
 \f
 ;;; Sorting
@@ -2652,7 +2813,14 @@ Thus, use \\[backward-page] to find the beginning of a group of errors."
 ;; So anything that does not contain these is sort "by name".
 
 (defvar dired-ls-sorting-switches "SXU"
-  "String of `ls' switches (single letters) except `t' that influence sorting.")
+  "String of `ls' switches \(single letters\) except `t' that influence sorting.
+
+This indicates to Dired which option switches to watch out for because they
+will change the sorting order behavior of `ls'.
+
+To change the default sorting order \(e.g. add a `-v' option\), see the
+variable `dired-listing-switches'.  To temporarily override the listing
+format, use `\\[universal-argument] \\[dired]'.")
 
 (defvar dired-sort-by-date-regexp
   (concat "^-[^" dired-ls-sorting-switches
@@ -2663,6 +2831,10 @@ Thus, use \\[backward-page] to find the beginning of a group of errors."
   (concat "^-[^t" dired-ls-sorting-switches "]+$")
   "Regexp recognized by dired to set `by name' mode.")
 
+(defvar dired-sort-inhibit nil
+  "Non-nil means the Dired sort command is disabled.
+The idea is to set this buffer-locally in special Dired buffers.")
+
 (defun dired-sort-set-modeline ()
   ;; Set modeline display according to dired-actual-switches.
   ;; Modeline display of "by name" or "by date" guarantees the user a
@@ -2682,6 +2854,8 @@ Thus, use \\[backward-page] to find the beginning of a group of errors."
   "Toggle between sort by date/name and refresh the dired buffer.
 With a prefix argument you can edit the current listing switches instead."
   (interactive "P")
+  (when dired-sort-inhibit
+    (error "Cannot sort this Dired buffer"))
   (if arg
       (dired-sort-other
        (read-string "ls switches (must contain -l): " dired-actual-switches))
@@ -2694,7 +2868,7 @@ With a prefix argument you can edit the current listing switches instead."
          (if (string-match " " dired-actual-switches)
              ;; New toggle scheme: add/remove a trailing " -t"
              (if (string-match " -t\\'" dired-actual-switches)
-                 (dired-replace-in-string " -t\\'" "" dired-actual-switches)
+                 (substring dired-actual-switches 0 (match-beginning 0))
                (concat dired-actual-switches " -t"))
            ;; old toggle scheme: look for some 't' switch and add/remove it
            (concat
@@ -2821,18 +2995,33 @@ If no files are marked or a specific numeric prefix arg is given,
 the next ARG files are used.  Just \\[universal-argument] means the current file.
 The prompt mentions the file(s) or the marker, as appropriate.
 
-If there is output, it goes to a separate buffer.
+If there is a `*' in COMMAND, surrounded by whitespace, this runs
+COMMAND just once with the entire file list substituted there.
+
+If there is no `*', but there is a `?' in COMMAND, surrounded by
+whitespace, this runs COMMAND on each file individually with the
+file name substituted for `?'.
+
+Otherwise, this runs COMMAND on each file individually with the
+file name added at the end of COMMAND (separated by a space).
 
-Normally the command is run on each file individually.
-However, if there is a `*' in the command then it is run
-just once with the entire file list substituted there.
+`*' and `?' when not surrounded by whitespace have no special
+significance for `dired-do-shell-command', and are passed through
+normally to the shell, but you must confirm first.  To pass `*' by
+itself to the shell as a wildcard, type `*\"\"'.
 
-No automatic redisplay of dired buffers is attempted, as there's no
-telling what files the command may have changed.  Type
-\\[dired-do-redisplay] to redisplay the marked files.
+If COMMAND produces output, it goes to a separate buffer.
 
-The shell command has the top level directory as working directory, so
-output files usually are created there instead of in a subdir."
+This feature does not try to redisplay Dired buffers afterward, as
+there's no telling what files COMMAND may have changed.
+Type \\[dired-do-redisplay] to redisplay the marked files.
+
+When COMMAND runs, its working directory is the top-level directory of
+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."
   t)
 
 (autoload 'dired-do-kill-lines "dired-aux"
@@ -2896,9 +3085,8 @@ As each match is found, the user must type a character saying
   what to do with it.  For directions, type \\[help-command] at that time.
 NEWNAME may contain \\=\\<n> or \\& as in `query-replace-regexp'.
 REGEXP defaults to the last regexp used.
-With a zero prefix arg, renaming by regexp affects the complete
-  pathname - usually only the non-directory part of file names is used
-  and changed."
+With a zero prefix arg, renaming by regexp affects the full file name;
+usually only the non-directory part of file names is used and changed."
   t)
 
 (autoload 'dired-do-copy-regexp "dired-aux"
@@ -2981,8 +3169,99 @@ Use \\[dired-hide-subdir] to (un)hide a particular subdirectory."
 (autoload 'dired-show-file-type "dired-aux"
   "Print the type of FILE, according to the `file' command.
 If FILE is a symbolic link and the optional argument DEREF-SYMLINKS is
-true then the type of the file linked to by FILE is printed instead." 
+true then the type of the file linked to by FILE is printed instead."
   t)
+
+(autoload 'dired-run-shell-command "dired-aux")
+
+(autoload 'dired-query "dired-aux")
+\f
+
+;;;;  Drag and drop support
+
+(defun dired-dnd-test-function (window action types)
+  "The test function for drag and drop into dired buffers.
+WINDOW is where the mouse is when this function is called.  It may be a frame
+if the mouse is over the menu bar, scroll bar or tool bar.
+ACTION is the suggested action from the source, and TYPES are the
+types the drop data can have.  This function only accepts drops with
+types in `x-dnd-known-types'.  It returns the action suggested by the source."
+  (let ((type (x-dnd-choose-type types)))
+    (if type
+       (cons action type)
+      nil)))
+
+(defun dired-dnd-popup-notice ()
+  (x-popup-dialog 
+   t
+   '("Recursive copies not enabled.\nSee variable dired-recursive-copies." 
+     ("Ok" . nil))))
+
+
+(defun dired-dnd-do-ask-action (uri)
+  ;; No need to get actions and descriptions from the source,
+  ;; we only have three actions anyway.
+  (let ((action (x-popup-menu 
+                t
+                (list "What action?"
+                      (cons ""
+                            '(("Copy here" . copy)
+                              ("Move here" . move)
+                              ("Link here" . link)
+                              "--"
+                              ("Cancel" . nil)))))))
+    (if action
+       (dired-dnd-handle-local-file uri action)
+      nil)))
+
+(defun dired-dnd-handle-local-file (uri action)
+  "Copy, move or link a file to the dired directory.
+URI is the file to handle, ACTION is one of copy, move, link or ask.
+Ask means pop up a menu for the user to select one of copy, move or link."
+  (require 'dired-aux)
+  (let* ((from (x-dnd-get-local-file-name uri t))
+        (to (if from (concat (dired-current-directory)
+                          (file-name-nondirectory from))
+              nil)))
+    (if from
+       (cond ((or (eq action 'copy)
+                  (eq action 'private))        ; Treat private as copy.
+
+              ;; If copying a directory and dired-recursive-copies is nil,
+              ;; dired-copy-file silently fails.  Pop up a notice.
+              (if (and (file-directory-p from)
+                       (not dired-recursive-copies))
+                  (dired-dnd-popup-notice)
+                (progn
+                  (dired-copy-file from to 1)
+                  (dired-relist-entry to)
+                  action)))
+
+              ((eq action 'move)
+               (dired-rename-file from to 1)
+               (dired-relist-entry to)
+               action)
+
+              ((eq action 'link)
+               (make-symbolic-link from to 1)
+               (dired-relist-entry to)
+               action)
+
+              ((eq action 'ask)
+               (dired-dnd-do-ask-action uri))
+
+              (t nil)))))
+
+(defun dired-dnd-handle-file (uri action)
+  "Copy, move or link a file to the dired directory if it is a local file.
+URI is the file to handle.  If the hostname in the URI isn't local, do nothing.
+ACTION is one of copy, move, link or ask.
+Ask means pop up a menu for the user to select one of copy, move or link."
+  (let ((local-file (x-dnd-get-local-file-uri uri)))
+    (if local-file (dired-dnd-handle-local-file local-file action)
+      nil)))
+
+
 \f
 (if (eq system-type 'vax-vms)
     (load "dired-vms"))
@@ -2991,4 +3270,5 @@ true then the type of the file linked to by FILE is printed instead."
 
 (run-hooks 'dired-load-hook)           ; for your customizations
 
+;;; arch-tag: e1af7a8f-691c-41a0-aac1-ddd4d3c87517
 ;;; dired.el ends here