]> code.delx.au - gnu-emacs/blobdiff - lisp/dired.el
*** empty log message ***
[gnu-emacs] / lisp / dired.el
index c3511baea475e498cc638a26016be1ef58bd9d3a..037bf282eda5b30a329784b7f9230744cd7adea9 100644 (file)
@@ -1,7 +1,7 @@
 ;;; dired.el --- directory-browsing commands
 
-;; Copyright (C) 1985, 86, 92, 93, 94, 95, 96, 1997, 2000, 2001, 2003
-;;  Free Software Foundation, Inc.
+;; Copyright (C) 1985, 1986, 1992, 1993, 1994, 1995, 1996, 1997, 2000,
+;;   2001, 2003, 2004  Free Software Foundation, Inc.
 
 ;; Author: Sebastian Kremer <sk@thp.uni-koeln.de>
 ;; Maintainer: FSF
@@ -39,6 +39,7 @@
 
 (defgroup dired nil
   "Directory editing."
+  :link '(custom-manual "(emacs)Dired")
   :group 'files)
 
 (defgroup dired-mark nil
@@ -59,6 +60,10 @@ some of the `ls' switches are not supported; see the doc string of
   :type 'string
   :group 'dired)
 
+(defvar dired-subdir-switches nil
+  "If non-nil, switches passed to `ls' for inserting subdirectories.
+If nil, `dired-listing-switches' is used.")
+
 ; 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.
@@ -78,6 +83,9 @@ some of the `ls' switches are not supported; see the doc string of
 (defvar dired-chmod-program "chmod"
   "Name of chmod command (usually `chmod').")
 
+(defvar dired-touch-program "touch"
+  "Name of touch command (usually `touch').")
+
 ;;;###autoload
 (defcustom dired-ls-F-marks-symlinks nil
   "*Informs dired about how `ls -lF' marks symbolic links.
@@ -192,11 +200,13 @@ 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.
 
+;; 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"))
+  '(("\\.\\(ps\\|ps_pages\\|eps\\)\\'" . "gv %s")
+    ("\\.pdf\\'" . "xpdf %s")
+    ;; ("\\.pod\\'" . "perldoc %s")
+    ("\\.\\(jpe?g\\|gif\\|png\\)\\'" . "eog %s")
+    ("\\.dvi\\'" . "xdvi %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'
@@ -269,13 +279,18 @@ The directory name must be absolute, but need not be fully expanded.")
 (defvar dired-re-perms "[-bcdlps][-r][-w].[-r][-w].[-r][-w].")
 (defvar dired-re-dot "^.* \\.\\.?/?$")
 
-;; The subdirectory names in this list are expanded.
+;; The subdirectory names in the next two lists are expanded.
 (defvar dired-subdir-alist nil
   "Association list of subdirectories and their buffer positions.
 Each subdirectory has an element: (DIRNAME . STARTMARKER).
 The order of elements is the reverse of the order in the buffer.
 In simple cases, this list contains one element.")
 
+(defvar dired-switches-alist nil
+  "Keeps track of which switches to use for inserted subdirectories.
+This is an alist of the form (SUBDIR . SWITCHES).")
+(make-variable-buffer-local 'dired-switches-alist)
+
 (defvar dired-subdir-regexp "^. \\([^\n\r]+\\)\\(:\\)[\n\r]"
   "Regexp matching a maybe hidden subdirectory line in `ls -lR' output.
 Subexpression 1 is the subdirectory proper, no trailing colon.
@@ -283,21 +298,96 @@ The match starts at the beginning of the line and ends after the end
 of the line (\\n or \\r).
 Subexpression 2 must end right before the \\n or \\r.")
 
+(defgroup dired-faces nil
+  "Faces used by dired."
+  :group 'dired
+  :group 'faces)
+
+(defface dired-header
+  '((t (:inherit font-lock-type-face)))
+  "Face used for directory headers."
+  :group 'dired-faces
+  :version "21.4")
+(defvar dired-header-face 'dired-header
+  "Face name used for directory headers.")
+
+(defface dired-mark
+  '((t (:inherit font-lock-constant-face)))
+  "Face used for dired marks."
+  :group 'dired-faces
+  :version "21.4")
+(defvar dired-mark-face 'dired-mark
+  "Face name used for dired marks.")
+
+(defface dired-marked
+  '((t (:inherit font-lock-warning-face)))
+  "Face used for marked files."
+  :group 'dired-faces
+  :version "21.4")
+(defvar dired-marked-face 'dired-marked
+  "Face name used for marked files.")
+
+(defface dired-flagged
+  '((t (:inherit font-lock-warning-face)))
+  "Face used for flagged files."
+  :group 'dired-faces
+  :version "21.4")
+(defvar dired-flagged-face 'dired-flagged
+  "Face name used for flagged files.")
+
+(defface dired-warning
+  '((t (:inherit font-lock-comment-face)))
+  "Face used to highlight a part of a buffer that needs user attention."
+  :group 'dired-faces
+  :version "21.4")
+(defvar dired-warning-face 'dired-warning
+  "Face name used for a part of a buffer that needs user attention.")
+
+(defface dired-directory
+  '((t (:inherit font-lock-function-name-face)))
+  "Face used for subdirectories."
+  :group 'dired-faces
+  :version "21.4")
+(defvar dired-directory-face 'dired-directory
+  "Face name used for subdirectories.")
+
+(defface dired-symlink
+  '((t (:inherit font-lock-keyword-face)))
+  "Face used for symbolic links."
+  :group 'dired-faces
+  :version "21.4")
+(defvar dired-symlink-face 'dired-symlink
+  "Face name used for symbolic links.")
+
+(defface dired-ignored
+  '((t (:inherit font-lock-string-face)))
+  "Face used for files suffixed with `completion-ignored-extensions'."
+  :group 'dired-faces
+  :version "21.4")
+(defvar dired-ignored-face 'dired-ignored
+  "Face name used for files suffixed with `completion-ignored-extensions'.")
+
 (defvar dired-font-lock-keywords
   (list
    ;;
    ;; Directory headers.
-   (list dired-subdir-regexp '(1 font-lock-type-face))
+   (list dired-subdir-regexp '(1 dired-header-face))
+   ;;
+   ;; Dired marks.
+   (list dired-re-mark '(0 dired-mark-face))
    ;;
    ;; We make heavy use of MATCH-ANCHORED, since the regexps don't identify the
    ;; file name itself.  We search for Dired defined regexps, and then use the
    ;; Dired defined function `dired-move-to-filename' before searching for the
    ;; simple regexp ".+".  It is that regexp which matches the file name.
    ;;
-   ;; Dired marks.
-   (list dired-re-mark
-        '(0 font-lock-constant-face)
-        '(".+" (dired-move-to-filename) nil (0 font-lock-warning-face)))
+   ;; Marked files.
+   (list (concat "^[" (char-to-string dired-marker-char) "]")
+         '(".+" (dired-move-to-filename) nil (0 dired-marked-face)))
+   ;;
+   ;; Flagged files.
+   (list (concat "^[" (char-to-string dired-del-marker) "]")
+         '(".+" (dired-move-to-filename) nil (0 dired-flagged-face)))
    ;; People who are paranoid about security would consider this more
    ;; important than other things such as whether it is a directory.
    ;; But we don't want to encourage paranoia, so our default
@@ -306,33 +396,43 @@ Subexpression 2 must end right before the \\n or \\r.")
 ;;;   ;; Files that are group or world writable.
 ;;;   (list (concat dired-re-maybe-mark dired-re-inode-size
 ;;;             "\\([-d]\\(....w....\\|.......w.\\)\\)")
-;;;     '(1 font-lock-comment-face)
-;;;     '(".+" (dired-move-to-filename) nil (0 font-lock-comment-face)))
+;;;     '(1 dired-warning-face)
+;;;     '(".+" (dired-move-to-filename) nil (0 dired-warning-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\\)....") ; group writable
+        '(1 dired-warning-face))
+   (list (concat dired-re-maybe-mark dired-re-inode-size
+                "[-d].......\\(w\\).") ; world writable
+        '(1 dired-warning-face))
    ;;
    ;; Subdirectories.
    (list dired-re-dir
-        '(".+" (dired-move-to-filename) nil (0 font-lock-function-name-face)))
+        '(".+" (dired-move-to-filename) nil (0 dired-directory-face)))
    ;;
    ;; Symbolic links.
    (list dired-re-sym
-        '(".+" (dired-move-to-filename) nil (0 font-lock-keyword-face)))
+        '(".+" (dired-move-to-filename) nil (0 dired-symlink-face)))
    ;;
    ;; Files suffixed with `completion-ignored-extensions'.
    '(eval .
      ;; 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)))))
+          '(".+" (dired-move-to-filename) nil (0 dired-ignored-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)
@@ -446,8 +546,14 @@ Optional third argument FILTER, if non-nil, is a function to select
            (if current-prefix-arg
                (read-string "Dired listing switches: "
                             dired-listing-switches))
-           (read-file-name (format "Dired %s(directory): " str)
-                           nil default-directory nil))))
+           ;; If a dialog is about to be used, call read-directory-name so
+           ;; the dialog code knows we want directories.  Some dialogs can
+           ;; only select directories or files when popped up, not both.
+           (if (next-read-file-uses-dialog-p)
+               (read-directory-name (format "Dired %s(directory): " str)
+                                    nil default-directory nil)
+             (read-file-name (format "Dired %s(directory): " str)
+                             nil default-directory nil)))))
 
 ;;;###autoload (define-key ctl-x-map "d" 'dired)
 ;;;###autoload
@@ -513,12 +619,33 @@ 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))
+            (equal (nth 5 attributes) 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.
@@ -544,20 +671,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
               (setq dired-directory dir-or-list)
               ;; this calls dired-revert
               (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")))))
@@ -618,10 +739,12 @@ 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 ()
+  "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.
   (let (dirname)
@@ -634,7 +757,6 @@ If DIRNAME is already in a dired buffer, that buffer is used without refresh."
       ;; 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)
-      (message "Reading directory %s..." dirname)
       (if (consp buffer-undo-list)
          (setq buffer-undo-list nil))
       (let (buffer-read-only
@@ -643,7 +765,6 @@ If DIRNAME is already in a dired buffer, that buffer is used without refresh."
        (widen)
        (erase-buffer)
        (dired-readin-insert))
-      (message "Reading directory %s...done" dirname)
       (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
@@ -683,6 +804,112 @@ If DIRNAME is already in a dired buffer, that buffer is used without refresh."
        (dired-insert-directory dir dired-actual-switches
                                file-list (not file-list) t)))))
 
+(defun dired-align-file (beg end)
+  "Align the fields of a file to the ones of surrounding lines.
+BEG..END is the line where the file info is located."
+  ;; Some versions of ls try to adjust the size of each field so as to just
+  ;; hold the largest element ("largest" in the current invocation, of
+  ;; course).  So when a single line is output, the size of each field is
+  ;; just big enough for that one output.  Thus when dired refreshes one
+  ;; line, the alignment if this line w.r.t the rest is messed up because
+  ;; the fields of that one line will generally be smaller.
+  ;;
+  ;; To work around this problem, we here add spaces to try and re-align the
+  ;; fields as needed.  Since this is purely aesthetic, it is of utmost
+  ;; importance that it doesn't mess up anything like
+  ;; `dired-move-to-filename'.  To this end, we limit ourselves to adding
+  ;; spaces only, and to only add them at places where there was already at
+  ;; least one space.  This way, as long as `dired-move-to-filename-regexp'
+  ;; always matches spaces with "*" or "+", we know we haven't made anything
+  ;; worse.  There is one spot where the exact number of spaces is
+  ;; important, which is just before the actual filename, so we refrain from
+  ;; adding spaces there (and within the filename as well, of course).
+  (save-excursion
+    (let (file file-col other other-col)
+      ;; Check the there is indeed a file, and that there is anoter adjacent
+      ;; file with which to align, and that additional spaces are needed to
+      ;; align the filenames.
+      (when (and (setq file (progn (goto-char beg)
+                                  (dired-move-to-filename nil end)))
+                (setq file-col (current-column))
+                (setq other
+                      (or (and (goto-char beg)
+                               (zerop (forward-line -1))
+                               (dired-move-to-filename))
+                          (and (goto-char beg)
+                               (zerop (forward-line 1))
+                               (dired-move-to-filename))))
+                (setq other-col (current-column))
+                (/= file other)
+                ;; Make sure there is some work left to do.
+                (> other-col file-col))
+       ;; If we've only looked at the line above, check to see if the line
+       ;; below exists as well and if so, align with the shorter one.
+       (when (and (< other file)
+                  (goto-char beg)
+                  (zerop (forward-line 1))
+                  (dired-move-to-filename))
+         (let ((alt-col (current-column)))
+           (when (< alt-col other-col)
+             (setq other-col alt-col)
+             (setq other (point)))))
+       ;; Keep positions uptodate when we insert stuff.
+       (if (> other file) (setq other (copy-marker other)))
+       (setq file (copy-marker file))
+       ;; Main loop.
+       (goto-char beg)
+       (skip-chars-forward " ")        ;Skip to the first field.
+       (while (and (> other-col file-col)
+                   ;; Don't touch anything just before (and after) the
+                   ;; beginning of the filename.
+                   (> file (point)))
+         ;; We're now just in front of a field, with a space behind us.
+         (let* ((curcol (current-column))
+                ;; Nums are right-aligned.
+                (num-align (looking-at "[0-9]"))
+                ;; Let's look at the other line, in the same column: we
+                ;; should be either near the end of the previous field, or
+                ;; in the space between that field and the next.
+                ;; [ Of course, it's also possible that we're already within
+                ;; the next field or even past it, but that's unlikely since
+                ;; other-col > file-col. ]
+                ;; Let's find the distance to the alignment-point (either
+                ;; the beginning or the end of the next field, depending on
+                ;; whether this field is left or right aligned).
+                (align-pt-offset
+                 (save-excursion
+                   (goto-char other)
+                   (move-to-column curcol)
+                   (when (looking-at
+                          (concat
+                           (if (eq (char-before) ?\ ) " *" "[^ ]* *")
+                           (if num-align "[0-9][^ ]*")))
+                     (- (match-end 0) (match-beginning 0)))))
+                ;; Now, the number of spaces to insert is align-pt-offset
+                ;; minus the distance to the equivalent point on the
+                ;; current line.
+                (spaces
+                 (if (not num-align)
+                     align-pt-offset
+                   (and align-pt-offset
+                        (save-excursion
+                          (skip-chars-forward "^ ")
+                          (- align-pt-offset (- (current-column) curcol)))))))
+           (when (and spaces (> spaces 0))
+             (setq file-col (+ spaces file-col))
+             (if (> file-col other-col)
+                 (setq spaces (- spaces (- file-col other-col))))
+             (insert-char ?\s spaces)
+             ;; Let's just make really sure we did not mess up.
+             (unless (save-excursion
+                       (eq (dired-move-to-filename) (marker-position file)))
+               ;; Damn!  We messed up: let's revert the change.
+               (delete-char (- spaces)))))
+         ;; Now skip to next field.
+         (skip-chars-forward "^ ") (skip-chars-forward " "))
+       (set-marker file nil)))))
+
+
 (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.
@@ -701,7 +928,10 @@ If HDR is non-nil, insert a header line with the directory name."
     ;; with the new value of dired-move-to-filename-regexp.
     (if file-list
        (dolist (f file-list)
-         (insert-directory f switches nil nil))
+         (let ((beg (point)))
+           (insert-directory f switches nil nil)
+           ;; Re-align fields, if necessary.
+           (dired-align-file beg (point))))
       (insert-directory dir switches wildcard (not wildcard)))
     ;; Quote certain characters, unless ls quoted them for us.
     (if (not (string-match "b" dired-actual-switches))
@@ -740,8 +970,8 @@ If HDR is non-nil, insert a header line with the directory name."
        ;; 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)
@@ -760,12 +990,13 @@ If HDR is non-nil, insert a header line with the directory name."
 ;; 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))
+  (let ((modflag (buffer-modified-p))
+       (opoint (point))
        (ofile (dired-get-filename nil t))
        (mark-alist nil)                ; save marked files
        (hidden-subdirs (dired-remember-hidden))
@@ -790,12 +1021,12 @@ If HDR is non-nil, insert a header line with the directory name."
        (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))))
+    (unless modflag (restore-buffer-modified-p nil)))
   ;; outside of the let scope
-;;; Might as well not override the user if the user changed this.
+;;;  Might as well not override the user if the user changed this.
 ;;;  (setq buffer-read-only t)
   )
 
@@ -803,7 +1034,7 @@ If HDR is non-nil, insert a header line with the directory name."
 ;; 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)))
@@ -816,9 +1047,9 @@ If HDR is non-nil, insert a header line with the directory name."
                  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)
@@ -831,8 +1062,8 @@ If HDR is non-nil, insert a header line with the directory name."
            (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))
@@ -844,9 +1075,9 @@ If HDR is non-nil, insert a header line with the directory name."
          (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
@@ -859,23 +1090,21 @@ If HDR is non-nil, insert a header line with the directory name."
                (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)
+    (define-key map [follow-link] 'mouse-face)
     ;; Commands to mark or flag certain categories of files
     (define-key map "#" 'dired-flag-auto-save-files)
     (define-key map "." 'dired-clean-directory)
@@ -895,6 +1124,7 @@ If HDR is non-nil, insert a header line with the directory name."
     (define-key map "Q" 'dired-do-query-replace-regexp)
     (define-key map "R" 'dired-do-rename)
     (define-key map "S" 'dired-do-symlink)
+    (define-key map "T" 'dired-do-touch)
     (define-key map "X" 'dired-do-shell-command)
     (define-key map "Z" 'dired-do-compress)
     (define-key map "!" 'dired-do-shell-command)
@@ -937,6 +1167,7 @@ If HDR is non-nil, insert a header line with the directory name."
     (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)
@@ -1056,6 +1287,8 @@ If HDR is non-nil, insert a header line with the directory name."
                  :help "Edit file at cursor"))
     (define-key map [menu-bar immediate create-directory]
       '(menu-item "Create Directory..." dired-create-directory))
+    (define-key map [menu-bar immediate wdired-mode]
+      '(menu-item "Edit File Names" wdired-change-to-wdired-mode))
 
     (define-key map [menu-bar regexp]
       (cons "Regexp" (make-sparse-keymap "Regexp")))
@@ -1164,6 +1397,9 @@ If HDR is non-nil, insert a header line with the directory name."
     (define-key map [menu-bar operate chmod]
       '(menu-item "Change Mode..." dired-do-chmod
                  :help "Change mode (attributes) of marked files"))
+    (define-key map [menu-bar operate touch]
+      '(menu-item "Change Timestamp..." dired-do-touch
+                 :help "Change timestamp of marked files"))
     (define-key map [menu-bar operate load]
       '(menu-item "Load" dired-do-load
                  :help "Load marked Emacs Lisp files"))
@@ -1196,7 +1432,8 @@ If HDR is non-nil, insert a header line with the directory name."
       '(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)
@@ -1246,22 +1483,22 @@ again for the directory tree.
 Customization variables (rename this buffer and type \\[describe-variable] on each line
 for more info):
 
-  dired-listing-switches
-  dired-trivial-filenames
-  dired-shrink-to-fit
-  dired-marker-char
-  dired-del-marker
-  dired-keep-marker-rename
-  dired-keep-marker-copy
-  dired-keep-marker-hardlink
-  dired-keep-marker-symlink
+  `dired-listing-switches'
+  `dired-trivial-filenames'
+  `dired-shrink-to-fit'
+  `dired-marker-char'
+  `dired-del-marker'
+  `dired-keep-marker-rename'
+  `dired-keep-marker-copy'
+  `dired-keep-marker-hardlink'
+  `dired-keep-marker-symlink'
 
 Hooks (use \\[describe-variable] to see their documentation):
 
-  dired-before-readin-hook
-  dired-after-readin-hook
-  dired-mode-hook
-  dired-load-hook
+  `dired-before-readin-hook'
+  `dired-after-readin-hook'
+  `dired-mode-hook'
+  `dired-load-hook'
 
 Keybindings:
 \\{dired-mode-map}"
@@ -1272,13 +1509,15 @@ Keybindings:
   (dired-advertise)                    ; default-directory is already set
   (setq major-mode 'dired-mode
        mode-name "Dired"
-;;     case-fold-search nil
+       ;; case-fold-search nil
        buffer-read-only t
        selective-display t             ; for subdirectory hiding
        mode-line-buffer-identification
        (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)
@@ -1290,9 +1529,13 @@ Keybindings:
                           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))
+  (set (make-local-variable 'desktop-save-buffer)
+       'dired-desktop-buffer-misc-data)
+  (setq dired-switches-alist nil)
   (dired-sort-other dired-actual-switches t)
-  (run-hooks 'dired-mode-hook)
+  (run-mode-hooks 'dired-mode-hook)
   (when (featurep 'x-dnd)
     (make-variable-buffer-local 'x-dnd-test-function)
     (make-variable-buffer-local 'x-dnd-protocol-alist)
@@ -1316,14 +1559,13 @@ Keybindings:
 (defun dired-undo ()
   "Undo in a dired buffer.
 This doesn't recover lost files, it just undoes changes in the buffer itself.
-You can use it to recover marks, killed lines or subdirs.
-In the latter case, you have to do \\[dired-build-subdir-alist] to
-parse the buffer again."
+You can use it to recover marks, killed lines or subdirs."
   (interactive)
   (let (buffer-read-only)
-    (undo)
-    (message "Change in Dired buffer undone.
-Actual changes in files cannot be undone by Emacs.")))
+    (undo))
+  (dired-build-subdir-alist)
+  (message "Change in Dired buffer undone.
+Actual changes in files cannot be undone by Emacs."))
 
 (defun dired-next-line (arg)
   "Move down lines then position at filename.
@@ -1519,9 +1761,7 @@ Otherwise, an error occurs in these cases."
      ((eq localp 'verbatim)
       file)
      ((and (not no-error-if-not-filep)
-          (save-excursion
-            (beginning-of-line)
-            (looking-at dired-re-dot)))
+          (member file '("." "..")))
       (error "Cannot operate on `.' or `..'"))
      ((and (eq localp 'no-dir) already-absolute)
       (file-name-nondirectory file))
@@ -1585,7 +1825,7 @@ DIR must be a directory name, not a file name."
       (setq dir (expand-file-name dir)))
   (if (string-match (concat "^" (regexp-quote dir)) file)
       (substring file (match-end 0))
-;;; (or no-error
+;;;  (or no-error
 ;;;    (error "%s: not in directory tree growing at %s" file dir))
     file))
 \f
@@ -1601,7 +1841,7 @@ DIR must be a directory name, not a file name."
         (s " ")
         (yyyy "[0-9][0-9][0-9][0-9]")
         (dd "[ 0-3][0-9]")
-        (HH:MM "[ 0-2][0-9]:[0-5][0-9]")
+        (HH:MM "[ 0-2][0-9][:.][0-5][0-9]")
         (seconds "[0-6][0-9]\\([.,][0-9]+\\)?")
         (zone "[-+][0-2][0-9][0-5][0-9]")
         (iso-mm-dd "[01][0-9]-[0-3][0-9]")
@@ -1639,6 +1879,8 @@ regardless of the language.")
 ;; Move to first char of filename on this line.
 ;; Returns position (point) or nil if no filename on this line."
 (defun dired-move-to-filename (&optional raise-error eol)
+  "Move to the beginning of the filename on the current line.
+Return the position of the beginning of the filename, or nil if none found."
   ;; This is the UNIX version.
   (or eol (setq eol (line-end-position)))
   (beginning-of-line)
@@ -1651,8 +1893,10 @@ regardless of the language.")
       (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"))
+      (if raise-error
+         (error "Unrecognized line!  Check dired-move-to-filename-regexp"))
+      (beginning-of-line)
+      nil)
      (raise-error
       (error "No file on this line")))))
 
@@ -1689,18 +1933,15 @@ regardless of the language.")
                                (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))))))
+                                 (match-string 2)
+                                 (match-string 3)
+                                 (match-string 4)))))
            (or no-error (error "No file on this line"))))
        ;; Move point to end of name:
        (if symlink
-           (if (search-forward " ->" eol t)
+           (if (search-forward " -> " eol t)
                (progn
-                 (forward-char -3)
+                 (forward-char -4)
                  (and used-F
                       dired-ls-F-marks-symlinks
                       (eq (preceding-char) ?@) ;; did ls really mark the link?
@@ -1765,7 +2006,7 @@ You can then feed the file name(s) to other commands with \\[yank]."
 ;; As a side effect, killed dired buffers for DIR are removed from
 ;; dired-buffers.
   (setq dir (file-name-as-directory dir))
-  (let ((alist dired-buffers) result elt buf pattern)
+  (let ((alist dired-buffers) result elt buf)
     (while alist
       (setq elt (car alist)
            buf (cdr elt))
@@ -1963,7 +2204,7 @@ instead of `dired-actual-switches'."
                             (goto-char (match-beginning 0))
                             (beginning-of-line)
                             (point-marker))))
-      (if (> count 1)
+      (if (and (> count 1) (interactive-p))
          (message "Buffer includes %d directories" count))
       ;; We don't need to sort it because it is in buffer order per
       ;; constructionem.  Return new alist:
@@ -2307,8 +2548,8 @@ if there are no flagged files."
 (defvar dired-no-confirm nil
   "A list of symbols for commands dired should not confirm.
 Command symbols are `byte-compile', `chgrp', `chmod', `chown', `compress',
-`copy', `delete', `hardlink', `load', `move', `print', `shell', `symlink' and
-`uncompress'.")
+`copy', `delete', `hardlink', `load', `move', `print', `shell', `symlink',
+`touch' and `uncompress'.")
 
 (defun dired-mark-pop-up (bufname op-symbol files function &rest args)
   "Return FUNCTION's result on ARGS after showing which files are marked.
@@ -2617,11 +2858,15 @@ A prefix argument says to unflag those files instead."
                    (file-name-nondirectory fn)))))
      "auto save file")))
 
-(defvar dired-garbage-files-regexp
+(defcustom dired-garbage-files-regexp
+  ;; `log' here is dubious, since 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'.")
+  "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'."
@@ -2777,7 +3022,7 @@ 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'.
@@ -2862,10 +3107,10 @@ With a prefix argument you can edit the current listing switches instead."
     (concat result (substring string start))))
 
 (defun dired-sort-other (switches &optional no-revert)
-  ;; Specify new ls SWITCHES for current dired buffer.  Values matching
-  ;; `dired-sort-by-date-regexp' or `dired-sort-by-name-regexp' set the
-  ;; minor mode accordingly, others appear literally in the mode line.
-  ;; With optional second arg NO-REVERT, don't refresh the listing afterwards.
+  "Specify new ls SWITCHES for current dired buffer.
+Values matching `dired-sort-by-date-regexp' or `dired-sort-by-name-regexp'
+set the minor mode accordingly, others appear literally in the mode line.
+With optional second arg NO-REVERT, don't refresh the listing afterwards."
   (dired-sort-R-check switches)
   (setq dired-actual-switches switches)
   (if (eq major-mode 'dired-mode) (dired-sort-set-modeline))
@@ -2906,243 +3151,22 @@ To be called first in body of `dired-sort-other', etc."
            ;; listing:
            (list (car (reverse dired-subdir-alist))))))))
 \f
-;; To make this file smaller, the less common commands
-;; go in a separate file.  But autoload them here
-;; to make the separation invisible.
-
-(autoload 'dired-diff "dired-aux"
-  "Compare file at point with file FILE using `diff'.
-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'."
-  t)
-
-(autoload 'dired-backup-diff "dired-aux"
-  "Diff this file with its backup file or vice versa.
-Uses the latest backup, if there are several numerical backups.
-If this file is a backup, diff it with its original.
-The backup file is the first file given to `diff'."
-  t)
-
-(autoload 'dired-clean-directory "dired-aux"
-  "Flag numerical backups for deletion.
-Spares `dired-kept-versions' latest versions, and `kept-old-versions' oldest.
-Positive prefix arg KEEP overrides `dired-kept-versions';
-Negative prefix arg KEEP overrides `kept-old-versions' with KEEP made positive.
-
-To clear the flags on these files, you can use \\[dired-flag-backup-files]
-with a prefix argument."
-  t)
-
-(autoload 'dired-do-chmod "dired-aux"
-  "Change the mode of the marked (or next ARG) files.
-This calls chmod, thus symbolic modes like `g+w' are allowed."
-  t)
-
-(autoload 'dired-do-chgrp "dired-aux"
-  "Change the group of the marked (or next ARG) files."
-  t)
-
-(autoload 'dired-do-chown "dired-aux"
-  "Change the owner of the marked (or next ARG) files."
-  t)
-
-(autoload 'dired-do-print "dired-aux"
-  "Print the marked (or next ARG) files.
-Uses the shell command coming from variables `lpr-command' and
-`lpr-switches' as default."
-  t)
-
-(autoload 'dired-do-shell-command "dired-aux"
-  "Run a shell command COMMAND on the marked files.
-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 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).
-
-`*' 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 `*\"\"'.
-
-If COMMAND produces output, it goes to a separate buffer.
-
-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"
-  "Kill all marked lines (not the files).
-With a prefix arg, kill all lines not marked or flagged."
-  t)
-
-(autoload 'dired-do-compress "dired-aux"
-  "Compress or uncompress marked (or next ARG) files."
-  t)
-
-(autoload 'dired-do-byte-compile "dired-aux"
-  "Byte compile marked (or next ARG) Emacs Lisp files."
-  t)
-
-(autoload 'dired-do-load "dired-aux"
-  "Load the marked (or next ARG) Emacs Lisp files."
-  t)
-
-(autoload 'dired-do-redisplay "dired-aux"
-  "Redisplay all marked (or next ARG) files.
-If on a subdir line, redisplay that subdirectory.  In that case,
-a prefix arg lets you edit the `ls' switches used for the new listing."
-  t)
-
-(autoload 'dired-create-directory "dired-aux"
-  "Create a directory called DIRECTORY."
-  t)
-
-(autoload 'dired-do-copy "dired-aux"
-  "Copy all marked (or next ARG) files, or copy the current file.
-Thus, a zero prefix argument copies nothing.  But it toggles the
-variable `dired-copy-preserve-time' (which see)."
-  t)
-
-(autoload 'dired-do-symlink "dired-aux"
-  "Make symbolic links to current file or all marked (or next ARG) files.
-When operating on just the current file, you specify the new name.
-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."
-  t)
-
-(autoload 'dired-do-hardlink "dired-aux"
-  "Add names (hard links) current file or all marked (or next ARG) files.
-When operating on just the current file, you specify the new name.
-When operating on multiple or marked files, you specify a directory
-and new hard links are made in that directory
-with the same names that the files currently have."
-  t)
-
-(autoload 'dired-do-rename "dired-aux"
-  "Rename current file or all marked (or next ARG) files.
-When renaming just the current file, you specify the new name.
-When renaming multiple or marked files, you specify a directory."
-  t)
-
-(autoload 'dired-do-rename-regexp "dired-aux"
-  "Rename marked files containing REGEXP to NEWNAME.
-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 full file name;
-usually only the non-directory part of file names is used and changed."
-  t)
-
-(autoload 'dired-do-copy-regexp "dired-aux"
-  "Copy all marked files containing REGEXP to NEWNAME.
-See function `dired-do-rename-regexp' for more info."
-  t)
-
-(autoload 'dired-do-hardlink-regexp "dired-aux"
-  "Hardlink all marked files containing REGEXP to NEWNAME.
-See function `dired-do-rename-regexp' for more info."
-  t)
-
-(autoload 'dired-do-symlink-regexp "dired-aux"
-  "Symlink all marked files containing REGEXP to NEWNAME.
-See function `dired-do-rename-regexp' for more info."
-  t)
-
-(autoload 'dired-upcase "dired-aux"
-  "Rename all marked (or next ARG) files to upper case."
-  t)
-
-(autoload 'dired-downcase "dired-aux"
-  "Rename all marked (or next ARG) files to lower case."
-  t)
-
-(autoload 'dired-maybe-insert-subdir "dired-aux"
-  "Insert this subdirectory into the same dired buffer.
-If it is already present, just move to it (type \\[dired-do-redisplay] to refresh),
-  else inserts it at its natural place (as `ls -lR' would have done).
-With a prefix arg, you may edit the ls switches used for this listing.
-  You can add `R' to the switches to expand the whole tree starting at
-  this subdirectory.
-This function takes some pains to conform to `ls -lR' output."
-  t)
-
-(autoload 'dired-next-subdir "dired-aux"
-  "Go to next subdirectory, regardless of level."
-  t)
-
-(autoload 'dired-prev-subdir "dired-aux"
-  "Go to previous subdirectory, regardless of level.
-When called interactively and not on a subdir line, go to this subdir's line."
-  t)
-
-(autoload 'dired-goto-subdir "dired-aux"
-  "Go to end of header line of DIR in this dired buffer.
-Return value of point on success, otherwise return nil.
-The next char is either \\n, or \\r if DIR is hidden."
-  t)
-
-(autoload 'dired-mark-subdir-files "dired-aux"
-  "Mark all files except `.' and `..'."
-  t)
-
-(autoload 'dired-kill-subdir "dired-aux"
-  "Remove all lines of current subdirectory.
-Lower levels are unaffected."
-  t)
-
-(autoload 'dired-tree-up "dired-aux"
-  "Go up ARG levels in the dired tree."
-  t)
-
-(autoload 'dired-tree-down "dired-aux"
-  "Go down in the dired tree."
-  t)
-
-(autoload 'dired-hide-subdir "dired-aux"
-  "Hide or unhide the current subdirectory and move to next directory.
-Optional prefix arg is a repeat factor.
-Use \\[dired-hide-all] to (un)hide all directories."
-  t)
-
-(autoload 'dired-hide-all "dired-aux"
-  "Hide all subdirectories, leaving only their header lines.
-If there is already something hidden, make everything visible again.
-Use \\[dired-hide-subdir] to (un)hide a particular subdirectory."
-  t)
-
-(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."
-  t)
-
-(autoload 'dired-run-shell-command "dired-aux")
-
-(autoload 'dired-query "dired-aux")
-\f
 
 ;;;;  Drag and drop support
 
+(defcustom dired-recursive-copies nil
+  "*Decide whether recursive copies are allowed.
+nil means no recursive copies.
+`always' means copy recursively without asking.
+`top' means ask for each directory at top level.
+Anything else means ask for each directory."
+  :type '(choice :tag "Copy directories"
+                (const :tag "No recursive copies" nil)
+                (const :tag "Ask for each directory" t)
+                (const :tag "Ask for each top directory only" top)
+                (const :tag "Copy directories without asking" always))
+  :group 'dired)
+
 (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
@@ -3156,16 +3180,16 @@ types in `x-dnd-known-types'.  It returns the action suggested by the source."
       nil)))
 
 (defun dired-dnd-popup-notice ()
-  (x-popup-dialog 
+  (x-popup-dialog
    t
-   '("Recursive copies not enabled.\nSee variable dired-recursive-copies." 
+   '("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 
+  (let ((action (x-popup-menu
                 t
                 (list "What action?"
                       (cons ""
@@ -3224,7 +3248,49 @@ 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
+
+;;;;  Desktop support
+
+(eval-when-compile (require 'desktop))
+
+(defun dired-desktop-buffer-misc-data (desktop-dirname)
+  "Auxiliary information to be saved in desktop file."
+  (cons
+   ;; Value of `dired-directory'.
+   (if (consp dired-directory)
+       ;; Directory name followed by list of files.
+       (cons (desktop-file-name (car dired-directory) desktop-dirname)
+             (cdr dired-directory))
+     ;; Directory name, optionally with with shell wildcard.
+     (desktop-file-name dired-directory desktop-dirname))
+   ;; Subdirectories in `dired-subdir-alist'.
+   (cdr
+     (nreverse
+       (mapcar
+         (function (lambda (f) (desktop-file-name (car f) desktop-dirname)))
+         dired-subdir-alist)))))
 
+;;;###autoload
+(defun dired-restore-desktop-buffer (desktop-buffer-file-name
+                                     desktop-buffer-name
+                                     desktop-buffer-misc)
+  "Restore a dired buffer specified in a desktop file."
+  ;; First element of `desktop-buffer-misc' is the value of `dired-directory'.
+  ;; This value is a directory name, optionally with with shell wildcard or
+  ;; a directory name followed by list of files.
+  (let* ((dired-dir (car desktop-buffer-misc))
+         (dir (if (consp dired-dir) (car dired-dir) dired-dir)))
+    (if (file-directory-p (file-name-directory dir))
+        (progn
+          (dired dired-dir)
+          ;; The following elements of `desktop-buffer-misc' are the keys
+          ;; from `dired-subdir-alist'.
+          (mapcar 'dired-maybe-insert-subdir (cdr desktop-buffer-misc))
+          (current-buffer))
+      (message "Desktop: Directory %s no longer exists." dir)
+      (when desktop-missing-file-warning (sit-for 1))
+      nil)))
 
 \f
 (if (eq system-type 'vax-vms)
@@ -3234,5 +3300,5 @@ Ask means pop up a menu for the user to select one of copy, move or link."
 
 (run-hooks 'dired-load-hook)           ; for your customizations
 
-;;; arch-tag: e1af7a8f-691c-41a0-aac1-ddd4d3c87517
+;; arch-tag: e1af7a8f-691c-41a0-aac1-ddd4d3c87517
 ;;; dired.el ends here