changeset 70258:5dfde453f39e

(defgroup grep): Doc fix. (grep-auto-highlight): Remove. (grep-template): New defcustom. (grep-find-template): Rename from grep-tree-template. (grep-files-aliases): Rename from grep-tree-files-aliases. Remove "all" alias, add "l" alias. (grep-tree-ignore-case, grep-tree-ignore-CVS-directories): Remove. (grep-find-ignored-directories): New defcustom to replace grep-tree-ignore-CVS-directories, to facilitate ignoring subdirectories for multiple version control systems. (grep-mode-map): Add Recursive grep item to GREP menu. (grep-regexp-history, grep-files-history): New defvars. (grep-probe): New helper function. (grep-compute-defaults): Use it to simplify code. Adapt to name changes. Use `.' as base in grep-find-template rather than <D>. (grep): Remove superfluous highlight-regexp arg. Fix doc. Call grep-compute-defaults unconditionally. (grep-expand-keywords): New defconst. (grep-expand-template): Rename from grep-expand-command-macros. Simplify via grep-expand-keywords. Look at case-fold-search instead of grep-tree-ignore-case to add -i option. Bind case-fold-search to nil while matching keywords. (grep-tree-last-regexp, grep-tree-last-files): Remove. (grep-read-regexp, grep-read-files): New helper functions. (rgrep): Rename from grep-tree. Rework to use proper histories. Adapt to changes in defcustoms and functions above. (lgrep): New command, as grep, but using same interactive api as rgrep.
author Kim F. Storm <storm@cua.dk>
date Fri, 28 Apr 2006 22:22:01 +0000
parents eef8c5755daa
children 58edc69a1813
files lisp/progmodes/grep.el
diffstat 1 files changed, 289 insertions(+), 200 deletions(-) [+]
line wrap: on
line diff
--- a/lisp/progmodes/grep.el	Fri Apr 28 22:20:57 2006 +0000
+++ b/lisp/progmodes/grep.el	Fri Apr 28 22:22:01 2006 +0000
@@ -35,7 +35,7 @@
 
 
 (defgroup grep nil
-  "Run compiler as inferior of Emacs, parse error messages."
+  "Run grep as inferior of Emacs, parse error messages."
   :group 'tools
   :group 'processes)
 
@@ -48,23 +48,6 @@
   :version "22.1"
   :group 'grep)
 
-(defcustom grep-auto-highlight t
-  "*Specify how many grep matches to highlight (and parse) initially.
-\(Highlighting applies to an grep match when the mouse is over it.)
-If this is a number N, all grep matches in the first N lines
-are highlighted and parsed as soon as they arrive in Emacs.
-If t, highlight and parse the whole grep output as soon as it arrives.
-If nil, don't highlight or parse any of the grep buffer until you try to
-move to the error messages.
-
-Those grep matches which are not parsed and highlighted initially
-will be parsed and highlighted as soon as you try to move to them."
-  :type '(choice (const :tag "All" t)
-		 (const :tag "None" nil)
-		 (integer :tag "First N lines"))
-  :version "22.1"
-  :group 'grep)
-
 (defcustom grep-highlight-matches 'auto-detect
   "If t, use special markers to highlight grep matches.
 
@@ -108,6 +91,20 @@
 		 (const :tag "Not Set" nil))
   :group 'grep)
 
+(defcustom grep-template nil
+  "The default command to run for \\[lgrep].
+The default value of this variable is set up by `grep-compute-defaults';
+call that function before using this variable in your program.
+The following place holders should be present in the string:
+ <C> - place to put -i if case insensitive grep.
+ <F> - file names and wildcards to search.
+ <R> - the regular expression searched for.
+ <N> - place to insert null-device."
+  :type '(choice string
+		 (const :tag "Not Set" nil))
+  :version "22.1"
+  :group 'grep)
+
 (defcustom grep-use-null-device 'auto-detect
   "If t, append the value of `null-device' to `grep' commands.
 This is done to ensure that the output of grep includes the filename of
@@ -130,8 +127,8 @@
 		 (const :tag "Not Set" nil))
   :group 'grep)
 
-(defcustom grep-tree-command nil
-  "The default find command for \\[grep-tree].
+(defcustom grep-find-template nil
+  "The default command to run for \\[rgrep].
 The default value of this variable is set up by `grep-compute-defaults';
 call that function before using this variable in your program.
 The following place holders should be present in the string:
@@ -145,27 +142,22 @@
   :version "22.1"
   :group 'grep)
 
-(defcustom grep-tree-files-aliases '(
+(defcustom grep-files-aliases '(
+	("el" .	"*.el")
 	("ch" .	"*.[ch]")
 	("c" .	"*.c")
 	("h" .	"*.h")
+	("asm" . "*.[sS]")
 	("m" .	"[Mm]akefile*")
-	("asm" . "*.[sS]")
-	("all" . "*")
-	("el" .	"*.el")
+	("l" . "[Cc]hange[Ll]og*")
 	)
-  "*Alist of aliases for the FILES argument to `grep-tree'."
+  "*Alist of aliases for the FILES argument to `lgrep' and `rgrep'."
   :type 'alist
   :group 'grep)
 
-(defcustom grep-tree-ignore-case t
-  "*If non-nil, `grep-tree' ignores case in matches."
-  :type 'boolean
-  :group 'grep)
-
-(defcustom grep-tree-ignore-CVS-directories t
-  "*If non-nil, `grep-tree' does no recurse into CVS directories."
-  :type 'boolean
+(defcustom grep-find-ignored-directories '("CVS" ".hg" "{arch}")
+  "*List of names of sub-directories which `rgrep' shall not recurse into."
+  :type '(repeat string)
   :group 'grep)
 
 (defcustom grep-error-screen-columns nil
@@ -208,6 +200,8 @@
       '("Compile..." . compile))
     (define-key map [menu-bar grep compilation-grep]
       '("Another grep..." . grep))
+    (define-key map [menu-bar grep compilation-grep-find]
+      '("Recursive grep..." . grep-find))
     (define-key map [menu-bar grep compilation-recompile]
       '("Repeat grep" . recompile))
     (define-key map [menu-bar grep compilation-separator2]
@@ -353,6 +347,11 @@
 ;;;###autoload
 (defvar grep-find-history nil)
 
+;; History of lgrep and rgrep regexp and files args.
+(defvar grep-regexp-history nil)
+(defvar grep-files-history '("ch" "el"))
+
+
 ;;;###autoload
 (defun grep-process-setup ()
   "Setup compilation variables and buffer for `grep'.
@@ -378,6 +377,12 @@
 	   (cons msg code))))
   (run-hooks 'grep-setup-hook))
 
+(defun grep-probe (command args &optional func result)
+  (equal (condition-case nil
+	     (apply (or func 'call-process) command args)
+	   (error nil))
+	 (or result 0)))
+
 ;;;###autoload
 (defun grep-compute-defaults ()
   (unless (or (not grep-use-null-device) (eq grep-use-null-device t))
@@ -385,73 +390,67 @@
 	  (with-temp-buffer
 	    (let ((hello-file (expand-file-name "HELLO" data-directory)))
 	      (not
-	       (and (equal (condition-case nil
-			       (if grep-command
-				   ;; `grep-command' is already set, so
-				   ;; use that for testing.
-				   (call-process-shell-command
-				    grep-command nil t nil
-				    "^English" hello-file)
-				 ;; otherwise use `grep-program'
-				 (call-process grep-program nil t nil
-					       "-nH" "^English" hello-file))
-			     (error nil))
-			   0)
+	       (and (if grep-command
+			;; `grep-command' is already set, so
+			;; use that for testing.
+			(grep-probe grep-command
+				    `(nil t nil "^English" ,hello-file)
+				    #'call-process-shell-command)
+		      ;; otherwise use `grep-program'
+		      (grep-probe grep-program
+				  `(nil t nil "-nH" "^English" ,hello-file)))
 		    (progn
 		      (goto-char (point-min))
 		      (looking-at
 		       (concat (regexp-quote hello-file)
 			       ":[0-9]+:English")))))))))
-  (unless grep-command
-    (setq grep-command
-	  (let ((required-options (if grep-use-null-device "-n" "-nH")))
-	    (if (equal (condition-case nil ; in case "grep" isn't in exec-path
-			   (call-process grep-program nil nil nil
-					 "-e" "foo" null-device)
-			 (error nil))
-		       1)
-		(format "%s %s -e " grep-program required-options)
-	      (format "%s %s " grep-program required-options)))))
-  (unless grep-find-use-xargs
-    (setq grep-find-use-xargs
-	  (if (and
-               (equal (call-process "find" nil nil nil
-                                    null-device "-print0")
-                      0)
-               (equal (call-process "xargs" nil nil nil
-                                    "-0" "-e" "echo")
-		      0))
-	      'gnu)))
-  (unless grep-find-command
-    (setq grep-find-command
-          (cond ((eq grep-find-use-xargs 'gnu)
-		 (format "%s . -type f -print0 | xargs -0 -e %s"
-			 find-program grep-command))
-		(grep-find-use-xargs
-		 (format "%s . -type f -print | xargs %s"
-                         find-program grep-command))
-		(t (cons (format "%s . -type f -exec %s {} %s \\;"
-				 find-program grep-command null-device)
-			 (+ 22 (length grep-command)))))))
-  (unless grep-tree-command
-    (setq grep-tree-command
-	  (let* ((glen (length grep-program))
-		 (gcmd (concat grep-program " <C>" (substring grep-command glen))))
-	    (cond ((eq grep-find-use-xargs 'gnu)
-		   (format "%s <D> <X> -type f <F> -print0 | xargs -0 -e %s <R>"
-			   find-program gcmd))
-		  (grep-find-use-xargs
-		   (format "%s <D> <X> -type f <F> -print | xargs %s <R>"
-			   find-program gcmd))
-		  (t (format "%s <D> <X> -type f <F> -exec %s <R> {} %s \\;"
-			     find-program gcmd null-device))))))
+  (unless (and grep-command grep-find-command
+	       grep-template grep-find-template)
+    (let ((grep-options
+	   (concat (if grep-use-null-device "-n" "-nH")
+		   (if (grep-probe grep-program
+				   `(nil nil nil "-e" "foo" ,null-device)
+				   nil 1)
+		       " -e"))))
+      (unless grep-command
+	(setq grep-command
+	      (format "%s %s " grep-program grep-options)))
+      (unless grep-template
+	(setq grep-template
+	      (format "%s <C> %s <R> <F>" grep-program grep-options)))
+      (unless grep-find-use-xargs
+	(setq grep-find-use-xargs
+	      (if (and
+		   (grep-probe find-program `(nil nil nil ,null-device "-print0"))
+		   (grep-probe "xargs" `(nil nil nil "-0" "-e" "echo")))
+		  'gnu)))
+      (unless grep-find-command
+	(setq grep-find-command
+	      (cond ((eq grep-find-use-xargs 'gnu)
+		     (format "%s . -type f -print0 | xargs -0 -e %s"
+			     find-program grep-command))
+		    (grep-find-use-xargs
+		     (format "%s . -type f -print | xargs %s"
+			     find-program grep-command))
+		    (t (cons (format "%s . -type f -exec %s {} %s \\;"
+				     find-program grep-command null-device)
+			     (+ 22 (length grep-command)))))))
+      (unless grep-find-template
+	(setq grep-find-template
+	      (let ((gcmd (format "%s <C> %s <R>"
+				  grep-program grep-options)))
+		(cond ((eq grep-find-use-xargs 'gnu)
+		       (format "%s . <X> -type f <F> -print0 | xargs -0 -e %s"
+			       find-program gcmd))
+		      (grep-find-use-xargs
+		       (format "%s . <X> -type f <F> -print | xargs %s"
+			       find-program gcmd))
+		      (t (format "%s . <X> -type f <F> -exec %s {} %s \\;"
+				 find-program gcmd null-device))))))))
   (unless (or (not grep-highlight-matches) (eq grep-highlight-matches t))
     (setq grep-highlight-matches
 	  (with-temp-buffer
-	    (and (equal (condition-case nil
-			    (call-process grep-program nil t nil "--help")
-			  (error nil))
-			0)
+	    (and (grep-probe grep-program '(nil t nil "--help"))
 		 (progn
 		   (goto-char (point-min))
 		   (search-forward "--color" nil t))
@@ -487,42 +486,6 @@
 				   (file-name-extension buffer-file-name))))
       (replace-match tag-default t t grep-default 1))))
 
-;;;###autoload
-(defun grep (command-args &optional highlight-regexp)
-  "Run grep, with user-specified args, and collect output in a buffer.
-While grep runs asynchronously, you can use \\[next-error] (M-x next-error),
-or \\<grep-mode-map>\\[compile-goto-error] in the grep \
-output buffer, to go to the lines
-where grep found matches.
-
-This command uses a special history list for its COMMAND-ARGS, so you can
-easily repeat a grep command.
-
-A prefix argument says to default the argument based upon the current
-tag the cursor is over, substituting it into the last grep command
-in the grep command history (or into `grep-command'
-if that history list is empty).
-
-If specified, optional second arg HIGHLIGHT-REGEXP is the regexp to
-temporarily highlight in visited source lines."
-  (interactive
-   (progn
-     (unless (and grep-command
-		  (or (not grep-use-null-device) (eq grep-use-null-device t)))
-       (grep-compute-defaults))
-     (let ((default (grep-default-command)))
-       (list (read-from-minibuffer "Run grep (like this): "
-				   (if current-prefix-arg
-				       default grep-command)
-				   nil nil 'grep-history
-				   (if current-prefix-arg nil default))))))
-
-  ;; Setting process-setup-function makes exit-message-function work
-  ;; even when async processes aren't supported.
-  (compilation-start (if (and grep-use-null-device null-device)
-			 (concat command-args " " null-device)
-		       command-args)
-		     'grep-mode nil highlight-regexp))
 
 ;;;###autoload
 (define-compilation-mode grep-mode "Grep"
@@ -536,6 +499,40 @@
        'grep-process-setup)
   (set (make-local-variable 'compilation-disable-input) t))
 
+
+;;;###autoload
+(defun grep (command-args)
+  "Run grep, with user-specified args, and collect output in a buffer.
+While grep runs asynchronously, you can use \\[next-error] (M-x next-error),
+or \\<grep-mode-map>\\[compile-goto-error] in the grep \
+output buffer, to go to the lines
+where grep found matches.
+
+This command uses a special history list for its COMMAND-ARGS, so you can
+easily repeat a grep command.
+
+A prefix argument says to default the argument based upon the current
+tag the cursor is over, substituting it into the last grep command
+in the grep command history (or into `grep-command'
+if that history list is empty)."
+  (interactive
+   (progn
+     (grep-compute-defaults)
+     (let ((default (grep-default-command)))
+       (list (read-from-minibuffer "Run grep (like this): "
+				   (if current-prefix-arg
+				       default grep-command)
+				   nil nil 'grep-history
+				   (if current-prefix-arg nil default))))))
+
+  ;; Setting process-setup-function makes exit-message-function work
+  ;; even when async processes aren't supported.
+  (compilation-start (if (and grep-use-null-device null-device)
+			 (concat command-args " " null-device)
+		       command-args)
+		     'grep-mode))
+
+
 ;;;###autoload
 (defun grep-find (command-args)
   "Run grep via find, with user-specified args COMMAND-ARGS.
@@ -547,9 +544,7 @@
 easily repeat a find command."
   (interactive
    (progn
-     (unless (and grep-command
-		  (or (not grep-use-null-device) (eq grep-use-null-device t)))
-       (grep-compute-defaults))
+     (grep-compute-defaults)
      (if grep-find-command
 	 (list (read-from-minibuffer "Run find (like this): "
 				     grep-find-command nil nil
@@ -565,84 +560,178 @@
 ;;;###autoload
 (defalias 'find-grep 'grep-find)
 
-(defun grep-expand-command-macros (command &optional regexp files dir excl case-fold)
-  "Patch grep COMMAND replacing <D>, etc."
-  (setq command
-	(replace-regexp-in-string "<D>"
-				  (or dir ".") command t t))
-  (setq command
-	(replace-regexp-in-string "<X>"
-				  (or excl "") command t t))
-  (setq command
-	(replace-regexp-in-string "<F>"
-				  (or files "") command t t))
-  (setq command
-	(replace-regexp-in-string "<C>"
-				  (if case-fold "-i" "") command t t))
-  (setq command
-	(replace-regexp-in-string "<R>"
-				  (or regexp "") command t t))
-  command)
+
+;; User-friendly interactive API.
+
+(defconst grep-expand-keywords
+  '(("<C>" . (and cf (isearch-no-upper-case-p regexp t) "-i"))
+    ("<D>" . dir)
+    ("<F>" . files)
+    ("<N>" . null-device)
+    ("<X>" . excl)
+    ("<R>" . (shell-quote-argument (or regexp ""))))
+  "List of substitutions performed by `grep-expand-template'.
+If car of an element matches, the cdr is evalled in to get the
+substitution string.  Note dynamic scoping of variables.")
+
+(defun grep-expand-template (template &optional regexp files dir excl)
+  "Patch grep COMMAND string replacing <C>, <D>, <F>, <R>, and <X>."
+  (let ((command template)
+	(cf case-fold-search)
+	(case-fold-search nil))
+    (dolist (kw grep-expand-keywords command)
+      (if (string-match (car kw) command)
+	  (setq command (replace-match (or (eval (cdr kw)) "")
+				       t t command))))))
 
-(defvar grep-tree-last-regexp "")
-(defvar grep-tree-last-files (car (car grep-tree-files-aliases)))
+(defun grep-read-regexp ()
+  "Read regexp arg for interactive grep."
+  (let ((default
+	  (or (funcall (or find-tag-default-function
+			   (get major-mode 'find-tag-default-function)
+			   'find-tag-default))
+	      "")))
+    (read-string
+     (concat "Search for"
+	     (if (and default (> (length default) 0))
+		 (format " (default %s): " default) ": "))
+     nil 'grep-regexp-history default)))
+
+(defun grep-read-files (regexp)
+  "Read files arg for interactive grep."
+  (let* ((default
+	   (or (and (stringp (buffer-file-name))
+		    (let ((fn (file-name-nondirectory (buffer-file-name)))
+			  (aliases grep-files-aliases)
+			  alias)
+		      (while aliases
+			(setq alias (car aliases)
+			      aliases (cdr aliases))
+			(if (string-match (wildcard-to-regexp (cdr alias)) fn)
+			    (setq aliases nil)
+			  (setq alias nil)))
+		      (cdr alias)))
+	       (car grep-files-history)))
+	 (files (read-string
+		 (concat "Search for \"" regexp
+			 "\" in files (default " default "): ")
+		 nil 'grep-files-history default)))
+    (and files
+	 (or (cdr (assoc files grep-files-aliases))
+	     files))))
 
 ;;;###autoload
-(defun grep-tree (regexp files dir &optional subdirs)
-  "Grep for REGEXP in FILES in directory tree rooted at DIR.
-Collect output in a buffer.
-Interactively, prompt separately for each search parameter.
-With prefix arg, reuse previous REGEXP.
+(defun lgrep (regexp &optional files)
+  "Run grep, searching for REGEXP in FILES in current directory.
 The search is limited to file names matching shell pattern FILES.
-FILES may use abbreviations defined in `grep-tree-files-aliases', e.g.
+FILES may use abbreviations defined in `grep-files-aliases', e.g.
 entering `ch' is equivalent to `*.[ch]'.
 
-While find runs asynchronously, you can use the \\[next-error] command
-to find the text that grep hits refer to.
+With \\[universal-argument] prefix, allow user to edit the constructed
+shell command line before it is executed.
+With two \\[universal-argument] prefixes, edit and run grep shell command.
 
-This command uses a special history list for its arguments, so you can
-easily repeat a find command.
+Collect output in a buffer.  While grep runs asynchronously, you
+can use \\[next-error] (M-x next-error), or \\<grep-mode-map>\\[compile-goto-error]
+in the grep output buffer, to go to the lines where grep found matches.
 
-When used non-interactively, optional arg SUBDIRS limits the search to
-those sub directories of DIR."
+This command shares argument histories with \\[rgrep] and \\[grep]."
   (interactive
-   (let* ((regexp
-	   (if current-prefix-arg
-	       grep-tree-last-regexp
-	     (let* ((default (current-word))
-		    (spec (read-string
-			   (concat "Search for"
-				   (if (and default (> (length default) 0))
-				       (format " (default %s): " default) ": ")))))
-	       (if (equal spec "") default spec))))
-	  (files
-	   (read-string (concat "Search for \"" regexp "\" in files (default "   grep-tree-last-files  "): ")))
-	  (dir
-	   (read-directory-name "Base directory: " nil default-directory t)))
-     (list regexp files dir)))
-  (unless grep-tree-command
-    (grep-compute-defaults))
-  (unless (and (stringp files) (> (length files) 0))
-    (setq files grep-tree-last-files))
-  (when files
-    (setq grep-tree-last-files files)
-    (let ((mf (assoc files grep-tree-files-aliases)))
-      (if mf
-	  (setq files (cdr mf)))))
-  (let ((command-args (grep-expand-command-macros
-		       grep-tree-command
-		       (setq grep-tree-last-regexp regexp)
-		       (and files (concat "-name '" files "'"))
-		       (if subdirs
-			   (if (stringp subdirs)
-			       subdirs
-			     (mapconcat 'identity subdirs " "))
-			 nil)  ;; we change default-directory to dir
-		       (and grep-tree-ignore-CVS-directories "-path '*/CVS' -prune -o ")
-		       grep-tree-ignore-case))
-	(default-directory (file-name-as-directory (expand-file-name dir)))
-	(null-device nil))		; see grep
-    (grep command-args regexp)))
+   (progn
+     (grep-compute-defaults)
+     (cond
+      ((and grep-command (equal current-prefix-arg '(16)))
+       (list (read-from-minibuffer "Run: " grep-command
+				   nil nil 'grep-history)
+	     nil))
+      ((not grep-template)
+       (list nil
+	     (read-string "grep.el: No `grep-template' available. Press RET.")))
+      (t (let* ((regexp (grep-read-regexp))
+		(files (grep-read-files regexp)))
+	   (list regexp files))))))
+  (when (and (stringp regexp) (> (length regexp) 0))
+    (let ((command regexp))
+      (if (null files)
+	  (if (string= command grep-command)
+	      (setq command nil))
+	(setq command (grep-expand-template
+		       grep-template
+		       regexp
+		       files))
+	(when command
+	  (if (equal current-prefix-arg '(4))
+	      (setq command
+		    (read-from-minibuffer "Confirm: "
+					  command nil nil 'grep-history))
+	    (push command grep-history))))
+      (when command
+	;; Setting process-setup-function makes exit-message-function work
+	;; even when async processes aren't supported.
+	(compilation-start (if (and grep-use-null-device null-device)
+			       (concat command " " null-device)
+			     command) 'grep-mode)))))
+
+
+;;;###autoload
+(defun rgrep (regexp &optional files dir)
+  "Recusively grep for REGEXP in FILES in directory tree rooted at DIR.
+The search is limited to file names matching shell pattern FILES.
+FILES may use abbreviations defined in `grep-files-aliases', e.g.
+entering `ch' is equivalent to `*.[ch]'.
+
+With \\[universal-argument] prefix, allow user to edit the constructed
+shell command line before it is executed.
+With two \\[universal-argument] prefixes, edit and run grep-find shell command.
+
+Collect output in a buffer.  While find runs asynchronously, you
+can use \\[next-error] (M-x next-error), or \\<grep-mode-map>\\[compile-goto-error]
+in the grep output buffer, to go to the lines where grep found matches.
+
+This command shares argument histories with \\[lgrep] and \\[grep-find]."
+  (interactive
+   (progn
+     (grep-compute-defaults)
+     (cond
+      ((and grep-find-command (equal current-prefix-arg '(16)))
+       (list (read-from-minibuffer "Run: " grep-find-command
+				   nil nil 'grep-find-history)
+	     nil))
+      ((not grep-find-template)
+       (list nil nil
+	     (read-string "grep.el: No `grep-find-template' available. Press RET.")))
+      (t (let* ((regexp (grep-read-regexp))
+		(files (grep-read-files regexp))
+		(dir (read-directory-name "Base directory: "
+					  nil default-directory t)))
+	   (list regexp files dir))))))
+  (when (and (stringp regexp) (> (length regexp) 0))
+    (if (null files)
+	(if (not (string= regexp grep-find-command))
+	    (compilation-start regexp 'grep-mode))
+      (let* ((default-directory (file-name-as-directory (expand-file-name dir)))
+	     (command (grep-expand-template
+		       grep-find-template
+		       regexp
+		       (concat "\\( -name "
+			       (mapconcat #'shell-quote-argument
+					  (split-string files)
+					  " -o -name ")
+			       " \\)")
+		       default-directory
+		       (and grep-find-ignored-directories
+			    (concat "\\( -path '*/"
+				    (mapconcat #'identity
+					       grep-find-ignored-directories
+					       "' -o -path '*/")
+				    "' \\) -prune -o ")))))
+	(when command
+	  (if current-prefix-arg
+	      (setq command
+		    (read-from-minibuffer "Confirm: "
+					  command nil nil 'grep-find-history))
+	    (push command grep-find-history))
+	  (compilation-start command 'grep-mode))))))
 
 
 (provide 'grep)