comparison lisp/efs/dired-shell.el @ 22:8fc7fe29b841 r19-15b94

Import from CVS: tag r19-15b94
author cvs
date Mon, 13 Aug 2007 08:50:29 +0200
parents
children 7e54bd776075 8619ce7e4c50
comparison
equal deleted inserted replaced
21:b88636d63495 22:8fc7fe29b841
1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2 ;;
3 ;; File: dired-shell.el
4 ;; Dired Version: $Revision: 1.1 $
5 ;; RCS:
6 ;; Description: Commands for running shell commands on marked files.
7 ;;
8 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
9
10 ;;; Requirements and provisions
11 (provide 'dired-shell)
12 (require 'dired)
13 (autoload 'comint-mode "comint")
14
15 ;;; Variables
16
17 (defvar dired-postscript-print-command
18 (concat
19 (if (boundp 'lpr-command)
20 lpr-command
21 (if (memq system-type
22 '(usg-unix-v hpux silicon-graphics-unix))
23 "lp"
24 "lpr"))
25 (if (and (boundp 'lpr-switches) lpr-switches)
26 (concat " "
27 (mapconcat 'identity lpr-switches " ")
28 " ")
29 " "))
30 "Command to print a postscript file.")
31
32 (defvar dired-text-print-command (concat dired-postscript-print-command " -p")
33 "Command to print a text file.")
34
35 (defvar dired-print-program-alist
36 (list
37 (cons "\\.gif$" (concat "giftoppm * | ppmtopgm | pnmtops | "
38 dired-postscript-print-command))
39 (cons "\\.\\(fts\\|FTS\\)$" (concat "fitstopgm * | pnmtops | "
40 dired-postscript-print-command))
41 ;; People with colour printers won't want the g-flag in djpeg
42 (cons "\\.\\(JPG\\|jpg\\)$" (concat "djpeg -Pg * | pnmtops | "
43 dired-postscript-print-command))
44 (cons "\\.ps\\.\\(gz\\|Z\\)$" (concat "zcat * | "
45 dired-postscript-print-command))
46 (cons "\\.ps$" dired-postscript-print-command)
47 (cons "\\.\\(gz\\|Z\\)$" (concat "zcat * | "
48 dired-postscript-print-command))
49 (cons "\\.dvi$" "dvips")
50 (cons ".*" dired-text-print-command))
51 "Alist of regexps and print commands.
52 This is used by `dired-do-print' to determine the default print command for
53 printing the marked files.")
54
55 (defvar dired-auto-shell-command-alist nil
56 "*Alist of regexps and command lists to guess shell commands.
57 Each element of this list should be a list of regular expression, and a list
58 of guesses for shell commands to be used if the file name matches the regular
59 expression. The list of guesses is evalled. This alist is appended to the front
60 of dired-default-auto-shell-command-alist before prompting for each shell
61 command.")
62
63 (defvar dired-default-auto-shell-command-alist
64 (list
65
66 ;; Archiving
67 '("\\.tar$"
68 (if dired-gnutar-program
69 (concat dired-gnutar-program " xvf")
70 "tar xvf")
71 (if dired-gnutar-program
72 (concat dired-gnutar-program " tvf")
73 "tar tvf"))
74 ;; regexps for compressed archives must come before the .Z rule to
75 ;; be recognized:
76 '("\\.tar\\.\\([zZ]\\|gz\\)\\|\\.tgz$" ; .tgz is for DOS
77 (if dired-gnutar-program
78 (concat dired-gnutar-program " zxvf")
79 "zcat * | tar xvf -")
80 (if dired-gnutar-program
81 (concat dired-gnutar-program " ztvf")
82 "zcat * | tar tvf -"))
83 '("\\.shar.[zZ]$" (if dired-unshar-program
84 (concat "zcat * | " dired-unshar-program)
85 "zcat * | sh"))
86 '("\\.zoo$" "zoo x//")
87 '("\\.zip$" "unzip" "unzip -v")
88 '("\\.lzh$" "lharc x")
89 '("\\.arc$" "arc x")
90 '("\\.shar$" (if dired-unshar-program dired-unshar-program "sh"))
91
92 ;; Encoding/compressing
93 '("\\.uu$" "uudecode")
94 '("\\.hqx$" "mcvert")
95
96 ;; Executing (in the generalized sense)
97 '("\\.sh$" "sh") ; execute shell scripts
98 '("^[Mm]akefile$" "make -f *")
99 '("\\.diff$" "patch -t <")
100
101 ;; Displaying (assumes X)
102 '("\\.xbm$" "bitmap") ; view X11 bitmaps
103 '("\\.gp$" "gnuplot")
104 '("\\.gif$" "xv") ; view gif pictures
105 '("\\.fig$" "xfig") ; edit fig pictures
106 '("\\.ps$" "ghostview")
107
108 ;; Typesetting. For printing documents, see dired-print-program-alist.
109 '("\\.tex$" "latex" "tex")
110 '("\\.texi\\(nfo\\)?$" "makeinfo" "texi2dvi")
111 (if (eq window-system 'x)
112 (if dired-use-file-transformers
113 '("\\.dvi$" "xdvi" "dvips -o *b.ps *")
114 '("\\.dvi$" "xdvi" "dvips"))
115 (if dired-use-file-transformers
116 '("\\.dvi$" "dvips -o *b.ps *")
117 '("\\.dvi$" "dvips")))
118
119 ;; The last word. Things that cannot be grokked with a regexp.
120 '("." (if (> (length files) 1)
121 "tar cvf "
122 (and (= (length files) 1) (file-directory-p
123 (expand-file-name
124 (car files)
125 (dired-current-directory)))
126 (concat "tar cvf " (file-name-nondirectory
127 (directory-file-name (car files)))
128 ".tar"))))
129 )
130 "Default for variable `dired-auto-shell-command-alist' (which see).
131 Set this to nil to turn off shell command guessing.")
132
133 ;; Might use {,} for bash or csh:
134 (defvar dired-shell-prefix ""
135 "Prepended to marked files in dired shell commands.")
136 (defvar dired-shell-postfix ""
137 "Appended to marked files in dired shell commands.")
138 (defvar dired-shell-separator " "
139 "Separates marked files in dired shell commands.")
140
141 (defvar dired-file-wildcard ?*
142 "Wildcard character used by dired shell commands.
143 Indicates where file names should be inserted.")
144
145 (defvar dired-shell-command-separators '(?\ ?| ?> ?< ?& ?;)
146 "Defines the start of a string specifying a word in a shell command.")
147
148 (defvar dired-trans-map
149 (list
150 (cons ?f 'identity)
151 (cons ?n 'file-name-nondirectory)
152 (cons ?d 'file-name-directory)
153 (cons ?b 'dired-file-name-base)
154 (cons ?e 'dired-file-name-extension)
155 (cons ?v 'dired-file-name-sans-rcs-extension)
156 (cons ?z 'dired-file-name-sans-compress-extension))
157 "Alist that associates keys with file transformer functions
158 Each transformer function should be a funcion of one argument, the file name.
159 The keys are characters.")
160
161 (defvar dired-shell-failure-marker ?!
162 "*A marker to mark files on which shell commands fail.
163 If nil, such files are not marked.")
164
165 ;;; Internal variables
166
167 ;; Make sure this gets defined.
168 (defvar shell-command-history nil
169 "History list of previous shell commands.")
170
171 (defvar dired-print-history nil
172 "History of commands used to print files.")
173
174 (defvar dired-shell-input-start) ; only defined in shell output buffers
175
176 ;;; Utility functions and Macros
177
178 (defun dired-shell-quote (filename)
179 ;; Quote a file name for inferior shell (see variable shell-file-name).
180 ;; Quote everything except POSIX filename characters.
181 ;; This should be safe enough even for really wierd shells.
182 (let ((result "") (start 0) end)
183 (while (string-match "[^---0-9a-zA-Z_./]" filename start)
184 (setq end (match-beginning 0)
185 result (concat result (substring filename start end)
186 "\\" (substring filename end (1+ end)))
187 start (1+ end)))
188 (concat result (substring filename start))))
189
190 (defun dired-uniquefy-list (list)
191 ;; Returns list, after removing 2nd and higher occurrences
192 ;; of all elements. Tests elements with equal. Retains the relative
193 ;; order of the elements.
194 ;; For small lists, this way is probably faster than sorting.
195 (let (result)
196 (while list
197 (or (member (car list) result)
198 (setq result (nconc result (list (car list)))))
199 (setq list (cdr list)))
200 result))
201
202 (defun dired-read-shell-command (prompt arg files)
203 ;; Read a dired shell command prompting with PROMPT (using read-string).
204 ;; ARG is the prefix arg and may be used to indicate in the prompt which
205 ;; files are affected.
206 (dired-mark-pop-up
207 nil 'shell files
208 (function
209 (lambda (prompt files)
210 (let* ((default (car shell-command-history))
211 (guesses (dired-guess-default files))
212 (len (length guesses))
213 cmd)
214 (or (zerop len)
215 (setq prompt (format "%s{%d guess%s} "
216 prompt len (if (= len 1) "" "es"))))
217 (if default (setq prompt (concat prompt "[" default "] ")))
218 (put 'guesses 'no-default t) ; for gmhist, in case.
219 (setq guesses (nconc guesses (copy-sequence shell-command-history))
220 cmd (dired-read-with-history prompt nil 'guesses))
221 (if (string-match "^[ \t\n]*$" cmd)
222 (if default
223 (setq cmd default)
224 (error "No shell command given.")))
225 (setq shell-command-history
226 (dired-uniquefy-list
227 (cons cmd shell-command-history)))
228 cmd)))
229 (format prompt (dired-mark-prompt arg files)) files))
230
231 (defmacro dired-trans-subst (transformers filename dir)
232 ;; Applies each transformer supplied in the string TRANSFORMERS in sequence
233 ;; to FILE and returns the concatenation of the results. Also unquotes \\'s.
234 ;; Returns a string if no file transformations were done, otherwise a list
235 ;; consisting of a single string.
236 (` (let* ((transformers (, transformers))
237 (filename (, filename))
238 (len (length transformers))
239 (pos 0)
240 (last 0)
241 (transformed nil)
242 (quoted nil)
243 char result trans)
244 (while (< pos len)
245 (setq char (aref transformers pos))
246 (cond
247 (quoted (setq pos (1+ pos)
248 quoted nil))
249 ((= ?\\ char)
250 (setq quoted t
251 result (concat result (substring transformers last pos))
252 pos (1+ pos)
253 last pos))
254 ((and (null quoted) (= char dired-file-wildcard))
255 (setq pos (1+ pos)
256 trans (and (< pos len)
257 dired-use-file-transformers
258 (assq (aref transformers pos)
259 dired-trans-map))
260 transformed t)
261 (if trans
262 (setq result (concat result
263 (substring transformers last (1- pos))
264 (funcall (cdr trans) filename))
265 pos (1+ pos)
266 last pos)
267 (setq result (concat result (substring transformers last (1- pos))
268 (dired-make-relative filename (, dir) t))
269 last pos)))
270 ((setq pos (1+ pos)))))
271 (if result
272 (progn
273 (setq result (dired-shell-quote
274 (concat result (substring transformers last))))
275 (if transformed (list result) result))
276 transformers))))
277
278 (defun dired-trans-filenames (transformers files dir)
279 ;; Applies a transformer string to a list of filenames,
280 ;; concatenating them into a string. The result will be prefixed
281 ;; by dired-shell-prefix, the filenames separated by dired-shell-separator,
282 ;; and postfixed by dired-shell-postfix.
283 ;; Returns a list if filename subst. was done. A string otherwise.
284 (let ((list files)
285 (res nil)
286 trans)
287 (while list
288 (setq trans (dired-trans-subst transformers (car list) dir))
289 (if (listp trans)
290 (setq res (nconc res trans)
291 list (cdr list))
292 (setq res trans
293 list nil)))
294 (if (listp res)
295 (list
296 (if (> (length files) 1)
297 (concat dired-shell-prefix
298 (mapconcat 'identity res dired-shell-separator)
299 dired-shell-postfix)
300 (car res)))
301 res)))
302
303 (defun dired-trans-command (command files dir)
304 ;; Do all of the trans substitutions in COMMAND for the list
305 ;; of files FILES. FILES must be a list of *absolute* pathnames.
306 ;; DIR is an absolute directory wrto which filenames may be relativized.
307 (let ((len (length command))
308 (start 0)
309 (pos 0)
310 (last 0)
311 result char transed transform)
312 (while (< pos len)
313 ;; read over word separators.
314 (while (and (< pos len) (memq (aref command pos)
315 dired-shell-command-separators))
316 (setq pos (1+ pos)))
317 (setq start pos)
318 ;; read a word
319 (while (and (< pos len) (not (memq (setq char (aref command pos))
320 dired-shell-command-separators)))
321 (setq pos (1+ pos))
322 ;; look out for quoted separators
323 (and (= ?\\ char) (< pos len) (or (memq (setq char (aref command pos))
324 dired-shell-command-separators)
325 (= ?\\ char))
326 (setq pos (1+ pos))))
327 (setq transform (if (= start pos)
328 ""
329 (dired-trans-filenames (substring command start pos)
330 files dir))
331 ;; remember if we did any transforming
332 transed (or transed (listp transform))
333 result (concat result
334 (substring command last start)
335 (if (listp transform)
336 (car transform)
337 transform))
338 last pos))
339 (if transed
340 ;; just return result
341 result
342 ;; add the filenames at the end.
343 (let ((fns (if (> (length files) 1)
344 (concat dired-shell-prefix
345 (mapconcat
346 (function
347 (lambda (fn)
348 (dired-shell-quote
349 (dired-make-relative fn dir t))))
350 files dired-shell-separator)
351 dired-shell-postfix)
352 (dired-shell-quote
353 (dired-make-relative (car files) dir t)))))
354 (concat result " " fns)))))
355
356 (defun dired-shell-stuff-it (command file-list dir on-each)
357 ;; Make up a shell command line from COMMAND and FILE-LIST.
358 ;; If ON-EACH is t, COMMAND should be applied to each file, else
359 ;; simply concat all files and apply COMMAND to this.
360 ;; If ON-EACH is 'dir, the command is run in the directory of each file
361 ;; In this case FILE-LIST must be a list of full paths.
362 ;; FILE-LIST's elements will be quoted for the shell.
363 (cond
364 ((eq on-each 'dir)
365 (let ((subshell-dir nil)
366 (list file-list)
367 (result nil))
368 (while list
369 (let ((cmd (dired-trans-command command (list (car list))
370 (file-name-directory (car list))))
371 (fdir (dired-shell-quote (file-name-directory (car list)))))
372 (setq result
373 (apply 'concat
374 result
375 (if subshell-dir
376 (if (string-equal dir subshell-dir)
377 (list "\; " cmd)
378 (if (string-equal dir fdir)
379 (progn
380 (setq subshell-dir nil)
381 (list "\)\; " cmd))
382 (setq subshell-dir fdir)
383 (list "\)\; \(cd "
384 fdir
385 "\; "
386 cmd)))
387 (if (string-equal fdir dir)
388 (list (and result "\; ")
389 cmd)
390 (setq subshell-dir fdir)
391 (list (and result "\; ")
392 "\(cd "
393 fdir
394 "\; "
395 cmd)))))
396 (setq list (cdr list))))
397 (concat result (and subshell-dir ")"))))
398 (on-each
399 (mapconcat (function
400 (lambda (fn)
401 (dired-trans-command command (list fn) dir)))
402 file-list "; "))
403
404 (t (dired-trans-command command file-list dir))))
405
406 (defun dired-guess-default (files)
407 ;; Guess a list of possible shell commands for FILES.
408 (and dired-default-auto-shell-command-alist
409 files
410 (let ((alist (append dired-auto-shell-command-alist
411 dired-default-auto-shell-command-alist))
412 guesses)
413 (while alist
414 (let* ((elt (car alist))
415 (regexp (car elt)))
416 (setq guesses
417 (nconc guesses
418 (catch 'missed
419 (mapcar (function
420 (lambda (file)
421 (or (string-match regexp file)
422 (throw 'missed nil))))
423 files)
424 (delq nil (mapcar 'eval (cdr elt)))))))
425 (setq alist (cdr alist)))
426 (dired-uniquefy-list guesses))))
427
428 (defun dired-shell-unhandle-file-name (filename)
429 "Turn a file name into a form that can be sent to a shell process.
430 This is particularly usefull if we are sending file names to a remote shell."
431 (let ((handler (find-file-name-handler filename 'dired-shell-unhandle-file-name)))
432 (if handler
433 (funcall handler 'dired-shell-unhandle-file-name filename)
434 filename)))
435
436 ;;; Actually running the shell command
437
438 (defun dired-run-shell-command-closeout (buffer &optional message)
439 ;; Report on the number of lines produced by a shell command.
440 (if (get-buffer buffer)
441 (save-excursion
442 (set-buffer buffer)
443 (if (zerop (buffer-size))
444 (progn
445 (if message
446 (message "Shell command completed with no output. %s"
447 message)
448 (message "Shell command completed with no output."))
449 (kill-buffer buffer))
450 (set-window-start (display-buffer buffer) 1)
451 (if message
452 (message "Shell command completed. %s" message)
453 (message "Shell command completed."))))))
454
455 (defun dired-rsc-filter (proc string)
456 ;; Do save-excursion by hand so that we can leave point
457 ;; numerically unchanged despite an insertion immediately
458 ;; after it.
459 (let* ((obuf (current-buffer))
460 (buffer (process-buffer proc))
461 opoint
462 (window (get-buffer-window buffer))
463 (pos (window-start window)))
464 (unwind-protect
465 (progn
466 (set-buffer buffer)
467 (setq opoint (point))
468 (goto-char (point-max))
469 (insert-before-markers string))
470 ;; insert-before-markers moved this marker: set it back.
471 (set-window-start window pos)
472 ;; Finish our save-excursion.
473 (goto-char opoint)
474 (set-buffer obuf))))
475
476 (defun dired-rsc-sentinel (process signal)
477 ;; Sentinel function used by dired-run-shell-command
478 (if (memq (process-status process) '(exit signal))
479 (let ((buffer (get-buffer (process-buffer process))))
480 (if buffer
481 (save-excursion
482 (set-buffer buffer)
483 (if (zerop (buffer-size))
484 (message
485 "Dired & shell command completed with no output.")
486 (let ((lines (count-lines dired-shell-input-start
487 (point-max))))
488 (message
489 "Dired & shell command completed with %d line%s of output."
490 lines (dired-plural-s lines))))
491 (setq mode-line-process nil)))
492 (delete-process process))))
493
494 (defun dired-shell-call-process (command dir &optional in-background)
495 ;; Call a shell command as a process in the current buffer.
496 ;; The process should try to run in DIR. DIR is also
497 ;; used to lookup a file-name-handler.
498 ;; Must return the process object if IN-BACKGROUND is non-nil,
499 ;; otherwise the process exit status.
500 (let ((handler (find-file-name-handler dir 'dired-shell-call-process)))
501 (if handler
502 (funcall handler 'dired-shell-call-process command dir in-background)
503 (let ((process-connection-type ; don't waste pty's
504 (null (null in-background))))
505 (setq default-directory dir)
506 (if in-background
507 (progn
508 (setq mode-line-process '(": %s"))
509 (start-process "Shell" (current-buffer)
510 shell-file-name "-c" command))
511 (call-process shell-file-name nil t nil "-c" command))))))
512
513 (defun dired-run-shell-command (command dir in-background &optional append)
514 ;; COMMAND is shell command
515 ;; DIR is directory in which to do the shell command.
516 ;; If IN-BACKGROUND is non-nil, the shell command is run in the background.
517 ;; If it is a string, this is written as header into the output buffer
518 ;; before the command is run.
519 ;; If APPEND is non-nil, the results are appended to the contents
520 ;; of *shell-command* buffer, without erasing its previous contents.
521 (save-excursion
522 (if in-background
523 (let* ((buffer (get-buffer-create
524 "*Background Shell Command Output*"))
525 (n 2)
526 proc)
527 ;; No reason why we can't run two+ background commands.
528 (while (get-buffer-process buffer)
529 (setq buffer (get-buffer-create
530 (concat "*Background Shell Command Output*<"
531 (int-to-string n) ">"))
532 n (1+ n)))
533 (set-buffer buffer)
534 (or (eq major-mode 'comint-mode)
535 (progn
536 (comint-mode)
537 (set (make-local-variable 'comint-prompt-regexp)
538 "^[^\n]*\\? *")))
539 (display-buffer buffer)
540 (barf-if-buffer-read-only)
541 ;; If will kill a process, query first.
542
543 (set (make-local-variable 'dired-shell-input-start) (point-min))
544 (if append
545 (progn
546 (goto-char (point-max))
547 (or (= (preceding-char) ?\n) (bobp) (insert "\n")))
548 (erase-buffer)
549 (if (stringp in-background)
550 (progn
551 (insert in-background)
552 (set (make-local-variable 'dired-shell-input-start)
553 (point)))))
554 (setq proc (dired-shell-call-process command dir t))
555 (set-marker (process-mark proc) (point))
556 (set-process-sentinel proc 'dired-rsc-sentinel)
557 (set-process-filter proc 'dired-rsc-filter)
558 nil) ; return
559 (let ((buffer (get-buffer-create "*Shell Command Output*")))
560 (set-buffer buffer)
561 (barf-if-buffer-read-only)
562 (set (make-local-variable 'dired-shell-input-start) (point-min))
563 (if append
564 (progn
565 (goto-char (point-max))
566 (or (= (preceding-char) ?\n) (bobp) (insert "\n")))
567 (erase-buffer))
568 (dired-shell-call-process command dir)))))
569
570 ;;; User commands
571
572 (defun dired-do-shell-command (command arg files &optional in-background)
573 ;; ARG = (16) means operate on each file, in its own directory.
574 ;; ARG = (4) means operate on each file, but in the current
575 ;; default-directory.
576 "Run a shell command COMMAND on the marked files.
577 If no files are marked or a non-zero numeric prefix arg is given,
578 the next ARG files are used. Use prefix 1 to indicate the current file.
579
580 Normally the shell command is executed in the current dired subdirectory.
581 This is the directory in the dired buffer which currently contains the point.
582 One shell command is run for all of the files.
583 e.g. cmd file1 file2 file3 ...
584 If the total length of of the command exceeds 10000 characters, the files will
585 be bunched to forms commands shorter than this length, and successive commands
586 will be sent.
587
588 With a prefix of \\[universal-argument], a separate command for each file will
589 be executed.
590
591 With a prefix of \\[universal-argument] \\[universal-argument], a separate command will be sent for each file,
592 and the command will be executed in the directory of that file. The explicit
593 command will be of the form
594
595 cd dir; cmd file
596
597 When prompting for the shell command, dired will always indicate the directory
598 in which the command will be executed.
599
600 The following documentation depends on the settings of `dired-file-wildcard',
601 `dired-shell-command-separators', `dired-trans-map', `dired-shell-prefix',
602 `dired-shell-separator', and `dired-shell-postfix'. See the documentation for
603 these variables. Below, I will assume default settings for these variables.
604
605 If the shell command contains a *, then the list of files is substituted for *.
606 The filenames will be written as relative to the directory in which the shell
607 command is executing. If there is no *, and the command does not end in &,
608 then the files are appended to the end of the command. If the command ends in
609 a &, then the files are inserted before the &.
610
611 If `dired-use-file-transformers' is non-nil, then certain 2-character
612 sequences represent parts of the file name.
613 The default transformers are:
614 *f = full file name
615 *n = file name without directory
616 *d = file name's directory
617 This will end in a \"/\" in unix.
618 *e = file names extension
619 By default this the part of the file name without directory, which
620 proceeds the first \".\". If \".\" is the first character of the name,
621 then this \".\" is ignored. The definition of extension can
622 be customized with `dired-filename-re-ext'.
623 *b = file base name
624 This is the part of the file name without directory that precedes
625 the extension.
626 *v = file name with out version control extension (i.e. \",v\")
627 *z = file name without compression extension
628 (i.e. \".Z\", \".z\", or \".gz\")
629
630 Shell commands are divided into words separated by spaces. Then for each
631 word the file name transformers are applied to the list of files, the result
632 concatenated together and substituted for the word in the shell command.
633
634 For example
635 cmd -a *f -b *d*b.fizzle applied to /foo/bar and /la/di/da results in
636 cmd -a /foo/bar /la/di/da -b /foo/bar.fizzle /la/di/da.fizzle
637
638 The \"on-each\" prefixes \\[universal-argument] and 0, also apply while
639 using file transformers. As well, when using file-transformers * still
640 represents the file name relative to the current directory. Not that this
641 differs from *f, which always represents the full pathname.
642
643 A \"\\\" can always be used to quote any character having special meaning.
644 For example, if the current directory is /la, then *n applied
645 to /la/di/da returns la, whereas *\\n returns di/dan. Similarly,
646 \"*d\\ *n\" returns \"/la/di da\".
647
648 The prefix character for file name transformers is always the same as
649 `dired-file-wildcard'."
650
651 (interactive
652 (let ((on-each (or (equal '(4) current-prefix-arg)
653 (equal '(16) current-prefix-arg)))
654 (files (dired-get-marked-files
655 nil (and (not (consp current-prefix-arg))
656 current-prefix-arg)))
657 (dir (and (not (equal current-prefix-arg '(16)))
658 (dired-current-directory))))
659 (list
660 (dired-read-shell-command
661 (concat (if dir
662 (format "! in %s" (dired-abbreviate-file-name dir))
663 "cd <dir>; ! ")
664 "on "
665 (if on-each "each ")
666 "%s: ")
667 (and (not on-each) current-prefix-arg)
668 (if dir
669 (mapcar (function
670 (lambda (fn)
671 (dired-make-relative fn dir t)))
672 files)
673 files))
674 current-prefix-arg files nil)))
675
676 ;; Check for background commands
677 (if (string-match "[ \t]*&[ \t]*$" command)
678 (setq command (substring command 0 (match-beginning 0))
679 in-background t))
680
681 ;; Look out for remote file names.
682
683 (let* ((on-each (or (equal arg '(4)) (and (equal arg '(16)) 'dir)))
684 (ufiles (mapcar 'dired-shell-unhandle-file-name files))
685 (dir (dired-current-directory))
686 (udir (dired-shell-unhandle-file-name dir)))
687
688 (save-excursion ; in case `shell-command' changes buffer
689 (cond
690
691 ((null ufiles)
692 ;; Just run as a command on no files.
693 (if in-background
694 (dired-run-shell-command command dir t)
695 (dired-run-shell-command command dir nil)
696 (dired-run-shell-command-closeout "*Shell Command Output*")))
697
698 (in-background
699 ;; Can't use dired-bunch-files for background shell commands.
700 ;; as we will create a bunch of process running simultaneously.
701 ;; A better solution needs to be found.
702 (dired-run-shell-command
703 (dired-shell-stuff-it command ufiles udir on-each)
704 dir (if (equal arg '(16))
705 (concat "cd <dir>; \"" command "\"\n\n")
706 (concat "\"" command "\" in " dir "\n\n"))))
707 (on-each
708 (let ((buff (get-buffer "*Shell Command Output*"))
709 failures this-command this-dir ufile return message)
710 (if buff
711 (save-excursion
712 (set-buffer buff)
713 (erase-buffer)))
714 (while ufiles
715 (setq ufile (car ufiles))
716 (if (eq on-each 'dir)
717 (setq this-dir (dired-shell-quote (file-name-directory (directory-file-name ufile)))
718 this-command (concat "cd " this-dir "; " command))
719 (setq this-command command)
720 (or this-dir (setq this-dir udir)))
721 (setq return
722 (dired-run-shell-command
723 (dired-shell-stuff-it this-command (list ufile) this-dir nil)
724 this-dir nil t))
725 (if (and (integerp return) (/= return 0))
726 (save-excursion
727 (let ((file (nth (- (length files) (length (member ufile ufiles))) files)))
728 (if (and dired-shell-failure-marker
729 (dired-goto-file file))
730 (let ((dired-marker-char dired-shell-failure-marker))
731 (dired-mark 1)))
732 (setq failures (cons file failures)))))
733 (setq ufiles (cdr ufiles)))
734 (if failures
735 (let ((num (length failures)))
736 (setq message
737 (if dired-shell-failure-marker
738 (format
739 "Marked %d failure%s with %c."
740 num (dired-plural-s num)
741 dired-shell-failure-marker)
742 "Failed on %d file%s." num
743 (dired-plural-s num)))
744 (dired-log
745 (current-buffer)
746 "Shell command %s failed (non-zero exit status) for:\n %s"
747 command failures)
748 (dired-log (current-buffer) t)))
749 (dired-run-shell-command-closeout "*Shell Command Output*" message)))
750
751 (t
752 (dired-bunch-files
753 (- 10000 (length command))
754 (function (lambda (&rest ufiles)
755 (dired-run-shell-command
756 (dired-shell-stuff-it command ufiles udir nil)
757 dir nil)
758 nil)) ; for the sake of nconc in dired-bunch-files
759 nil ufiles)
760 (dired-run-shell-command-closeout "*Shell Command Output*"))))
761 ;; Update any directories
762 (or in-background
763 (let ((dired-no-confirm '(revert-subdirs)))
764 (dired-verify-modtimes)))))
765
766 (defun dired-do-background-shell-command (command arg files)
767 "Like \\[dired-do-shell-command], but starts command in background.
768 Note that you can type input to the command in its buffer.
769 This requires background.el from the comint package to work."
770 ;; With the version in emacs-19.el, you can alternatively just
771 ;; append an `&' to any shell command to make it run in the
772 ;; background, but you can't type input to it.
773 (interactive
774 (let ((on-each (or (equal '(4) current-prefix-arg)
775 (equal '(16) current-prefix-arg)))
776 (files (dired-get-marked-files
777 nil (and (not (consp current-prefix-arg))
778 current-prefix-arg)))
779 (dir (and (not (equal current-prefix-arg '(16)))
780 (dired-current-directory))))
781 (list
782 (dired-read-shell-command
783 (concat "& "
784 (if dir
785 (format "in %s " (dired-abbreviate-file-name dir))
786 "cd <dir>; ")
787 "on "
788 (if on-each "each ")
789 "%s: ")
790 (and (not on-each) current-prefix-arg)
791 (if dir
792 (mapcar (function
793 (lambda (fn)
794 (dired-make-relative fn dir t)))
795 files)
796 files))
797 current-prefix-arg files)))
798 (dired-do-shell-command command arg files t))
799
800 ;;; Printing files
801
802 (defun dired-do-print (&optional arg command files)
803 "Print the marked (or next ARG) files.
804 Uses the shell command coming from variable `dired-print-program-alist'."
805 (interactive
806 (progn
807 (if dired-print-history
808 (setq dired-print-history (dired-uniquefy-list dired-print-history))
809 (setq dired-print-history (mapcar 'cdr dired-print-program-alist)))
810 (let* ((files (dired-get-marked-files nil current-prefix-arg))
811 (rel-files (mapcar (function
812 (lambda (fn)
813 (dired-make-relative
814 fn
815 (dired-current-directory) t)))
816 files))
817 (alist dired-print-program-alist)
818 (first (car files))
819 (dired-print-history (copy-sequence dired-print-history))
820 elt initial command)
821 ;; For gmhist
822 (put 'dired-print-history 'no-default t)
823 (if first
824 (while (and alist (not initial))
825 (if (string-match (car (car alist)) first)
826 (setq initial (cdr (car alist)))
827 (setq alist (cdr alist)))))
828 (if (and initial (setq elt (member initial dired-print-history)))
829 (setq dired-print-history (nconc
830 (delq (car elt) dired-print-history)
831 (list initial))))
832 (setq command
833 (dired-mark-read-string
834 "Print %s with: "
835 initial 'print current-prefix-arg rel-files
836 'dired-print-history))
837 (list current-prefix-arg command files))))
838 (or files
839 (setq files (dired-get-marked-files nil arg)))
840 (while files
841 (dired-print-file command (car files))
842 (setq files (cdr files))))
843
844 (defun dired-print-file (command file)
845 ;; Using COMMAND, print FILE.
846 (let ((handler (find-file-name-handler file 'dired-print-file)))
847 (if handler
848 (funcall handler 'dired-print-file command file)
849 (let ((rel-file (dired-make-relative file (dired-current-directory) t)))
850 (message "Spooling %s..." rel-file)
851 (shell-command (dired-trans-command command (list file) ""))
852 (message "Spooling %s...done" rel-file)))))
853
854 ;;; end of dired-shell.el