Mercurial > hg > xemacs-beta
diff lisp/efs/dired-shell.el @ 98:0d2f883870bc r20-1b1
Import from CVS: tag r20-1b1
author | cvs |
---|---|
date | Mon, 13 Aug 2007 09:13:56 +0200 |
parents | 8fc7fe29b841 |
children | 7e54bd776075 8619ce7e4c50 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lisp/efs/dired-shell.el Mon Aug 13 09:13:56 2007 +0200 @@ -0,0 +1,854 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; File: dired-shell.el +;; Dired Version: $Revision: 1.1 $ +;; RCS: +;; Description: Commands for running shell commands on marked files. +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;;; Requirements and provisions +(provide 'dired-shell) +(require 'dired) +(autoload 'comint-mode "comint") + +;;; Variables + +(defvar dired-postscript-print-command + (concat + (if (boundp 'lpr-command) + lpr-command + (if (memq system-type + '(usg-unix-v hpux silicon-graphics-unix)) + "lp" + "lpr")) + (if (and (boundp 'lpr-switches) lpr-switches) + (concat " " + (mapconcat 'identity lpr-switches " ") + " ") + " ")) + "Command to print a postscript file.") + +(defvar dired-text-print-command (concat dired-postscript-print-command " -p") + "Command to print a text file.") + +(defvar dired-print-program-alist + (list + (cons "\\.gif$" (concat "giftoppm * | ppmtopgm | pnmtops | " + dired-postscript-print-command)) + (cons "\\.\\(fts\\|FTS\\)$" (concat "fitstopgm * | pnmtops | " + dired-postscript-print-command)) + ;; People with colour printers won't want the g-flag in djpeg + (cons "\\.\\(JPG\\|jpg\\)$" (concat "djpeg -Pg * | pnmtops | " + dired-postscript-print-command)) + (cons "\\.ps\\.\\(gz\\|Z\\)$" (concat "zcat * | " + dired-postscript-print-command)) + (cons "\\.ps$" dired-postscript-print-command) + (cons "\\.\\(gz\\|Z\\)$" (concat "zcat * | " + dired-postscript-print-command)) + (cons "\\.dvi$" "dvips") + (cons ".*" dired-text-print-command)) + "Alist of regexps and print commands. +This is used by `dired-do-print' to determine the default print command for +printing the marked files.") + +(defvar dired-auto-shell-command-alist nil + "*Alist of regexps and command lists to guess shell commands. +Each element of this list should be a list of regular expression, and a list +of guesses for shell commands to be used if the file name matches the regular +expression. The list of guesses is evalled. This alist is appended to the front +of dired-default-auto-shell-command-alist before prompting for each shell +command.") + +(defvar dired-default-auto-shell-command-alist + (list + + ;; Archiving + '("\\.tar$" + (if dired-gnutar-program + (concat dired-gnutar-program " xvf") + "tar xvf") + (if dired-gnutar-program + (concat dired-gnutar-program " tvf") + "tar tvf")) + ;; regexps for compressed archives must come before the .Z rule to + ;; be recognized: + '("\\.tar\\.\\([zZ]\\|gz\\)\\|\\.tgz$" ; .tgz is for DOS + (if dired-gnutar-program + (concat dired-gnutar-program " zxvf") + "zcat * | tar xvf -") + (if dired-gnutar-program + (concat dired-gnutar-program " ztvf") + "zcat * | tar tvf -")) + '("\\.shar.[zZ]$" (if dired-unshar-program + (concat "zcat * | " dired-unshar-program) + "zcat * | sh")) + '("\\.zoo$" "zoo x//") + '("\\.zip$" "unzip" "unzip -v") + '("\\.lzh$" "lharc x") + '("\\.arc$" "arc x") + '("\\.shar$" (if dired-unshar-program dired-unshar-program "sh")) + + ;; Encoding/compressing + '("\\.uu$" "uudecode") + '("\\.hqx$" "mcvert") + + ;; Executing (in the generalized sense) + '("\\.sh$" "sh") ; execute shell scripts + '("^[Mm]akefile$" "make -f *") + '("\\.diff$" "patch -t <") + + ;; Displaying (assumes X) + '("\\.xbm$" "bitmap") ; view X11 bitmaps + '("\\.gp$" "gnuplot") + '("\\.gif$" "xv") ; view gif pictures + '("\\.fig$" "xfig") ; edit fig pictures + '("\\.ps$" "ghostview") + + ;; Typesetting. For printing documents, see dired-print-program-alist. + '("\\.tex$" "latex" "tex") + '("\\.texi\\(nfo\\)?$" "makeinfo" "texi2dvi") + (if (eq window-system 'x) + (if dired-use-file-transformers + '("\\.dvi$" "xdvi" "dvips -o *b.ps *") + '("\\.dvi$" "xdvi" "dvips")) + (if dired-use-file-transformers + '("\\.dvi$" "dvips -o *b.ps *") + '("\\.dvi$" "dvips"))) + + ;; The last word. Things that cannot be grokked with a regexp. + '("." (if (> (length files) 1) + "tar cvf " + (and (= (length files) 1) (file-directory-p + (expand-file-name + (car files) + (dired-current-directory))) + (concat "tar cvf " (file-name-nondirectory + (directory-file-name (car files))) + ".tar")))) + ) + "Default for variable `dired-auto-shell-command-alist' (which see). +Set this to nil to turn off shell command guessing.") + +;; Might use {,} for bash or csh: +(defvar dired-shell-prefix "" + "Prepended to marked files in dired shell commands.") +(defvar dired-shell-postfix "" + "Appended to marked files in dired shell commands.") +(defvar dired-shell-separator " " + "Separates marked files in dired shell commands.") + +(defvar dired-file-wildcard ?* + "Wildcard character used by dired shell commands. +Indicates where file names should be inserted.") + +(defvar dired-shell-command-separators '(?\ ?| ?> ?< ?& ?;) + "Defines the start of a string specifying a word in a shell command.") + +(defvar dired-trans-map + (list + (cons ?f 'identity) + (cons ?n 'file-name-nondirectory) + (cons ?d 'file-name-directory) + (cons ?b 'dired-file-name-base) + (cons ?e 'dired-file-name-extension) + (cons ?v 'dired-file-name-sans-rcs-extension) + (cons ?z 'dired-file-name-sans-compress-extension)) + "Alist that associates keys with file transformer functions +Each transformer function should be a funcion of one argument, the file name. +The keys are characters.") + +(defvar dired-shell-failure-marker ?! + "*A marker to mark files on which shell commands fail. +If nil, such files are not marked.") + +;;; Internal variables + +;; Make sure this gets defined. +(defvar shell-command-history nil + "History list of previous shell commands.") + +(defvar dired-print-history nil + "History of commands used to print files.") + +(defvar dired-shell-input-start) ; only defined in shell output buffers + +;;; Utility functions and Macros + +(defun dired-shell-quote (filename) + ;; Quote a file name for inferior shell (see variable shell-file-name). + ;; Quote everything except POSIX filename characters. + ;; This should be safe enough even for really wierd shells. + (let ((result "") (start 0) end) + (while (string-match "[^---0-9a-zA-Z_./]" filename start) + (setq end (match-beginning 0) + result (concat result (substring filename start end) + "\\" (substring filename end (1+ end))) + start (1+ end))) + (concat result (substring filename start)))) + +(defun dired-uniquefy-list (list) + ;; Returns list, after removing 2nd and higher occurrences + ;; of all elements. Tests elements with equal. Retains the relative + ;; order of the elements. + ;; For small lists, this way is probably faster than sorting. + (let (result) + (while list + (or (member (car list) result) + (setq result (nconc result (list (car list))))) + (setq list (cdr list))) + result)) + +(defun dired-read-shell-command (prompt arg files) + ;; Read a dired shell command prompting with PROMPT (using read-string). + ;; ARG is the prefix arg and may be used to indicate in the prompt which + ;; files are affected. + (dired-mark-pop-up + nil 'shell files + (function + (lambda (prompt files) + (let* ((default (car shell-command-history)) + (guesses (dired-guess-default files)) + (len (length guesses)) + cmd) + (or (zerop len) + (setq prompt (format "%s{%d guess%s} " + prompt len (if (= len 1) "" "es")))) + (if default (setq prompt (concat prompt "[" default "] "))) + (put 'guesses 'no-default t) ; for gmhist, in case. + (setq guesses (nconc guesses (copy-sequence shell-command-history)) + cmd (dired-read-with-history prompt nil 'guesses)) + (if (string-match "^[ \t\n]*$" cmd) + (if default + (setq cmd default) + (error "No shell command given."))) + (setq shell-command-history + (dired-uniquefy-list + (cons cmd shell-command-history))) + cmd))) + (format prompt (dired-mark-prompt arg files)) files)) + +(defmacro dired-trans-subst (transformers filename dir) +;; Applies each transformer supplied in the string TRANSFORMERS in sequence +;; to FILE and returns the concatenation of the results. Also unquotes \\'s. +;; Returns a string if no file transformations were done, otherwise a list +;; consisting of a single string. + (` (let* ((transformers (, transformers)) + (filename (, filename)) + (len (length transformers)) + (pos 0) + (last 0) + (transformed nil) + (quoted nil) + char result trans) + (while (< pos len) + (setq char (aref transformers pos)) + (cond + (quoted (setq pos (1+ pos) + quoted nil)) + ((= ?\\ char) + (setq quoted t + result (concat result (substring transformers last pos)) + pos (1+ pos) + last pos)) + ((and (null quoted) (= char dired-file-wildcard)) + (setq pos (1+ pos) + trans (and (< pos len) + dired-use-file-transformers + (assq (aref transformers pos) + dired-trans-map)) + transformed t) + (if trans + (setq result (concat result + (substring transformers last (1- pos)) + (funcall (cdr trans) filename)) + pos (1+ pos) + last pos) + (setq result (concat result (substring transformers last (1- pos)) + (dired-make-relative filename (, dir) t)) + last pos))) + ((setq pos (1+ pos))))) + (if result + (progn + (setq result (dired-shell-quote + (concat result (substring transformers last)))) + (if transformed (list result) result)) + transformers)))) + +(defun dired-trans-filenames (transformers files dir) + ;; Applies a transformer string to a list of filenames, + ;; concatenating them into a string. The result will be prefixed + ;; by dired-shell-prefix, the filenames separated by dired-shell-separator, + ;; and postfixed by dired-shell-postfix. + ;; Returns a list if filename subst. was done. A string otherwise. + (let ((list files) + (res nil) + trans) + (while list + (setq trans (dired-trans-subst transformers (car list) dir)) + (if (listp trans) + (setq res (nconc res trans) + list (cdr list)) + (setq res trans + list nil))) + (if (listp res) + (list + (if (> (length files) 1) + (concat dired-shell-prefix + (mapconcat 'identity res dired-shell-separator) + dired-shell-postfix) + (car res))) + res))) + +(defun dired-trans-command (command files dir) + ;; Do all of the trans substitutions in COMMAND for the list + ;; of files FILES. FILES must be a list of *absolute* pathnames. + ;; DIR is an absolute directory wrto which filenames may be relativized. + (let ((len (length command)) + (start 0) + (pos 0) + (last 0) + result char transed transform) + (while (< pos len) + ;; read over word separators. + (while (and (< pos len) (memq (aref command pos) + dired-shell-command-separators)) + (setq pos (1+ pos))) + (setq start pos) + ;; read a word + (while (and (< pos len) (not (memq (setq char (aref command pos)) + dired-shell-command-separators))) + (setq pos (1+ pos)) + ;; look out for quoted separators + (and (= ?\\ char) (< pos len) (or (memq (setq char (aref command pos)) + dired-shell-command-separators) + (= ?\\ char)) + (setq pos (1+ pos)))) + (setq transform (if (= start pos) + "" + (dired-trans-filenames (substring command start pos) + files dir)) + ;; remember if we did any transforming + transed (or transed (listp transform)) + result (concat result + (substring command last start) + (if (listp transform) + (car transform) + transform)) + last pos)) + (if transed + ;; just return result + result + ;; add the filenames at the end. + (let ((fns (if (> (length files) 1) + (concat dired-shell-prefix + (mapconcat + (function + (lambda (fn) + (dired-shell-quote + (dired-make-relative fn dir t)))) + files dired-shell-separator) + dired-shell-postfix) + (dired-shell-quote + (dired-make-relative (car files) dir t))))) + (concat result " " fns))))) + +(defun dired-shell-stuff-it (command file-list dir on-each) + ;; Make up a shell command line from COMMAND and FILE-LIST. + ;; If ON-EACH is t, COMMAND should be applied to each file, else + ;; simply concat all files and apply COMMAND to this. + ;; If ON-EACH is 'dir, the command is run in the directory of each file + ;; In this case FILE-LIST must be a list of full paths. + ;; FILE-LIST's elements will be quoted for the shell. + (cond + ((eq on-each 'dir) + (let ((subshell-dir nil) + (list file-list) + (result nil)) + (while list + (let ((cmd (dired-trans-command command (list (car list)) + (file-name-directory (car list)))) + (fdir (dired-shell-quote (file-name-directory (car list))))) + (setq result + (apply 'concat + result + (if subshell-dir + (if (string-equal dir subshell-dir) + (list "\; " cmd) + (if (string-equal dir fdir) + (progn + (setq subshell-dir nil) + (list "\)\; " cmd)) + (setq subshell-dir fdir) + (list "\)\; \(cd " + fdir + "\; " + cmd))) + (if (string-equal fdir dir) + (list (and result "\; ") + cmd) + (setq subshell-dir fdir) + (list (and result "\; ") + "\(cd " + fdir + "\; " + cmd))))) + (setq list (cdr list)))) + (concat result (and subshell-dir ")")))) + (on-each + (mapconcat (function + (lambda (fn) + (dired-trans-command command (list fn) dir))) + file-list "; ")) + + (t (dired-trans-command command file-list dir)))) + +(defun dired-guess-default (files) + ;; Guess a list of possible shell commands for FILES. + (and dired-default-auto-shell-command-alist + files + (let ((alist (append dired-auto-shell-command-alist + dired-default-auto-shell-command-alist)) + guesses) + (while alist + (let* ((elt (car alist)) + (regexp (car elt))) + (setq guesses + (nconc guesses + (catch 'missed + (mapcar (function + (lambda (file) + (or (string-match regexp file) + (throw 'missed nil)))) + files) + (delq nil (mapcar 'eval (cdr elt))))))) + (setq alist (cdr alist))) + (dired-uniquefy-list guesses)))) + +(defun dired-shell-unhandle-file-name (filename) + "Turn a file name into a form that can be sent to a shell process. +This is particularly usefull if we are sending file names to a remote shell." + (let ((handler (find-file-name-handler filename 'dired-shell-unhandle-file-name))) + (if handler + (funcall handler 'dired-shell-unhandle-file-name filename) + filename))) + +;;; Actually running the shell command + +(defun dired-run-shell-command-closeout (buffer &optional message) + ;; Report on the number of lines produced by a shell command. + (if (get-buffer buffer) + (save-excursion + (set-buffer buffer) + (if (zerop (buffer-size)) + (progn + (if message + (message "Shell command completed with no output. %s" + message) + (message "Shell command completed with no output.")) + (kill-buffer buffer)) + (set-window-start (display-buffer buffer) 1) + (if message + (message "Shell command completed. %s" message) + (message "Shell command completed.")))))) + +(defun dired-rsc-filter (proc string) + ;; Do save-excursion by hand so that we can leave point + ;; numerically unchanged despite an insertion immediately + ;; after it. + (let* ((obuf (current-buffer)) + (buffer (process-buffer proc)) + opoint + (window (get-buffer-window buffer)) + (pos (window-start window))) + (unwind-protect + (progn + (set-buffer buffer) + (setq opoint (point)) + (goto-char (point-max)) + (insert-before-markers string)) + ;; insert-before-markers moved this marker: set it back. + (set-window-start window pos) + ;; Finish our save-excursion. + (goto-char opoint) + (set-buffer obuf)))) + +(defun dired-rsc-sentinel (process signal) + ;; Sentinel function used by dired-run-shell-command + (if (memq (process-status process) '(exit signal)) + (let ((buffer (get-buffer (process-buffer process)))) + (if buffer + (save-excursion + (set-buffer buffer) + (if (zerop (buffer-size)) + (message + "Dired & shell command completed with no output.") + (let ((lines (count-lines dired-shell-input-start + (point-max)))) + (message + "Dired & shell command completed with %d line%s of output." + lines (dired-plural-s lines)))) + (setq mode-line-process nil))) + (delete-process process)))) + +(defun dired-shell-call-process (command dir &optional in-background) + ;; Call a shell command as a process in the current buffer. + ;; The process should try to run in DIR. DIR is also + ;; used to lookup a file-name-handler. + ;; Must return the process object if IN-BACKGROUND is non-nil, + ;; otherwise the process exit status. + (let ((handler (find-file-name-handler dir 'dired-shell-call-process))) + (if handler + (funcall handler 'dired-shell-call-process command dir in-background) + (let ((process-connection-type ; don't waste pty's + (null (null in-background)))) + (setq default-directory dir) + (if in-background + (progn + (setq mode-line-process '(": %s")) + (start-process "Shell" (current-buffer) + shell-file-name "-c" command)) + (call-process shell-file-name nil t nil "-c" command)))))) + +(defun dired-run-shell-command (command dir in-background &optional append) + ;; COMMAND is shell command + ;; DIR is directory in which to do the shell command. + ;; If IN-BACKGROUND is non-nil, the shell command is run in the background. + ;; If it is a string, this is written as header into the output buffer + ;; before the command is run. + ;; If APPEND is non-nil, the results are appended to the contents + ;; of *shell-command* buffer, without erasing its previous contents. + (save-excursion + (if in-background + (let* ((buffer (get-buffer-create + "*Background Shell Command Output*")) + (n 2) + proc) + ;; No reason why we can't run two+ background commands. + (while (get-buffer-process buffer) + (setq buffer (get-buffer-create + (concat "*Background Shell Command Output*<" + (int-to-string n) ">")) + n (1+ n))) + (set-buffer buffer) + (or (eq major-mode 'comint-mode) + (progn + (comint-mode) + (set (make-local-variable 'comint-prompt-regexp) + "^[^\n]*\\? *"))) + (display-buffer buffer) + (barf-if-buffer-read-only) + ;; If will kill a process, query first. + + (set (make-local-variable 'dired-shell-input-start) (point-min)) + (if append + (progn + (goto-char (point-max)) + (or (= (preceding-char) ?\n) (bobp) (insert "\n"))) + (erase-buffer) + (if (stringp in-background) + (progn + (insert in-background) + (set (make-local-variable 'dired-shell-input-start) + (point))))) + (setq proc (dired-shell-call-process command dir t)) + (set-marker (process-mark proc) (point)) + (set-process-sentinel proc 'dired-rsc-sentinel) + (set-process-filter proc 'dired-rsc-filter) + nil) ; return + (let ((buffer (get-buffer-create "*Shell Command Output*"))) + (set-buffer buffer) + (barf-if-buffer-read-only) + (set (make-local-variable 'dired-shell-input-start) (point-min)) + (if append + (progn + (goto-char (point-max)) + (or (= (preceding-char) ?\n) (bobp) (insert "\n"))) + (erase-buffer)) + (dired-shell-call-process command dir))))) + +;;; User commands + +(defun dired-do-shell-command (command arg files &optional in-background) + ;; ARG = (16) means operate on each file, in its own directory. + ;; ARG = (4) means operate on each file, but in the current + ;; default-directory. + "Run a shell command COMMAND on the marked files. +If no files are marked or a non-zero numeric prefix arg is given, +the next ARG files are used. Use prefix 1 to indicate the current file. + +Normally the shell command is executed in the current dired subdirectory. +This is the directory in the dired buffer which currently contains the point. +One shell command is run for all of the files. +e.g. cmd file1 file2 file3 ... +If the total length of of the command exceeds 10000 characters, the files will +be bunched to forms commands shorter than this length, and successive commands +will be sent. + +With a prefix of \\[universal-argument], a separate command for each file will +be executed. + +With a prefix of \\[universal-argument] \\[universal-argument], a separate command will be sent for each file, +and the command will be executed in the directory of that file. The explicit +command will be of the form + + cd dir; cmd file + +When prompting for the shell command, dired will always indicate the directory +in which the command will be executed. + +The following documentation depends on the settings of `dired-file-wildcard', +`dired-shell-command-separators', `dired-trans-map', `dired-shell-prefix', +`dired-shell-separator', and `dired-shell-postfix'. See the documentation for +these variables. Below, I will assume default settings for these variables. + +If the shell command contains a *, then the list of files is substituted for *. +The filenames will be written as relative to the directory in which the shell +command is executing. If there is no *, and the command does not end in &, +then the files are appended to the end of the command. If the command ends in +a &, then the files are inserted before the &. + +If `dired-use-file-transformers' is non-nil, then certain 2-character +sequences represent parts of the file name. +The default transformers are: +*f = full file name +*n = file name without directory +*d = file name's directory + This will end in a \"/\" in unix. +*e = file names extension + By default this the part of the file name without directory, which + proceeds the first \".\". If \".\" is the first character of the name, + then this \".\" is ignored. The definition of extension can + be customized with `dired-filename-re-ext'. +*b = file base name + This is the part of the file name without directory that precedes + the extension. +*v = file name with out version control extension (i.e. \",v\") +*z = file name without compression extension + (i.e. \".Z\", \".z\", or \".gz\") + +Shell commands are divided into words separated by spaces. Then for each +word the file name transformers are applied to the list of files, the result +concatenated together and substituted for the word in the shell command. + +For example + cmd -a *f -b *d*b.fizzle applied to /foo/bar and /la/di/da results in + cmd -a /foo/bar /la/di/da -b /foo/bar.fizzle /la/di/da.fizzle + +The \"on-each\" prefixes \\[universal-argument] and 0, also apply while +using file transformers. As well, when using file-transformers * still +represents the file name relative to the current directory. Not that this +differs from *f, which always represents the full pathname. + +A \"\\\" can always be used to quote any character having special meaning. +For example, if the current directory is /la, then *n applied +to /la/di/da returns la, whereas *\\n returns di/dan. Similarly, +\"*d\\ *n\" returns \"/la/di da\". + +The prefix character for file name transformers is always the same as +`dired-file-wildcard'." + + (interactive + (let ((on-each (or (equal '(4) current-prefix-arg) + (equal '(16) current-prefix-arg))) + (files (dired-get-marked-files + nil (and (not (consp current-prefix-arg)) + current-prefix-arg))) + (dir (and (not (equal current-prefix-arg '(16))) + (dired-current-directory)))) + (list + (dired-read-shell-command + (concat (if dir + (format "! in %s" (dired-abbreviate-file-name dir)) + "cd <dir>; ! ") + "on " + (if on-each "each ") + "%s: ") + (and (not on-each) current-prefix-arg) + (if dir + (mapcar (function + (lambda (fn) + (dired-make-relative fn dir t))) + files) + files)) + current-prefix-arg files nil))) + + ;; Check for background commands + (if (string-match "[ \t]*&[ \t]*$" command) + (setq command (substring command 0 (match-beginning 0)) + in-background t)) + + ;; Look out for remote file names. + + (let* ((on-each (or (equal arg '(4)) (and (equal arg '(16)) 'dir))) + (ufiles (mapcar 'dired-shell-unhandle-file-name files)) + (dir (dired-current-directory)) + (udir (dired-shell-unhandle-file-name dir))) + + (save-excursion ; in case `shell-command' changes buffer + (cond + + ((null ufiles) + ;; Just run as a command on no files. + (if in-background + (dired-run-shell-command command dir t) + (dired-run-shell-command command dir nil) + (dired-run-shell-command-closeout "*Shell Command Output*"))) + + (in-background + ;; Can't use dired-bunch-files for background shell commands. + ;; as we will create a bunch of process running simultaneously. + ;; A better solution needs to be found. + (dired-run-shell-command + (dired-shell-stuff-it command ufiles udir on-each) + dir (if (equal arg '(16)) + (concat "cd <dir>; \"" command "\"\n\n") + (concat "\"" command "\" in " dir "\n\n")))) + (on-each + (let ((buff (get-buffer "*Shell Command Output*")) + failures this-command this-dir ufile return message) + (if buff + (save-excursion + (set-buffer buff) + (erase-buffer))) + (while ufiles + (setq ufile (car ufiles)) + (if (eq on-each 'dir) + (setq this-dir (dired-shell-quote (file-name-directory (directory-file-name ufile))) + this-command (concat "cd " this-dir "; " command)) + (setq this-command command) + (or this-dir (setq this-dir udir))) + (setq return + (dired-run-shell-command + (dired-shell-stuff-it this-command (list ufile) this-dir nil) + this-dir nil t)) + (if (and (integerp return) (/= return 0)) + (save-excursion + (let ((file (nth (- (length files) (length (member ufile ufiles))) files))) + (if (and dired-shell-failure-marker + (dired-goto-file file)) + (let ((dired-marker-char dired-shell-failure-marker)) + (dired-mark 1))) + (setq failures (cons file failures))))) + (setq ufiles (cdr ufiles))) + (if failures + (let ((num (length failures))) + (setq message + (if dired-shell-failure-marker + (format + "Marked %d failure%s with %c." + num (dired-plural-s num) + dired-shell-failure-marker) + "Failed on %d file%s." num + (dired-plural-s num))) + (dired-log + (current-buffer) + "Shell command %s failed (non-zero exit status) for:\n %s" + command failures) + (dired-log (current-buffer) t))) + (dired-run-shell-command-closeout "*Shell Command Output*" message))) + + (t + (dired-bunch-files + (- 10000 (length command)) + (function (lambda (&rest ufiles) + (dired-run-shell-command + (dired-shell-stuff-it command ufiles udir nil) + dir nil) + nil)) ; for the sake of nconc in dired-bunch-files + nil ufiles) + (dired-run-shell-command-closeout "*Shell Command Output*")))) + ;; Update any directories + (or in-background + (let ((dired-no-confirm '(revert-subdirs))) + (dired-verify-modtimes))))) + +(defun dired-do-background-shell-command (command arg files) + "Like \\[dired-do-shell-command], but starts command in background. +Note that you can type input to the command in its buffer. +This requires background.el from the comint package to work." + ;; With the version in emacs-19.el, you can alternatively just + ;; append an `&' to any shell command to make it run in the + ;; background, but you can't type input to it. + (interactive + (let ((on-each (or (equal '(4) current-prefix-arg) + (equal '(16) current-prefix-arg))) + (files (dired-get-marked-files + nil (and (not (consp current-prefix-arg)) + current-prefix-arg))) + (dir (and (not (equal current-prefix-arg '(16))) + (dired-current-directory)))) + (list + (dired-read-shell-command + (concat "& " + (if dir + (format "in %s " (dired-abbreviate-file-name dir)) + "cd <dir>; ") + "on " + (if on-each "each ") + "%s: ") + (and (not on-each) current-prefix-arg) + (if dir + (mapcar (function + (lambda (fn) + (dired-make-relative fn dir t))) + files) + files)) + current-prefix-arg files))) + (dired-do-shell-command command arg files t)) + +;;; Printing files + +(defun dired-do-print (&optional arg command files) + "Print the marked (or next ARG) files. +Uses the shell command coming from variable `dired-print-program-alist'." + (interactive + (progn + (if dired-print-history + (setq dired-print-history (dired-uniquefy-list dired-print-history)) + (setq dired-print-history (mapcar 'cdr dired-print-program-alist))) + (let* ((files (dired-get-marked-files nil current-prefix-arg)) + (rel-files (mapcar (function + (lambda (fn) + (dired-make-relative + fn + (dired-current-directory) t))) + files)) + (alist dired-print-program-alist) + (first (car files)) + (dired-print-history (copy-sequence dired-print-history)) + elt initial command) + ;; For gmhist + (put 'dired-print-history 'no-default t) + (if first + (while (and alist (not initial)) + (if (string-match (car (car alist)) first) + (setq initial (cdr (car alist))) + (setq alist (cdr alist))))) + (if (and initial (setq elt (member initial dired-print-history))) + (setq dired-print-history (nconc + (delq (car elt) dired-print-history) + (list initial)))) + (setq command + (dired-mark-read-string + "Print %s with: " + initial 'print current-prefix-arg rel-files + 'dired-print-history)) + (list current-prefix-arg command files)))) + (or files + (setq files (dired-get-marked-files nil arg))) + (while files + (dired-print-file command (car files)) + (setq files (cdr files)))) + +(defun dired-print-file (command file) + ;; Using COMMAND, print FILE. + (let ((handler (find-file-name-handler file 'dired-print-file))) + (if handler + (funcall handler 'dired-print-file command file) + (let ((rel-file (dired-make-relative file (dired-current-directory) t))) + (message "Spooling %s..." rel-file) + (shell-command (dired-trans-command command (list file) "")) + (message "Spooling %s...done" rel-file))))) + +;;; end of dired-shell.el