Move dired-guess commands from dired-x to dired

* lisp/dired-x.el (dired-shell-command-history)
(dired-guess-shell-alist-default, dired-guess-default)
(dired-guess-shell-command): Move from here...
* lisp/dired-aux.el (dired-shell-command-history)
(dired-guess-shell-alist-default, dired-guess-default)
(dired-guess-shell-command): ...to here.  (Bug#21981)

* lisp/dired-x.el (dired-guess-shell-gnutar)
(dired-guess-shell-gzip-quiet, dired-guess-shell-znew-switches)
(dired-guess-shell-case-fold-search, dired-guess-shell-alist-user):
Move from here...
* lisp/dired.el (dired-guess-shell-gnutar)
(dired-guess-shell-gzip-quiet, dired-guess-shell-znew-switches)
(dired-guess-shell-case-fold-search, dired-guess-shell-alist-user):
...to here.  Change :group to dired-guess.
(dired-guess): New defgroup.

* test/lisp/dired-x-tests.el (dired-guess-default): Move from here...
* test/lisp/dired-aux-tests.el (dired-guess-default): ...to here.

* doc/misc/dired-x.texi (Features, Technical Details, Installation):
Delete any mention of shell command guessing.
(Shell Command Guessing): Move from here...
* doc/emacs/dired.texi (Shell Command Guessing): ...to here.  Adapt to
better fit the Emacs Manual conventions.
* lisp/dired-aux.el (dired-do-shell-command): Doc fix to adjust for
above changes.
* etc/NEWS: Announce the above change.
This commit is contained in:
Stefan Kangas 2022-08-16 17:14:33 +02:00
parent d214dd67cd
commit a6412b96e7
8 changed files with 453 additions and 450 deletions

View file

@ -41,6 +41,7 @@ you to operate on the listed files. @xref{Directories}.
* Operating on Files:: How to copy, rename, print, compress, etc.
either one file or several files.
* Shell Commands in Dired:: Running a shell command on the marked files.
* Shell Command Guessing:: Guessing shell commands for files.
* Transforming File Names:: Using patterns to rename multiple files.
* Comparison in Dired:: Running @code{diff} by way of Dired.
* Subdirectories in Dired:: Adding subdirectories to the Dired buffer.
@ -1121,6 +1122,109 @@ buffer (@pxref{Dired Updating}).
@xref{Single Shell}, for information about running shell commands
outside Dired.
@node Shell Command Guessing
@section Shell Command Guessing
@cindex guessing shell commands for files (in Dired)
Based upon the name of a file, Dired tries to guess what shell command
you might want to apply to it. For example, if you have point on a
file named @file{foo.tar} and you press @kbd{!}, Dired will guess that
you want to run @samp{tar xvf}, and suggest that as the default shell
command.
The default is mentioned in brackets and you can type @kbd{M-n} to get
the default into the minibuffer for editing. If there are several
commands for a given file, e.g., @samp{xtex} and @samp{dvips} for a
@file{.dvi} file, you can type @kbd{M-n} several times to see each of
the matching commands.
Dired only tries to guess a command for a single file, never for a
list of marked files.
The following variables control guessing of shell commands:
@defvar dired-guess-shell-alist-default
This variable specifies the predefined rules for guessing shell
commands suitable for certain files. Set this to @code{nil} to turn
guessing off. The elements of @code{dired-guess-shell-alist-user}
(defined by the user) will override these rules.
@end defvar
@defvar dired-guess-shell-alist-user
If non-@code{nil}, this variables specifies the user-defined alist of
file regexps and their suggested commands. These rules take
precedence over the predefined rules in the variable
@code{dired-guess-shell-alist-default} (to which they are prepended)
when @code{dired-do-shell-command} is run). The default is
@code{nil}.
Each element of the alist looks like
@example
(@var{regexp} @var{command}@dots{})
@end example
@noindent
where each @var{command} can either be a string or a Lisp expression
that evaluates to a string. If several commands are given, all of
them will temporarily be pushed onto the history.
A @samp{*} in the shell command stands for the file name that matched
@var{regexp}. When Emacs invokes the @var{command}, it replaces each
instance of @samp{*} with the matched file name.
You can set this variable in your Init file. For example, to add
rules for @samp{.foo} and @samp{.bar} file extensions:
@example
(setq dired-guess-shell-alist-user
(list
(list "\\.foo$" "@var{foo-command}") ; fixed rule
;; possibly more rules...
(list "\\.bar$" ; rule with condition test
'(if @var{condition}
"@var{bar-command-1}"
"@var{bar-command-2}"))))
@end example
@noindent
This will override any predefined rules for the same extensions.
@end defvar
@defvar dired-guess-shell-case-fold-search
If this variable is non-@code{nil},
@code{dired-guess-shell-alist-default} and
@code{dired-guess-shell-alist-user} are matched case-insensitively.
The default is @code{t}.
@end defvar
@defvar dired-guess-shell-gnutar
If this variable is non-@code{nil}, it specifies the name of the GNU
Tar executable (e.g., @file{tar} or @file{gtar}). GNU Tar's @samp{z}
switch is used for compressed archives. If you don't have GNU Tar,
set this to @code{nil}: a pipe using @command{zcat} is then used
instead.
@end defvar
@defvar dired-guess-shell-gzip-quiet
A non-@code{nil} value of this variable means that @samp{-q} is passed
to @command{gzip}, possibly overriding a verbose option in the
@env{GZIP} environment variable. The default is @code{t}.
@end defvar
@defvar dired-guess-shell-znew-switches nil
This variable specifies a string of switches passed to @command{znew}.
An example is @samp{-K} which will make @command{znew} keep a
@file{.Z} file when it is smaller than the @file{.gz} file. The
default is @code{nil}: no additional switches are passed to
@command{znew}.
@end defvar
@defvar dired-shell-command-history nil
This variable holds the history list for commands that read
dired-shell commands.
@end defvar
@node Transforming File Names
@section Transforming File Names in Dired

View file

@ -92,7 +92,6 @@ For @file{dired-x.el} as distributed with GNU Emacs @value{EMACSVER}.
* Introduction::
* Installation::
* Omitting Files in Dired::
* Shell Command Guessing::
* Virtual Dired::
* Advanced Mark Commands::
* Multiple Dired Directories::
@ -135,9 +134,6 @@ Some features provided by Dired Extra:
Omitting uninteresting files from Dired listing
(@pxref{Omitting Files in Dired}).
@item
Guessing shell commands in Dired buffers
(@pxref{Shell Command Guessing}).
@item
Running Dired command in non-Dired buffers
(@pxref{Virtual Dired}).
@item
@ -165,8 +161,6 @@ When @file{dired-x.el} is loaded, some standard Dired functions from
Dired}), if it is active. @code{dired-find-buffer-nocreate} and
@code{dired-initial-position} respect the value of
@code{dired-find-subdir} (@pxref{Miscellaneous Commands}).
@code{dired-read-shell-command} uses @code{dired-guess-shell-command}
(@pxref{Shell Command Guessing}) to offer a smarter default command.
@node Installation
@chapter Installation
@ -184,7 +178,6 @@ In your @file{~/.emacs} file, or in the system-wide initialization file
(with-eval-after-load 'dired
(require 'dired-x)
;; Set dired-x global variables here. For example:
;; (setq dired-guess-shell-gnutar "gtar")
;; (setq dired-x-hands-off-my-keys nil)
))
(add-hook 'dired-mode-hook
@ -436,111 +429,6 @@ Loading @file{dired-x.el} will install Dired Omit by putting
call @code{dired-extra-startup}, which in turn calls @code{dired-omit-startup}
in your @code{dired-mode-hook}.
@node Shell Command Guessing
@chapter Shell Command Guessing
@cindex guessing shell commands for files.
Based upon the name of a file, Dired tries to guess what shell
command you might want to apply to it. For example, if you have point
on a file named @file{foo.tar} and you press @kbd{!}, Dired will guess
you want to @samp{tar xvf} it and suggest that as the default shell
command.
The default is mentioned in brackets and you can type @kbd{M-n} to get
the default into the minibuffer and then edit it, e.g., to change
@samp{tar xvf} to @samp{tar tvf}. If there are several commands for a given
file, e.g., @samp{xtex} and @samp{dvips} for a @file{.dvi} file, you can type
@kbd{M-n} several times to see each of the matching commands.
Dired only tries to guess a command for a single file, never for a list
of marked files.
The following variables control guessing of shell commands:
@defvar dired-guess-shell-alist-default
This variable specifies the predefined rules for guessing shell
commands suitable for certain files. Set this to @code{nil} to turn
guessing off. The elements of @code{dired-guess-shell-alist-user}
(defined by the user) will override these rules.
@end defvar
@defvar dired-guess-shell-alist-user
If non-@code{nil}, this variables specifies the user-defined alist of
file regexps and their suggested commands. These rules take
precedence over the predefined rules in the variable
@code{dired-guess-shell-alist-default} (to which they are prepended)
when @code{dired-do-shell-command} is run). The default is
@code{nil}.
Each element of the alist looks like
@example
(@var{regexp} @var{command}@dots{})
@end example
@noindent
where each @var{command} can either be a string or a Lisp expression
that evaluates to a string. If several commands are given, all of
them will temporarily be pushed onto the history.
A @samp{*} in the shell command stands for the file name that matched
@var{regexp}. When Emacs invokes the @var{command}, it replaces each
instance of @samp{*} with the matched file name.
You can set this variable in your @file{~/.emacs}. For example,
to add rules for @samp{.foo} and @samp{.bar} file extensions, write
@example
(setq dired-guess-shell-alist-user
(list
(list "\\.foo$" "@var{foo-command}");; fixed rule
;; possibly more rules...
(list "\\.bar$";; rule with condition test
'(if @var{condition}
"@var{bar-command-1}"
"@var{bar-command-2}"))))
@end example
@noindent
This will override any predefined rules for the same extensions.
@end defvar
@defvar dired-guess-shell-case-fold-search
If this variable is non-@code{nil},
@code{dired-guess-shell-alist-default} and
@code{dired-guess-shell-alist-user} are matched case-insensitively.
The default is @code{t}.
@end defvar
@cindex passing GNU Tar its @samp{z} switch.
@defvar dired-guess-shell-gnutar
If this variable is non-@code{nil}, it specifies the name of the GNU
Tar executable (e.g., @file{tar} or @file{gnutar}). GNU Tar's
@samp{z} switch is used for compressed archives. If you don't have
GNU Tar, set this to @code{nil}: a pipe using @command{zcat} is then
used instead. The default is @code{nil}.
@end defvar
@cindex @code{gzip}
@defvar dired-guess-shell-gzip-quiet
A non-@code{nil} value of this variable means that @samp{-q} is passed
to @command{gzip}, possibly overriding a verbose option in the @env{GZIP}
environment variable. The default is @code{t}.
@end defvar
@cindex @code{znew}
@defvar dired-guess-shell-znew-switches nil
This variable specifies a string of switches passed to @command{znew}.
An example is @samp{-K} which will make @command{znew} keep a @file{.Z}
file when it is smaller than the @file{.gz} file. The default is
@code{nil}: no additional switches are passed to @command{znew}.
@end defvar
@defvar dired-shell-command-history nil
This variable holds the history list for commands that read
dired-shell commands.
@end defvar
@node Virtual Dired
@chapter Virtual Dired

View file

@ -1159,6 +1159,12 @@ change the input method's translation rules, customize the user option
** Dired
+++
*** 'dired-guess-shell-command' moved from dired-x to dired.
This means that 'dired-do-shell-command' will now provide smarter
defaults without first having to require 'dired-x'. See the node
"(emacs) Shell Command Guessing" in the Emacs manual for more details.
---
*** 'dired-clean-up-buffers-too' moved from dired-x to dired.
This means that Dired now offers to kill buffers visiting files and

View file

@ -1070,6 +1070,265 @@ Return the result of `process-file' - zero for success."
(pop-to-buffer out-buffer))
res)))))
;;; Guess shell command
;; * `dired-guess-shell-command' provides smarter defaults for
;; `dired-read-shell-command'.
;;
;; * `dired-guess-shell-command' calls `dired-guess-default' with list of
;; marked files.
;;
;; * Parse `dired-guess-shell-alist-user' and
;; `dired-guess-shell-alist-default' (in that order) for the first REGEXP
;; that matches the first file in the file list.
;;
;; * If the REGEXP matches all the entries of the file list then evaluate
;; COMMAND, which is either a string or a Lisp expression returning a
;; string. COMMAND may be a list of commands.
;;
;; * Return this command to `dired-guess-shell-command' which prompts user
;; with it. The list of commands is put into the list of default values.
;; If a command is used successfully then it is stored permanently in
;; `dired-shell-command-history'.
;; Guess what shell command to apply to a file.
(defvar dired-shell-command-history nil
"History list for commands that read dired-shell commands.")
;; Default list of shell commands.
;; NOTE: Use `gunzip -c' instead of `zcat' on `.gz' files. Some do not
;; install GNU zip's version of zcat.
(autoload 'Man-support-local-filenames "man")
(autoload 'vc-responsible-backend "vc")
(defvar dired-guess-shell-alist-default
(list
(list "\\.tar\\'"
'(if dired-guess-shell-gnutar
(concat dired-guess-shell-gnutar " xvf")
"tar xvf")
;; Extract files into a separate subdirectory
'(if dired-guess-shell-gnutar
(concat "mkdir " (file-name-sans-extension file)
"; " dired-guess-shell-gnutar " -C "
(file-name-sans-extension file) " -xvf")
(concat "mkdir " (file-name-sans-extension file)
"; tar -C " (file-name-sans-extension file) " -xvf"))
;; List archive contents.
'(if dired-guess-shell-gnutar
(concat dired-guess-shell-gnutar " tvf")
"tar tvf"))
;; REGEXPS for compressed archives must come before the .Z rule to
;; be recognized:
(list "\\.tar\\.Z\\'"
;; Untar it.
'(if dired-guess-shell-gnutar
(concat dired-guess-shell-gnutar " zxvf")
(concat "zcat * | tar xvf -"))
;; Optional conversion to gzip format.
'(concat "znew" (if dired-guess-shell-gzip-quiet " -q")
" " dired-guess-shell-znew-switches))
;; gzip'ed archives
(list "\\.t\\(ar\\.\\)?gz\\'"
'(if dired-guess-shell-gnutar
(concat dired-guess-shell-gnutar " zxvf")
(concat "gunzip -qc * | tar xvf -"))
;; Extract files into a separate subdirectory
'(if dired-guess-shell-gnutar
(concat "mkdir " (file-name-sans-extension file)
"; " dired-guess-shell-gnutar " -C "
(file-name-sans-extension file) " -zxvf")
(concat "mkdir " (file-name-sans-extension file)
"; gunzip -qc * | tar -C "
(file-name-sans-extension file) " -xvf -"))
;; Optional decompression.
'(concat "gunzip" (if dired-guess-shell-gzip-quiet " -q" ""))
;; List archive contents.
'(if dired-guess-shell-gnutar
(concat dired-guess-shell-gnutar " ztvf")
(concat "gunzip -qc * | tar tvf -")))
;; bzip2'ed archives
(list "\\.t\\(ar\\.bz2\\|bz\\)\\'"
"bunzip2 -c * | tar xvf -"
;; Extract files into a separate subdirectory
'(concat "mkdir " (file-name-sans-extension file)
"; bunzip2 -c * | tar -C "
(file-name-sans-extension file) " -xvf -")
;; Optional decompression.
"bunzip2")
;; xz'ed archives
(list "\\.t\\(ar\\.\\)?xz\\'"
"unxz -c * | tar xvf -"
;; Extract files into a separate subdirectory
'(concat "mkdir " (file-name-sans-extension file)
"; unxz -c * | tar -C "
(file-name-sans-extension file) " -xvf -")
;; Optional decompression.
"unxz")
'("\\.shar\\.Z\\'" "zcat * | unshar")
'("\\.shar\\.g?z\\'" "gunzip -qc * | unshar")
'("\\.e?ps\\'" "ghostview" "xloadimage" "lpr")
(list "\\.e?ps\\.g?z\\'" "gunzip -qc * | ghostview -"
;; Optional decompression.
'(concat "gunzip" (if dired-guess-shell-gzip-quiet " -q")))
(list "\\.e?ps\\.Z\\'" "zcat * | ghostview -"
;; Optional conversion to gzip format.
'(concat "znew" (if dired-guess-shell-gzip-quiet " -q")
" " dired-guess-shell-znew-switches))
(list "\\.patch\\'"
'(if (eq (ignore-errors (vc-responsible-backend default-directory)) 'Git)
"cat * | git apply"
"cat * | patch"))
(list "\\.patch\\.g?z\\'" "gunzip -qc * | patch"
;; Optional decompression.
'(concat "gunzip" (if dired-guess-shell-gzip-quiet " -q")))
(list "\\.patch\\.Z\\'" "zcat * | patch"
;; Optional conversion to gzip format.
'(concat "znew" (if dired-guess-shell-gzip-quiet " -q")
" " dired-guess-shell-znew-switches))
;; The following four extensions are useful with dired-man ("N" key)
;; FIXME "man ./" does not work with dired-do-shell-command,
;; because there seems to be no way for us to modify the filename,
;; only the command. Hmph. `dired-man' works though.
(list "\\.\\(?:[0-9]\\|man\\)\\'"
'(let ((loc (Man-support-local-filenames)))
(cond ((eq loc 'man-db) "man -l")
((eq loc 'man) "man ./")
(t
"cat * | tbl | nroff -man -h | col -b"))))
(list "\\.\\(?:[0-9]\\|man\\)\\.g?z\\'"
'(let ((loc (Man-support-local-filenames)))
(cond ((eq loc 'man-db)
"man -l")
((eq loc 'man)
"man ./")
(t "gunzip -qc * | tbl | nroff -man -h | col -b")))
;; Optional decompression.
'(concat "gunzip" (if dired-guess-shell-gzip-quiet " -q")))
(list "\\.[0-9]\\.Z\\'"
'(let ((loc (Man-support-local-filenames)))
(cond ((eq loc 'man-db) "man -l")
((eq loc 'man) "man ./")
(t "zcat * | tbl | nroff -man -h | col -b")))
;; Optional conversion to gzip format.
'(concat "znew" (if dired-guess-shell-gzip-quiet " -q")
" " dired-guess-shell-znew-switches))
'("\\.pod\\'" "perldoc" "pod2man * | nroff -man")
'("\\.dvi\\'" "xdvi" "dvips") ; preview and printing
'("\\.au\\'" "play") ; play Sun audiofiles
'("\\.mpe?g\\'\\|\\.avi\\'" "xine -p")
'("\\.ogg\\'" "ogg123")
'("\\.mp3\\'" "mpg123")
'("\\.wav\\'" "play")
'("\\.uu\\'" "uudecode") ; for uudecoded files
'("\\.hqx\\'" "mcvert")
'("\\.sh\\'" "sh") ; execute shell scripts
'("\\.xbm\\'" "bitmap") ; view X11 bitmaps
'("\\.gp\\'" "gnuplot")
'("\\.p[bgpn]m\\'" "xloadimage")
'("\\.gif\\'" "xloadimage") ; view gif pictures
'("\\.tif\\'" "xloadimage")
'("\\.png\\'" "display") ; xloadimage 4.1 doesn't grok PNG
'("\\.jpe?g\\'" "xloadimage")
'("\\.fig\\'" "xfig") ; edit fig pictures
'("\\.out\\'" "xgraph") ; for plotting purposes.
'("\\.tex\\'" "latex" "tex")
'("\\.texi\\(nfo\\)?\\'" "makeinfo" "texi2dvi")
'("\\.pdf\\'" "xpdf")
'("\\.doc\\'" "antiword" "strings")
'("\\.rpm\\'" "rpm -qilp" "rpm -ivh")
'("\\.dia\\'" "dia")
'("\\.mgp\\'" "mgp")
;; Some other popular archivers.
(list "\\.zip\\'" "unzip" "unzip -l"
;; Extract files into a separate subdirectory
'(concat "unzip" (if dired-guess-shell-gzip-quiet " -q")
" -d " (file-name-sans-extension file)))
'("\\.zoo\\'" "zoo x//")
'("\\.lzh\\'" "lharc x")
'("\\.arc\\'" "arc x")
'("\\.shar\\'" "unshar")
'("\\.rar\\'" "unrar x")
'("\\.7z\\'" "7z x")
;; Compression.
(list "\\.g?z\\'" '(concat "gunzip" (if dired-guess-shell-gzip-quiet " -q")))
(list "\\.dz\\'" "dictunzip")
(list "\\.bz2\\'" "bunzip2")
(list "\\.xz\\'" "unxz")
(list "\\.Z\\'" "uncompress"
;; Optional conversion to gzip format.
'(concat "znew" (if dired-guess-shell-gzip-quiet " -q")
" " dired-guess-shell-znew-switches))
'("\\.sign?\\'" "gpg --verify"))
"Default alist used for shell command guessing.
See `dired-guess-shell-alist-user'.")
(defun dired-guess-default (files)
"Return a shell command, or a list of commands, appropriate for FILES.
See `dired-guess-shell-alist-user'."
(let* ((case-fold-search dired-guess-shell-case-fold-search)
(programs
(delete-dups
(mapcar
(lambda (command)
(eval command `((file . ,(car files)))))
(seq-reduce
#'append
(mapcar #'cdr
(seq-filter (lambda (elem)
(seq-every-p
(lambda (file)
(string-match-p (car elem) file))
files))
(append dired-guess-shell-alist-user
dired-guess-shell-alist-default)))
nil)))))
(if (length= programs 1)
(car programs)
programs)))
;;;###autoload
(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 val)
(if (null default)
;; Nothing to guess
(read-shell-command prompt nil 'dired-shell-command-history)
(setq prompt (replace-regexp-in-string ": $" " " prompt))
(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 the first guess in the prompt but not in the initial value.
(setq prompt (concat prompt (format "[%s]: " default)))
;; All guesses can be retrieved with M-n
(setq val (read-shell-command prompt nil
'dired-shell-command-history
default-list))
;; If we got a return, then return default.
(if (equal val "") default val))))
;;; Commands that delete or redisplay part of the dired buffer

View file

@ -196,35 +196,6 @@ toggle between those two."
:type 'boolean
:group 'dired-x)
(defcustom dired-guess-shell-gnutar
(catch 'found
(dolist (exe '("tar" "gtar"))
(if (with-temp-buffer
(ignore-errors (call-process exe nil t nil "--version"))
(and (re-search-backward "GNU tar" nil t) t))
(throw 'found exe))))
"If non-nil, name of GNU tar executable.
\(E.g., \"tar\" or \"gtar\"). The `z' switch will be used with it for
compressed or gzip'ed tar files. If you don't have GNU tar, set this
to nil: a pipe using `zcat' or `gunzip -c' will be used."
;; Changed from system-type test to testing --version output.
;; Maybe test --help for -z instead?
:version "24.1"
:type '(choice (const :tag "Not GNU tar" nil)
(string :tag "Command name"))
:group 'dired-x)
(defcustom dired-guess-shell-gzip-quiet t
"Non-nil says pass -q to gzip overriding verbose GZIP environment."
:type 'boolean
:group 'dired-x)
(defcustom dired-guess-shell-znew-switches nil
"If non-nil, then string of switches passed to `znew', example: \"-K\"."
:type '(choice (const :tag "None" nil)
(string :tag "Switches"))
:group 'dired-x)
;;; Key bindings
@ -726,302 +697,6 @@ Also useful for `auto-mode-alist' like this:
default-directory)))
(shell-command command output-buffer error-buffer)))
;;; Guess shell command
;; Brief Description:
;;
;; * `dired-do-shell-command' is bound to `!' by dired.el.
;;
;; * `dired-guess-shell-command' provides smarter defaults for
;; dired-aux.el's `dired-read-shell-command'.
;;
;; * `dired-guess-shell-command' calls `dired-guess-default' with list of
;; marked files.
;;
;; * Parse `dired-guess-shell-alist-user' and
;; `dired-guess-shell-alist-default' (in that order) for the first REGEXP
;; that matches the first file in the file list.
;;
;; * If the REGEXP matches all the entries of the file list then evaluate
;; COMMAND, which is either a string or a Lisp expression returning a
;; string. COMMAND may be a list of commands.
;;
;; * Return this command to `dired-guess-shell-command' which prompts user
;; with it. The list of commands is put into the list of default values.
;; If a command is used successfully then it is stored permanently in
;; `dired-shell-command-history'.
;; Guess what shell command to apply to a file.
(defvar dired-shell-command-history nil
"History list for commands that read dired-shell commands.")
;; Default list of shell commands.
;; NOTE: Use `gunzip -c' instead of `zcat' on `.gz' files. Some do not
;; install GNU zip's version of zcat.
(autoload 'Man-support-local-filenames "man")
(autoload 'vc-responsible-backend "vc")
(defvar dired-guess-shell-alist-default
(list
(list "\\.tar\\'"
'(if dired-guess-shell-gnutar
(concat dired-guess-shell-gnutar " xvf")
"tar xvf")
;; Extract files into a separate subdirectory
'(if dired-guess-shell-gnutar
(concat "mkdir " (file-name-sans-extension file)
"; " dired-guess-shell-gnutar " -C "
(file-name-sans-extension file) " -xvf")
(concat "mkdir " (file-name-sans-extension file)
"; tar -C " (file-name-sans-extension file) " -xvf"))
;; List archive contents.
'(if dired-guess-shell-gnutar
(concat dired-guess-shell-gnutar " tvf")
"tar tvf"))
;; REGEXPS for compressed archives must come before the .Z rule to
;; be recognized:
(list "\\.tar\\.Z\\'"
;; Untar it.
'(if dired-guess-shell-gnutar
(concat dired-guess-shell-gnutar " zxvf")
(concat "zcat * | tar xvf -"))
;; Optional conversion to gzip format.
'(concat "znew" (if dired-guess-shell-gzip-quiet " -q")
" " dired-guess-shell-znew-switches))
;; gzip'ed archives
(list "\\.t\\(ar\\.\\)?gz\\'"
'(if dired-guess-shell-gnutar
(concat dired-guess-shell-gnutar " zxvf")
(concat "gunzip -qc * | tar xvf -"))
;; Extract files into a separate subdirectory
'(if dired-guess-shell-gnutar
(concat "mkdir " (file-name-sans-extension file)
"; " dired-guess-shell-gnutar " -C "
(file-name-sans-extension file) " -zxvf")
(concat "mkdir " (file-name-sans-extension file)
"; gunzip -qc * | tar -C "
(file-name-sans-extension file) " -xvf -"))
;; Optional decompression.
'(concat "gunzip" (if dired-guess-shell-gzip-quiet " -q" ""))
;; List archive contents.
'(if dired-guess-shell-gnutar
(concat dired-guess-shell-gnutar " ztvf")
(concat "gunzip -qc * | tar tvf -")))
;; bzip2'ed archives
(list "\\.t\\(ar\\.bz2\\|bz\\)\\'"
"bunzip2 -c * | tar xvf -"
;; Extract files into a separate subdirectory
'(concat "mkdir " (file-name-sans-extension file)
"; bunzip2 -c * | tar -C "
(file-name-sans-extension file) " -xvf -")
;; Optional decompression.
"bunzip2")
;; xz'ed archives
(list "\\.t\\(ar\\.\\)?xz\\'"
"unxz -c * | tar xvf -"
;; Extract files into a separate subdirectory
'(concat "mkdir " (file-name-sans-extension file)
"; unxz -c * | tar -C "
(file-name-sans-extension file) " -xvf -")
;; Optional decompression.
"unxz")
'("\\.shar\\.Z\\'" "zcat * | unshar")
'("\\.shar\\.g?z\\'" "gunzip -qc * | unshar")
'("\\.e?ps\\'" "ghostview" "xloadimage" "lpr")
(list "\\.e?ps\\.g?z\\'" "gunzip -qc * | ghostview -"
;; Optional decompression.
'(concat "gunzip" (if dired-guess-shell-gzip-quiet " -q")))
(list "\\.e?ps\\.Z\\'" "zcat * | ghostview -"
;; Optional conversion to gzip format.
'(concat "znew" (if dired-guess-shell-gzip-quiet " -q")
" " dired-guess-shell-znew-switches))
(list "\\.patch\\'"
'(if (eq (ignore-errors (vc-responsible-backend default-directory)) 'Git)
"cat * | git apply"
"cat * | patch"))
(list "\\.patch\\.g?z\\'" "gunzip -qc * | patch"
;; Optional decompression.
'(concat "gunzip" (if dired-guess-shell-gzip-quiet " -q")))
(list "\\.patch\\.Z\\'" "zcat * | patch"
;; Optional conversion to gzip format.
'(concat "znew" (if dired-guess-shell-gzip-quiet " -q")
" " dired-guess-shell-znew-switches))
;; The following four extensions are useful with dired-man ("N" key)
;; FIXME "man ./" does not work with dired-do-shell-command,
;; because there seems to be no way for us to modify the filename,
;; only the command. Hmph. `dired-man' works though.
(list "\\.\\(?:[0-9]\\|man\\)\\'"
'(let ((loc (Man-support-local-filenames)))
(cond ((eq loc 'man-db) "man -l")
((eq loc 'man) "man ./")
(t
"cat * | tbl | nroff -man -h | col -b"))))
(list "\\.\\(?:[0-9]\\|man\\)\\.g?z\\'"
'(let ((loc (Man-support-local-filenames)))
(cond ((eq loc 'man-db)
"man -l")
((eq loc 'man)
"man ./")
(t "gunzip -qc * | tbl | nroff -man -h | col -b")))
;; Optional decompression.
'(concat "gunzip" (if dired-guess-shell-gzip-quiet " -q")))
(list "\\.[0-9]\\.Z\\'"
'(let ((loc (Man-support-local-filenames)))
(cond ((eq loc 'man-db) "man -l")
((eq loc 'man) "man ./")
(t "zcat * | tbl | nroff -man -h | col -b")))
;; Optional conversion to gzip format.
'(concat "znew" (if dired-guess-shell-gzip-quiet " -q")
" " dired-guess-shell-znew-switches))
'("\\.pod\\'" "perldoc" "pod2man * | nroff -man")
'("\\.dvi\\'" "xdvi" "dvips") ; preview and printing
'("\\.au\\'" "play") ; play Sun audiofiles
'("\\.mpe?g\\'\\|\\.avi\\'" "xine -p")
'("\\.ogg\\'" "ogg123")
'("\\.mp3\\'" "mpg123")
'("\\.wav\\'" "play")
'("\\.uu\\'" "uudecode") ; for uudecoded files
'("\\.hqx\\'" "mcvert")
'("\\.sh\\'" "sh") ; execute shell scripts
'("\\.xbm\\'" "bitmap") ; view X11 bitmaps
'("\\.gp\\'" "gnuplot")
'("\\.p[bgpn]m\\'" "xloadimage")
'("\\.gif\\'" "xloadimage") ; view gif pictures
'("\\.tif\\'" "xloadimage")
'("\\.png\\'" "display") ; xloadimage 4.1 doesn't grok PNG
'("\\.jpe?g\\'" "xloadimage")
'("\\.fig\\'" "xfig") ; edit fig pictures
'("\\.out\\'" "xgraph") ; for plotting purposes.
'("\\.tex\\'" "latex" "tex")
'("\\.texi\\(nfo\\)?\\'" "makeinfo" "texi2dvi")
'("\\.pdf\\'" "xpdf")
'("\\.doc\\'" "antiword" "strings")
'("\\.rpm\\'" "rpm -qilp" "rpm -ivh")
'("\\.dia\\'" "dia")
'("\\.mgp\\'" "mgp")
;; Some other popular archivers.
(list "\\.zip\\'" "unzip" "unzip -l"
;; Extract files into a separate subdirectory
'(concat "unzip" (if dired-guess-shell-gzip-quiet " -q")
" -d " (file-name-sans-extension file)))
'("\\.zoo\\'" "zoo x//")
'("\\.lzh\\'" "lharc x")
'("\\.arc\\'" "arc x")
'("\\.shar\\'" "unshar")
'("\\.rar\\'" "unrar x")
'("\\.7z\\'" "7z x")
;; Compression.
(list "\\.g?z\\'" '(concat "gunzip" (if dired-guess-shell-gzip-quiet " -q")))
(list "\\.dz\\'" "dictunzip")
(list "\\.bz2\\'" "bunzip2")
(list "\\.xz\\'" "unxz")
(list "\\.Z\\'" "uncompress"
;; Optional conversion to gzip format.
'(concat "znew" (if dired-guess-shell-gzip-quiet " -q")
" " dired-guess-shell-znew-switches))
'("\\.sign?\\'" "gpg --verify"))
"Default alist used for shell command guessing.
See `dired-guess-shell-alist-user'.")
(defcustom dired-guess-shell-alist-user nil
"User-defined alist of rules for suggested commands.
These rules take precedence over the predefined rules in the variable
`dired-guess-shell-alist-default' (to which they are prepended).
Each element of this list looks like
(REGEXP COMMAND...)
COMMAND will be used if REGEXP matches the file to be processed.
If several files are to be processed, REGEXP has to match all the
files.
Each COMMAND can either be a string or a Lisp expression that evaluates
to a string. If this expression needs to consult the name of the file for
which the shell commands are being requested, it can access that file name
as the variable `file'.
If several COMMANDs are given, the first one will be the default
and the rest will be added temporarily to the history and can be retrieved
with `previous-history-element' (\\<minibuffer-mode-map>\\[previous-history-element]).
The variable `dired-guess-shell-case-fold-search' controls whether
REGEXP is matched case-sensitively."
:group 'dired-x
:type '(alist :key-type regexp :value-type (repeat sexp)))
(defcustom dired-guess-shell-case-fold-search t
"If non-nil, `dired-guess-shell-alist-default' and
`dired-guess-shell-alist-user' are matched case-insensitively."
:group 'dired-x
:type 'boolean)
(defun dired-guess-default (files)
"Return a shell command, or a list of commands, appropriate for FILES.
See `dired-guess-shell-alist-user'."
(let* ((case-fold-search dired-guess-shell-case-fold-search)
(programs
(delete-dups
(mapcar
(lambda (command)
(eval command `((file . ,(car files)))))
(seq-reduce
#'append
(mapcar #'cdr
(seq-filter (lambda (elem)
(seq-every-p
(lambda (file)
(string-match-p (car elem) file))
files))
(append dired-guess-shell-alist-user
dired-guess-shell-alist-default)))
nil)))))
(if (length= programs 1)
(car programs)
programs)))
(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 val)
(if (null default)
;; Nothing to guess
(read-shell-command prompt nil 'dired-shell-command-history)
(setq prompt (replace-regexp-in-string ": $" " " prompt))
(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 the first guess in the prompt but not in the initial value.
(setq prompt (concat prompt (format "[%s]: " default)))
;; All guesses can be retrieved with M-n
(setq val (read-shell-command prompt nil
'dired-shell-command-history
default-list))
;; If we got a return, then return default.
(if (equal val "") default val))))
;;; Visit all marked files simultaneously

View file

@ -53,6 +53,11 @@
:prefix "dired-"
:group 'dired)
(defgroup dired-guess nil
"Guess shell command in Dired."
:prefix "dired-"
:group 'dired)
;;;###autoload
(defcustom dired-listing-switches (purecopy "-al")
"Switches passed to `ls' for Dired. MUST contain the `l' option.
@ -419,6 +424,72 @@ is anywhere on its Dired line, except the beginning of the line."
:type 'boolean
:version "28.1")
(defcustom dired-guess-shell-case-fold-search t
"If non-nil, `dired-guess-shell-alist-default' and
`dired-guess-shell-alist-user' are matched case-insensitively."
:group 'dired-guess
:type 'boolean
:version "29.1")
(defcustom dired-guess-shell-alist-user nil
"User-defined alist of rules for suggested commands.
These rules take precedence over the predefined rules in the variable
`dired-guess-shell-alist-default' (to which they are prepended).
Each element of this list looks like
(REGEXP COMMAND...)
COMMAND will be used if REGEXP matches the file to be processed.
If several files are to be processed, REGEXP has to match all the
files.
Each COMMAND can either be a string or a Lisp expression that evaluates
to a string. If this expression needs to consult the name of the file for
which the shell commands are being requested, it can access that file name
as the variable `file'.
If several COMMANDs are given, the first one will be the default
and the rest will be added temporarily to the history and can be retrieved
with `previous-history-element' (\\<minibuffer-mode-map>\\[previous-history-element]).
The variable `dired-guess-shell-case-fold-search' controls whether
REGEXP is matched case-sensitively."
:group 'dired-guess
:type '(alist :key-type regexp :value-type (repeat sexp))
:version "29.1")
(defcustom dired-guess-shell-gnutar
(catch 'found
(dolist (exe '("tar" "gtar"))
(if (with-temp-buffer
(ignore-errors (call-process exe nil t nil "--version"))
(and (re-search-backward "GNU tar" nil t) t))
(throw 'found exe))))
"If non-nil, name of GNU tar executable.
\(E.g., \"tar\" or \"gtar\"). The `z' switch will be used with it for
compressed or gzip'ed tar files. If you don't have GNU tar, set this
to nil: a pipe using `zcat' or `gunzip -c' will be used."
;; Changed from system-type test to testing --version output.
;; Maybe test --help for -z instead?
:group 'dired-guess
:type '(choice (const :tag "Not GNU tar" nil)
(string :tag "Command name"))
:version "29.1")
(defcustom dired-guess-shell-gzip-quiet t
"Non-nil says pass -q to gzip overriding verbose GZIP environment."
:group 'dired-guess
:type 'boolean
:version "29.1")
(defcustom dired-guess-shell-znew-switches nil
"If non-nil, then string of switches passed to `znew', example: \"-K\"."
:group 'dired-guess
:type '(choice (const :tag "None" nil)
(string :tag "Switches"))
:version "29.1")
;;; Internal variables

View file

@ -154,5 +154,18 @@
(should (string-match (regexp-quote command) (nth 0 lines)))
(dired-test--check-highlighting (nth 0 lines) '(8))))
(ert-deftest dired-guess-default ()
(let ((dired-guess-shell-alist-user nil)
(dired-guess-shell-alist-default
'(("\\.png\\'" "display")
("\\.gif\\'" "display" "xloadimage")
("\\.gif\\'" "feh")
("\\.jpe?g\\'" "xloadimage"))))
(should (equal (dired-guess-default '("/tmp/foo.png")) "display"))
(should (equal (dired-guess-default '("/tmp/foo.gif"))
'("display" "xloadimage" "feh")))
(should (equal (dired-guess-default '("/tmp/foo.png" "/tmp/foo.txt"))
nil))))
(provide 'dired-aux-tests)
;;; dired-aux-tests.el ends here

View file

@ -47,19 +47,6 @@
(should (equal all-but-c
(sort (dired-get-marked-files 'local) #'string<))))))
(ert-deftest dired-guess-default ()
(let ((dired-guess-shell-alist-user nil)
(dired-guess-shell-alist-default
'(("\\.png\\'" "display")
("\\.gif\\'" "display" "xloadimage")
("\\.gif\\'" "feh")
("\\.jpe?g\\'" "xloadimage"))))
(should (equal (dired-guess-default '("/tmp/foo.png")) "display"))
(should (equal (dired-guess-default '("/tmp/foo.gif"))
'("display" "xloadimage" "feh")))
(should (equal (dired-guess-default '("/tmp/foo.png" "/tmp/foo.txt"))
nil))))
(ert-deftest dired-x--string-to-number ()
(should (= (dired-x--string-to-number "2.4K") 2457.6))
(should (= (dired-x--string-to-number "2400") 2400))