(all): Make `indicate-buffer-boundaries' display values set outside
[bpt/emacs.git] / lisp / progmodes / grep.el
1 ;;; grep.el --- run Grep as inferior of Emacs, parse match messages
2
3 ;; Copyright (C) 1985, 1986, 1987, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
4 ;; 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
5
6 ;; Author: Roland McGrath <roland@gnu.org>
7 ;; Maintainer: FSF
8 ;; Keywords: tools, processes
9
10 ;; This file is part of GNU Emacs.
11
12 ;; GNU Emacs is free software; you can redistribute it and/or modify
13 ;; it under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation; either version 2, or (at your option)
15 ;; any later version.
16
17 ;; GNU Emacs is distributed in the hope that it will be useful,
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 ;; GNU General Public License for more details.
21
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with GNU Emacs; see the file COPYING. If not, write to the
24 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
25 ;; Boston, MA 02110-1301, USA.
26
27 ;;; Commentary:
28
29 ;; This package provides the grep facilities documented in the Emacs
30 ;; user's manual.
31
32 ;;; Code:
33
34 (require 'compile)
35
36 (defgroup grep nil
37 "Run compiler as inferior of Emacs, parse error messages."
38 :group 'tools
39 :group 'processes)
40
41
42 ;;;###autoload
43 (defcustom grep-window-height nil
44 "*Number of lines in a grep window. If nil, use `compilation-window-height'."
45 :type '(choice (const :tag "Default" nil)
46 integer)
47 :version "22.1"
48 :group 'grep)
49
50 (defcustom grep-auto-highlight t
51 "*Specify how many grep matches to highlight (and parse) initially.
52 \(Highlighting applies to an grep match when the mouse is over it.)
53 If this is a number N, all grep matches in the first N lines
54 are highlighted and parsed as soon as they arrive in Emacs.
55 If t, highlight and parse the whole grep output as soon as it arrives.
56 If nil, don't highlight or parse any of the grep buffer until you try to
57 move to the error messages.
58
59 Those grep matches which are not parsed and highlighted initially
60 will be parsed and highlighted as soon as you try to move to them."
61 :type '(choice (const :tag "All" t)
62 (const :tag "None" nil)
63 (integer :tag "First N lines"))
64 :version "22.1"
65 :group 'grep)
66
67 (defcustom grep-highlight-matches 'auto-detect
68 "If t, use special markers to highlight grep matches.
69
70 Some grep programs are able to surround matches with special
71 markers in grep output. Such markers can be used to highlight
72 matches in grep mode.
73
74 This option sets the environment variable GREP_COLOR to specify
75 markers for highlighting and GREP_OPTIONS to add the --color
76 option in front of any explicit grep options before starting
77 the grep.
78
79 The default value of this variable is set up by `grep-compute-defaults';
80 call that function before using this variable in your program."
81 :type '(choice (const :tag "Do not highlight matches with grep markers" nil)
82 (const :tag "Highlight matches with grep markers" t)
83 (other :tag "Not Set" auto-detect))
84 :version "22.1"
85 :group 'grep)
86
87 (defcustom grep-scroll-output nil
88 "*Non-nil to scroll the *grep* buffer window as output appears.
89
90 Setting it causes the grep commands to put point at the end of their
91 output window so that the end of the output is always visible rather
92 than the begining."
93 :type 'boolean
94 :version "22.1"
95 :group 'grep)
96
97 ;;;###autoload
98 (defcustom grep-command nil
99 "The default grep command for \\[grep].
100 If the grep program used supports an option to always include file names
101 in its output (such as the `-H' option to GNU grep), it's a good idea to
102 include it when specifying `grep-command'.
103
104 The default value of this variable is set up by `grep-compute-defaults';
105 call that function before using this variable in your program."
106 :type '(choice string
107 (const :tag "Not Set" nil))
108 :group 'grep)
109
110 (defcustom grep-use-null-device 'auto-detect
111 "If t, append the value of `null-device' to `grep' commands.
112 This is done to ensure that the output of grep includes the filename of
113 any match in the case where only a single file is searched, and is not
114 necessary if the grep program used supports the `-H' option.
115
116 The default value of this variable is set up by `grep-compute-defaults';
117 call that function before using this variable in your program."
118 :type '(choice (const :tag "Do Not Append Null Device" nil)
119 (const :tag "Append Null Device" t)
120 (other :tag "Not Set" auto-detect))
121 :group 'grep)
122
123 ;;;###autoload
124 (defcustom grep-find-command nil
125 "The default find command for \\[grep-find].
126 The default value of this variable is set up by `grep-compute-defaults';
127 call that function before using this variable in your program."
128 :type '(choice string
129 (const :tag "Not Set" nil))
130 :group 'grep)
131
132 (defcustom grep-tree-command nil
133 "The default find command for \\[grep-tree].
134 The default value of this variable is set up by `grep-compute-defaults';
135 call that function before using this variable in your program.
136 The following place holders should be present in the string:
137 <D> - base directory for find
138 <X> - find options to restrict or expand the directory list
139 <F> - find options to limit the files matched
140 <C> - place to put -i if case insensitive grep
141 <R> - the regular expression searched for."
142 :type '(choice string
143 (const :tag "Not Set" nil))
144 :version "22.1"
145 :group 'grep)
146
147 (defcustom grep-tree-files-aliases '(
148 ("ch" . "*.[ch]")
149 ("c" . "*.c")
150 ("h" . "*.h")
151 ("m" . "[Mm]akefile*")
152 ("asm" . "*.[sS]")
153 ("all" . "*")
154 ("el" . "*.el")
155 )
156 "*Alist of aliases for the FILES argument to `grep-tree'."
157 :type 'alist
158 :group 'grep)
159
160 (defcustom grep-tree-ignore-case t
161 "*If non-nil, `grep-tree' ignores case in matches."
162 :type 'boolean
163 :group 'grep)
164
165 (defcustom grep-tree-ignore-CVS-directories t
166 "*If non-nil, `grep-tree' does no recurse into CVS directories."
167 :type 'boolean
168 :group 'grep)
169
170 (defcustom grep-error-screen-columns nil
171 "*If non-nil, column numbers in grep hits are screen columns.
172 See `compilation-error-screen-columns'"
173 :type '(choice (const :tag "Default" nil)
174 integer)
175 :version "22.1"
176 :group 'grep)
177
178 ;;;###autoload
179 (defcustom grep-setup-hook nil
180 "List of hook functions run by `grep-process-setup' (see `run-hooks')."
181 :type 'hook
182 :group 'grep)
183
184 (defvar grep-mode-map
185 (let ((map (cons 'keymap compilation-minor-mode-map)))
186 (define-key map " " 'scroll-up)
187 (define-key map "\^?" 'scroll-down)
188 (define-key map "\C-c\C-f" 'next-error-follow-minor-mode)
189
190 ;; This is intolerable -- rms
191 ;;; (define-key map [remap next-line] 'compilation-next-error)
192 ;;; (define-key map [remap previous-line] 'compilation-previous-error)
193
194 (define-key map "\r" 'compile-goto-error) ;; ?
195 (define-key map "n" 'next-error-no-select)
196 (define-key map "p" 'previous-error-no-select)
197 (define-key map "{" 'compilation-previous-file)
198 (define-key map "}" 'compilation-next-file)
199 (define-key map [backtab] 'compilation-previous-file)
200 (define-key map "\t" 'compilation-next-file)
201
202 ;; Set up the menu-bar
203 (define-key map [menu-bar grep]
204 (cons "Grep" (make-sparse-keymap "Grep")))
205
206 (define-key map [menu-bar grep compilation-kill-compilation]
207 '("Kill Grep" . kill-compilation))
208 (define-key map [menu-bar grep compilation-separator2]
209 '("----" . nil))
210 (define-key map [menu-bar grep compilation-compile]
211 '("Compile..." . compile))
212 (define-key map [menu-bar grep compilation-grep]
213 '("Another grep" . grep))
214 (define-key map [menu-bar grep compilation-recompile]
215 '("Repeat grep" . recompile))
216 (define-key map [menu-bar grep compilation-separator2]
217 '("----" . nil))
218 (define-key map [menu-bar grep compilation-first-error]
219 '("First Match" . first-error))
220 (define-key map [menu-bar grep compilation-previous-error]
221 '("Previous Match" . previous-error))
222 (define-key map [menu-bar grep compilation-next-error]
223 '("Next Match" . next-error))
224 map)
225 "Keymap for grep buffers.
226 `compilation-minor-mode-map' is a cdr of this.")
227
228 (defalias 'kill-grep 'kill-compilation)
229
230 ;;;; TODO --- refine this!!
231
232 ;;; (defcustom grep-use-compilation-buffer t
233 ;;; "When non-nil, grep specific commands update `compilation-last-buffer'.
234 ;;; This means that standard compile commands like \\[next-error] and \\[compile-goto-error]
235 ;;; can be used to navigate between grep matches (the default).
236 ;;; Otherwise, the grep specific commands like \\[grep-next-match] must
237 ;;; be used to navigate between grep matches."
238 ;;; :type 'boolean
239 ;;; :group 'grep)
240
241 ;; override compilation-last-buffer
242 (defvar grep-last-buffer nil
243 "The most recent grep buffer.
244 A grep buffer becomes most recent when its process is started
245 or when it is used with \\[grep-next-match].
246 Notice that using \\[next-error] or \\[compile-goto-error] modifies
247 `complation-last-buffer' rather than `grep-last-buffer'.")
248
249 ;;;###autoload
250 (defvar grep-regexp-alist
251 ;; rms: I removed the code to match parens around the line number
252 ;; because it causes confusion and so we will find out if anyone needs it.
253 ;; It causes confusion with a file name that contains a number in parens.
254 '(("^\\(.+?\\)\\([: \t]\\)+\
255 \\([0-9]+\\)\\([.:]?\\)\\([0-9]+\\)?\
256 \\(?:-\\(?:\\([0-9]+\\)\\4\\)?\\.?\\([0-9]+\\)?\\)?\\2"
257 1 (3 . 6) (5 . 7))
258 ("^\\(\\(.+?\\):\\([0-9]+\\):\\).*?\
259 \\(\033\\[01;31m\\(?:\033\\[K\\)?\\)\\(.*?\\)\\(\033\\[[0-9]*m\\)"
260 2 3
261 ;; Calculate column positions (beg . end) of first grep match on a line
262 ((lambda ()
263 (setq compilation-error-screen-columns nil)
264 (- (match-beginning 4) (match-end 1)))
265 .
266 (lambda () (- (match-end 5) (match-end 1)
267 (- (match-end 4) (match-beginning 4)))))
268 nil 1)
269 ("^Binary file \\(.+\\) matches$" 1 nil nil 1 1))
270 "Regexp used to match grep hits. See `compilation-error-regexp-alist'.")
271
272 (defvar grep-error "grep hit"
273 "Message to print when no matches are found.")
274
275 ;; Reverse the colors because grep hits are not errors (though we jump there
276 ;; with `next-error'), and unreadable files can't be gone to.
277 (defvar grep-hit-face compilation-info-face
278 "Face name to use for grep hits.")
279
280 ;; compilation-error-face is wrong for this; it's designed to look like a link.
281 (defvar grep-error-face font-lock-keyword-face
282 "Face name to use for grep error messages.")
283
284 (defvar grep-match-face 'match
285 "Face name to use for grep matches.")
286
287 (defvar grep-mode-font-lock-keywords
288 '(;; Command output lines.
289 ("^\\([A-Za-z_0-9/\.+-]+\\)[ \t]*:" 1 font-lock-function-name-face)
290 (": \\(.+\\): \\(?:Permission denied\\|No such \\(?:file or directory\\|device or address\\)\\)$"
291 1 grep-error-face)
292 ;; remove match from grep-regexp-alist before fontifying
293 ("^Grep finished \\(?:(\\(matches found\\))\\|with \\(no matches found\\)\\).*"
294 (0 '(face nil message nil help-echo nil mouse-face nil) t)
295 (1 font-lock-keyword-face nil t)
296 (2 font-lock-keyword-face nil t))
297 ("^Grep \\(exited abnormally\\) with code \\([0-9]+\\).*"
298 (0 '(face nil message nil help-echo nil mouse-face nil) t)
299 (1 grep-error-face)
300 (2 grep-error-face))
301 ;; Highlight grep matches and delete markers
302 ("\\(\033\\[01;31m\\)\\(.*?\\)\\(\033\\[[0-9]*m\\)"
303 ;; Refontification does not work after the markers have been
304 ;; deleted. So we use the font-lock-face property here as Font
305 ;; Lock does not clear that.
306 (2 (list 'face nil 'font-lock-face grep-match-face))
307 ((lambda (bound))
308 (progn
309 ;; Delete markers with `replace-match' because it updates
310 ;; the match-data, whereas `delete-region' would render it obsolete.
311 (replace-match "" t t nil 3)
312 (replace-match "" t t nil 1))))
313 ("\\(\033\\[[0-9;]*[mK]\\)"
314 ;; Delete all remaining escape sequences
315 ((lambda (bound))
316 (replace-match "" t t nil 1))))
317 "Additional things to highlight in grep output.
318 This gets tacked on the end of the generated expressions.")
319
320 ;;;###autoload
321 (defvar grep-program
322 ;; Currently zgrep has trouble. It runs egrep instead of grep,
323 ;; and it doesn't pass along long options right.
324 "grep"
325 ;; (if (equal (condition-case nil ; in case "zgrep" isn't in exec-path
326 ;; (call-process "zgrep" nil nil nil
327 ;; "foo" null-device)
328 ;; (error nil))
329 ;; 1)
330 ;; "zgrep"
331 ;; "grep")
332 "The default grep program for `grep-command' and `grep-find-command'.
333 This variable's value takes effect when `grep-compute-defaults' is called.")
334
335 ;;;###autoload
336 (defvar find-program "find"
337 "The default find program for `grep-find-command'.
338 This variable's value takes effect when `grep-compute-defaults' is called.")
339
340 ;;;###autoload
341 (defvar grep-find-use-xargs nil
342 "Whether \\[grep-find] uses the `xargs' utility by default.
343
344 If nil, it uses `find -exec'; if `gnu', it uses `find -print0' and `xargs -0';
345 if not nil and not `gnu', it uses `find -print' and `xargs'.
346
347 This variable's value takes effect when `grep-compute-defaults' is called.")
348
349 ;; History of grep commands.
350 ;;;###autoload
351 (defvar grep-history nil)
352 ;;;###autoload
353 (defvar grep-find-history nil)
354
355 ;;;###autoload
356 (defun grep-process-setup ()
357 "Setup compilation variables and buffer for `grep'.
358 Set up `compilation-exit-message-function' and run `grep-setup-hook'."
359 (unless (or (not grep-highlight-matches) (eq grep-highlight-matches t))
360 (grep-compute-defaults))
361 (when (eq grep-highlight-matches t)
362 ;; Modify `process-environment' locally bound in `compilation-start'
363 (setenv "GREP_OPTIONS" (concat (getenv "GREP_OPTIONS") " --color=always"))
364 ;; for GNU grep 2.5.1
365 (setenv "GREP_COLOR" "01;31")
366 ;; for GNU grep 2.5.1-cvs
367 (setenv "GREP_COLORS" "mt=01;31:fn=:ln=:bn=:se=:ml=:cx=:ne"))
368 (set (make-local-variable 'compilation-exit-message-function)
369 (lambda (status code msg)
370 (if (eq status 'exit)
371 (cond ((zerop code)
372 '("finished (matches found)\n" . "matched"))
373 ((= code 1)
374 '("finished with no matches found\n" . "no match"))
375 (t
376 (cons msg code)))
377 (cons msg code))))
378 (run-hooks 'grep-setup-hook))
379
380 ;;;###autoload
381 (defun grep-compute-defaults ()
382 (unless (or (not grep-use-null-device) (eq grep-use-null-device t))
383 (setq grep-use-null-device
384 (with-temp-buffer
385 (let ((hello-file (expand-file-name "HELLO" data-directory)))
386 (not
387 (and (equal (condition-case nil
388 (if grep-command
389 ;; `grep-command' is already set, so
390 ;; use that for testing.
391 (call-process-shell-command
392 grep-command nil t nil
393 "^English" hello-file)
394 ;; otherwise use `grep-program'
395 (call-process grep-program nil t nil
396 "-nH" "^English" hello-file))
397 (error nil))
398 0)
399 (progn
400 (goto-char (point-min))
401 (looking-at
402 (concat (regexp-quote hello-file)
403 ":[0-9]+:English")))))))))
404 (unless grep-command
405 (setq grep-command
406 (let ((required-options (if grep-use-null-device "-n" "-nH")))
407 (if (equal (condition-case nil ; in case "grep" isn't in exec-path
408 (call-process grep-program nil nil nil
409 "-e" "foo" null-device)
410 (error nil))
411 1)
412 (format "%s %s -e " grep-program required-options)
413 (format "%s %s " grep-program required-options)))))
414 (unless grep-find-use-xargs
415 (setq grep-find-use-xargs
416 (if (and
417 (equal (call-process "find" nil nil nil
418 null-device "-print0")
419 0)
420 (equal (call-process "xargs" nil nil nil
421 "-0" "-e" "echo")
422 0))
423 'gnu)))
424 (unless grep-find-command
425 (setq grep-find-command
426 (cond ((eq grep-find-use-xargs 'gnu)
427 (format "%s . -type f -print0 | xargs -0 -e %s"
428 find-program grep-command))
429 (grep-find-use-xargs
430 (format "%s . -type f -print | xargs %s"
431 find-program grep-command))
432 (t (cons (format "%s . -type f -exec %s {} %s \\;"
433 find-program grep-command null-device)
434 (+ 22 (length grep-command)))))))
435 (unless grep-tree-command
436 (setq grep-tree-command
437 (let* ((glen (length grep-program))
438 (gcmd (concat grep-program " <C>" (substring grep-command glen))))
439 (cond ((eq grep-find-use-xargs 'gnu)
440 (format "%s <D> <X> -type f <F> -print0 | xargs -0 -e %s <R>"
441 find-program gcmd))
442 (grep-find-use-xargs
443 (format "%s <D> <X> -type f <F> -print | xargs %s <R>"
444 find-program gcmd))
445 (t (format "%s <D> <X> -type f <F> -exec %s <R> {} %s \\;"
446 find-program gcmd null-device))))))
447 (unless (or (not grep-highlight-matches) (eq grep-highlight-matches t))
448 (setq grep-highlight-matches
449 (with-temp-buffer
450 (and (equal (condition-case nil
451 (call-process grep-program nil t nil "--help")
452 (error nil))
453 0)
454 (progn
455 (goto-char (point-min))
456 (search-forward "--color" nil t))
457 t)))))
458
459 (defun grep-default-command ()
460 (let ((tag-default
461 (shell-quote-argument
462 (or (funcall (or find-tag-default-function
463 (get major-mode 'find-tag-default-function)
464 'find-tag-default))
465 "")))
466 (sh-arg-re "\\(\\(?:\"\\(?:[^\"]\\|\\\\\"\\)+\"\\|'[^']+'\\|[^\"' \t\n]\\)+\\)")
467 (grep-default (or (car grep-history) grep-command)))
468 ;; Replace the thing matching for with that around cursor.
469 (when (or (string-match
470 (concat "[^ ]+\\s +\\(?:-[^ ]+\\s +\\)*"
471 sh-arg-re "\\(\\s +\\(\\S +\\)\\)?")
472 grep-default)
473 ;; If the string is not yet complete.
474 (string-match "\\(\\)\\'" grep-default))
475 (unless (or (not (stringp buffer-file-name))
476 (when (match-beginning 2)
477 (save-match-data
478 (string-match
479 (wildcard-to-regexp
480 (file-name-nondirectory
481 (match-string 3 grep-default)))
482 (file-name-nondirectory buffer-file-name)))))
483 (setq grep-default (concat (substring grep-default
484 0 (match-beginning 2))
485 " *."
486 (file-name-extension buffer-file-name))))
487 (replace-match tag-default t t grep-default 1))))
488
489 ;;;###autoload
490 (defun grep (command-args &optional highlight-regexp)
491 "Run grep, with user-specified args, and collect output in a buffer.
492 While grep runs asynchronously, you can use \\[next-error] (M-x next-error),
493 or \\<grep-mode-map>\\[compile-goto-error] in the grep \
494 output buffer, to go to the lines
495 where grep found matches.
496
497 This command uses a special history list for its COMMAND-ARGS, so you can
498 easily repeat a grep command.
499
500 A prefix argument says to default the argument based upon the current
501 tag the cursor is over, substituting it into the last grep command
502 in the grep command history (or into `grep-command'
503 if that history list is empty).
504
505 If specified, optional second arg HIGHLIGHT-REGEXP is the regexp to
506 temporarily highlight in visited source lines."
507 (interactive
508 (progn
509 (unless (and grep-command
510 (or (not grep-use-null-device) (eq grep-use-null-device t)))
511 (grep-compute-defaults))
512 (let ((default (grep-default-command)))
513 (list (read-from-minibuffer "Run grep (like this): "
514 (if current-prefix-arg
515 default grep-command)
516 nil nil 'grep-history
517 (if current-prefix-arg nil default))))))
518
519 ;; Setting process-setup-function makes exit-message-function work
520 ;; even when async processes aren't supported.
521 (let ((compilation-process-setup-function 'grep-process-setup))
522 (compilation-start (if (and grep-use-null-device null-device)
523 (concat command-args " " null-device)
524 command-args)
525 'grep-mode nil highlight-regexp)))
526
527 ;;;###autoload
528 (define-compilation-mode grep-mode "Grep"
529 "Sets `grep-last-buffer' and `compilation-window-height'."
530 (setq grep-last-buffer (current-buffer))
531 (set (make-local-variable 'compilation-error-face)
532 grep-hit-face)
533 (set (make-local-variable 'compilation-error-regexp-alist)
534 grep-regexp-alist)
535 ;; Set `font-lock-lines-before' to 0 to not refontify the previous
536 ;; line where grep markers may be already removed.
537 (set (make-local-variable 'font-lock-lines-before) 0))
538
539 ;;;###autoload
540 (defun grep-find (command-args)
541 "Run grep via find, with user-specified args COMMAND-ARGS.
542 Collect output in a buffer.
543 While find runs asynchronously, you can use the \\[next-error] command
544 to find the text that grep hits refer to.
545
546 This command uses a special history list for its arguments, so you can
547 easily repeat a find command."
548 (interactive
549 (progn
550 (unless (and grep-command
551 (or (not grep-use-null-device) (eq grep-use-null-device t)))
552 (grep-compute-defaults))
553 (if grep-find-command
554 (list (read-from-minibuffer "Run find (like this): "
555 grep-find-command nil nil
556 'grep-find-history))
557 ;; No default was set
558 (read-string
559 "compile.el: No `grep-find-command' command available. Press RET.")
560 (list nil))))
561 (when (and grep-find-command command-args)
562 (let ((null-device nil)) ; see grep
563 (grep command-args))))
564
565 ;;;###autoload
566 (defalias 'find-grep 'grep-find)
567
568 (defun grep-expand-command-macros (command &optional regexp files dir excl case-fold)
569 "Patch grep COMMAND replacing <D>, etc."
570 (setq command
571 (replace-regexp-in-string "<D>"
572 (or dir ".") command t t))
573 (setq command
574 (replace-regexp-in-string "<X>"
575 (or excl "") command t t))
576 (setq command
577 (replace-regexp-in-string "<F>"
578 (or files "") command t t))
579 (setq command
580 (replace-regexp-in-string "<C>"
581 (if case-fold "-i" "") command t t))
582 (setq command
583 (replace-regexp-in-string "<R>"
584 (or regexp "") command t t))
585 command)
586
587 (defvar grep-tree-last-regexp "")
588 (defvar grep-tree-last-files (car (car grep-tree-files-aliases)))
589
590 ;;;###autoload
591 (defun grep-tree (regexp files dir &optional subdirs)
592 "Grep for REGEXP in FILES in directory tree rooted at DIR.
593 Collect output in a buffer.
594 Interactively, prompt separately for each search parameter.
595 With prefix arg, reuse previous REGEXP.
596 The search is limited to file names matching shell pattern FILES.
597 FILES may use abbreviations defined in `grep-tree-files-aliases', e.g.
598 entering `ch' is equivalent to `*.[ch]'.
599
600 While find runs asynchronously, you can use the \\[next-error] command
601 to find the text that grep hits refer to.
602
603 This command uses a special history list for its arguments, so you can
604 easily repeat a find command.
605
606 When used non-interactively, optional arg SUBDIRS limits the search to
607 those sub directories of DIR."
608 (interactive
609 (let* ((regexp
610 (if current-prefix-arg
611 grep-tree-last-regexp
612 (let* ((default (current-word))
613 (spec (read-string
614 (concat "Search for"
615 (if (and default (> (length default) 0))
616 (format " (default %s): " default) ": ")))))
617 (if (equal spec "") default spec))))
618 (files
619 (read-string (concat "Search for \"" regexp "\" in files (default " grep-tree-last-files "): ")))
620 (dir
621 (read-directory-name "Base directory: " nil default-directory t)))
622 (list regexp files dir)))
623 (unless grep-tree-command
624 (grep-compute-defaults))
625 (unless (and (stringp files) (> (length files) 0))
626 (setq files grep-tree-last-files))
627 (when files
628 (setq grep-tree-last-files files)
629 (let ((mf (assoc files grep-tree-files-aliases)))
630 (if mf
631 (setq files (cdr mf)))))
632 (let ((command-args (grep-expand-command-macros
633 grep-tree-command
634 (setq grep-tree-last-regexp regexp)
635 (and files (concat "-name '" files "'"))
636 (if subdirs
637 (if (stringp subdirs)
638 subdirs
639 (mapconcat 'identity subdirs " "))
640 nil) ;; we change default-directory to dir
641 (and grep-tree-ignore-CVS-directories "-path '*/CVS' -prune -o ")
642 grep-tree-ignore-case))
643 (default-directory (file-name-as-directory (expand-file-name dir)))
644 (null-device nil)) ; see grep
645 (grep command-args regexp)))
646
647
648 (provide 'grep)
649
650 ;; arch-tag: 5a5b9169-a79d-4f38-9c38-f69615f39c4d
651 ;;; grep.el ends here