Mercurial > hg > xemacs-beta
diff lisp/dired/dired-x.el @ 0:376386a54a3c r19-14
Import from CVS: tag r19-14
author | cvs |
---|---|
date | Mon, 13 Aug 2007 08:45:50 +0200 |
parents | |
children | ac2d302a0011 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lisp/dired/dired-x.el Mon Aug 13 08:45:50 2007 +0200 @@ -0,0 +1,1678 @@ +;;; dired-x.el --- Sebastian Kremer's Extra DIRED hacked up for GNU Emacs19 +;; Keywords: dired extensions + +(defconst dired-extra-version (substring "!Revision: 1.191 !" 11 -2) + "Id: dired-x.el,v 1.191 1992/05/14 11:41:54 sk RelBeta ") + +;; Copyright (C) 1991 Sebastian Kremer. + +;; This file is part of XEmacs. + +;; XEmacs is free software; you can redistribute it and/or modify it +;; under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 2, or (at your option) +;; any later version. + +;; XEmacs is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with XEmacs; see the file COPYING. If not, write to the Free +;; Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + +;; LISPDIR ENTRY for the Elisp Archive =============================== +;; LCD Archive Entry: +;; dired-x|Sebastian Kremer|sk@thp.uni-koeln.de +;; |Extra Features for Tree Dired +;; |Date: 1992/05/14 11:41:54 |Revision: 1.191 | + +;; INSTALLATION ====================================================== + +;; In your ~/.emacs, say +;; +;; (setq dired-load-hook '(lambda () (load "dired-x"))) +;; +;; At load time dired-x will install itself using the various other +;; dired hooks. It will redefine some functions and bind dired keys. +;; If gmhist is present, dired-x will take advantage of it. + +(require 'dired) ; we will redefine some functions + ; and also need some macros + +(provide 'dired-extra) ; but this file is "dired-x" +(provide 'dired-x) ; but this file is "dired-x" + +;; Customization (see also defvars in other sections below) + +;; user should define this as `nil' prior to loading dired-x in order that the +;; compression/decompression material of emacs19 is not overwritten. +(defvar dired-mark-keys '("Z") + "*List of keys (strings) that insert themselves as file markers.") + +(defvar dired-dangerous-shell-command "^rm" ; e.g. "rm" or "rmdir" + "*Regexp for dangerous shell commands that should never be the default.") + +;; Add key bindings. This file is supposed to be loaded immediately +;; after dired, inside dired-load-hook. + +(define-key dired-mode-map "V" 'dired-vm) +(define-key dired-mode-map "\(" 'dired-set-marker-char) +(define-key dired-mode-map "\)" 'dired-restore-marker-char) +(define-key dired-mode-map "I" 'dired-do-insert-subdir) +;;(define-key dired-mode-map "\M-f" 'dired-flag-extension) +(define-key dired-mode-map "\M-M" 'dired-do-unmark) +(define-key dired-mode-map "\M-o" 'dired-omit-toggle) +(define-key dired-mode-map "\M-(" 'dired-mark-sexp) +(define-key dired-mode-map "," 'dired-mark-rcs-files) +(define-key dired-mode-map "\M-!" 'dired-smart-shell-command) +(define-key dired-mode-map "\M-&" 'dired-smart-background-shell-command) +(define-key dired-mode-map "T" 'dired-do-toggle) +(define-key dired-mode-map "w" 'dired-copy-filename-as-kill) +(define-key dired-mode-map "\M-g" 'dired-goto-file) +(define-key dired-mode-map "\M-G" 'dired-goto-subdir) +(define-key dired-mode-map "&" 'dired-do-background-shell-command) +(define-key dired-mode-map "A" 'dired-do-byte-compile-and-load) +(define-key dired-mode-map "F" 'dired-do-find-file) +(define-key dired-mode-map "S" 'dired-do-relsymlink) +(define-key dired-mode-map "%S" 'dired-do-relsymlink-regexp) + +(mapcar (function;; do this last to override bindings above + (lambda (x) + (define-key dired-mode-map x 'dired-mark-with-this-char))) + dired-mark-keys) + +;; Install ourselves into the appropriate hooks + +(defun dired-add-hook (hook-var function) + "Add a function to a hook. +First argument HOOK-VAR (a symbol) is the name of a hook, second +argument FUNCTION is the function to add. +Returns nil if FUNCTION was already present in HOOK-VAR, else new +value of HOOK-VAR." + (interactive "SAdd to hook-var (symbol): \naAdd which function to %s? ") + (if (not (boundp hook-var)) (set hook-var nil)) + (if (or (not (listp (symbol-value hook-var))) + (eq (car (symbol-value hook-var)) 'lambda)) + (set hook-var (list (symbol-value hook-var)))) + (if (memq function (symbol-value hook-var)) + nil + (set hook-var (cons function (symbol-value hook-var))))) + +(dired-add-hook 'dired-mode-hook 'dired-extra-startup) +(dired-add-hook 'dired-after-readin-hook 'dired-omit-expunge) + +(defvar dired-default-marker dired-marker-char + "*The value of `dired-marker-char' in effect before dired-x was +loaded and the value which is restored if the marker stack underflows. +This is usually the asterisk `*'.") + +;;;###autoload +(defun dired-extra-startup () + "Automatically put on dired-mode-hook to get extra dired features: +\\<dired-mode-map> + \\[dired-vm]\t-- VM on folder + \\[dired-rmail]\t-- Rmail on folder + \\[dired-do-insert-subdir]\t-- insert all marked subdirs + \\[dired-do-find-file]\t-- visit all marked files simultaneously + \\[dired-set-marker-char], \\[dired-restore-marker-char]\t-- change and display dired-marker-char dynamically. + \\[dired-omit-toggle]\t-- toggle omitting of files + \\[dired-mark-sexp]\t-- mark by lisp expression + \\[dired-do-unmark]\t-- replace existing marker with another. + \\[dired-mark-rcs-files]\t-- mark all RCS controlled files + \\[dired-mark-files-compilation-buffer]\t-- mark compilation files + \\[dired-copy-filename-as-kill]\t-- copy the file or subdir names into the kill ring. + \t You can feed it to other commands using \\[yank]. + +For more features, see variables + + dired-omit-files + dired-omit-extenstions + dired-dangerous-shell-command + dired-mark-keys + dired-local-variables-file + dired-find-subdir + dired-guess-have-gnutar + dired-auto-shell-command-alist + +See also functions + + dired-sort-on-size + dired-do-relsymlink + dired-flag-extension + dired-virtual + dired-jump-back + dired-jump-back-other-window +" + (interactive) + ;; This must be done in each new dired buffer: + (dired-hack-local-variables) + (dired-omit-startup) + (dired-marker-stack-startup)) + +;;; Handle customization + +(or (fboundp 'read-with-history-in) ; it's loaded + (not (subrp (symbol-function 'read-from-minibuffer))) ; it's 19.4L + ;; else try to load gmhist + (load "gmhist" t)) + +(if (not (fboundp 'read-with-history-in)) + + nil ; Gmhist is not available + + ;; Else use generic minibuffer history + (put 'dired-shell-command-history 'dangerous dired-dangerous-shell-command) + + ;; Redefinition - when this is loaded, dired.el has alreay been loaded. + + (defun dired-read-regexp (prompt &optional initial) + (setq dired-flagging-regexp + (if (null initial) + (read-with-history-in 'regexp-history prompt initial) + (put 'regexp-history 'default + nil) + (put 'regexp-history 'default + (read-with-history-in 'regexp-history prompt initial))))) + + (defun dired-read-dir-and-switches (str) + (nreverse + (list + (if current-prefix-arg + (read-string "Dired listing switches: " dired-listing-switches)) + (read-file-name-with-history-in + 'file-history ; or 'dired-history? + (format "Dired %s(directory): " str) nil default-directory nil)))) +) + + + +;;; Dynamic Markers + +(defun dired-mark-with-this-char (arg) + "Mark the current file or subdir with the last key you pressed to invoke +this command. Else like \\[dired-mark-subdir-or-file] command." + (interactive "p") + (let ((dired-marker-char;; use last character, in case of prefix cmd + last-command-char)) + (dired-mark-subdir-or-file arg))) + +(defvar dired-marker-stack nil + "List of previously used dired marker characters.") + +(defvar dired-marker-string "" + "String version of `dired-marker-stack'.") + +(defun dired-current-marker-string () + "Computes and returns `dired-marker-string'." + (setq dired-marker-string + (concat " " + (mapconcat (function char-to-string) + (reverse dired-marker-stack) + "")))) + +(defun dired-marker-stack-startup () + (make-local-variable 'dired-marker-char) + (make-local-variable 'dired-del-marker) + (make-local-variable 'dired-marker-stack) + (or (assq 'dired-marker-stack minor-mode-alist) + (setq minor-mode-alist + (cons '(dired-marker-stack dired-marker-string) + minor-mode-alist)))) + +(defun dired-set-marker-char (c) + "Set the marker character to something else. +Use \\[dired-restore-marker-char] to restore the previous value." + (interactive "cNew marker character: ") + (setq dired-marker-stack (cons c dired-marker-stack)) + (dired-current-marker-string) + (setq dired-marker-char c) + (set-buffer-modified-p (buffer-modified-p)) ; update mode line + (message "New marker is %c" dired-marker-char)) + +(defun dired-restore-marker-char () + "Restore the marker character to its previous value. +Uses `dired-default-marker' if the marker stack is empty." + (interactive) + (setq dired-marker-stack (cdr dired-marker-stack) + dired-marker-char (car dired-marker-stack)) + (dired-current-marker-string) + (set-buffer-modified-p (buffer-modified-p)) ; update mode line + (or dired-marker-char (setq dired-marker-char dired-default-marker)) + (message "Marker is %c" dired-marker-char)) + +;;; Sort on Size kludge if your ls can't do it + +(defun dired-sort-on-size () + "Sorts a dired listing on file size. +If your ls cannot sort on size, this is useful as `dired-after-readin-hook': + \(setq dired-after-readin-hook 'dired-sort-on-size\)" + (require 'sort) + (goto-char (point-min)) + (dired-goto-next-file) ; skip `total' line + (beginning-of-line) + (sort-subr t ; biggest file first + 'forward-line 'end-of-line 'dired-get-file-size)) + +(defun dired-get-file-size () + (re-search-forward "\\(Jan\\|Feb\\|Mar\\|Apr\\|May\\|Jun\\|Jul\\|Aug\\|Sep\\|Oct\\|Nov\\|Dec\\)") + (goto-char (match-beginning 1)) + (forward-char -1) + (string-to-int (buffer-substring (save-excursion + (backward-word 1) + (point)) + (point)))) + + +;;; Misc. (mostly featurismic) commands + +;; Mail folders + +(defvar dired-vm-read-only-folders nil + "*If t, \\[dired-vm] will visit all folders read-only. +If neither nil nor t, e.g. the symbol `if-file-read-only', only +files not writable by you are visited read-only. + +Read-only folders only work in VM 5, not in VM 4.") + +(defun dired-vm (&optional read-only) + "Run VM on this file. +With prefix arg, visit folder read-only (this requires at least VM 5). +See also variable `dired-vm-read-only-folders'." + (interactive "P") + (let ((dir (dired-current-directory)) + (fil (dired-get-filename))) + ;; take care to supply 2nd arg only if requested - may still run VM 4! + (cond (read-only (vm-visit-folder fil t)) + ((eq t dired-vm-read-only-folders) (vm-visit-folder fil t)) + ((null dired-vm-read-only-folders) (vm-visit-folder fil)) + (t (vm-visit-folder fil (not (file-writable-p fil))))) + ;; so that pressing `v' inside VM does prompt within current directory: + (set (make-local-variable 'vm-folder-directory) dir))) + +(defun dired-rmail () + "Run RMAIL on this file." + (interactive) + (rmail (dired-get-filename))) + +;; More subdir operations + +(defun dired-do-insert-subdir () + "Insert all marked subdirectories in situ that are not yet inserted. +Non-directories are silently ignored." + (interactive) + (let ((files (or (dired-mark-get-files) + (error "No files marked.")))) + (while files + (if (file-directory-p (car files)) + (save-excursion (dired-maybe-insert-subdir (car files)))) + (setq files (cdr files))))) + +(defun dired-mark-extension (extension &optional marker-char) + "Mark all files with a certain extension for use in later commands. +A `.' is not automatically prepended to the string entered." + ;; EXTENSION may also be a list of extensions instead of a single one. + ;; Optional MARKER-CHAR is marker to use. + (interactive "sMarking extension: \nP") + (or (listp extension) + (setq extension (list extension))) + (dired-mark-files-regexp + (concat ".";; don't match names with nothing but an extension + "\\(" + (mapconcat 'regexp-quote extension "\\|") + "\\)$") + marker-char)) + +(defun dired-flag-extension (extension) + "In dired, flag all files with a certain extension for deletion. +A `.' is *not* automatically prepended to the string entered." + (interactive "sFlagging extension: ") + (dired-mark-extension extension dired-del-marker)) + +(defvar patch-unclean-extensions + '(".rej" ".orig") + "List of extensions of dispensable files created by the `patch' program.") + +(defvar tex-unclean-extensions + '(".toc" ".log" ".aux");; these are already in completion-ignored-extensions + "List of extensions of dispensable files created by TeX.") + +(defvar latex-unclean-extensions + '(".idx" ".lof" ".lot" ".glo") + "List of extensions of dispensable files created by LaTeX.") + +(defvar bibtex-unclean-extensions + '(".blg" ".bbl") + "List of extensions of dispensable files created by BibTeX.") + +(defvar texinfo-unclean-extensions + '(".cp" ".cps" ".fn" ".fns" ".ky" ".kys" ".pg" ".pgs" + ".tp" ".tps" ".vr" ".vrs") + "List of extensions of dispensable files created by texinfo.") + +(defun dired-clean-patch () + "Flag dispensable files created by patch for deletion. +See variable `patch-unclean-extensions'." + (interactive) + (dired-flag-extension patch-unclean-extensions)) + +(defun dired-clean-tex () + "Flag dispensable files created by tex etc. for deletion. +See variable `texinfo-unclean-extensions', `latex-unclean-extensions', +`bibtex-unclean-extensions' and `texinfo-unclean-extensions'." + (interactive) + (dired-flag-extension (append texinfo-unclean-extensions + latex-unclean-extensions + bibtex-unclean-extensions + tex-unclean-extensions))) + +(defun dired-do-unmark (unmarker) + "Unmark marked files by replacing the marker with another character. +The new character defaults to a space, effectively unmarking them." + (interactive "sChange marker to: ") + (if (string= unmarker "") + (setq unmarker " ")) + (setq unmarker (substring unmarker 0 1)) + (let ((regexp (dired-marker-regexp)) + (buffer-read-only nil)) + (save-excursion + (goto-char (point-min)) + (while (re-search-forward regexp nil t) + (replace-match unmarker))))) + +;; This is unused but might come in handy sometime +;(defun dired-directories-of (files) +; ;; Return unique list of parent directories of FILES. +; (let (dirs dir file) +; (while files +; (setq file (car files) +; files (cdr files) +; dir (file-name-directory file)) +; (or (member dir dirs) +; (setq dirs (cons dir dirs)))) +; dirs)) + +;; Adapted from code by wurgler@zippysun.math.uakron.edu (Tom Wurgler). +;; Suggest you bind it to a key. I use C-x C-j. +(defun dired-jump-back (&optional other-window) + "Jump back to dired: +If in a file, dired the current directory and move to file's line. +If in dired already, pop up a level and goto old directory's line. +In case the proper dired file line cannot be found, refresh the dired + buffer and try again." + (interactive) + (let* ((file buffer-file-name) + (dir (if file (file-name-directory file) default-directory))) + (if (eq major-mode 'dired-mode) + (progn + (setq dir (dired-current-directory)) + (if other-window + (dired-up-directory-other-window) + (dired-up-directory)) + (dired-really-goto-file dir)) + (if other-window + (dired-other-window dir) + (dired dir)) + (if file (dired-really-goto-file file))))) + +(defun dired-jump-back-other-window () + "Like \\[dired-jump-back], but to other window." + (interactive) + (dired-jump-back t)) + +(defun dired-really-goto-file (file) + (or (dired-goto-file file) + (progn ; refresh and try again + (dired-insert-subdir (file-name-directory file)) + (dired-goto-file file)))) + +(defun dired-up-directory-other-window () + "Like `dired-up-directory', but in other window." + (interactive) + (let* ((dir (dired-current-directory)) + (up (file-name-directory (directory-file-name dir)))) + (or (dired-goto-file (directory-file-name dir)) + (dired-goto-subdir up) + ;; Only in this case it really uses another window: + (progn + (dired-other-window up) + (dired-goto-file dir))))) + +(defun dired-mark-rcs-files (&optional unflag-p) + "Mark all files that are under RCS control. +With prefix argument, unflag all those files. +Mentions RCS files for which a working file was not found in this buffer. +Type \\[dired-why] to see them again." + ;; Returns failures, or nil on success. + ;; Finding those with locks would require to peek into the ,v file, + ;; depends slightly on the RCS version used and should be done + ;; together with the Emacs RCS interface. + ;; Unfortunately, there is no definitive RCS interface yet. + (interactive "P") + (message "%sarking RCS controlled files..." (if unflag-p "Unm" "M")) + (let ((dired-marker-char (if unflag-p ?\ dired-marker-char)) + rcs-files wf failures count total) + (mapcar ; loop over subdirs + (function + (lambda (dir) + (or (equal (file-name-nondirectory (directory-file-name dir)) + "RCS") + ;; skip inserted RCS subdirs + (setq rcs-files + (append (directory-files dir t ",v$") ; *,v and RCS/*,v + (let ((rcs-dir (expand-file-name "RCS" dir))) + (if (file-directory-p rcs-dir) + (mapcar ; working files from ./RCS are in ./ + (function + (lambda (x) + (expand-file-name x dir))) + (directory-files + (file-name-as-directory rcs-dir) nil ",v$")))) + rcs-files))))) + (mapcar (function car) dired-subdir-alist)) + (setq total (length rcs-files)) + (while rcs-files + (setq wf (substring (car rcs-files) 0 -2) + rcs-files (cdr rcs-files)) + (save-excursion (if (dired-goto-file wf) + (dired-mark-file 1) + (setq failures (cons wf failures))))) + (if (null failures) + (message "%d RCS file%s %smarked." + total (dired-plural-s total) (if unflag-p "un" "")) + (setq count (length failures)) + (dired-log-summary "RCS working file not found %s" failures) + (message "%d RCS file%s: %d %smarked - %d not found %s." + total (dired-plural-s total) (- total count) + (if unflag-p "un" "") count failures)) + failures)) + +(defun dired-do-toggle () + "Toggle marks. +That is, currently marked files become unmarked and vice versa. +Files marked with other flags (such as `D') are not affected. +`.' and `..' are never toggled. +As always, hidden subdirs are not affected." + (interactive) + (save-excursion + (goto-char (point-min)) + (let (buffer-read-only) + (while (not (eobp)) + (or (dired-between-files) + (looking-at dired-re-dot) + ;; use subst instead of insdel because it does not move + ;; the gap and thus should be faster and because + ;; other characters are left alone automatically + (apply 'subst-char-in-region + (point) (1+ (point)) + (if (eq ?\040 (following-char)) ; SPC + (list ?\040 dired-marker-char) + (list dired-marker-char ?\040)))) + (forward-line 1))))) + +;; This function is missing in simple.el +(defun copy-string-as-kill (string) + "Save STRING as if killed in a buffer." + (setq kill-ring (cons string kill-ring)) + (if (> (length kill-ring) kill-ring-max) + (setcdr (nthcdr (1- kill-ring-max) kill-ring) nil)) + (setq kill-ring-yank-pointer kill-ring)) + +(defvar dired-marked-files nil + "List of filenames from last `dired-copy-filename-as-kill' call.") + +(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 a raw (just \\[universal-argument]) prefix arg, use the relative pathname of each marked file. + +If on a subdir headerline and no prefix arg given, use subdirname instead. + +You can then feed the file name to other commands with \\[yank]. + +The list of names is also stored onto the variable +`dired-marked-files' for use, e.g., in an `\\[eval-expression]' command." + (interactive "P") + (copy-string-as-kill + (or (and (not arg) + (dired-get-subdir)) + (mapconcat (function identity) + (setq dired-marked-files + (if arg + (cond ((zerop (prefix-numeric-value arg)) + (dired-mark-get-files)) + ((integerp arg) + (dired-mark-get-files 'no-dir arg)) + (t ; else a raw arg + (dired-mark-get-files t))) + (dired-mark-get-files 'no-dir))) + " "))) + (message "%s" (car kill-ring))) + +(defun dired-do-background-shell-command (&optional arg) + "Like \\[dired-do-shell-command], but starts command in background. +Note that you can type input to the command in its buffer. +This requires background.el from the comint package to work." + ;; With the version in emacs-19.el, you can alternatively just + ;; append an `&' to any shell command to make it run in the + ;; background, but you can't type input to it. + (interactive "P") + (dired-do-shell-command arg t)) + +;; redefines dired.el to put back in the dired-offer-kill-buffer +;; feature which rms didn't like. +(defun dired-clean-up-after-deletion (fn) + ;; Clean up after a deleted file or directory FN. + ;; Remove expanded subdir of deleted dir, if any + (save-excursion (and (dired-goto-subdir fn) + (dired-kill-subdir))) + ;; Offer to kill buffer of deleted file FN. + (let ((buf (get-file-buffer fn))) + (and buf + (funcall (function y-or-n-p) + (format "Kill buffer of %s, too? " + (file-name-nondirectory fn))) + (save-excursion;; you never know where kill-buffer leaves you + (kill-buffer buf)))) + (let ((buf-list (dired-buffers-for-top-dir fn)) + (buf nil)) + (and buf-list + (y-or-n-p (format "Kill dired buffer%s of %s, too? " + (dired-plural-s (length buf-list)) + (file-name-nondirectory fn))) + (while buf-list + (save-excursion (kill-buffer (car buf-list))) + (setq buf-list (cdr buf-list))))) + ;; Anything else? + ) + +;;; Omitting + +;;; Enhanced omitting of lines from directory listings. +;;; Marked files are never omitted. +;;; Adapted from code submitted by: +;;; Michael D. Ernst, mernst@theory.lcs.mit.edu, 1/11/91 + +(make-variable-buffer-local + (defvar dired-omit-files-p nil + "*If non-nil, \"uninteresting\" files are not listed (buffer-local). +Use \\[dired-omit-toggle] to toggle its value. +Uninteresting files are those whose filenames match regexp `dired-omit-files', +plus those ending with extensions in `dired-omit-extensions'.")) + +(defvar dired-omit-files "^#\\|\\.$" + "*Filenames matching this regexp will not be displayed (buffer-local). +This only has effect when `dired-omit-files-p' is t. +See also `dired-omit-extensions'.") + +(defvar dired-omit-extensions + (append completion-ignored-extensions + latex-unclean-extensions + bibtex-unclean-extensions + texinfo-unclean-extensions) + "*If non-nil, a list of extensions (strings) to omit from Dired +listings. Defaults to the elements of +`completion-ignored-extensions', `latex-unclean-extensions', +`bibtex-unclean-extensions' and `texinfo-unclean-extensions'.") + +;; should probably get rid of this and always use 'no-dir. +;; sk 28-Aug-1991 09:37 +(defvar dired-omit-localp 'no-dir + "The LOCALP argument dired-omit-expunge passes to dired-get-filename. +If it is 'no-dir, omitting is much faster, but you can only match +against the basename of the file. Set it to nil if you need to match the +whole pathname.") + +;; \017=^O for Omit - other packages can chose other control characters. +(defvar dired-omit-marker-char ?\017 + "Temporary marker used by by dired-omit. +Should never be used as marker by the user or other packages.") + +(defun dired-omit-startup () + (or (assq 'dired-omit-files-p minor-mode-alist) + ;; Append at end so that it doesn't get between "Dired" and "by name". + (setq minor-mode-alist + (append minor-mode-alist '((dired-omit-files-p " Omit")))))) + +(defun dired-omit-toggle (&optional flag) + "Toggle between displaying and omitting files matching `dired-omit-files'. +With an arg, and if omitting was off, don't toggle and just mark the + files but don't actually omit them. +With an arg, and if omitting was on, turn it off but don't refresh the buffer." + (interactive "P") + (if flag + (if dired-omit-files-p + (setq dired-omit-files-p (not dired-omit-files-p)) + (dired-mark-unmarked-files (dired-omit-regexp) nil nil + dired-omit-localp)) + ;; no FLAG + (setq dired-omit-files-p (not dired-omit-files-p)) + (if (not dired-omit-files-p) + (revert-buffer) + ;; this will mention how many were omitted: + (dired-omit-expunge)))) + +;; This is sometimes let-bound to t if messages would be annoying, +;; e.g., in dired-awrh.el. +(defvar dired-omit-silent nil) + +;; in emacs19 `(dired-do-kill)' is called `(dired-do-kill-lines)' +(if (fboundp 'dired-do-kill-lines) + (fset 'dired-do-kill 'dired-do-kill-lines)) + +(defun dired-omit-expunge (&optional regexp) + "Erases all unmarked files matching REGEXP. +Does nothing if global variable `dired-omit-files-p' is nil. +If REGEXP is nil or not specified, uses `dired-omit-files', and also omits + filenames ending in `dired-omit-extensions'. +If REGEXP is the empty string, this function is a no-op. + +This functions works by temporarily binding `dired-marker-char' to +`dired-omit-marker-char' and calling `dired-do-kill'." + (interactive "sOmit files (regexp): ") + (if dired-omit-files-p + (let ((omit-re (or regexp (dired-omit-regexp))) + count) + (or (string= omit-re "") + (let ((dired-marker-char dired-omit-marker-char)) + (or dired-omit-silent (message "Omitting...")) + (if (dired-mark-unmarked-files + omit-re nil nil dired-omit-localp) + (setq count (dired-do-kill nil (if dired-omit-silent + "" + "Omitted %d line%s."))) + (or dired-omit-silent + (message "(Nothing to omit)"))))) + count))) + +(defun dired-omit-regexp () + (concat (if dired-omit-files (concat "\\(" dired-omit-files "\\)") "") + (if (and dired-omit-files dired-omit-extensions) "\\|" "") + (if dired-omit-extensions + (concat ".";; a non-extension part should exist + "\\(" + (mapconcat 'regexp-quote dired-omit-extensions "\\|") + "\\)$") + ""))) + +;; Returns t if any work was done, nil otherwise. +(defun dired-mark-unmarked-files (regexp msg &optional unflag-p localp) + "Marks unmarked files matching REGEXP, displaying MSG. +REGEXP is matched against the complete pathname. +Does not re-mark files which already have a mark. +With prefix argument, unflag all those files. +Second optional argument LOCALP is as in `dired-get-filename'." + (interactive "P") + (let ((dired-marker-char (if unflag-p ?\ dired-marker-char))) + (dired-mark-if + (and + ;; not already marked + (looking-at " ") + ;; uninteresting + (let ((fn (dired-get-filename localp t))) + (and fn (string-match regexp fn)))) + msg))) + +(defun dired-omit-new-add-entry (filename &optional marker-char) + ;; This redefines dired.el's dired-add-entry to avoid calling ls for + ;; files that are going to be omitted anyway. + (if dired-omit-files-p + ;; perhaps return t without calling ls + (let ((omit-re (dired-omit-regexp))) + (if (or (string= omit-re "") + (not + (string-match omit-re + (cond + ((eq 'no-dir dired-omit-localp) + filename) + ((eq t dired-omit-localp) + (dired-make-relative filename)) + (t + (dired-make-absolute filename directory)))))) + ;; if it didn't match, go ahead and add the entry + (dired-omit-old-add-entry filename marker-char) + ;; dired-add-entry returns t for success, perhaps we should + ;; return file-exists-p + t)) + ;; omitting is not turned on at all + (dired-omit-old-add-entry filename marker-char))) + +;; Save old defun if not already done: +(or (fboundp 'dired-omit-old-add-entry) + (fset 'dired-omit-old-add-entry (symbol-function 'dired-add-entry))) +;; Redefine dired.el +(fset 'dired-add-entry 'dired-omit-new-add-entry) + + +;; +(defun dired-mark-sexp (predicate &optional unflag-p) + "Mark files for which PREDICATE returns non-nil. +With a prefix arg, unflag those files instead. + +PREDICATE is a lisp expression that can refer to the following symbols: + + inode [integer] the inode of the file (only for ls -i output) + s [integer] the size of the file for ls -s output + (ususally in blocks or, with -k, in KByte) + mode [string] file permission bits, e.g. \"-rw-r--r--\" + nlink [integer] number of links to file + uid [string] owner + gid [string] group (If the gid is not displayed by ls, + this will still be set (to the same as uid)) + size [integer] file size in bytes + time [string] the time that ls displays, e.g. \"Feb 12 14:17\" + name [string] the name of the file + sym [string] if file is a symbolic link, the linked-to name, else \"\" + +For example, use + + (equal 0 size) + +to mark all zero length files." + ;; Using sym="" instead of nil avoids the trap of + ;; (string-match "foo" sym) into which a user would soon fall. + ;; Give `equal' instead of `=' in the example, as this works on + ;; integers and strings. + (interactive "xMark if (lisp expr): \nP") + (message "%s" predicate) + (let ((dired-marker-char (if unflag-p ?\040 dired-marker-char)) + inode s mode nlink uid gid size time name sym) + (dired-mark-if + (save-excursion (and (dired-parse-ls) + (eval predicate))) + (format "'%s file" predicate)) + ;; With Jamie's compiler we could do the following instead: +; (eval (byte-compile-sexp +; (macroexpand +; (` (dired-mark-if +; (save-excursion (and (dired-parse-ls) +; (, predicate))) +; (format "'%s file" (quote (, predicate)))))))) + ;; This isn't measurably faster, though, at least for simple predicates. + ;; Caching compiled predicates might be interesting if you use + ;; this command a lot or with complicated predicates. + ;; Alternatively compiling PREDICATE by hand should not be too + ;; hard - e.g., if it uses just one variable, not all of the ls + ;; line needs to be parsed. + )) + +(if (fboundp 'gmhist-make-magic) + (gmhist-make-magic 'dired-mark-sexp 'eval-expression-history)) + +(defun dired-parse-ls () + ;; Sets vars + ;; inode s mode nlink uid gid size time name sym + ;; (probably let-bound in caller) according to current file line. + ;; Returns t for succes, nil if this is no file line. + ;; Upon success, all variables are set, either to nil or the + ;; appropriate value, so they need not be initialized. + ;; Moves point within the current line. + (if (dired-move-to-filename) + (let (pos + (mode-len 10) ; length of mode string + ;; like in dired.el, but with subexpressions \1=inode, \2=s: + (dired-re-inode-size "\\s *\\([0-9]*\\)\\s *\\([0-9]*\\) ?")) + (beginning-of-line) + (forward-char 2) + (if (looking-at dired-re-inode-size) + (progn + (goto-char (match-end 0)) + (setq inode (string-to-int (buffer-substring (match-beginning 1) + (match-end 1))) + s (string-to-int (buffer-substring (match-beginning 2) + (match-end 2))))) + (setq inode nil + s nil)) + (setq mode (buffer-substring (point) (+ mode-len (point)))) + (forward-char mode-len) + (setq nlink (read (current-buffer))) + (setq uid (buffer-substring (point) (progn (forward-word 1) (point)))) + (re-search-forward "\\(Jan\\|Feb\\|Mar\\|Apr\\|May\\|Jun\\|Jul\\|Aug\\|Sep\\|Oct\\|Nov\\|Dec\\)") + (goto-char (match-beginning 1)) + (forward-char -1) + (setq size (string-to-int (buffer-substring (save-excursion + (backward-word 1) + (setq pos (point))) + (point)))) + (goto-char pos) + (backward-word 1) + ;; if no gid is displayed, gid will be set to uid + ;; but user will then not reference it anyway in PREDICATE. + (setq gid (buffer-substring (save-excursion (forward-word 1) (point)) + (point)) + time (buffer-substring (match-beginning 1) + (1- (dired-move-to-filename))) + name (buffer-substring (point) + (or (dired-move-to-end-of-filename t) + (point))) + sym (progn + (if (looking-at " -> ") + (buffer-substring (progn (forward-char 4) (point)) + (progn (end-of-line) (point))) + ""))) + t) + nil)) + + +;; tester +;;(defun dired-parse-ls-show () +;; (interactive) +;; (let (inode s mode size uid gid nlink time name sym) +;; (if (dired-parse-ls) +;; (message "%s" (list inode s mode nlink uid gid size time name sym)) +;; (message "Not on a file line.")))) + + +;; Mark files whose names appear in another buffer. + +(defun dired-mark-these-files (file-list from) + ;; Mark the files in FILE-LIST. Relative filenames are taken to be + ;; in the current dired directory. + ;; FROM is a string (used for logging) describing where FILE-LIST + ;; came from. + ;; Logs files that were not found and displays a success or failure + ;; message. + (message "Marking files %s..." from) + (let ((total (length file-list)) + (cur-dir (dired-current-directory)) + file failures) + (while file-list + (setq file (dired-make-absolute (car file-list) cur-dir) + file-list (cdr file-list)) + ;;(message "Marking file `%s'" file) + (save-excursion + (if (dired-goto-file file) + (dired-mark-file 1) + (setq failures (cons (dired-make-relative file) failures)) + (dired-log "Cannot mark this file (not found): %s\n" file)))) + (if failures + (dired-log-summary (message "Failed to mark %d of %d files %s %s" + (length failures) total from failures)) + (message "Marked %d file%s %s." total (dired-plural-s total) from)))) + +(defun dired-mark-files-from-other-dired-buffer (buf) + "Mark files that are marked in the other Dired buffer. +I.e, mark those files in this Dired buffer that have the same +non-directory part as the marked files in the Dired buffer in the other window." + (interactive (list (window-buffer (next-window)))) + (if (eq (get-buffer buf) (current-buffer)) + (error "Other dired buffer is the same")) + (or (stringp buf) (setq buf (buffer-name buf))) + (let ((other-files (save-excursion + (set-buffer buf) + (or (eq major-mode 'dired-mode) + (error "%s is not a dired buffer" buf)) + (dired-mark-get-files 'no-dir)))) + (dired-mark-these-files other-files (concat "from buffer " buf)))) + +(defun dired-mark-files-compilation-buffer (&optional regexp buf) + "Mark the files mentioned in the `*compilation*' buffer. +With an arg, you may specify the other buffer and your own regexp +instead of `compilation-error-regexp'. +Use `^.+$' (the default with a prefix arg) to match complete lines or +an empty string for `compilation-error-regexp'. +In conjunction with narrowing the other buffer you can mark an +arbitrary list of files, one per line, with this command." + (interactive + (if current-prefix-arg + (list + (read-string "Use compilation regexp: " "^.+$") + (read-buffer "Use buffer: " + (let ((next-buffer (window-buffer (next-window)))) + (if (eq next-buffer (current-buffer)) + (other-buffer) + next-buffer)))))) + (let (other-files user-regexp-p) + (if (zerop (length regexp)) ; nil or "" + (setq regexp compilation-error-regexp) + (setq user-regexp-p t)) + (or buf (setq buf "*compilation*")) + (or (stringp buf) (setq buf (buffer-name buf))) + (save-excursion + (set-buffer (or (get-buffer buf) + (error "No %s buffer!" buf))) + (goto-char (point-min)) + (let (file new-file) + (while (re-search-forward regexp nil t) + (setq new-file + (buffer-substring + ;; If user specified a regexp with subexpr 1, and it + ;; matched, take that one for the file name, else + ;; take whole match. + ;; Else take the match from the compile regexp + (if user-regexp-p + (or (match-beginning 1) + (match-beginning 0)) + (match-beginning 1)) + (if user-regexp-p + (or (match-end 1) + (match-end 0)) + (match-beginning 2)))) + (or (equal file new-file) + ;; Avoid marking files twice as this is slow. Multiple + ;; lines for the same file are common when compiling. + (setq other-files (cons new-file other-files) + file new-file))))) + (dired-mark-these-files other-files (concat "from buffer " buf)))) + + +;; make-symbolic-link always expand-file-name's its args, so relative +;; symlinks (e.g. "foo" -> "../bar/foo") are impossible to create. +;; Following code uses ln -s for a workaround. + +(defvar dired-keep-marker-relsymlink ?S + "See variable `dired-keep-marker-move'.") + +(defun dired-make-symbolic-link (name1 name2 &optional ok-if-already-exists) + ;; Args NAME1 NAME2 &optional OK-IF-ALREADY-EXISTS. + ;; Create file NAME2, a symbolic link pointing to NAME1 (which may + ;; be any string whatsoever and is passed untouched to ln -s). + ;; OK-IF-ALREADY-EXISTS means that NAME2 will be overwritten if it + ;; already exists. If it is an integer, user will be asked about this. + ;; On error, signals a file-error. + (interactive "FSymlink to (string): \nFMake symbolic link to `%s': \np") + (setq name2 (expand-file-name name2)) + (let* ((file-symlink-p (file-symlink-p name2)) + (file-exists-p (file-exists-p name2)) ; dereferences symlinks + (file-or-symlink-exists (or file-symlink-p file-exists-p))) + (if (and file-symlink-p (not file-exists-p)) + ;; We do something dirty here as dired.el never checks + ;; file-symlink-p in addition to file-exists-p. + ;; This way me make sure we never silently overwrite even + ;; symlinks to non-existing files (what an achievement! ;-) + (setq ok-if-already-exists 1)) + (if (or (null ok-if-already-exists) + (integerp ok-if-already-exists)) + (if (and file-or-symlink-exists + (not (and (integerp ok-if-already-exists) + (yes-or-no-p + (format + "File %s already exists; symlink anyway? " + name2))))) + (signal 'file-error (cons "File already exists" name2)))) + ;; Bombs if NAME1 starts with "-", but not all ln programs may + ;; understand "--" to mean end of options...sigh + (let (err) + (if file-or-symlink-exists (delete-file name2)) + (setq err (dired-check-process "SymLink" "ln" "-s" name1 name2)) + (if err + (signal 'file-error (cons "ln" err)))))) + +(defun dired-make-relative-symlink (file1 file2 &optional ok-if-already-exists) + "Three arguments: FILE1 FILE2 &optional OK-IF-ALREADY-EXISTS +Make a symbolic link (pointing to FILE1) in FILE2. +The link is relative (if possible), for example + + \"/vol/tex/bin/foo\" \"/vol/local/bin/foo\" + +results in + + \"../../tex/bin/foo\" \"/vol/local/bin/foo\" +" + (interactive "FRelSymLink: \nFRelSymLink %s: \np") + (let (name1 name2 len1 len2 (index 0) sub) + (setq file1 (expand-file-name file1) + file2 (expand-file-name file2) + len1 (length file1) + len2 (length file2)) + ;; Find common initial pathname components: + (let (next) + (while (and (setq next (string-match "/" file1 index)) + (setq next (1+ next)) + (< next (min len1 len2)) + ;; For the comparison, both substrings must end in + ;; `/', so NEXT is *one plus* the result of the + ;; string-match. + ;; E.g., consider the case of linking "/tmp/a/abc" + ;; to "/tmp/abc" erronously giving "/tmp/a" instead + ;; of "/tmp/" as common initial component + (string-equal (substring file1 0 next) + (substring file2 0 next))) + (setq index next)) + (setq name2 file2 + sub (substring file1 0 index) + name1 (substring file1 index))) + (if (string-equal sub "/") + ;; No common initial pathname found + (setq name1 file1) + ;; Else they have a common parent directory + (let ((tem (substring file2 index)) + (start 0) + (count 0)) + ;; Count number of slashes we must compensate for ... + (while (setq start (string-match "/" tem start)) + (setq count (1+ count) + start (1+ start))) + ;; ... and prepend a "../" for each slash found: + (while (> count 0) + (setq count (1- count) + name1 (concat "../" name1))))) + (dired-make-symbolic-link + (directory-file-name name1) ; must not link to foo/ + ; (trailing slash!) + name2 ok-if-already-exists))) + +(defun dired-do-relsymlink (&optional arg) + "Symlink all marked (or next ARG) files into a directory, +or make a symbolic link to the current file. +This creates relative symbolic links like + + foo -> ../bar/foo + +not absolute ones like + + foo -> /ugly/path/that/may/change/any/day/bar/foo" + (interactive "P") + (dired-do-create-files 'relsymlink (function dired-make-relative-symlink) + "RelSymLink" arg dired-keep-marker-relsymlink)) + +;; XEmacs: added extra arg per tbarker@sun059.cpdsc.com (Ted Barker) +(defun dired-do-relsymlink-regexp (regexp newname &optional arg whole-path) + "RelSymlink all marked files containing REGEXP to NEWNAME. +See functions `dired-rename-regexp' and `dired-do-relsymlink' + for more info. With optional prefix ARG, will operate on ARG files following +point if no files are marked." + (interactive (dired-mark-read-regexp "RelSymLink")) + (dired-do-create-files-regexp + (function dired-make-relative-symlink) + "RelSymLink" arg regexp newname whole-path dired-keep-marker-relsymlink)) + +;; Virtual dired mode to browse ls -lR listings +;; sk@sun5 7-Mar-1991 16:00 + +(fset 'virtual-dired 'dired-virtual) +(defun dired-virtual (dirname &optional switches) + "Put this buffer into Virtual Dired mode. + +In Virtual Dired mode, all commands that do not actually consult the +filesystem will work. + +This is useful if you want to peruse and move around in an ls -lR +output file, for example one you got from an ftp server. With +ange-ftp, you can even dired a directory containing an ls-lR file, +visit that file and turn on virtual dired mode. But don't try to save +this file, as dired-virtual indents the listing and thus changes the +buffer. + +If you have save a Dired buffer in a file you can use \\[dired-virtual] to +resume it in a later session. + +Type \\<dired-mode-map>\\[revert-buffer] in the +Virtual Dired buffer and answer `y' to convert the virtual to a real +dired buffer again. You don't have to do this, though: you can relist +single subdirs using \\[dired-do-redisplay]. +" + + ;; DIRNAME is the top level directory of the buffer. It will become + ;; its `default-directory'. If nil, the old value of + ;; default-directory is used. + + ;; Optional SWITCHES are the ls switches to use. + + ;; Shell wildcards will be used if there already is a `wildcard' + ;; line in the buffer (thus it is a saved Dired buffer), but there + ;; is no other way to get wildcards. Insert a `wildcard' line by + ;; hand if you want them. + + (interactive + (list (read-string "Virtual Dired directory: " (dired-virtual-guess-dir)))) + (goto-char (point-min)) + (or (looking-at " ") + ;; if not already indented, do it now: + (indent-region (point-min) (point-max) 2)) + (or dirname (setq dirname default-directory)) + (setq dirname (expand-file-name (file-name-as-directory dirname))) + (setq default-directory dirname) ; contains no wildcards + (let ((wildcard (save-excursion + (goto-char (point-min)) + (forward-line 1) + (and (looking-at "^ wildcard ") + (buffer-substring (match-end 0) + (progn (end-of-line) (point))))))) + (if wildcard + (setq dirname (expand-file-name wildcard default-directory)))) + ;; If raw ls listing (not a saved old dired buffer), give it a + ;; decent subdir headerline: + (goto-char (point-min)) + (or (looking-at dired-subdir-regexp) + (dired-insert-headerline default-directory)) + (dired-mode dirname (or switches dired-listing-switches)) + (setq mode-name "Virtual Dired" + revert-buffer-function 'dired-virtual-revert) + (set (make-local-variable 'dired-subdir-alist) nil) + (dired-build-subdir-alist) + (goto-char (point-min)) + (dired-initial-position dirname)) + +(defun dired-virtual-guess-dir () + + ;; Guess and return appropriate working directory of this buffer, + ;; assumed to be in Dired or ls -lR format. + ;; The guess is based upon buffer contents. + ;; If nothing could be guessed, returns nil. + + (let ((regexp "^\\( \\)?\\([^ \n\r]*\\)\\(:\\)[\n\r]") + (subexpr 2)) + (goto-char (point-min)) + (cond ((looking-at regexp) + ;; If a saved dired buffer, look to which dir and + ;; perhaps wildcard it belongs: + (let ((dir (buffer-substring (match-beginning subexpr) + (match-end subexpr)))) + (file-name-as-directory dir))) + ;; Else no match for headerline found. It's a raw ls listing. + ;; In raw ls listings the directory does not have a headerline + ;; try parent of first subdir, if any + ((re-search-forward regexp nil t) + (file-name-directory + (directory-file-name + (file-name-as-directory + (buffer-substring (match-beginning subexpr) + (match-end subexpr)))))) + (t ; if all else fails + nil)))) + + +(defun dired-virtual-revert (&optional arg noconfirm) + (if (not + (y-or-n-p "Cannot revert a Virtual Dired buffer - switch to Real Dired mode? ")) + (error "Cannot revert a Virtual Dired buffer.") + (setq mode-name "Dired" + revert-buffer-function 'dired-revert) + (revert-buffer))) + +;; A zero-arg version of dired-virtual. +;; You need my modified version of set-auto-mode for the +;; `buffer-contents-mode-alist'. +;; Or you use infer-mode.el and infer-mode-alist, same syntax. +(defun dired-virtual-mode () + "Put current buffer into virtual dired mode (see `dired-virtual'). +Useful on `buffer-contents-mode-alist' (which see) with the regexp + + \"^ \\(/[^ /]+\\)/?+:$\" + +to put saved dired buffers automatically into virtual dired mode. + +Also useful for `auto-mode-alist' (which see) like this: + + \(setq auto-mode-alist (cons '(\"[^/]\\.dired$\" . dired-virtual-mode) + auto-mode-alist)\) +" + (interactive) + (dired-virtual (dired-virtual-guess-dir))) + + +(defvar dired-find-subdir nil ; t is pretty near to DWIM... + "*If non-nil, Dired does not make a new buffer for a directory if it +can be found (perhaps as subdir) in some existing Dired buffer. + +If there are several Dired buffers for a directory, the most recently +used is chosen. + +Dired avoids switching to the current buffer, so that if you have +a normal and a wildcard buffer for the same directory, C-x d RET will +toggle between those two.") + +(or (fboundp 'dired-old-find-buffer-nocreate) + (fset 'dired-old-find-buffer-nocreate + (symbol-function 'dired-find-buffer-nocreate))) + +(defun dired-find-buffer-nocreate (dirname) ; redefine dired.el + (if dired-find-subdir + (let* ((cur-buf (current-buffer)) + (buffers (nreverse (dired-buffers-for-dir-exact dirname))) + (cur-buf-matches (and (memq cur-buf buffers) + ;; wildcards must match, too: + (equal dired-directory dirname)))) + ;; We don't want to switch to the same buffer--- + (setq buffers (delq cur-buf buffers));;need setq with delq + (or (car (sort buffers (function dired-x-buffer-more-recently-used-p))) + ;; ---unless it's the only possibility: + (and cur-buf-matches cur-buf))) + (dired-old-find-buffer-nocreate dirname))) + +;; this should be a builtin +(defun dired-x-buffer-more-recently-used-p (buffer1 buffer2) + "Return t if BUFFER1 is more recently used than BUFFER2." + (if (equal buffer1 buffer2) + nil + (let ((more-recent nil) + (list (buffer-list))) + (while (and list + (not (setq more-recent (equal buffer1 (car list)))) + (not (equal buffer2 (car list)))) + (setq list (cdr list))) + more-recent))) + +(defun dired-buffers-for-dir-exact (dir) +;; Return a list of buffers that dired DIR (a directory or wildcard) +;; at top level, or as subdirectory. +;; Top level matches must match the wildcard part too, if any. +;; The list is in reverse order of buffer creation, most recent last. +;; As a side effect, killed dired buffers for DIR are removed from +;; dired-buffers. + (let ((alist dired-buffers) result elt) + (while alist + (setq elt (car alist) + alist (cdr alist)) + (let ((buf (cdr elt))) + (if (buffer-name buf) + ;; Top level must match exactly against dired-directory in + ;; case one of them is a wildcard. + (if (or (equal dir (save-excursion (set-buffer buf) + dired-directory)) + (assoc dir (save-excursion (set-buffer buf) + dired-subdir-alist))) + (setq result (cons buf result))) + ;; else buffer is killed - clean up: + (setq dired-buffers (delq elt dired-buffers))))) + result)) + +(defun dired-buffers-for-top-dir (dir) +;; Return a list of buffers that dired DIR (a directory, not a wildcard) +;; at top level, with or without wildcards. +;; 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) + (while alist + (setq elt (car alist) + alist (cdr alist)) + (let ((buf (cdr elt))) + (if (buffer-name buf) + (if (equal dir (save-excursion (set-buffer buf) default-directory)) + (setq result (cons buf result))) + ;; else buffer is killed - clean up: + (setq dired-buffers (delq elt dired-buffers))))) + result)) + +(defun dired-initial-position (dirname) ; redefine dired.el + (end-of-line) + (if dired-find-subdir (dired-goto-subdir dirname)) ; new + (if dired-trivial-filenames (dired-goto-next-nontrivial-file))) + +;;; Let `C-x f' and `C-x 4 f' know about Tree Dired's multiple directories. +;;; As a bonus, you get filename-at-point as default with a prefix arg. + +;; It's easier to add to this alist than redefine function +;; default-directory while keeping the old information. +(defconst default-directory-alist + '((dired-mode . (if (fboundp 'dired-current-directory) + (dired-current-directory) + default-directory))) + "Alist of major modes and their opinion on default-directory, as a +lisp expression to evaluate. A resulting value of nil is ignored in +favor of default-directory.") + +(defun default-directory () + "Usage like variable `default-directory', but knows about the special +cases in variable `default-directory-alist' (which see)." + (or (eval (cdr (assq major-mode default-directory-alist))) + default-directory)) + +(defun find-file-read-filename-at-point (prompt) + (if (fboundp 'gmhist-read-file-name) + (if current-prefix-arg + (let ((fn (filename-at-point))) + (gmhist-read-file-name + prompt (default-directory) fn nil + ;; the INITIAL arg is only accepted in Emacs 19 or with gmhist: + fn)) + (gmhist-read-file-name prompt (default-directory))) + ;; Else gmhist is not available, thus no initial input possible. + ;; Could use filename-at-point as default and mung prompt...ugh. + ;; Nah, get gmhist, folks! + (read-file-name prompt (default-directory)))) + +(defun filename-at-point () + "Get the filename closest to point, but don't change your position. +Has a preference for looking backward when not directly on a symbol." + ;; Not at all perfect - point must be right in the name. + (let ((filename-chars ".a-zA-Z0-9---_/:$") start end filename + (bol (save-excursion (beginning-of-line) (point))) + (eol (save-excursion (end-of-line) (point)))) + (save-excursion + ;; first see if you're just past a filename + (if (not (eobp)) + (if (looking-at "[] \t\n[{}()]") ; whitespace or some parens + (progn + (skip-chars-backward " \n\t\r({[]})") + (if (not (bobp)) + (backward-char 1))))) + (if (string-match (concat "[" filename-chars "]") + (char-to-string (following-char))) + (progn + (skip-chars-backward filename-chars) + (setq start (point)) + (if (string-match "[/~]" (char-to-string (preceding-char))) + (setq start (1- start))) + (skip-chars-forward filename-chars)) + (error "No file found around point!")) + (expand-file-name (buffer-substring start (point)))))) + +(defun find-this-file (fn) + "Edit file FILENAME. +Switch to a buffer visiting file FILENAME, creating one if none already exists. + +Interactively, with a prefix arg, calls `filename-at-point'. +Useful to edit the file mentioned in the buffer you are editing, or to +test if that file exists: use minibuffer completion after snatching the +name or part of it." + (interactive (list (find-file-read-filename-at-point "Find file: "))) + (find-file (expand-file-name fn))) + +(defun find-this-file-other-window (fn) + "Edit file FILENAME in other window. +Switch to a buffer visiting file FILENAME, creating one if none already exists. + +Interactively, with a prefix arg, call `filename-at-point'. +Useful to edit the file mentioned in the buffer you are editing, or to +test if that file exists: use minibuffer completion after snatching the +name or part of it." + (interactive (list (find-file-read-filename-at-point "Find file: "))) + (find-file-other-window (expand-file-name fn))) + +(defun dired-smart-shell-command (cmd &optional insert) + "Like function `shell-command', but in the current Tree Dired directory." + (interactive "sShell command: \nP") + (let ((default-directory (default-directory))) + (shell-command cmd insert))) + +(if (fboundp 'gmhist-make-magic) + (gmhist-make-magic 'dired-smart-shell-command 'shell-history)) + +(defun dired-smart-background-shell-command (cmd) + "Run a shell command in the background. +Like function `background' but in the current Tree Dired directory." + (interactive "s%% ") + (shell-command (concat "cd " (default-directory) "; " cmd " &"))) + +(if (fboundp 'gmhist-make-magic) + (gmhist-make-magic 'dired-smart-background-shell-command 'shell-history)) + + +;; Local variables for Dired buffers + +(defvar dired-local-variables-file ".dired" + "If non-nil, filename for local variables for Dired. +If Dired finds a file with that name in the current directory, it will +temporarily insert it into the dired buffer and run `hack-local-variables'. + +Type \\[info] and and `g' `(emacs)File Variables' `RET' for more info on +local variables.") + +(defun dired-hack-local-variables () + "Parse, and bind or evaluate as appropriate, any local variables +for current dired buffer. +See variable `dired-local-variables-file'." + (if (and dired-local-variables-file + (file-exists-p dired-local-variables-file)) + (let (buffer-read-only opoint ) + (save-excursion + (goto-char (point-max)) + (setq opoint (point-marker)) + (insert "\^L\n") + (insert-file-contents dired-local-variables-file)) + (let ((buffer-file-name dired-local-variables-file)) + (hack-local-variables)) + ;; Must delete it as (eobp) is often used as test for last + ;; subdir in dired.el. + (delete-region opoint (point-max)) + (set-marker opoint nil)))) + +;; Guess what shell command to apply to a file. + +(defvar dired-guess-have-gnutar nil + "*If non-nil, name of the GNU tar executable (e.g. \"tar\" or \"gnutar\"). +GNU tar's `z' switch is used for compressed tar files. +If you don't have GNU tar, set this to nil: a pipe using `zcat' is then used.") + +(defvar dired-make-gzip-quiet t + "*If non-nil, pass -q to shell commands involving gzip this will override +GZIP environment variable.") + +(defvar dired-znew-switches nil + "*If non-nil, a string of switches that will be passed to `znew' +example: \"-K\"") + +(defvar dired-auto-shell-command-alist-default + (list + (list "\\.tar$" (if dired-guess-have-gnutar + (concat dired-guess-have-gnutar " xvf") + "tar xvf")) + + ;; regexps for compressed archives must come before the .Z rule to + ;; be recognized: + (list "\\.tar\\.Z$" (if dired-guess-have-gnutar + (concat dired-guess-have-gnutar " zxvf") + (concat "zcat * | tar xvf -")) + ;; optional conversion to gzip (GNU zip) format + (concat "znew" + (if dired-make-gzip-quiet " -q") + " " dired-znew-switches)) + + ;; gzip'ed (GNU zip) archives + (list "\\.tar\\.g?z$\\|\\.tgz$" (if dired-guess-have-gnutar + (concat dired-guess-have-gnutar " zxvf") + ;; use `gunzip -qc' instead of `zcat' since some + ;; people don't install GNU zip's version of zcat + (concat "gunzip -qc * | tar xvf -"))) + '("\\.shar.Z$" "zcat * | unshar") + ;; use `gunzip -c' instead of `zcat' + '("\\.shar.g?z$" "gunzip -qc * | unshar") + '("\\.ps$" "ghostview" "xv" "lpr") + '("\\.ps.g?z$" "gunzip -qc * | ghostview -" + ;; optional decompression + (concat "gunzip" (if dired-make-gzip-quiet " -q"))) + '("\\.ps.Z$" "zcat * | ghostview -" + ;; optional conversion to gzip (GNU zip) format + (concat "znew" + (if dired-make-gzip-quiet " -q") + " " dired-znew-switches)) + '("\\.dvi$" "xdvi" "dvips") + '("\\.au$" "play") ; play Sun audiofiles + '("\\.mpg$" "mpeg_play") + '("\\.dl$" "xdl") ; loop pictures + '("\\.fli$" "xanim") + '("\\.gl$" "xgrasp") + '("\\.uu$" "uudecode") + '("\\.hqx$" "mcvert") + '("\\.sh$" "sh") ; execute shell scripts + '("\\.xbm$" "bitmap") ; view X11 bitmaps + '("\\.xpm$" "sxpm") + '("\\.gp$" "gnuplot") + '("\\.p[bgpn]m$" "xv") + '("\\.gif$" "xv") ; view gif pictures + '("\\.tif$" "xv") + '("\\.jpg$" "xv") + '("\\.fig$" "xfig") ; edit fig pictures + '("\.tex$" "latex" "tex") + '("\\.texi\\(nfo\\)?$" "makeinfo" "texi2dvi") + (if (eq window-system 'x) ; under X, offer both... + '("\\.dvi$" "xtex" "dvips") ; ...preview and printing + '("\\.dvi$" "dvips")) + '("\\.g?z$" (concat "gunzip" (if dired-make-gzip-quiet " -q" ""))) ; quiet? + '("\\.Z$" "uncompress" + ;; optional conversion to gzip (GNU zip) format + (concat "znew" (if dired-make-gzip-quiet " -q") " " dired-znew-switches)) + ;; some popular archivers: + '("\\.zoo$" "zoo x//") + '("\\.zip$" "unzip") + '("\\.lzh$" "lharc x") + '("\\.arc$" "arc x") + '("\\.shar$" "unshar") ; use "sh" if you don't have unshar + ) + + "Default for variable `dired-auto-shell-command-alist' (which see). +Set this to nil to turn off shell command guessing.") + +(defvar dired-auto-shell-command-alist nil + "*If non-nil, an alist of file regexps and their suggested commands. +Dired shell commands will look up the name of a file in this list +and suggest the matching command as default. + +Each element of this list looks like + + \(REGEXP COMMAND...\) + +where each COMMAND can either be a string or a lisp expression that +evaluates to a string. If several COMMANDs are given, the first one +will be the default and minibuffer completion will use the given set. + +These rules take precedence over the predefined rules in the variable +`dired-auto-shell-command-alist-default' (to which they are prepended). + +You can set this variable in your ~/.emacs. For example, to add +rules for `.foo' and `.bar' files, write + +\(setq dired-auto-shell-command-alist + (list (list \"\\\\.foo$\" \"FOO-COMMAND\");; fixed rule + ;; possibly more rules ... + (list \"\\\\.bar$\";; rule with condition test + '(if condition + \"BAR-COMMAND-1\" + \"BAR-COMMAND-2\")))\) +") + +(setq dired-auto-shell-command-alist + (if dired-auto-shell-command-alist;; join user and default value: + (append dired-auto-shell-command-alist + dired-auto-shell-command-alist-default) + ;; else just copy the default value: + dired-auto-shell-command-alist-default)) + +(defun dired-guess-default (files) + ;; Guess a shell command for FILES. + ;; Returns a command or a list of commands. + ;; You may want to redefine this to try something smarter. + (if (or (cdr files) + (null dired-auto-shell-command-alist)) + nil ; If more than one file, don't guess + (let* ((file (car files)) + (alist dired-auto-shell-command-alist) + (case-fold-search nil) ; need search to be case-sensitive in order + ; to distinguish between gzip'ed (`.z') and + ; compressed (`.Z') files + elt re cmds) + (while alist + (setq elt (car alist) + re (car elt) + alist (cdr alist)) + (if (string-match re file) + (setq cmds (cdr elt) + alist nil))) + (cond ((not (cdr cmds)) (eval (car cmds))) ; single command + (t (mapcar (function eval) cmds)))))) + +(defun dired-guess-shell-command (prompt files) + ;;"Ask user with PROMPT for a shell command, guessing a default from FILES." + (let ((default (dired-guess-default files)) + default-list old-history val (failed t)) + (if (not (featurep 'gmhist)) + (read-string prompt (if (listp default) (car default) default)) + ;; else we have gmhist + (if (null default) + (read-with-history-in 'dired-shell-command-history prompt) + (or (boundp 'dired-shell-command-history) + (setq dired-shell-command-history nil)) + (setq old-history dired-shell-command-history) + (if (listp default) + ;; more than one guess + (setq default-list default + default (car default) + prompt (concat + prompt + (format "{%d guesses} " (length default-list)))) + ;; just one guess + (setq default-list (list default))) + (put 'dired-shell-command-history 'default default) + ;; push guesses onto history so that they can be retrieved with M-p + (setq dired-shell-command-history + (append default-list dired-shell-command-history)) + ;; the unwind-protect returns VAL, and we too. + (unwind-protect + (progn + (setq val (read-with-history-in + 'dired-shell-command-history prompt) + failed nil) + val) + (progn + ;; Undo pushing onto the history list so that an aborted + ;; command doesn't get the default in the next command. + (setq dired-shell-command-history old-history) + (if (not failed) + (or (equal val (car-safe dired-shell-command-history)) + (setq dired-shell-command-history + (cons val dired-shell-command-history)))))))))) + +;; redefine dired.el's version: +(defun dired-read-shell-command (prompt arg files) + "Read a dired shell command using generic minibuffer history. +This command tries to guess a command from the filename(s) +from the variable `dired-auto-shell-command-alist' (which see)." + (dired-mark-pop-up + nil 'shell files ; bufname type files + 'dired-guess-shell-command ; function &rest args + (format prompt (dired-mark-prompt arg files)) files)) + + +;; Byte-compile-and-load (requires jwz@lucid.com's new byte compiler) +(defun dired-do-byte-compile-and-load (&optional arg) + "Byte compile marked and load (or next ARG) Emacs lisp files. +This requires jwz@lucid.com's new optimizing byte compiler." + (interactive "P") + (dired-mark-map-check (function dired-byte-compile-and-load) arg + 'byte-compile-and-load t)) + +(defun dired-byte-compile-and-load () + ;; Return nil for success, offending file name else. + (let* (buffer-read-only + (from-file (dired-get-filename)) + (new-file (byte-compile-dest-file from-file))) + (if (not (string-match elisp-source-extention-re from-file)) + (progn + (dired-log "Attempt to compile non-elisp file %s\n" from-file) + ;; return a non-nil value as error indication + (dired-make-relative from-file)) + (save-excursion;; Jamie's compiler may switch buffer + (byte-compile-and-load-file from-file)) + (dired-remove-file new-file) + (forward-line) ; insert .elc after its .el file + (dired-add-file new-file) + nil))) + +;; Visit all marked files simultaneously. +;; After an idea by wurgler@zippysun.math.uakron.edu (Tom Wurgler). + +(defun dired-do-find-file (&optional arg) + "Visit all marked files at once, and display them simultaneously. +See also function `simultaneous-find-file'. +If you want to keep the dired buffer displayed, type \\[split-window-vertically] first. +If you want just the marked files displayed and nothing else, type \\[delete-other-windows] first." + (interactive "P") + (simultaneous-find-file (dired-mark-get-files nil arg))) + +(defun simultaneous-find-file (file-list) + "Visit all files in FILE-LIST and display them simultaneously. + +The current window is split across all files in FILE-LIST, as evenly +as possible. Remaining lines go to the bottommost window. + +The number of files that can be displayed this way is restricted by +the height of the current window and the variable `window-min-height'." + ;; It is usually too clumsy to specify FILE-LIST interactively + ;; unless via dired (dired-do-find-file). + (let ((size (/ (window-height) (length file-list)))) + (or (<= window-min-height size) + (error "Too many files to visit simultaneously")) + (find-file (car file-list)) + (setq file-list (cdr file-list)) + (while file-list + ;; Split off vertically a window of the desired size + ;; The upper window will have SIZE lines. We select the lower + ;; (larger) window because we want to split that again. + (select-window (split-window nil size)) + (find-file (car file-list)) + (setq file-list (cdr file-list)))))