diff lisp/dired/dired-cd.el @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/dired/dired-cd.el	Mon Aug 13 08:45:50 2007 +0200
@@ -0,0 +1,219 @@
+;;; -*- Mode: Emacs-lisp -*- ;;;
+;;; dired-cd.el - Adjust Working Directory for Tree Dired Shell Commands 
+;;; Id: dired-cd.el,v 1.14 1991/11/01 14:28:27 sk RelBeta 
+;;; Copyright (C) 1991 Hugh Secker-Walker
+;;;
+;;; Author:  Hugh Secker-Walker   hugh@ear-ache.mit.edu
+;;;
+;;; Modified by Sebastian Kremer <sk@thp.uni-koeln.de>
+;;;
+;;; This program is free software; you can redistribute it and/or modify
+;;; it under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 1, or (at your option)
+;;; any later version.
+;;;
+;;; This program is distributed in the hope that it will be useful,
+;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; A copy of the GNU General Public License can be obtained from this
+;;; program's author (send electronic mail to the above address) or from
+;;; Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+;; LISPDIR ENTRY for the Elisp Archive ===============================
+;;    LCD Archive Entry:
+;;    dired-cd|Hugh Secker-Walker|hugh@ear-ache.mit.edu
+;;    |Adjust Working Directory for Tree Dired Shell Commands 
+;;    |Date: 1991/11/01 14:28:27 |Revision: 1.14 |
+
+;;; SUMMARY
+
+;;; This extension to Sebastian Kremer's (sk@thp.Uni-Koeln.DE) Tree-Dired
+;;; permits the working directory of the dired shell commands
+;;; dired-do-shell-command and dired-do-background-shell-command
+;;; to be the files' subdirectory under certain circumstances.
+;;; Loading this extension does not change the behavior of dired until
+;;; the variables dired-cd-same-subdir and/or dired-cd-on-each are
+;;; non-nil.  
+
+
+;;; FUNCTIONALITY PROVIDED
+
+;;; If dired-cd-same-subdir is non-nil and if all the selected files
+;;; (marked, non-zero numeric ARG, etc.) are in the same directory, then
+;;; dired-do-shell-command and dired-do-background-shell-command will
+;;; cause the shell to perform a cd into that directory before the
+;;; commands are executed.  Also, the selected filenames will be provided
+;;; to the command without any directory components.
+
+;;; If dired-cd-on-each is non-nil and if the on-each option is specified
+;;; (numeric arg of zero), then dired-do-shell-command and
+;;; dired-do-background-shell-command will perform a cd into the
+;;; directory of each file before the commands on that file are executed.
+;;; Also, each filename will be provided to the command without any
+;;; directory components.  Note that this on-each behavior occurs
+;;; regardless of whether the files are all in the same directory or not.
+
+;;; After the above "cd wrapping" has occured, the existing
+;;; dired-shell-stuff-it is used to do file-name substitution and
+;;; quoting, so custom versions of this procedure should work, e.g.
+;;; dired-trans will transform commands correctly.  However, since
+;;; filenames lack any directory components, features that use the
+;;; directory components will fail, e.g. the dired-trans [d] transform
+;;; specifier will be empty.
+
+;;; New variables (user options):
+;;;    dired-cd-same-subdir
+;;;    dired-cd-on-each
+;;;
+;;; Replaces procedures:
+;;;    dired-do-shell-command  (new doc and prompt, calls dired-cd-wrap-it)
+;;;
+;;; Adds procedures:
+;;;    dired-cd-wrap-it  (wraps calls to dired-shell-stuff-it with "cd <dir>")
+;;;    dired-files-same-directory
+
+
+;; INSTALLATION
+;;
+;; Put this file into your load-path and add (load "dired-cd") to
+;; your dired-load-hook, e.g.
+;;
+;; (setq dired-load-hook '(lambda ()
+;; 			  ;; possibly more statements here
+;;			  (load "dired-cd")))
+;;
+;; Do (setq dired-cd-same-subdir t) and perhaps (setq dired-cd-on-each t)
+;; in your .emacs.  By default, dired-cd doesn't change the behavior of 
+;; dired when it is loaded. 
+;;
+;; If dired-cd-same-subdir is non-nil, then the shell commands cd to
+;; the appropriate directory if all the selected files (marked,
+;; numeric ARG, etc.) are in that directory; however, on-each behavior
+;; is not changed.
+;;
+;; If dired-cd-on-each is non-nil, then each instance of the command
+;; for an on-each shell command runs in the file's directory
+;; regardless of whether the files are all in the same directory.
+
+
+(defvar dired-cd-same-subdir nil
+  "*If non-nil, and selected file(s) (by marks, numeric arg, \\[universal-argument]) are in same
+subdir, causes dired shell command to run in that subdir.  Filenames provided
+to shell commands are stripped of their directory components.  Does not
+affect behavior of on-each, for that see variable dired-cd-on-each.")
+
+(defvar dired-cd-on-each nil
+  "*If non-nil, on-each causes each dired shell command to run in the 
+file's directory.  Filenames provided to shell commands are stripped of 
+their directory components.  Also see variable dired-cd-same-subdir.")
+
+;; Redefines dired.el's version.
+;; Changes to documentation and prompt, and uses dired-cd-wrap-it.
+(defun dired-do-shell-command (&optional arg in-background)
+  "Run a shell command on the marked files.
+If there is output, it goes to a separate buffer.
+The list of marked files is appended to the command string unless asterisks
+  `*' indicate the place(s) where the list should go.
+If no files are marked or a specific numeric prefix arg is given, uses
+  next ARG files.  With a zero argument, run command on each marked file
+  separately: `cmd * foo' results in `cmd F1 foo; ...; cmd Fn foo'.
+  As always, a raw arg (\\[universal-argument]) means the current file.
+The option variables dired-cd-same-subdir and dired-cd-on-each
+  permit the command\(s\) to run in the files' directories if appropriate,
+  and thus determine where output files are created.  Default is top
+  directory.  The prompt mentions the file(s) or the marker, the cd subdir,
+  and the on-each flags when they apply.
+No automatic redisplay is attempted, as the file names may have
+  changed.  Type \\[dired-do-redisplay] to redisplay the marked files."
+  ;; Function dired-shell-stuff-it (called by dired-cd-wrap-it) does the
+  ;; actual file-name substitution and can be redefined for customization.
+  (interactive "P")
+  (let* ((on-each (equal arg 0))
+	 (file-list (dired-mark-get-files t (if on-each nil arg)))
+	 (prompt (concat (if in-background "& " "! ")
+			 (if (or (and on-each dired-cd-on-each)
+				 (and dired-cd-same-subdir
+				      (not on-each)
+				      (dired-files-same-directory file-list)))
+			     "cd <dir>; " "")
+			 "on "
+			 (if on-each "each " "")
+			 "%s: "))
+	 ;; Give feedback on file(s) and working directory status
+	 (command (dired-read-shell-command
+		   prompt (if on-each nil arg) file-list))
+	 (result (dired-cd-wrap-it command file-list on-each arg)))
+    ;; execute the shell command
+    (dired-run-shell-command result in-background)))
+
+(defun dired-cd-wrap-it (command files on-each &optional raw)
+  "Args COMMAND FILES ON-EACH &optional RAW-ARG, like dired-shell-stuff-it.
+Calls dired-shell-stuff-it, but wraps the resulting command\(s\)
+with \"cd <dir>\" commands when appropriate.  Note: when ON-EACH is non-nil, 
+dired-shell-stuff-it is called once for each file in FILES.
+See documentation of variables dired-cd-same-subdir and dired-cd-on-each 
+for wrap conditions." 
+  (if on-each;; command applied to each file separately
+      ;; cd's are done in subshells since all shells I know of have subshells
+      (let* ((cwd "");; current working directory
+	     (in-subshell nil)
+	     (cmd (mapconcat;; files over command, fuss with "cd <dir>"
+		   (function
+		    (lambda (file)
+		      (let ((cd "") d);; cd command and file's directory
+			(if (not dired-cd-on-each) nil;; poor man's (when ...)
+			  (setq d;; directory, relative to default-directory
+				(directory-file-name 
+				 (or (file-name-directory file) ""))
+				file (file-name-nondirectory file))
+			  (if (not (string= d cwd));; new subdir, new subshell
+			      (setq cwd d
+				    ;; close existing subshell, 
+				    ;; open a new one
+				    cd (concat (if in-subshell "); " "") 
+					       "(cd " (shell-quote cwd) "; ")
+				    in-subshell t))
+			  )
+			;; existing dired-shell-stuff-it does 
+			;; actual command substitution
+			(concat cd (dired-shell-stuff-it command (list file) 
+							 on-each raw)))))
+		   files "; ")))
+	(if in-subshell (concat cmd ")") cmd));; close an open subshell
+    
+    ;; not on-each, all files are args to single command instance
+    (let ((same-dir (and dired-cd-same-subdir
+			 (dired-files-same-directory files nil)))
+	  (cd ""))
+      ;; Let the prepended cd command be relative to default-directory,
+      ;; and only give it if necessary.  This way, after ange-ftp
+      ;; prepends its own cd command, it will still work.
+      ;; sk  3-Sep-1991 14:23
+      ;; hsw 31-Oct-1991 -- filenames relative to default-directory
+      (if (and same-dir (not (equal same-dir "")))
+	  (setq files (mapcar (function file-name-nondirectory) files)
+		cd  (concat "cd " (shell-quote same-dir) "; ")))
+      ;; existing dired-shell-stuff-it does the command substitution
+      (concat cd (dired-shell-stuff-it command files on-each raw)))))
+
+(defun dired-files-same-directory (file-list &optional absolute)
+  "If all files in LIST are in the same directory return it, otherwise nil.
+Returned name has no trailing slash.  \"Same\" means file-name-directory of
+the files are string=.  File names in LIST must all be absolute or all be
+relative.  Implicitly, relative file names are in default-directory.  If
+optional ABS is non-nil, the returned name will be absolute, otherwise the
+returned name will be absolute or relative as per the files in LIST."
+  (let ((dir (file-name-directory (car file-list))))
+    (if (memq nil (mapcar (function
+			   (lambda (file)
+			     (string= dir (file-name-directory file))))
+			  file-list))
+	nil
+      (directory-file-name
+       (if (or (not absolute) (and dir (file-name-absolute-p dir)))
+	   (or dir "")
+	 (concat default-directory dir))))))
+
+(provide 'dired-cd)