(gnus-blocked-images): Clarify privacy implications
[emacs.git] / lisp / dired-x.el
blob4517dedeeb4009d7862b1f69caf428f15ed44bed
1 ;;; dired-x.el --- extra Dired functionality -*- lexical-binding:t -*-
3 ;; Copyright (C) 1993-1994, 1997, 2001-2018 Free Software Foundation,
4 ;; Inc.
6 ;; Author: Sebastian Kremer <sk@thp.uni-koeln.de>
7 ;; Lawrence R. Dodd <dodd@roebling.poly.edu>
8 ;; Maintainer: Romain Francoise <rfrancoise@gnu.org>
9 ;; Keywords: dired extensions files
10 ;; Package: emacs
12 ;; This file is part of GNU Emacs.
14 ;; GNU Emacs is free software: you can redistribute it and/or modify
15 ;; it under the terms of the GNU General Public License as published by
16 ;; the Free Software Foundation, either version 3 of the License, or
17 ;; (at your option) any later version.
19 ;; GNU Emacs is distributed in the hope that it will be useful,
20 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
21 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 ;; GNU General Public License for more details.
24 ;; You should have received a copy of the GNU General Public License
25 ;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
27 ;;; Commentary:
29 ;; This is based on Sebastian Kremer's excellent dired-x.el (Dired Extra),
30 ;; version 1.191, adapted for GNU Emacs. See the `dired-x' info pages.
32 ;; At load time dired-x.el will install itself and bind some dired keys.
33 ;; Some dired.el and dired-aux.el functions have extra features if
34 ;; dired-x is loaded.
36 ;; User customization: M-x customize-group RET dired-x RET.
38 ;; *Please* see the `dired-x' info pages for more details.
41 ;;; Code:
43 ;; This is a no-op if dired-x is being loaded via `dired-load-hook',
44 ;; but maybe not if a dired-x function is being autoloaded.
45 (require 'dired)
47 ;;; User-defined variables.
49 (defgroup dired-x nil
50 "Extended directory editing (dired-x)."
51 :group 'dired)
53 (defgroup dired-keys nil
54 "Dired keys customizations."
55 :prefix "dired-"
56 :group 'dired-x)
58 (defcustom dired-bind-vm nil
59 "Non-nil means \"V\" runs `dired-vm', otherwise \"V\" runs `dired-rmail'.
60 RMAIL files in the old Babyl format (used before Emacs 23.1)
61 contain \"-*- rmail -*-\" at the top, so `dired-find-file'
62 will run `rmail' on these files. New RMAIL files use the standard
63 mbox format, and so cannot be distinguished in this way."
64 :type 'boolean
65 :group 'dired-keys)
67 (defcustom dired-bind-jump t
68 "Non-nil means bind `dired-jump' to C-x C-j, otherwise do not.
69 Setting this variable directly after dired-x is loaded has no effect -
70 use \\[customize]."
71 :type 'boolean
72 :set (lambda (sym val)
73 (if (set sym val)
74 (progn
75 (define-key ctl-x-map "\C-j" 'dired-jump)
76 (define-key ctl-x-4-map "\C-j" 'dired-jump-other-window))
77 (if (eq 'dired-jump (lookup-key ctl-x-map "\C-j"))
78 (define-key ctl-x-map "\C-j" nil))
79 (if (eq 'dired-jump-other-window (lookup-key ctl-x-4-map "\C-j"))
80 (define-key ctl-x-4-map "\C-j" nil))))
81 :group 'dired-keys)
83 (defcustom dired-bind-man t
84 "Non-nil means bind `dired-man' to \"N\" in Dired, otherwise do not.
85 Setting this variable directly after dired-x is loaded has no effect -
86 use \\[customize]."
87 :type 'boolean
88 :set (lambda (sym val)
89 (if (set sym val)
90 (define-key dired-mode-map "N" 'dired-man)
91 (if (eq 'dired-man (lookup-key dired-mode-map "N"))
92 (define-key dired-mode-map "N" nil))))
93 :group 'dired-keys)
95 (defcustom dired-bind-info t
96 "Non-nil means bind `dired-info' to \"I\" in Dired, otherwise do not.
97 Setting this variable directly after dired-x is loaded has no effect -
98 use \\[customize]."
99 :type 'boolean
100 :set (lambda (sym val)
101 (if (set sym val)
102 (define-key dired-mode-map "I" 'dired-info)
103 (if (eq 'dired-info (lookup-key dired-mode-map "I"))
104 (define-key dired-mode-map "I" nil))))
105 :group 'dired-keys)
107 (defcustom dired-vm-read-only-folders nil
108 "If non-nil, \\[dired-vm] will visit all folders read-only.
109 If neither nil nor t, e.g. the symbol `if-file-read-only', only
110 files not writable by you are visited read-only."
111 :type '(choice (const :tag "off" nil)
112 (const :tag "on" t)
113 (other :tag "non-writable only" if-file-read-only))
114 :group 'dired-x)
116 (defcustom dired-omit-size-limit 30000
117 "Maximum size for the \"omitting\" feature.
118 If nil, there is no maximum size."
119 :type '(choice (const :tag "no maximum" nil) integer)
120 :group 'dired-x)
122 (defcustom dired-omit-case-fold 'filesystem
123 "Determine whether \"omitting\" patterns are case-sensitive.
124 When nil, always be case-sensitive; when t, always be
125 case-insensitive; the default value, `filesystem', causes case
126 folding to be used on case-insensitive filesystems only."
127 :type '(choice (const :tag "Always case-sensitive" nil)
128 (const :tag "Always case-insensitive" t)
129 (const :tag "According to filesystem" filesystem))
130 :group 'dired-x
131 :version "26.1")
133 (declare-function file-name-case-insensitive-p "fileio.c" (filename))
134 (defun dired-omit-case-fold-p (dir)
135 "Non-nil if `dired-omit-mode' should be case-insensitive in DIR."
136 (if (eq dired-omit-case-fold 'filesystem)
137 (file-name-case-insensitive-p dir)
138 dired-omit-case-fold))
140 (define-minor-mode dired-omit-mode
141 "Toggle omission of uninteresting files in Dired (Dired-Omit mode).
142 With a prefix argument ARG, enable Dired-Omit mode if ARG is
143 positive, and disable it otherwise. If called from Lisp, enable
144 the mode if ARG is omitted or nil.
146 Dired-Omit mode is a buffer-local minor mode. When enabled in a
147 Dired buffer, Dired does not list files whose filenames match
148 regexp `dired-omit-files', nor files ending with extensions in
149 `dired-omit-extensions'.
151 To enable omitting in every Dired buffer, you can put this in
152 your init file:
154 (add-hook \\='dired-mode-hook (lambda () (dired-omit-mode)))
156 See Info node `(dired-x) Omitting Variables' for more information."
157 :group 'dired-x
158 (if dired-omit-mode
159 ;; This will mention how many lines were omitted:
160 (let ((dired-omit-size-limit nil)) (dired-omit-expunge))
161 (revert-buffer)))
163 (put 'dired-omit-mode 'safe-local-variable 'booleanp)
165 (defcustom dired-omit-files "^\\.?#\\|^\\.$\\|^\\.\\.$"
166 "Filenames matching this regexp will not be displayed.
167 This only has effect when `dired-omit-mode' is t. See interactive function
168 `dired-omit-mode' (\\[dired-omit-mode]) and variable
169 `dired-omit-extensions'. The default is to omit `.', `..', auto-save
170 files and lock files."
171 :type 'regexp
172 :group 'dired-x)
174 (defcustom dired-omit-verbose t
175 "When non-nil, show messages when omitting files.
176 When nil, don't show messages."
177 :version "24.1"
178 :type 'boolean
179 :group 'dired-x)
181 (defcustom dired-find-subdir nil ; t is pretty near to DWIM...
182 "If non-nil, Dired always finds a directory in a buffer of its own.
183 If nil, Dired finds the directory as a subdirectory in some other buffer
184 if it is present as one.
186 If there are several Dired buffers for a directory, the most recently
187 used is chosen.
189 Dired avoids switching to the current buffer, so that if you have
190 a normal and a wildcard buffer for the same directory, \\[dired] will
191 toggle between those two."
192 :type 'boolean
193 :group 'dired-x)
195 (defcustom dired-guess-shell-gnutar
196 (catch 'found
197 (dolist (exe '("tar" "gtar"))
198 (if (with-temp-buffer
199 (ignore-errors (call-process exe nil t nil "--version"))
200 (and (re-search-backward "GNU tar" nil t) t))
201 (throw 'found exe))))
202 "If non-nil, name of GNU tar executable.
203 \(E.g., \"tar\" or \"gtar\"). The `z' switch will be used with it for
204 compressed or gzip'ed tar files. If you don't have GNU tar, set this
205 to nil: a pipe using `zcat' or `gunzip -c' will be used."
206 ;; Changed from system-type test to testing --version output.
207 ;; Maybe test --help for -z instead?
208 :version "24.1"
209 :type '(choice (const :tag "Not GNU tar" nil)
210 (string :tag "Command name"))
211 :group 'dired-x)
213 (defcustom dired-guess-shell-gzip-quiet t
214 "Non-nil says pass -q to gzip overriding verbose GZIP environment."
215 :type 'boolean
216 :group 'dired-x)
218 (defcustom dired-guess-shell-znew-switches nil
219 "If non-nil, then string of switches passed to `znew', example: \"-K\"."
220 :type '(choice (const :tag "None" nil)
221 (string :tag "Switches"))
222 :group 'dired-x)
224 (defcustom dired-clean-up-buffers-too t
225 "Non-nil means offer to kill buffers visiting files and dirs deleted in Dired."
226 :type 'boolean
227 :group 'dired-x)
229 (defcustom dired-clean-confirm-killing-deleted-buffers t
230 "If nil, don't ask whether to kill buffers visiting deleted files."
231 :version "26.1"
232 :type 'boolean
233 :group 'dired-x)
235 ;;; KEY BINDINGS.
237 (define-key dired-mode-map "\C-x\M-o" 'dired-omit-mode)
238 (define-key dired-mode-map "*O" 'dired-mark-omitted)
239 (define-key dired-mode-map "\M-(" 'dired-mark-sexp)
240 (define-key dired-mode-map "*(" 'dired-mark-sexp)
241 (define-key dired-mode-map "*." 'dired-mark-extension)
242 (define-key dired-mode-map "\M-!" 'dired-smart-shell-command)
243 (define-key dired-mode-map "\M-G" 'dired-goto-subdir)
244 (define-key dired-mode-map "F" 'dired-do-find-marked-files)
245 (define-key dired-mode-map "Y" 'dired-do-relsymlink)
246 (define-key dired-mode-map "%Y" 'dired-do-relsymlink-regexp)
247 (define-key dired-mode-map "V" 'dired-do-run-mail)
249 ;;; MENU BINDINGS
251 (require 'easymenu)
253 (let ((menu (lookup-key dired-mode-map [menu-bar])))
254 (easy-menu-add-item menu '("Operate")
255 ["Find Files" dired-do-find-marked-files
256 :help "Find current or marked files"]
257 "Shell Command...")
258 (easy-menu-add-item menu '("Operate")
259 ["Relative Symlink to..." dired-do-relsymlink
260 :visible (fboundp 'make-symbolic-link)
261 :help "Make relative symbolic links for current or \
262 marked files"]
263 "Hardlink to...")
264 (easy-menu-add-item menu '("Mark")
265 ["Flag Extension..." dired-flag-extension
266 :help "Flag files with a certain extension for deletion"]
267 "Mark Executables")
268 (easy-menu-add-item menu '("Mark")
269 ["Mark Extension..." dired-mark-extension
270 :help "Mark files with a certain extension"]
271 "Unmark All")
272 (easy-menu-add-item menu '("Mark")
273 ["Mark Omitted" dired-mark-omitted
274 :help "Mark files matching `dired-omit-files' \
275 and `dired-omit-extensions'"]
276 "Unmark All")
277 (easy-menu-add-item menu '("Regexp")
278 ["Relative Symlink..." dired-do-relsymlink-regexp
279 :visible (fboundp 'make-symbolic-link)
280 :help "Make relative symbolic links for files \
281 matching regexp"]
282 "Hardlink...")
283 (easy-menu-add-item menu '("Immediate")
284 ["Omit Mode" dired-omit-mode
285 :style toggle :selected dired-omit-mode
286 :help "Enable or disable omitting \"uninteresting\" \
287 files"]
288 "Refresh"))
291 ;; Install into appropriate hooks.
293 (add-hook 'dired-mode-hook 'dired-extra-startup)
294 (add-hook 'dired-after-readin-hook 'dired-omit-expunge)
296 (defun dired-extra-startup ()
297 "Automatically put on `dired-mode-hook' to get extra Dired features:
298 \\<dired-mode-map>
299 \\[dired-do-run-mail]\t-- run mail on folder (see `dired-bind-vm')
300 \\[dired-info]\t-- run info on file
301 \\[dired-man]\t-- run man on file
302 \\[dired-do-find-marked-files]\t-- visit all marked files simultaneously
303 \\[dired-omit-mode]\t-- toggle omitting of files
304 \\[dired-mark-sexp]\t-- mark by Lisp expression
306 To see the options you can set, use M-x customize-group RET dired-x RET.
307 See also the functions:
308 `dired-flag-extension'
309 `dired-virtual'
310 `dired-jump'
311 `dired-man'
312 `dired-vm'
313 `dired-rmail'
314 `dired-info'
315 `dired-do-find-marked-files'"
316 (interactive)
317 ;; These must be done in each new dired buffer.
318 (dired-omit-startup))
321 ;;; EXTENSION MARKING FUNCTIONS.
323 (defun dired--mark-suffix-interactive-spec ()
324 (let* ((default
325 (let ((file (dired-get-filename nil t)))
326 (when file
327 (file-name-extension file))))
328 (suffix
329 (read-string (format "%s extension%s: "
330 (if (equal current-prefix-arg '(4))
331 "UNmarking"
332 "Marking")
333 (if default
334 (format " (default %s)" default)
335 "")) nil nil default))
336 (marker
337 (pcase current-prefix-arg
338 ('(4) ?\s)
339 ('(16)
340 (let* ((dflt (char-to-string dired-marker-char))
341 (input (read-string
342 (format
343 "Marker character to use (default %s): " dflt)
344 nil nil dflt)))
345 (aref input 0)))
346 (_ dired-marker-char))))
347 (list suffix marker)))
349 ;; Mark files with some extension.
350 (defun dired-mark-extension (extension &optional marker-char)
351 "Mark all files with a certain EXTENSION for use in later commands.
352 A `.' is automatically prepended to EXTENSION when not present.
353 EXTENSION may also be a list of extensions instead of a single one.
354 Optional MARKER-CHAR is marker to use.
355 Interactively, ask for EXTENSION.
356 Prefixed with one C-u, unmark files instead.
357 Prefixed with two C-u's, prompt for MARKER-CHAR and mark files with it."
358 (interactive (dired--mark-suffix-interactive-spec))
359 (unless (listp extension)
360 (setq extension (list extension)))
361 (dired-mark-files-regexp
362 (concat ".";; don't match names with nothing but an extension
363 "\\("
364 (mapconcat
365 (lambda (x)
366 (regexp-quote
367 (if (string-prefix-p "." x) x (concat "." x))))
368 extension "\\|")
369 "\\)$")
370 marker-char))
372 ;; Mark files ending with some suffix.
373 (defun dired-mark-suffix (suffix &optional marker-char)
374 "Mark all files with a certain SUFFIX for use in later commands.
375 A `.' is *not* automatically prepended to the string entered; see
376 also `dired-mark-extension', which is similar but automatically
377 prepends `.' when not present.
378 SUFFIX may also be a list of suffixes instead of a single one.
379 Optional MARKER-CHAR is marker to use.
380 Interactively, ask for SUFFIX.
381 Prefixed with one C-u, unmark files instead.
382 Prefixed with two C-u's, prompt for MARKER-CHAR and mark files with it."
383 (interactive (dired--mark-suffix-interactive-spec))
384 (unless (listp suffix)
385 (setq suffix (list suffix)))
386 (dired-mark-files-regexp
387 (concat ".";; don't match names with nothing but an extension
388 "\\("
389 (mapconcat 'regexp-quote suffix "\\|")
390 "\\)$")
391 marker-char))
393 (defun dired-flag-extension (extension)
394 "In Dired, flag all files with a certain EXTENSION for deletion.
395 A `.' is *not* automatically prepended to the string entered."
396 (interactive "sFlagging extension: ")
397 (dired-mark-extension extension dired-del-marker))
399 ;; Define some unpopular file extensions. Used for cleaning and omitting.
401 (defvar dired-patch-unclean-extensions
402 '(".rej" ".orig")
403 "List of extensions of dispensable files created by the `patch' program.")
405 (defvar dired-tex-unclean-extensions
406 '(".toc" ".log" ".aux");; these are already in completion-ignored-extensions
407 "List of extensions of dispensable files created by TeX.")
409 (defvar dired-latex-unclean-extensions
410 '(".idx" ".lof" ".lot" ".glo")
411 "List of extensions of dispensable files created by LaTeX.")
413 (defvar dired-bibtex-unclean-extensions
414 '(".blg" ".bbl")
415 "List of extensions of dispensable files created by BibTeX.")
417 (defvar dired-texinfo-unclean-extensions
418 '(".cp" ".cps" ".fn" ".fns" ".ky" ".kys" ".pg" ".pgs"
419 ".tp" ".tps" ".vr" ".vrs")
420 "List of extensions of dispensable files created by texinfo.")
422 (defun dired-clean-patch ()
423 "Flag dispensable files created by patch for deletion.
424 See variable `dired-patch-unclean-extensions'."
425 (interactive)
426 (dired-flag-extension dired-patch-unclean-extensions))
428 (defun dired-clean-tex ()
429 "Flag dispensable files created by [La]TeX etc. for deletion.
430 See variables `dired-tex-unclean-extensions',
431 `dired-latex-unclean-extensions', `dired-bibtex-unclean-extensions' and
432 `dired-texinfo-unclean-extensions'."
433 (interactive)
434 (dired-flag-extension (append dired-texinfo-unclean-extensions
435 dired-latex-unclean-extensions
436 dired-bibtex-unclean-extensions
437 dired-tex-unclean-extensions)))
439 (defun dired-very-clean-tex ()
440 "Flag dispensable files created by [La]TeX *and* \".dvi\" for deletion.
441 See variables `dired-texinfo-unclean-extensions',
442 `dired-latex-unclean-extensions', `dired-bibtex-unclean-extensions' and
443 `dired-texinfo-unclean-extensions'."
444 (interactive)
445 (dired-flag-extension (append dired-texinfo-unclean-extensions
446 dired-latex-unclean-extensions
447 dired-bibtex-unclean-extensions
448 dired-tex-unclean-extensions
449 (list ".dvi"))))
451 (defvar tar-superior-buffer)
452 ;;; JUMP.
454 ;;;###autoload
455 (defun dired-jump (&optional other-window file-name)
456 "Jump to Dired buffer corresponding to current buffer.
457 If in a file, Dired the current directory and move to file's line.
458 If in Dired already, pop up a level and goto old directory's line.
459 In case the proper Dired file line cannot be found, refresh the dired
460 buffer and try again.
461 When OTHER-WINDOW is non-nil, jump to Dired buffer in other window.
462 When FILE-NAME is non-nil, jump to its line in Dired.
463 Interactively with prefix argument, read FILE-NAME."
464 (interactive
465 (list nil (and current-prefix-arg
466 (read-file-name "Jump to Dired file: "))))
467 (if (bound-and-true-p tar-subfile-mode)
468 (switch-to-buffer tar-superior-buffer)
469 ;; Expand file-name before `dired-goto-file' call:
470 ;; `dired-goto-file' requires its argument to be an absolute
471 ;; file name; the result of `read-file-name' could be
472 ;; an abbreviated file name (Bug#24409).
473 (let* ((file (or (and file-name (expand-file-name file-name))
474 buffer-file-name))
475 (dir (if file (file-name-directory file) default-directory)))
476 (if (and (eq major-mode 'dired-mode) (null file-name))
477 (progn
478 (setq dir (dired-current-directory))
479 (dired-up-directory other-window)
480 (unless (dired-goto-file dir)
481 ;; refresh and try again
482 (dired-insert-subdir (file-name-directory dir))
483 (dired-goto-file dir)))
484 (if other-window
485 (dired-other-window dir)
486 (dired dir))
487 (if file
488 (or (dired-goto-file file)
489 ;; refresh and try again
490 (progn
491 (dired-insert-subdir (file-name-directory file))
492 (dired-goto-file file))
493 ;; Toggle omitting, if it is on, and try again.
494 (when dired-omit-mode
495 (dired-omit-mode)
496 (dired-goto-file file))))))))
498 ;;;###autoload
499 (defun dired-jump-other-window (&optional file-name)
500 "Like \\[dired-jump] (`dired-jump') but in other window."
501 (interactive
502 (list (and current-prefix-arg
503 (read-file-name "Jump to Dired file: "))))
504 (dired-jump t file-name))
506 ;;; OMITTING.
508 ;; Enhanced omitting of lines from directory listings.
509 ;; Marked files are never omitted.
511 ;; should probably get rid of this and always use 'no-dir.
512 ;; sk 28-Aug-1991 09:37
513 (defvar dired-omit-localp 'no-dir
514 "The LOCALP argument `dired-omit-expunge' passes to `dired-get-filename'.
515 If it is `no-dir', omitting is much faster, but you can only match
516 against the non-directory part of the file name. Set it to nil if you
517 need to match the entire file name.")
519 ;; \017=^O for Omit - other packages can chose other control characters.
520 (defvar dired-omit-marker-char ?\017
521 "Temporary marker used by Dired-Omit.
522 Should never be used as marker by the user or other packages.")
524 (defun dired-omit-startup ()
525 (or (assq 'dired-omit-mode minor-mode-alist)
526 (setq minor-mode-alist
527 (append '((dired-omit-mode
528 (:eval (if (eq major-mode 'dired-mode)
529 " Omit" ""))))
530 minor-mode-alist))))
532 (defun dired-mark-omitted ()
533 "Mark files matching `dired-omit-files' and `dired-omit-extensions'."
534 (interactive)
535 (let ((dired-omit-mode nil)) (revert-buffer)) ;; Show omitted files
536 (dired-mark-unmarked-files (dired-omit-regexp) nil nil dired-omit-localp
537 (dired-omit-case-fold-p (if (stringp dired-directory)
538 dired-directory
539 (car dired-directory)))))
541 (defcustom dired-omit-extensions
542 (append completion-ignored-extensions
543 dired-latex-unclean-extensions
544 dired-bibtex-unclean-extensions
545 dired-texinfo-unclean-extensions)
546 "If non-nil, a list of extensions (strings) to omit from Dired listings.
547 Defaults to elements of `completion-ignored-extensions',
548 `dired-latex-unclean-extensions', `dired-bibtex-unclean-extensions', and
549 `dired-texinfo-unclean-extensions'.
551 See interactive function `dired-omit-mode' (\\[dired-omit-mode]) and
552 variables `dired-omit-mode' and `dired-omit-files'."
553 :type '(repeat string)
554 :group 'dired-x)
556 (defun dired-omit-expunge (&optional regexp)
557 "Erases all unmarked files matching REGEXP.
558 Does nothing if global variable `dired-omit-mode' is nil, or if called
559 non-interactively and buffer is bigger than `dired-omit-size-limit'.
560 If REGEXP is nil or not specified, uses `dired-omit-files', and also omits
561 filenames ending in `dired-omit-extensions'.
562 If REGEXP is the empty string, this function is a no-op.
564 This functions works by temporarily binding `dired-marker-char' to
565 `dired-omit-marker-char' and calling `dired-do-kill-lines'."
566 (interactive "sOmit files (regexp): ")
567 (if (and dired-omit-mode
568 (or (called-interactively-p 'interactive)
569 (not dired-omit-size-limit)
570 (< (buffer-size) dired-omit-size-limit)
571 (progn
572 (when dired-omit-verbose
573 (message "Not omitting: directory larger than %d characters."
574 dired-omit-size-limit))
575 (setq dired-omit-mode nil)
576 nil)))
577 (let ((omit-re (or regexp (dired-omit-regexp)))
578 (old-modified-p (buffer-modified-p))
579 count)
580 (or (string= omit-re "")
581 (let ((dired-marker-char dired-omit-marker-char))
582 (when dired-omit-verbose (message "Omitting..."))
583 (if (dired-mark-unmarked-files omit-re nil nil dired-omit-localp
584 (dired-omit-case-fold-p (if (stringp dired-directory)
585 dired-directory
586 (car dired-directory))))
587 (progn
588 (setq count (dired-do-kill-lines
590 (if dired-omit-verbose "Omitted %d line%s." "")))
591 (force-mode-line-update))
592 (when dired-omit-verbose (message "(Nothing to omit)")))))
593 ;; Try to preserve modified state of buffer. So `%*' doesn't appear
594 ;; in mode-line of omitted buffers.
595 (set-buffer-modified-p (and old-modified-p
596 (save-excursion
597 (goto-char (point-min))
598 (re-search-forward dired-re-mark nil t))))
599 count)))
601 (defun dired-omit-regexp ()
602 (concat (if dired-omit-files (concat "\\(" dired-omit-files "\\)") "")
603 (if (and dired-omit-files dired-omit-extensions) "\\|" "")
604 (if dired-omit-extensions
605 (concat ".";; a non-extension part should exist
606 "\\("
607 (mapconcat 'regexp-quote dired-omit-extensions "\\|")
608 "\\)$")
609 "")))
611 ;; Returns t if any work was done, nil otherwise.
612 (defun dired-mark-unmarked-files (regexp msg &optional unflag-p localp case-fold-p)
613 "Mark unmarked files matching REGEXP, displaying MSG.
614 REGEXP is matched against the entire file name. When called
615 interactively, prompt for REGEXP.
616 With prefix argument, unflag all those files.
617 Optional fourth argument LOCALP is as in `dired-get-filename'.
618 Optional fifth argument CASE-FOLD-P specifies the value of
619 `case-fold-search' used for matching REGEXP."
620 (interactive
621 (list (read-regexp
622 "Mark unmarked files matching regexp (default all): "
623 nil 'dired-regexp-history)
624 nil current-prefix-arg nil))
625 (let ((dired-marker-char (if unflag-p ?\s dired-marker-char)))
626 (dired-mark-if
627 (and
628 ;; not already marked
629 (= (following-char) ?\s)
630 ;; uninteresting
631 (let ((fn (dired-get-filename localp t))
632 ;; Match patterns case-insensitively on case-insensitive
633 ;; systems
634 (case-fold-search case-fold-p))
635 (and fn (string-match-p regexp fn))))
636 msg)))
639 ;;; VIRTUAL DIRED MODE.
641 ;; For browsing `ls -lR' listings in a dired-like fashion.
643 (defalias 'virtual-dired 'dired-virtual)
644 (defun dired-virtual (dirname &optional switches)
645 "Put this buffer into Virtual Dired mode.
647 In Virtual Dired mode, all commands that do not actually consult the
648 filesystem will work.
650 This is useful if you want to peruse and move around in an ls -lR
651 output file, for example one you got from an ftp server. With
652 ange-ftp, you can even Dired a directory containing an ls-lR file,
653 visit that file and turn on Virtual Dired mode. But don't try to save
654 this file, as dired-virtual indents the listing and thus changes the
655 buffer.
657 If you have save a Dired buffer in a file you can use \\[dired-virtual] to
658 resume it in a later session.
660 Type \\<dired-mode-map>\\[revert-buffer] \
661 in the Virtual Dired buffer and answer `y' to convert
662 the virtual to a real Dired buffer again. You don't have to do this, though:
663 you can relist single subdirs using \\[dired-do-redisplay]."
665 ;; DIRNAME is the top level directory of the buffer. It will become
666 ;; its `default-directory'. If nil, the old value of
667 ;; default-directory is used.
669 ;; Optional SWITCHES are the ls switches to use.
671 ;; Shell wildcards will be used if there already is a `wildcard'
672 ;; line in the buffer (thus it is a saved Dired buffer), but there
673 ;; is no other way to get wildcards. Insert a `wildcard' line by
674 ;; hand if you want them.
676 (interactive
677 (list (read-string "Virtual Dired directory: " (dired-virtual-guess-dir))))
678 (goto-char (point-min))
679 (or (looking-at-p " ")
680 ;; if not already indented, do it now:
681 (indent-region (point-min) (point-max) 2))
682 (or dirname (setq dirname default-directory))
683 (setq dirname (expand-file-name (file-name-as-directory dirname)))
684 (setq default-directory dirname) ; contains no wildcards
685 (let ((wildcard (save-excursion
686 (goto-char (point-min))
687 (forward-line 1)
688 (and (looking-at "^ wildcard ")
689 (buffer-substring (match-end 0)
690 (line-end-position))))))
691 (if wildcard
692 (setq dirname (expand-file-name wildcard default-directory))))
693 ;; If raw ls listing (not a saved old dired buffer), give it a
694 ;; decent subdir headerline:
695 (goto-char (point-min))
696 (or (looking-at-p dired-subdir-regexp)
697 (insert " "
698 (directory-file-name (file-name-directory default-directory))
699 ":\n"))
700 (dired-mode dirname (or switches dired-listing-switches))
701 (setq mode-name "Virtual Dired"
702 revert-buffer-function 'dired-virtual-revert)
703 (set (make-local-variable 'dired-subdir-alist) nil)
704 (dired-build-subdir-alist)
705 (goto-char (point-min))
706 (dired-initial-position dirname))
708 (defun dired-virtual-guess-dir ()
709 "Guess and return appropriate working directory of this buffer.
710 The buffer is assumed to be in Dired or ls -lR format. The guess is
711 based upon buffer contents. If nothing could be guessed, returns
712 nil."
714 (let ((regexp "^\\( \\)?\\([^ \n\r]*\\)\\(:\\)[\n\r]")
715 (subexpr 2))
716 (goto-char (point-min))
717 (cond ((looking-at regexp)
718 ;; If a saved dired buffer, look to which dir and
719 ;; perhaps wildcard it belongs:
720 (let ((dir (buffer-substring (match-beginning subexpr)
721 (match-end subexpr))))
722 (file-name-as-directory dir)))
723 ;; Else no match for headerline found. It's a raw ls listing.
724 ;; In raw ls listings the directory does not have a headerline
725 ;; try parent of first subdir, if any
726 ((re-search-forward regexp nil t)
727 (file-name-directory
728 (directory-file-name
729 (file-name-as-directory
730 (buffer-substring (match-beginning subexpr)
731 (match-end subexpr))))))
732 (t ; if all else fails
733 nil))))
736 (defun dired-virtual-revert (&optional _arg _noconfirm)
737 (if (not
738 (y-or-n-p "Cannot revert a Virtual Dired buffer - switch to Real Dired mode? "))
739 (error "Cannot revert a Virtual Dired buffer")
740 (setq mode-name "Dired"
741 revert-buffer-function 'dired-revert)
742 (revert-buffer)))
744 ;; A zero-arg version of dired-virtual.
745 (defun dired-virtual-mode ()
746 "Put current buffer into Virtual Dired mode (see `dired-virtual').
747 Useful on `magic-mode-alist' with the regexp
749 \"^ \\\\(/[^ /]+\\\\)+/?:$\"
751 to put saved Dired buffers automatically into Virtual Dired mode.
753 Also useful for `auto-mode-alist' like this:
755 (add-to-list \\='auto-mode-alist
756 \\='(\"[^/]\\\\.dired\\\\\\='\" . dired-virtual-mode))"
757 (interactive)
758 (dired-virtual (dired-virtual-guess-dir)))
761 ;;; SMART SHELL.
763 ;; An Emacs buffer can have but one working directory, stored in the
764 ;; buffer-local variable `default-directory'. A Dired buffer may have
765 ;; several subdirectories inserted, but still has but one working directory:
766 ;; that of the top level Dired directory in that buffer. For some commands
767 ;; it is appropriate that they use the current Dired directory instead of
768 ;; `default-directory', e.g., `find-file' and `compile'. This is a general
769 ;; mechanism is provided for special handling of the working directory in
770 ;; special major modes.
772 (defun dired-smart-shell-command (command &optional output-buffer error-buffer)
773 "Like function `shell-command', but in the current Virtual Dired directory."
774 (interactive
775 (list
776 (read-shell-command "Shell command: " nil nil
777 (cond
778 (buffer-file-name (file-relative-name buffer-file-name))
779 ((eq major-mode 'dired-mode) (dired-get-filename t t))))
780 current-prefix-arg
781 shell-command-default-error-buffer))
782 (let ((default-directory (or (and (eq major-mode 'dired-mode)
783 (dired-current-directory))
784 default-directory)))
785 (shell-command command output-buffer error-buffer)))
788 ;;; GUESS SHELL COMMAND.
790 ;; Brief Description:
792 ;; * `dired-do-shell-command' is bound to `!' by dired.el.
794 ;; * `dired-guess-shell-command' provides smarter defaults for
795 ;;; dired-aux.el's `dired-read-shell-command'.
797 ;; * `dired-guess-shell-command' calls `dired-guess-default' with list of
798 ;;; marked files.
800 ;; * Parse `dired-guess-shell-alist-user' and
801 ;;; `dired-guess-shell-alist-default' (in that order) for the first REGEXP
802 ;;; that matches the first file in the file list.
804 ;; * If the REGEXP matches all the entries of the file list then evaluate
805 ;;; COMMAND, which is either a string or a Lisp expression returning a
806 ;;; string. COMMAND may be a list of commands.
808 ;; * Return this command to `dired-guess-shell-command' which prompts user
809 ;;; with it. The list of commands is put into the list of default values.
810 ;;; If a command is used successfully then it is stored permanently in
811 ;;; `dired-shell-command-history'.
813 ;; Guess what shell command to apply to a file.
814 (defvar dired-shell-command-history nil
815 "History list for commands that read dired-shell commands.")
817 ;; Default list of shell commands.
819 ;; NOTE: Use `gunzip -c' instead of `zcat' on `.gz' files. Some do not
820 ;; install GNU zip's version of zcat.
822 (autoload 'Man-support-local-filenames "man")
824 (defvar dired-guess-shell-alist-default
825 (list
826 (list "\\.tar\\'"
827 '(if dired-guess-shell-gnutar
828 (concat dired-guess-shell-gnutar " xvf")
829 "tar xvf")
830 ;; Extract files into a separate subdirectory
831 '(if dired-guess-shell-gnutar
832 (concat "mkdir " (file-name-sans-extension file)
833 "; " dired-guess-shell-gnutar " -C "
834 (file-name-sans-extension file) " -xvf")
835 (concat "mkdir " (file-name-sans-extension file)
836 "; tar -C " (file-name-sans-extension file) " -xvf"))
837 ;; List archive contents.
838 '(if dired-guess-shell-gnutar
839 (concat dired-guess-shell-gnutar " tvf")
840 "tar tvf"))
842 ;; REGEXPS for compressed archives must come before the .Z rule to
843 ;; be recognized:
844 (list "\\.tar\\.Z\\'"
845 ;; Untar it.
846 '(if dired-guess-shell-gnutar
847 (concat dired-guess-shell-gnutar " zxvf")
848 (concat "zcat * | tar xvf -"))
849 ;; Optional conversion to gzip format.
850 '(concat "znew" (if dired-guess-shell-gzip-quiet " -q")
851 " " dired-guess-shell-znew-switches))
853 ;; gzip'ed archives
854 (list "\\.t\\(ar\\.\\)?gz\\'"
855 '(if dired-guess-shell-gnutar
856 (concat dired-guess-shell-gnutar " zxvf")
857 (concat "gunzip -qc * | tar xvf -"))
858 ;; Extract files into a separate subdirectory
859 '(if dired-guess-shell-gnutar
860 (concat "mkdir " (file-name-sans-extension file)
861 "; " dired-guess-shell-gnutar " -C "
862 (file-name-sans-extension file) " -zxvf")
863 (concat "mkdir " (file-name-sans-extension file)
864 "; gunzip -qc * | tar -C "
865 (file-name-sans-extension file) " -xvf -"))
866 ;; Optional decompression.
867 '(concat "gunzip" (if dired-guess-shell-gzip-quiet " -q" ""))
868 ;; List archive contents.
869 '(if dired-guess-shell-gnutar
870 (concat dired-guess-shell-gnutar " ztvf")
871 (concat "gunzip -qc * | tar tvf -")))
873 ;; bzip2'ed archives
874 (list "\\.t\\(ar\\.bz2\\|bz\\)\\'"
875 "bunzip2 -c * | tar xvf -"
876 ;; Extract files into a separate subdirectory
877 '(concat "mkdir " (file-name-sans-extension file)
878 "; bunzip2 -c * | tar -C "
879 (file-name-sans-extension file) " -xvf -")
880 ;; Optional decompression.
881 "bunzip2")
883 ;; xz'ed archives
884 (list "\\.t\\(ar\\.\\)?xz\\'"
885 "unxz -c * | tar xvf -"
886 ;; Extract files into a separate subdirectory
887 '(concat "mkdir " (file-name-sans-extension file)
888 "; unxz -c * | tar -C "
889 (file-name-sans-extension file) " -xvf -")
890 ;; Optional decompression.
891 "unxz")
893 '("\\.shar\\.Z\\'" "zcat * | unshar")
894 '("\\.shar\\.g?z\\'" "gunzip -qc * | unshar")
896 '("\\.e?ps\\'" "ghostview" "xloadimage" "lpr")
897 (list "\\.e?ps\\.g?z\\'" "gunzip -qc * | ghostview -"
898 ;; Optional decompression.
899 '(concat "gunzip" (if dired-guess-shell-gzip-quiet " -q")))
900 (list "\\.e?ps\\.Z\\'" "zcat * | ghostview -"
901 ;; Optional conversion to gzip format.
902 '(concat "znew" (if dired-guess-shell-gzip-quiet " -q")
903 " " dired-guess-shell-znew-switches))
905 '("\\.patch\\'" "cat * | patch")
906 (list "\\.patch\\.g?z\\'" "gunzip -qc * | patch"
907 ;; Optional decompression.
908 '(concat "gunzip" (if dired-guess-shell-gzip-quiet " -q")))
909 (list "\\.patch\\.Z\\'" "zcat * | patch"
910 ;; Optional conversion to gzip format.
911 '(concat "znew" (if dired-guess-shell-gzip-quiet " -q")
912 " " dired-guess-shell-znew-switches))
914 ;; The following four extensions are useful with dired-man ("N" key)
915 ;; FIXME "man ./" does not work with dired-do-shell-command,
916 ;; because there seems to be no way for us to modify the filename,
917 ;; only the command. Hmph. `dired-man' works though.
918 (list "\\.\\(?:[0-9]\\|man\\)\\'"
919 '(let ((loc (Man-support-local-filenames)))
920 (cond ((eq loc 'man-db) "man -l")
921 ((eq loc 'man) "man ./")
923 "cat * | tbl | nroff -man -h | col -b"))))
924 (list "\\.\\(?:[0-9]\\|man\\)\\.g?z\\'"
925 '(let ((loc (Man-support-local-filenames)))
926 (cond ((eq loc 'man-db)
927 "man -l")
928 ((eq loc 'man)
929 "man ./")
930 (t "gunzip -qc * | tbl | nroff -man -h | col -b")))
931 ;; Optional decompression.
932 '(concat "gunzip" (if dired-guess-shell-gzip-quiet " -q")))
933 (list "\\.[0-9]\\.Z\\'"
934 '(let ((loc (Man-support-local-filenames)))
935 (cond ((eq loc 'man-db) "man -l")
936 ((eq loc 'man) "man ./")
937 (t "zcat * | tbl | nroff -man -h | col -b")))
938 ;; Optional conversion to gzip format.
939 '(concat "znew" (if dired-guess-shell-gzip-quiet " -q")
940 " " dired-guess-shell-znew-switches))
941 '("\\.pod\\'" "perldoc" "pod2man * | nroff -man")
943 '("\\.dvi\\'" "xdvi" "dvips") ; preview and printing
944 '("\\.au\\'" "play") ; play Sun audiofiles
945 '("\\.mpe?g\\'\\|\\.avi\\'" "xine -p")
946 '("\\.ogg\\'" "ogg123")
947 '("\\.mp3\\'" "mpg123")
948 '("\\.wav\\'" "play")
949 '("\\.uu\\'" "uudecode") ; for uudecoded files
950 '("\\.hqx\\'" "mcvert")
951 '("\\.sh\\'" "sh") ; execute shell scripts
952 '("\\.xbm\\'" "bitmap") ; view X11 bitmaps
953 '("\\.gp\\'" "gnuplot")
954 '("\\.p[bgpn]m\\'" "xloadimage")
955 '("\\.gif\\'" "xloadimage") ; view gif pictures
956 '("\\.tif\\'" "xloadimage")
957 '("\\.png\\'" "display") ; xloadimage 4.1 doesn't grok PNG
958 '("\\.jpe?g\\'" "xloadimage")
959 '("\\.fig\\'" "xfig") ; edit fig pictures
960 '("\\.out\\'" "xgraph") ; for plotting purposes.
961 '("\\.tex\\'" "latex" "tex")
962 '("\\.texi\\(nfo\\)?\\'" "makeinfo" "texi2dvi")
963 '("\\.pdf\\'" "xpdf")
964 '("\\.doc\\'" "antiword" "strings")
965 '("\\.rpm\\'" "rpm -qilp" "rpm -ivh")
966 '("\\.dia\\'" "dia")
967 '("\\.mgp\\'" "mgp")
969 ;; Some other popular archivers.
970 (list "\\.zip\\'" "unzip" "unzip -l"
971 ;; Extract files into a separate subdirectory
972 '(concat "unzip" (if dired-guess-shell-gzip-quiet " -q")
973 " -d " (file-name-sans-extension file)))
974 '("\\.zoo\\'" "zoo x//")
975 '("\\.lzh\\'" "lharc x")
976 '("\\.arc\\'" "arc x")
977 '("\\.shar\\'" "unshar")
978 '("\\.rar\\'" "unrar x")
979 '("\\.7z\\'" "7z x")
981 ;; Compression.
982 (list "\\.g?z\\'" '(concat "gunzip" (if dired-guess-shell-gzip-quiet " -q")))
983 (list "\\.dz\\'" "dictunzip")
984 (list "\\.bz2\\'" "bunzip2")
985 (list "\\.xz\\'" "unxz")
986 (list "\\.Z\\'" "uncompress"
987 ;; Optional conversion to gzip format.
988 '(concat "znew" (if dired-guess-shell-gzip-quiet " -q")
989 " " dired-guess-shell-znew-switches))
991 '("\\.sign?\\'" "gpg --verify"))
993 "Default alist used for shell command guessing.
994 See `dired-guess-shell-alist-user'.")
996 (defcustom dired-guess-shell-alist-user nil
997 "User-defined alist of rules for suggested commands.
998 These rules take precedence over the predefined rules in the variable
999 `dired-guess-shell-alist-default' (to which they are prepended).
1001 Each element of this list looks like
1003 (REGEXP COMMAND...)
1005 where each COMMAND can either be a string or a Lisp expression that evaluates
1006 to a string. If this expression needs to consult the name of the file for
1007 which the shell commands are being requested, it can access that file name
1008 as the variable `file'.
1009 If several COMMANDs are given, the first one will be the default
1010 and the rest will be added temporarily to the history and can be retrieved
1011 with \\[previous-history-element] (M-p) .
1013 The variable `dired-guess-shell-case-fold-search' controls whether
1014 REGEXP is matched case-sensitively."
1015 :group 'dired-x
1016 :type '(alist :key-type regexp :value-type (repeat sexp)))
1018 (defcustom dired-guess-shell-case-fold-search t
1019 "If non-nil, `dired-guess-shell-alist-default' and
1020 `dired-guess-shell-alist-user' are matched case-insensitively."
1021 :group 'dired-x
1022 :type 'boolean)
1024 (defun dired-guess-default (files)
1025 "Return a shell command, or a list of commands, appropriate for FILES.
1026 See `dired-guess-shell-alist-user'."
1028 (let* ((case-fold-search dired-guess-shell-case-fold-search)
1029 ;; Prepend the user's alist to the default alist.
1030 (alist (append dired-guess-shell-alist-user
1031 dired-guess-shell-alist-default))
1032 (file (car files))
1033 (flist (cdr files))
1034 elt regexp cmds)
1036 ;; Find the first match in the alist for first file in FILES.
1037 (while alist
1038 (setq elt (car alist)
1039 regexp (car elt)
1040 alist (cdr alist))
1041 (if (string-match-p regexp file)
1042 (setq cmds (cdr elt)
1043 alist nil)))
1045 ;; If more than one file, see if all of FILES match regular expression.
1046 (while (and flist
1047 (string-match-p regexp (car flist)))
1048 (setq flist (cdr flist)))
1050 ;; If flist is still non-nil, then do not guess since this means that not
1051 ;; all the files in FILES were matched by the regexp.
1052 (setq cmds (and (not flist) cmds))
1054 ;; Return commands or nil if flist is still non-nil.
1055 ;; Evaluate the commands in order that any logical testing will be done.
1056 (if (cdr cmds)
1057 (delete-dups (mapcar (lambda (cmd) (eval cmd `((file . ,file)))) cmds))
1058 (eval (car cmds) `((file . ,file)))))) ; single command
1060 (defun dired-guess-shell-command (prompt files)
1061 "Ask user with PROMPT for a shell command, guessing a default from FILES."
1062 (let ((default (dired-guess-default files))
1063 default-list val)
1064 (if (null default)
1065 ;; Nothing to guess
1066 (read-shell-command prompt nil 'dired-shell-command-history)
1067 (setq prompt (replace-regexp-in-string ": $" " " prompt))
1068 (if (listp default)
1069 ;; More than one guess
1070 (setq default-list default
1071 default (car default)
1072 prompt (concat
1073 prompt
1074 (format "{%d guesses} " (length default-list))))
1075 ;; Just one guess
1076 (setq default-list (list default)))
1077 ;; Put the first guess in the prompt but not in the initial value.
1078 (setq prompt (concat prompt (format "[%s]: " default)))
1079 ;; All guesses can be retrieved with M-n
1080 (setq val (read-shell-command prompt nil
1081 'dired-shell-command-history
1082 default-list))
1083 ;; If we got a return, then return default.
1084 (if (equal val "") default val))))
1087 ;;; RELATIVE SYMBOLIC LINKS.
1089 (declare-function make-symbolic-link "fileio.c")
1091 (defvar dired-keep-marker-relsymlink ?S
1092 "See variable `dired-keep-marker-move'.")
1094 (defun dired-make-relative-symlink (file1 file2 &optional ok-if-already-exists)
1095 "Make a symbolic link (pointing to FILE1) in FILE2.
1096 The link is relative (if possible), for example
1098 \"/vol/tex/bin/foo\" \"/vol/local/bin/foo\"
1100 results in
1102 \"../../tex/bin/foo\" \"/vol/local/bin/foo\""
1103 (interactive "FRelSymLink: \nFRelSymLink %s: \np")
1104 (let (name1 name2 len1 len2 (index 0) sub)
1105 (setq file1 (expand-file-name file1)
1106 file2 (expand-file-name file2)
1107 len1 (length file1)
1108 len2 (length file2))
1109 ;; Find common initial file name components:
1110 (let (next)
1111 (while (and (setq next (string-match "/" file1 index))
1112 (< (setq next (1+ next)) (min len1 len2))
1113 ;; For the comparison, both substrings must end in
1114 ;; `/', so NEXT is *one plus* the result of the
1115 ;; string-match.
1116 ;; E.g., consider the case of linking "/tmp/a/abc"
1117 ;; to "/tmp/abc" erroneously giving "/tmp/a" instead
1118 ;; of "/tmp/" as common initial component
1119 (string-equal (substring file1 0 next)
1120 (substring file2 0 next)))
1121 (setq index next))
1122 (setq name2 file2
1123 sub (substring file1 0 index)
1124 name1 (substring file1 index)))
1125 (if (string-equal sub "/")
1126 ;; No common initial file name found
1127 (setq name1 file1)
1128 ;; Else they have a common parent directory
1129 (let ((tem (substring file2 index))
1130 (start 0)
1131 (count 0))
1132 ;; Count number of slashes we must compensate for ...
1133 (while (setq start (string-match "/" tem start))
1134 (setq count (1+ count)
1135 start (1+ start)))
1136 ;; ... and prepend a "../" for each slash found:
1137 (dotimes (_ count)
1138 (setq name1 (concat "../" name1)))))
1139 (make-symbolic-link
1140 (directory-file-name name1) ; must not link to foo/
1141 ; (trailing slash!)
1142 name2 ok-if-already-exists)))
1144 (autoload 'dired-do-create-files "dired-aux")
1146 ;;;###autoload
1147 (defun dired-do-relsymlink (&optional arg)
1148 "Relative symlink all marked (or next ARG) files into a directory.
1149 Otherwise make a relative symbolic link to the current file.
1150 This creates relative symbolic links like
1152 foo -> ../bar/foo
1154 not absolute ones like
1156 foo -> /ugly/file/name/that/may/change/any/day/bar/foo
1158 For absolute symlinks, use \\[dired-do-symlink]."
1159 (interactive "P")
1160 (dired-do-create-files 'relsymlink #'dired-make-relative-symlink
1161 "RelSymLink" arg dired-keep-marker-relsymlink))
1163 (autoload 'dired-mark-read-regexp "dired-aux")
1164 (autoload 'dired-do-create-files-regexp "dired-aux")
1166 (defun dired-do-relsymlink-regexp (regexp newname &optional arg whole-name)
1167 "RelSymlink all marked files containing REGEXP to NEWNAME.
1168 See functions `dired-do-rename-regexp' and `dired-do-relsymlink'
1169 for more info."
1170 (interactive (dired-mark-read-regexp "RelSymLink"))
1171 (dired-do-create-files-regexp
1172 #'dired-make-relative-symlink
1173 "RelSymLink" arg regexp newname whole-name dired-keep-marker-relsymlink))
1176 ;;; VISIT ALL MARKED FILES SIMULTANEOUSLY.
1178 ;; Brief Description:
1180 ;; `dired-do-find-marked-files' is bound to `F' by dired-x.el.
1182 ;; * Use `dired-get-marked-files' to collect the marked files in the current
1183 ;;; Dired Buffer into a list of filenames `FILE-LIST'.
1185 ;; * Pass FILE-LIST to `dired-simultaneous-find-file' all with
1186 ;;; `dired-do-find-marked-files''s prefix argument NOSELECT.
1188 ;; * `dired-simultaneous-find-file' runs through FILE-LIST decrementing the
1189 ;;; list each time.
1191 ;; * If NOSELECT is non-nil then just run `find-file-noselect' on each
1192 ;;; element of FILE-LIST.
1194 ;; * If NOSELECT is nil then calculate the `size' of the window for each file
1195 ;;; by dividing the `window-height' by length of FILE-LIST. Thus, `size' is
1196 ;;; cognizant of the window-configuration.
1198 ;; * If `size' is too small abort, otherwise run `find-file' on each element
1199 ;;; of FILE-LIST giving each a window of height `size'.
1201 (defun dired-do-find-marked-files (&optional noselect)
1202 "Find all marked files displaying all of them simultaneously.
1203 With optional NOSELECT just find files but do not select them.
1205 The current window is split across all files marked, as evenly as possible.
1206 Remaining lines go to bottom-most window. The number of files that can be
1207 displayed this way is restricted by the height of the current window and
1208 `window-min-height'.
1210 To keep Dired buffer displayed, type \\[split-window-below] first.
1211 To display just marked files, type \\[delete-other-windows] first."
1212 (interactive "P")
1213 (dired-simultaneous-find-file (dired-get-marked-files nil nil nil nil t)
1214 noselect))
1216 (defun dired-simultaneous-find-file (file-list noselect)
1217 "Visit all files in FILE-LIST and display them simultaneously.
1218 The current window is split across all files in FILE-LIST, as evenly as
1219 possible. Remaining lines go to the bottom-most window. The number of
1220 files that can be displayed this way is restricted by the height of the
1221 current window and the variable `window-min-height'. With non-nil
1222 NOSELECT the files are merely found but not selected."
1223 ;; We don't make this function interactive because it is usually too clumsy
1224 ;; to specify FILE-LIST interactively unless via dired.
1225 (let (size)
1226 (if noselect
1227 ;; Do not select the buffer.
1228 (find-file-noselect (car file-list))
1229 ;; We will have to select the buffer. Calculate and check window size.
1230 (setq size (/ (window-height) (length file-list)))
1231 (or (<= window-min-height size)
1232 (error "Too many files to visit simultaneously. Try C-u prefix"))
1233 (find-file (car file-list)))
1234 ;; Decrement.
1235 (dolist (file (cdr file-list))
1236 (if noselect
1237 ;; Do not select the buffer.
1238 (find-file-noselect file)
1239 ;; Vertically split off a window of desired size. Upper window will
1240 ;; have SIZE lines. Select lower (larger) window. We split it again.
1241 (select-window (split-window nil size))
1242 (find-file file)))))
1245 ;;; MISCELLANEOUS COMMANDS.
1247 ;; Run man on files.
1249 (declare-function Man-getpage-in-background "man" (topic))
1251 (defvar manual-program) ; from man.el
1253 (defun dired-man ()
1254 "Run `man' on this file."
1255 ;; Used also to say: "Display old buffer if buffer name matches filename."
1256 ;; but I have no idea what that means.
1257 (interactive)
1258 (require 'man)
1259 (let* ((file (dired-get-filename))
1260 (manual-program (replace-regexp-in-string "\\*" "%s"
1261 (dired-guess-shell-command
1262 "Man command: " (list file)))))
1263 (Man-getpage-in-background file)))
1265 ;; Run Info on files.
1267 (defun dired-info ()
1268 "Run `info' on this file."
1269 (interactive)
1270 (info (dired-get-filename)))
1272 ;; Run mail on mail folders.
1274 (declare-function vm-visit-folder "ext:vm" (folder &optional read-only))
1275 (defvar vm-folder-directory)
1277 (defun dired-vm (&optional read-only)
1278 "Run VM on this file.
1279 With optional prefix argument, visits the folder read-only.
1280 Otherwise obeys the value of `dired-vm-read-only-folders'."
1281 (interactive "P")
1282 (let ((dir (dired-current-directory))
1283 (fil (dired-get-filename)))
1284 (vm-visit-folder fil (or read-only
1285 (eq t dired-vm-read-only-folders)
1286 (and dired-vm-read-only-folders
1287 (not (file-writable-p fil)))))
1288 ;; So that pressing `v' inside VM does prompt within current directory:
1289 (set (make-local-variable 'vm-folder-directory) dir)))
1291 (defun dired-rmail ()
1292 "Run RMAIL on this file."
1293 (interactive)
1294 (rmail (dired-get-filename)))
1296 (defun dired-do-run-mail ()
1297 "Visit the current file as a mailbox, using VM or RMAIL.
1298 Prompt for confirmation first; if the user says yes, call
1299 `dired-vm' if `dired-bind-vm' is non-nil, `dired-rmail'
1300 otherwise."
1301 (interactive)
1302 (let ((file (dired-get-filename t)))
1303 (if dired-bind-vm
1304 (if (y-or-n-p (format-message
1305 "Visit `%s' as a mail folder with VM?" file))
1306 (dired-vm))
1307 ;; Read mail folder using rmail.
1308 (if (y-or-n-p (format-message
1309 "Visit `%s' as a mailbox with RMAIL?" file))
1310 (dired-rmail)))))
1313 ;;; MISCELLANEOUS INTERNAL FUNCTIONS.
1315 ;; This should be a builtin
1316 (defun dired-buffer-more-recently-used-p (buffer1 buffer2)
1317 "Return t if BUFFER1 is more recently used than BUFFER2.
1318 Considers buffers closer to the car of `buffer-list' to be more recent."
1319 (and (not (equal buffer1 buffer2))
1320 (memq buffer1 (buffer-list))
1321 (not (memq buffer1 (memq buffer2 (buffer-list))))))
1324 ;; Needed if ls -lh is supported and also for GNU ls -ls.
1325 (defun dired-x--string-to-number (str)
1326 "Like `string-to-number' but recognize a trailing unit prefix.
1327 For example, 2K is expanded to 2048.0. The caller should make
1328 sure that a trailing letter in STR is one of BKkMGTPEZY."
1329 (let* ((val (string-to-number str))
1330 (u (unless (zerop val)
1331 (aref str (1- (length str))))))
1332 (when (and u (> u ?9))
1333 (when (= u ?k)
1334 (setq u ?K))
1335 (let ((units '(?B ?K ?M ?G ?T ?P ?E ?Z ?Y)))
1336 (while (and units (/= (pop units) u))
1337 (setq val (* 1024.0 val)))))
1338 val))
1340 (defun dired-mark-sexp (predicate &optional unflag-p)
1341 "Mark files for which PREDICATE returns non-nil.
1342 With a prefix arg, unmark or unflag those files instead.
1344 PREDICATE is a lisp expression that can refer to the following symbols:
1346 inode [integer] the inode of the file (only for ls -i output)
1347 s [integer] the size of the file for ls -s output
1348 (usually in blocks or, with -k, in KByte)
1349 mode [string] file permission bits, e.g. \"-rw-r--r--\"
1350 nlink [integer] number of links to file
1351 uid [string] owner
1352 gid [string] group (If the gid is not displayed by ls,
1353 this will still be set (to the same as uid))
1354 size [integer] file size in bytes
1355 time [string] the time that ls displays, e.g. \"Feb 12 14:17\"
1356 name [string] the name of the file
1357 sym [string] if file is a symbolic link, the linked-to name, else \"\"
1359 For example, use
1361 (equal 0 size)
1363 to mark all zero length files.
1365 There's an ambiguity when a single integer not followed by a unit
1366 prefix precedes the file mode: It is then parsed as inode number
1367 and not as block size (this always works for GNU coreutils ls).
1369 Another limitation is that the uid field is needed for the
1370 function to work correctly. In particular, the field is not
1371 present for some values of `ls-lisp-emulation'.
1373 This function operates only on the buffer content and does not
1374 refer at all to the underlying file system. Contrast this with
1375 `find-dired', which might be preferable for the task at hand."
1376 ;; Using sym="" instead of nil avoids the trap of
1377 ;; (string-match "foo" sym) into which a user would soon fall.
1378 ;; Give `equal' instead of `=' in the example, as this works on
1379 ;; integers and strings.
1380 (interactive
1381 (list (read--expression
1382 (format "%s if (lisp expr): "
1383 (if current-prefix-arg
1384 "UNmark"
1385 "Mark")))
1386 current-prefix-arg))
1387 (message "%s" predicate)
1388 (let ((dired-marker-char (if unflag-p ?\040 dired-marker-char))
1389 inode s mode nlink uid gid size time name sym)
1390 (dired-mark-if
1391 (save-excursion
1392 (and
1393 ;; Sets vars
1394 ;; inode s mode nlink uid gid size time name sym
1396 ;; according to current file line. Returns t for success, nil if
1397 ;; there is no file line. Upon success, all variables are set, either
1398 ;; to nil or the appropriate value, so they need not be initialized.
1399 ;; Moves point within the current line.
1400 (dired-move-to-filename)
1401 (let ((mode-len 10) ; length of mode string
1402 ;; like in dired.el, but with subexpressions \1=inode, \2=s:
1403 ;; GNU ls -hs suffixes the block count with a unit and
1404 ;; prints it as a float, FreeBSD does neither.
1405 (dired-re-inode-size "\\=\\s *\\([0-9]+\\s +\\)?\
1406 \\(?:\\([0-9]+\\(?:\\.[0-9]*\\)?[BkKMGTPEZY]?\\)? ?\\)"))
1407 (beginning-of-line)
1408 (forward-char 2)
1409 (search-forward-regexp dired-re-inode-size nil t)
1410 ;; XXX Might be a size not followed by a unit prefix.
1411 ;; We could set s to inode if it were otherwise nil,
1412 ;; with a similar reasoning as below for setting gid to uid,
1413 ;; but it would be even more whimsical.
1414 (setq inode (when (match-string 1)
1415 (string-to-number (match-string 1))))
1416 (setq s (when (match-string 2)
1417 (dired-x--string-to-number (match-string 2))))
1418 (setq mode (buffer-substring (point) (+ mode-len (point))))
1419 (forward-char mode-len)
1420 ;; Skip any extended attributes marker ("." or "+").
1421 (or (= (following-char) ?\s)
1422 (forward-char 1))
1423 (setq nlink (read (current-buffer)))
1424 ;; Karsten Wenger <kw@cis.uni-muenchen.de> fixed uid.
1425 ;; Another issue is that GNU ls -n right-justifies numerical
1426 ;; UIDs and GIDs, while FreeBSD left-justifies them, so
1427 ;; don't rely on a specific whitespace layout. Both of them
1428 ;; right-justify all other numbers, though.
1429 ;; XXX Return a number if the uid or gid seems to be
1430 ;; numerical?
1431 (setq uid (buffer-substring (progn
1432 (skip-chars-forward " \t")
1433 (point))
1434 (progn
1435 (skip-chars-forward "^ \t")
1436 (point))))
1437 (dired-move-to-filename)
1438 (save-excursion
1439 (setq time
1440 ;; The regexp below tries to match from the last
1441 ;; digit of the size field through a space after the
1442 ;; date. Also, dates may have different formats
1443 ;; depending on file age, so the date column need
1444 ;; not be aligned to the right.
1445 (buffer-substring (save-excursion
1446 (skip-chars-backward " \t")
1447 (point))
1448 (progn
1449 (re-search-backward
1450 directory-listing-before-filename-regexp)
1451 (skip-chars-forward "^ \t")
1452 (1+ (point))))
1453 size (dired-x--string-to-number
1454 ;; We know that there's some kind of number
1455 ;; before point because the regexp search
1456 ;; above succeeded. I don't think it's worth
1457 ;; doing an extra check for leading garbage.
1458 (buffer-substring (point)
1459 (progn
1460 (skip-chars-backward "^ \t")
1461 (point))))
1462 ;; If no gid is displayed, gid will be set to uid
1463 ;; but the user will then not reference it anyway in
1464 ;; PREDICATE.
1465 gid (buffer-substring (progn
1466 (skip-chars-backward " \t")
1467 (point))
1468 (progn
1469 (skip-chars-backward "^ \t")
1470 (point)))))
1471 (setq name (buffer-substring (point)
1473 (dired-move-to-end-of-filename t)
1474 (point)))
1475 sym (if (looking-at " -> ")
1476 (buffer-substring (progn (forward-char 4) (point))
1477 (line-end-position))
1478 ""))
1480 (eval predicate
1481 `((inode . ,inode)
1482 (s . ,s)
1483 (mode . ,mode)
1484 (nlink . ,nlink)
1485 (uid . ,uid)
1486 (gid . ,gid)
1487 (size . ,size)
1488 (time . ,time)
1489 (name . ,name)
1490 (sym . ,sym)))))
1491 (format "'%s file" predicate))))
1494 ;;; FIND FILE AT POINT.
1496 (defcustom dired-x-hands-off-my-keys t
1497 "Non-nil means don't remap `find-file' to `dired-x-find-file'.
1498 Similarly for `find-file-other-window' and `dired-x-find-file-other-window'.
1499 If you change this variable without using \\[customize] after `dired-x.el'
1500 is loaded then call \\[dired-x-bind-find-file]."
1501 :type 'boolean
1502 :initialize 'custom-initialize-default
1503 :set (lambda (symbol value)
1504 (set symbol value)
1505 (dired-x-bind-find-file))
1506 :group 'dired-x)
1508 (defun dired-x-bind-find-file ()
1509 "Bind `dired-x-find-file' in place of `find-file' (or vice-versa).
1510 Similarly for `dired-x-find-file-other-window' and `find-file-other-window'.
1511 Binding direction based on `dired-x-hands-off-my-keys'."
1512 (interactive)
1513 (if (called-interactively-p 'interactive)
1514 (setq dired-x-hands-off-my-keys
1515 (not (y-or-n-p "Bind dired-x-find-file over find-file? "))))
1516 (unless dired-x-hands-off-my-keys
1517 (define-key (current-global-map) [remap find-file]
1518 'dired-x-find-file)
1519 (define-key (current-global-map) [remap find-file-other-window]
1520 'dired-x-find-file-other-window)))
1522 ;; Now call it so binding is correct. This could go in the :initialize
1523 ;; slot, but then dired-x-bind-find-file has to be defined before the
1524 ;; defcustom, and we get free variable warnings.
1525 (dired-x-bind-find-file)
1527 (defun dired-x-find-file (filename)
1528 "Edit file FILENAME.
1529 Like `find-file', except that when called interactively with a
1530 prefix argument, it offers the filename near point as a default."
1531 (interactive (list (dired-x-read-filename-at-point "Find file: ")))
1532 (find-file filename))
1534 (defun dired-x-find-file-other-window (filename)
1535 "Edit file FILENAME, in another window.
1536 Like `find-file-other-window', except that when called interactively with
1537 a prefix argument, when it offers the filename near point as a default."
1538 (interactive (list (dired-x-read-filename-at-point "Find file: ")))
1539 (find-file-other-window filename))
1541 ;;; Internal functions.
1543 ;; Fixme: This should probably use `thing-at-point'. -- fx
1544 (defun dired-filename-at-point ()
1545 "Return the filename closest to point, expanded.
1546 Point should be in or after a filename."
1547 (save-excursion
1548 ;; First see if just past a filename.
1549 (or (eobp) ; why?
1550 (when (looking-at-p "[] \t\n[{}()]") ; whitespace or some parens
1551 (skip-chars-backward " \n\t\r({[]})")
1552 (or (bobp) (backward-char 1))))
1553 (let ((filename-chars "-.[:alnum:]_/:$+@")
1554 start prefix)
1555 (if (looking-at-p (format "[%s]" filename-chars))
1556 (progn
1557 (skip-chars-backward filename-chars)
1558 (setq start (point)
1559 prefix
1560 ;; This is something to do with ange-ftp filenames.
1561 ;; It convert foo@bar to /foo@bar.
1562 ;; But when does the former occur in dired buffers?
1563 (and (string-match-p
1564 "^\\w+@"
1565 (buffer-substring start (line-end-position)))
1566 "/"))
1567 (if (string-match-p "[/~]" (char-to-string (preceding-char)))
1568 (setq start (1- start)))
1569 (skip-chars-forward filename-chars))
1570 (error "No file found around point!"))
1571 ;; Return string.
1572 (expand-file-name (concat prefix (buffer-substring start (point)))))))
1574 (defun dired-x-read-filename-at-point (prompt)
1575 "Return filename prompting with PROMPT with completion.
1576 If `current-prefix-arg' is non-nil, uses name at point as guess."
1577 (if current-prefix-arg
1578 (let ((guess (dired-filename-at-point)))
1579 (read-file-name prompt
1580 (file-name-directory guess)
1581 guess
1582 nil (file-name-nondirectory guess)))
1583 (read-file-name prompt default-directory)))
1585 (define-obsolete-function-alias 'read-filename-at-point
1586 'dired-x-read-filename-at-point "24.1") ; is this even needed?
1588 ;;; BUG REPORTS
1590 (define-obsolete-function-alias 'dired-x-submit-report 'report-emacs-bug "24.1")
1593 ;; As Barry Warsaw would say: "This might be useful..."
1594 (provide 'dired-x)
1596 ;; Local Variables:
1597 ;; byte-compile-dynamic: t
1598 ;; generated-autoload-file: "dired-loaddefs.el"
1599 ;; End:
1601 ;;; dired-x.el ends here