Subject: bug#6665: rgrep does not work on Windows

From: "Colin Fraizer" <[email-masked]>
Date: Wed, 17 Nov 2010 07:26:54 -0500

On July 19, 2010, Eli Zaretskii wrote:

>I think this bug report should be closed, as it is not something Emacs
can fix.

I believe Mr. Zaretskii is correct that Emacs is not the cause of the
problem. It appears to be a bug in the GnuWin32 and/or MSys port of
"find". *However*, I would argue that rgrep constructs a bogus find
command anyway and that *that* should be fixed. Specifically, it
creates

[NOTE: I replaced text with the string "[...]".]

find . "(" -path "*/SCCS" [...] ")" -prune -o "(" -name ".#*" [...] ")"
-prune -o -type f "(" -iname "*.el" ")" -exec grep -nH "mystring" {} ";"

That second "-prune" is really bogus.  It is supposed to mean "don't descend
into
the matched file as a directory", but it's not matching directories.
It is matching regular filenames.

I beleive a better string to generate is

find . "(" -path "*/SCCS" [...] ")" -prune -o -not "(" -name ".#*" [...] ")"
-and -type f "(" -iname "*.el" ")" -exec grep -nH "mystring" {} ";"

That is, I would:
1. Add "-not" before the list of excluded files.
2. Remove the second "-prune".
3. Change the "-o" after the list of excluded files to "-and".

I believe this should work on all systems (even with the buggy version of
"find") and doesn't rely on "-prune" to mean "-noop".

[I'm new here, so please excuse any breaches of rules or etiquette. I'll be
happy to conform to your norms (or to leave) if you instruct me.]

Best regards,
--Colin Fraizer
Indianapolis, Indiana, USA, Earth 8-)

My (very-lightly-tested) version of rgrep follows:
(defun rgrep (regexp &optional files dir confirm)
  "Recursively 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, you can edit the constructed shell
command line
before it is executed.
With two \\[universal-argument] prefixes, directly edit and run
`grep-find-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)))
      ((not grep-find-template)
       (error "grep.el: No `grep-find-template' available"))
      (t (let* ((regexp (grep-read-regexp))
		(files (grep-read-files regexp))
		(dir (read-directory-name "Base directory: "
					  nil default-directory t))
		(confirm (equal current-prefix-arg '(4))))
	   (list regexp files dir confirm))))))
  (when (and (stringp regexp) (> (length regexp) 0))
    (unless (and dir (file-directory-p dir) (file-readable-p dir))
      (setq dir default-directory))
    (if (null files)
	(if (not (string= regexp grep-find-command))
	    (compilation-start regexp 'grep-mode))
      (setq dir (file-name-as-directory (expand-file-name dir)))
      (require 'find-dired)		; for `find-name-arg'
      (let ((command (grep-expand-template
		      grep-find-template
		      regexp
		      (concat (shell-quote-argument "(")
			      " " find-name-arg " "
			      (mapconcat #'shell-quote-argument
					 (split-string files)
					 (concat " -o " find-name-arg " "))
			      " "
			      (shell-quote-argument ")"))
		      dir
		      (concat
		       (and grep-find-ignored-directories
			    (concat (shell-quote-argument "(")
				    ;; we should use shell-quote-argument
here
				    " -path "
				    (mapconcat
				     #'(lambda (ignore)
					 (cond ((stringp ignore)
						(shell-quote-argument
						 (concat "*/" ignore)))
					       ((consp ignore)
						(and (funcall (car ignore)
dir)
						     (shell-quote-argument
						      (concat "*/"
							      (cdr
ignore)))))))
				     grep-find-ignored-directories
				     " -o -path ")
				    " "
				    (shell-quote-argument ")")
				    " -prune -o "))
		       (and grep-find-ignored-files
			    (concat (shell-quote-argument "-not")
                        " "
                        (shell-quote-argument "(")
                        ;; we should use shell-quote-argument here
                        " -name "
				    (mapconcat
				     #'(lambda (ignore)
					 (cond ((stringp ignore)
						(shell-quote-argument
ignore))
					       ((consp ignore)
						(and (funcall (car ignore)
dir)
						     (shell-quote-argument
						      (cdr ignore))))))
				     grep-find-ignored-files
				     " -o -name ")
				    " "
				    (shell-quote-argument ")")
				    " -and "))))))
	(when command
	  (if confirm
	      (setq command
		    (read-from-minibuffer "Confirm: "
					  command nil nil
'grep-find-history))
	    (add-to-history 'grep-find-history command))
	  (let ((default-directory dir))
	    (compilation-start command 'grep-mode))
	  ;; Set default-directory if we started rgrep in the *grep* buffer.
	  (if (eq next-error-last-buffer (current-buffer))
	      (setq default-directory dir)))))))