# HG changeset patch # User Richard M. Stallman # Date 1032054774 0 # Node ID 6ca0edea0a567927ce5e48c05e5a6a7cf868a4ad # Parent 12d639f1385eb21562e265949d69d92a7141f994 (dired-use-ls-dired): New variable. (dired-directory): Document the rules better. (dired-insert-headerline): Function deleted. (dired-revert): Pass no args to dired-readin. (dired-move-to-filename): First try using dired-filename property. (dired-move-to-end-of-filename): Likewise. (dired-why): Try to show the start of this page of warnings. (dired-log): Insert the buffer name at start of page, not end. (dired-log-summary): If just one failure, explain it in echo area. (dired-internal-noselect): Always set dired-directory, when buffer is not new. Pass dir-or-list, not dirname, to dired-mode. Call dired-readin with no args. Don't call dired-after-readin-hook here. (dired-find-buffer-nocreate): Expand dirname. Expand the dir from dired-directory to compare with dirname. (dired-readin): Take no args. Get the directory from dired-directory. Run dired-before-reading hook inside save-excursion. Run dired-after-readin-hook here. Don't make undo entries at all. Call dired-readin-insert with no args. Don't change indentation here. Don't insert headerline here. (dired-readin-insert): Take no args. Get dir and file-list from dired-directory. Call dired-insert-directory the new way. Don't insert "wildcard" info here. (dired-insert-directory): New arg FILE-LIST. First arg now DIR, always just the directory. This function fully handles setting up the buffer text: update indentation, insert headerline and "wildcard" info. Pass --dired arg if appropriate; put info in dired-filename props. Don't expand file names here. diff -r 12d639f1385e -r 6ca0edea0a56 lisp/dired.el --- a/lisp/dired.el Sun Sep 15 01:52:08 2002 +0000 +++ b/lisp/dired.el Sun Sep 15 01:52:54 2002 +0000 @@ -1,6 +1,6 @@ ;;; dired.el --- directory-browsing commands -;; Copyright (C) 1985, 86, 92, 93, 94, 95, 96, 1997, 2000, 2001, 2002 +;; Copyright (C) 1985, 86, 92, 93, 94, 95, 96, 1997, 2000, 2001 ;; Free Software Foundation, Inc. ;; Author: Sebastian Kremer @@ -72,6 +72,9 @@ "/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').") @@ -217,9 +220,10 @@ (defvar dired-file-version-alist) (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.") @@ -420,9 +424,6 @@ (push file result))) result))) -;; Function dired-ls is redefinable for VMS, ange-ftp, Prospero or -;; other special applications. - ;; The dired command (defun dired-read-dir-and-switches (str) @@ -511,14 +512,17 @@ ;; 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 @@ -529,8 +533,7 @@ (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)) ;; If directory has changed on disk, offer to revert. @@ -553,21 +556,16 @@ (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 +581,7 @@ ;; 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 +590,11 @@ (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))) + (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)))))) @@ -605,40 +606,30 @@ ;; 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 () ;; 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 + ;; 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) (message "Reading directory %s..." dirname) - (set-buffer buffer) - (let (buffer-read-only (failed t)) + (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. - ) + (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 (set (make-local-variable 'dired-subdir-alist) nil) @@ -646,56 +637,56 @@ (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)) + (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"))))))) + (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-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). +(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 dired-use-ls-dired + (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 @@ -707,8 +698,25 @@ (while (search-forward "\^m" end t) (replace-match "\\015" 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 + (forward-line -1) + (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) @@ -726,13 +734,6 @@ 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"))) - ;; Reverting a dired buffer @@ -755,7 +756,7 @@ ;; 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)) @@ -1474,6 +1475,7 @@ (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))) @@ -1584,10 +1586,14 @@ ;; This is the UNIX version. (or eol (setq eol (progn (end-of-line) (point)))) (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))) + (if change (goto-char change) + (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")))))) (defun dired-move-to-end-of-filename (&optional no-error) ;; Assumes point is at beginning of filename, @@ -1596,63 +1602,65 @@ ;; (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))))) ;;; COPY NAMES OF MARKED FILES INTO KILL-RING. @@ -2645,38 +2653,50 @@ (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)) ;;; Sorting