Add 2010 to copyright years.
[bpt/emacs.git] / lisp / progmodes / grep.el
CommitLineData
09e22417 1;;; grep.el --- run Grep as inferior of Emacs, parse match messages
318e2976 2
1bcd42e2 3;; Copyright (C) 1985, 1986, 1987, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
114f9c96 4;; 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
26dac56c 5;; Free Software Foundation, Inc.
318e2976
KS
6
7;; Author: Roland McGrath <roland@gnu.org>
8;; Maintainer: FSF
9;; Keywords: tools, processes
10
11;; This file is part of GNU Emacs.
12
b1fc2b50 13;; GNU Emacs is free software: you can redistribute it and/or modify
318e2976 14;; it under the terms of the GNU General Public License as published by
b1fc2b50
GM
15;; the Free Software Foundation, either version 3 of the License, or
16;; (at your option) any later version.
318e2976
KS
17
18;; GNU Emacs is distributed in the hope that it will be useful,
19;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21;; GNU General Public License for more details.
22
23;; You should have received a copy of the GNU General Public License
b1fc2b50 24;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
318e2976
KS
25
26;;; Commentary:
27
28;; This package provides the grep facilities documented in the Emacs
29;; user's manual.
30
31;;; Code:
32
33(require 'compile)
34
d70b5ac5 35
318e2976 36(defgroup grep nil
0acfb7ce 37 "Run grep as inferior of Emacs, parse error messages."
318e2976
KS
38 :group 'tools
39 :group 'processes)
40
d4bbd646
CY
41(defvar grep-host-defaults-alist nil
42 "Default values depending on target host.
43`grep-compute-defaults' returns default values for every local or
44remote host `grep' runs. These values can differ from host to
45host. Once computed, the default values are kept here in order
46to avoid computing them again.")
47
48(defun grep-apply-setting (symbol value)
49 "Set SYMBOL to VALUE, and update `grep-host-defaults-alist'.
50SYMBOL should be one of `grep-command', `grep-template',
51`grep-use-null-device', `grep-find-command',
52`grep-find-template', `grep-find-use-xargs', or
53`grep-highlight-matches'."
54 (when grep-host-defaults-alist
55 (let* ((host-id
56 (intern (or (file-remote-p default-directory) "localhost")))
57 (host-defaults (assq host-id grep-host-defaults-alist))
58 (defaults (assq nil grep-host-defaults-alist)))
59 (setcar (cdr (assq symbol host-defaults)) value)
60 (setcar (cdr (assq symbol defaults)) value)))
61 (set-default symbol value))
318e2976
KS
62
63;;;###autoload
64(defcustom grep-window-height nil
65 "*Number of lines in a grep window. If nil, use `compilation-window-height'."
66 :type '(choice (const :tag "Default" nil)
67 integer)
bf247b6e 68 :version "22.1"
318e2976
KS
69 :group 'grep)
70
00889cf9 71(defcustom grep-highlight-matches 'auto-detect
a601d313 72 "Use special markers to highlight grep matches.
277df088
JL
73
74Some grep programs are able to surround matches with special
75markers in grep output. Such markers can be used to highlight
76matches in grep mode.
77
a601d313 78This option sets the environment variable GREP_COLORS to specify
277df088
JL
79markers for highlighting and GREP_OPTIONS to add the --color
80option in front of any explicit grep options before starting
a58c94a2
JL
81the grep.
82
a601d313
JL
83When this option is `auto', grep uses `--color=auto' to highlight
84matches only when it outputs to a terminal (when `grep' is the last
85command in the pipe), thus avoiding the use of any potentially-harmful
86escape sequences when standard output goes to a file or pipe.
87
88To make grep highlight matches even into a pipe, you need the option
89`always' that forces grep to use `--color=always' to unconditionally
90output escape sequences.
91
d4bbd646 92In interactive usage, the actual value of this variable is set up
a601d313
JL
93by `grep-compute-defaults' when the default value is `auto-detect'.
94To change the default value, use Customize or call the function
95`grep-apply-setting'."
00889cf9
JL
96 :type '(choice (const :tag "Do not highlight matches with grep markers" nil)
97 (const :tag "Highlight matches with grep markers" t)
a601d313
JL
98 (const :tag "Use --color=always" always)
99 (const :tag "Use --color=auto" auto)
00889cf9 100 (other :tag "Not Set" auto-detect))
d4bbd646 101 :set 'grep-apply-setting
bf247b6e 102 :version "22.1"
277df088
JL
103 :group 'grep)
104
318e2976
KS
105(defcustom grep-scroll-output nil
106 "*Non-nil to scroll the *grep* buffer window as output appears.
107
108Setting it causes the grep commands to put point at the end of their
109output window so that the end of the output is always visible rather
110than the begining."
111 :type 'boolean
bf247b6e 112 :version "22.1"
318e2976
KS
113 :group 'grep)
114
9b19858e 115;;;###autoload
318e2976
KS
116(defcustom grep-command nil
117 "The default grep command for \\[grep].
118If the grep program used supports an option to always include file names
119in its output (such as the `-H' option to GNU grep), it's a good idea to
120include it when specifying `grep-command'.
121
d4bbd646
CY
122In interactive usage, the actual value of this variable is set up
123by `grep-compute-defaults'; to change the default value, use
124Customize or call the function `grep-apply-setting'."
318e2976
KS
125 :type '(choice string
126 (const :tag "Not Set" nil))
d4bbd646 127 :set 'grep-apply-setting
318e2976
KS
128 :group 'grep)
129
0acfb7ce
KS
130(defcustom grep-template nil
131 "The default command to run for \\[lgrep].
0acfb7ce
KS
132The following place holders should be present in the string:
133 <C> - place to put -i if case insensitive grep.
134 <F> - file names and wildcards to search.
7ae62430 135 <X> - file names and wildcards to exclude.
0acfb7ce 136 <R> - the regular expression searched for.
d4bbd646
CY
137 <N> - place to insert null-device.
138
139In interactive usage, the actual value of this variable is set up
140by `grep-compute-defaults'; to change the default value, use
141Customize or call the function `grep-apply-setting'."
0acfb7ce
KS
142 :type '(choice string
143 (const :tag "Not Set" nil))
d4bbd646 144 :set 'grep-apply-setting
0acfb7ce
KS
145 :version "22.1"
146 :group 'grep)
147
318e2976
KS
148(defcustom grep-use-null-device 'auto-detect
149 "If t, append the value of `null-device' to `grep' commands.
150This is done to ensure that the output of grep includes the filename of
151any match in the case where only a single file is searched, and is not
152necessary if the grep program used supports the `-H' option.
153
d4bbd646
CY
154In interactive usage, the actual value of this variable is set up
155by `grep-compute-defaults'; to change the default value, use
156Customize or call the function `grep-apply-setting'."
318e2976
KS
157 :type '(choice (const :tag "Do Not Append Null Device" nil)
158 (const :tag "Append Null Device" t)
159 (other :tag "Not Set" auto-detect))
d4bbd646 160 :set 'grep-apply-setting
318e2976
KS
161 :group 'grep)
162
9b19858e 163;;;###autoload
318e2976
KS
164(defcustom grep-find-command nil
165 "The default find command for \\[grep-find].
d4bbd646
CY
166In interactive usage, the actual value of this variable is set up
167by `grep-compute-defaults'; to change the default value, use
168Customize or call the function `grep-apply-setting'."
318e2976
KS
169 :type '(choice string
170 (const :tag "Not Set" nil))
d4bbd646 171 :set 'grep-apply-setting
318e2976
KS
172 :group 'grep)
173
0acfb7ce
KS
174(defcustom grep-find-template nil
175 "The default command to run for \\[rgrep].
318e2976
KS
176The following place holders should be present in the string:
177 <D> - base directory for find
178 <X> - find options to restrict or expand the directory list
179 <F> - find options to limit the files matched
180 <C> - place to put -i if case insensitive grep
d4bbd646
CY
181 <R> - the regular expression searched for.
182In interactive usage, the actual value of this variable is set up
183by `grep-compute-defaults'; to change the default value, use
184Customize or call the function `grep-apply-setting'."
318e2976
KS
185 :type '(choice string
186 (const :tag "Not Set" nil))
d4bbd646 187 :set 'grep-apply-setting
bf247b6e 188 :version "22.1"
318e2976
KS
189 :group 'grep)
190
2796180f 191(defcustom grep-files-aliases
7ae62430
JL
192 '(("all" . "* .*")
193 ("el" . "*.el")
194 ("ch" . "*.[ch]")
2796180f 195 ("c" . "*.c")
01623c28 196 ("cc" . "*.cc *.cxx *.cpp *.C *.CC *.c++")
7ae62430 197 ("cchh" . "*.cc *.[ch]xx *.[ch]pp *.[CHh] *.CC *.HH *.[ch]++")
01623c28 198 ("hh" . "*.hxx *.hpp *.[Hh] *.HH *.h++")
2796180f 199 ("h" . "*.h")
7ae62430 200 ("l" . "[Cc]hange[Ll]og*")
2796180f 201 ("m" . "[Mm]akefile*")
7ae62430
JL
202 ("tex" . "*.tex")
203 ("texi" . "*.texi")
204 ("asm" . "*.[sS]"))
0acfb7ce 205 "*Alist of aliases for the FILES argument to `lgrep' and `rgrep'."
318e2976
KS
206 :type 'alist
207 :group 'grep)
208
a2e548a9 209(defcustom grep-find-ignored-directories
1f0bee0a 210 vc-directory-exclusion-list
49405d0e
SS
211 "*List of names of sub-directories which `rgrep' shall not recurse into.
212If an element is a cons cell, the car is called on the search directory
213to determine whether cdr should not be recursed into."
7ae62430
JL
214 :type '(choice (repeat :tag "Ignored directories" string)
215 (const :tag "No ignored directories" nil))
216 :group 'grep)
217
218(defcustom grep-find-ignored-files
219 (cons ".#*" (delq nil (mapcar (lambda (s)
220 (unless (string-match-p "/\\'" s)
221 (concat "*" s)))
222 completion-ignored-extensions)))
223 "*List of file names which `rgrep' and `lgrep' shall exclude.
224If an element is a cons cell, the car is called on the search directory
225to determine whether cdr should not be excluded."
226 :type '(choice (repeat :tag "Ignored file" string)
227 (const :tag "No ignored files" nil))
318e2976
KS
228 :group 'grep)
229
bb72b9d0
DP
230(defcustom grep-error-screen-columns nil
231 "*If non-nil, column numbers in grep hits are screen columns.
232See `compilation-error-screen-columns'"
233 :type '(choice (const :tag "Default" nil)
234 integer)
bf247b6e 235 :version "22.1"
bb72b9d0
DP
236 :group 'grep)
237
318e2976
KS
238;;;###autoload
239(defcustom grep-setup-hook nil
240 "List of hook functions run by `grep-process-setup' (see `run-hooks')."
241 :type 'hook
242 :group 'grep)
243
244(defvar grep-mode-map
86c7460f
SS
245 (let ((map (make-sparse-keymap)))
246 (set-keymap-parent map compilation-minor-mode-map)
318e2976
KS
247 (define-key map " " 'scroll-up)
248 (define-key map "\^?" 'scroll-down)
0443d889 249 (define-key map "\C-c\C-f" 'next-error-follow-minor-mode)
318e2976 250
318e2976
KS
251 (define-key map "\r" 'compile-goto-error) ;; ?
252 (define-key map "n" 'next-error-no-select)
253 (define-key map "p" 'previous-error-no-select)
254 (define-key map "{" 'compilation-previous-file)
255 (define-key map "}" 'compilation-next-file)
94c713b2
JL
256 (define-key map "\t" 'compilation-next-error)
257 (define-key map [backtab] 'compilation-previous-error)
318e2976
KS
258
259 ;; Set up the menu-bar
260 (define-key map [menu-bar grep]
261 (cons "Grep" (make-sparse-keymap "Grep")))
262
263 (define-key map [menu-bar grep compilation-kill-compilation]
38805987
DN
264 '(menu-item "Kill Grep" kill-compilation
265 :help "Kill the currently running grep process"))
266 (define-key map [menu-bar grep compilation-separator2] '("----"))
318e2976 267 (define-key map [menu-bar grep compilation-compile]
38805987
DN
268 '(menu-item "Compile..." compile
269 :help "Compile the program including the current buffer. Default: run `make'"))
5d91db30
JL
270 (define-key map [menu-bar grep compilation-rgrep]
271 '(menu-item "Recursive grep..." rgrep
272 :help "User-friendly recursive grep in directory tree"))
273 (define-key map [menu-bar grep compilation-lgrep]
274 '(menu-item "Local grep..." lgrep
275 :help "User-friendly grep in a directory"))
276 (define-key map [menu-bar grep compilation-grep-find]
277 '(menu-item "Grep via Find..." grep-find
278 :help "Run grep via find, with user-specified args"))
318e2976 279 (define-key map [menu-bar grep compilation-grep]
38805987
DN
280 '(menu-item "Another grep..." grep
281 :help "Run grep, with user-specified args, and collect output in a buffer."))
318e2976 282 (define-key map [menu-bar grep compilation-recompile]
38805987
DN
283 '(menu-item "Repeat grep" recompile
284 :help "Run grep again"))
285 (define-key map [menu-bar grep compilation-separator2] '("----"))
318e2976 286 (define-key map [menu-bar grep compilation-first-error]
38805987
DN
287 '(menu-item "First Match" first-error
288 :help "Restart at the first match, visit corresponding location"))
318e2976 289 (define-key map [menu-bar grep compilation-previous-error]
38805987
DN
290 '(menu-item "Previous Match" previous-error
291 :help "Visit the previous match and corresponding location"))
318e2976 292 (define-key map [menu-bar grep compilation-next-error]
38805987
DN
293 '(menu-item "Next Match" next-error
294 :help "Visit the next match and corresponding location"))
318e2976
KS
295 map)
296 "Keymap for grep buffers.
297`compilation-minor-mode-map' is a cdr of this.")
298
38cd9f17 299(defvar grep-mode-tool-bar-map
30b72491
CY
300 ;; When bootstrapping, tool-bar-map is not properly initialized yet,
301 ;; so don't do anything.
302 (when (keymapp (butlast tool-bar-map))
303 (let ((map (butlast (copy-keymap tool-bar-map)))
304 (help (last tool-bar-map))) ;; Keep Help last in tool bar
305 (tool-bar-local-item
306 "left-arrow" 'previous-error-no-select 'previous-error-no-select map
307 :rtl "right-arrow"
308 :help "Goto previous match")
309 (tool-bar-local-item
310 "right-arrow" 'next-error-no-select 'next-error-no-select map
311 :rtl "left-arrow"
312 :help "Goto next match")
313 (tool-bar-local-item
314 "cancel" 'kill-compilation 'kill-compilation map
315 :enable '(let ((buffer (compilation-find-buffer)))
316 (get-buffer-process buffer))
317 :help "Stop grep")
318 (tool-bar-local-item
319 "refresh" 'recompile 'recompile map
320 :help "Restart grep")
321 (append map help))))
38cd9f17 322
ab55f76f
SM
323(defalias 'kill-grep 'kill-compilation)
324
318e2976
KS
325;;;; TODO --- refine this!!
326
6b3a84f2
SM
327;; (defcustom grep-use-compilation-buffer t
328;; "When non-nil, grep specific commands update `compilation-last-buffer'.
329;; This means that standard compile commands like \\[next-error] and \\[compile-goto-error]
330;; can be used to navigate between grep matches (the default).
331;; Otherwise, the grep specific commands like \\[grep-next-match] must
332;; be used to navigate between grep matches."
333;; :type 'boolean
334;; :group 'grep)
318e2976
KS
335
336;; override compilation-last-buffer
337(defvar grep-last-buffer nil
338 "The most recent grep buffer.
f911a445 339A grep buffer becomes most recent when you select Grep mode in it.
318e2976
KS
340Notice that using \\[next-error] or \\[compile-goto-error] modifies
341`complation-last-buffer' rather than `grep-last-buffer'.")
342
9b19858e 343;;;###autoload
2f7f4bee 344(defconst grep-regexp-alist
7462142d 345 '(("^\\(.+?\\)\\(:[ \t]*\\)\\([0-9]+\\)\\2"
35952129
JL
346 1 3)
347 ;; Rule to match column numbers is commented out since no known grep
348 ;; produces them
7462142d 349 ;; ("^\\(.+?\\)\\(:[ \t]*\\)\\([0-9]+\\)\\2\\(?:\\([0-9]+\\)\\(?:-\\([0-9]+\\)\\)?\\2\\)?"
35952129 350 ;; 1 3 (4 . 5))
f04a8969 351 ("^\\(\\(.+?\\):\\([0-9]+\\):\\).*?\
983203ee 352\\(\033\\[01;31m\\(?:\033\\[K\\)?\\)\\(.*?\\)\\(\033\\[[0-9]*m\\)"
f04a8969 353 2 3
1bcd42e2 354 ;; Calculate column positions (beg . end) of first grep match on a line
277df088 355 ((lambda ()
983203ee
JL
356 (setq compilation-error-screen-columns nil)
357 (- (match-beginning 4) (match-end 1)))
277df088 358 .
983203ee
JL
359 (lambda () (- (match-end 5) (match-end 1)
360 (- (match-end 4) (match-beginning 4)))))
f04a8969 361 nil 1)
ba65be24 362 ("^Binary file \\(.+\\) matches$" 1 nil nil 0 1))
318e2976
KS
363 "Regexp used to match grep hits. See `compilation-error-regexp-alist'.")
364
ab55f76f
SM
365(defvar grep-error "grep hit"
366 "Message to print when no matches are found.")
367
368;; Reverse the colors because grep hits are not errors (though we jump there
369;; with `next-error'), and unreadable files can't be gone to.
370(defvar grep-hit-face compilation-info-face
371 "Face name to use for grep hits.")
372
7462142d 373(defvar grep-error-face 'compilation-error
ab55f76f
SM
374 "Face name to use for grep error messages.")
375
f30c45e1
JL
376(defvar grep-match-face 'match
377 "Face name to use for grep matches.")
378
35952129
JL
379(defvar grep-context-face 'shadow
380 "Face name to use for grep context lines.")
381
ab55f76f
SM
382(defvar grep-mode-font-lock-keywords
383 '(;; Command output lines.
384 ("^\\([A-Za-z_0-9/\.+-]+\\)[ \t]*:" 1 font-lock-function-name-face)
385 (": \\(.+\\): \\(?:Permission denied\\|No such \\(?:file or directory\\|device or address\\)\\)$"
386 1 grep-error-face)
387 ;; remove match from grep-regexp-alist before fontifying
c2c04c28 388 ("^Grep[/a-zA-z]* started.*"
19b3905f 389 (0 '(face nil message nil help-echo nil mouse-face nil) t))
c2c04c28 390 ("^Grep[/a-zA-z]* finished \\(?:(\\(matches found\\))\\|with \\(no matches found\\)\\).*"
ab55f76f 391 (0 '(face nil message nil help-echo nil mouse-face nil) t)
7462142d
JL
392 (1 compilation-info-face nil t)
393 (2 compilation-warning-face nil t))
c2c04c28 394 ("^Grep[/a-zA-z]* \\(exited abnormally\\|interrupt\\|killed\\|terminated\\)\\(?:.*with code \\([0-9]+\\)\\)?.*"
ab55f76f 395 (0 '(face nil message nil help-echo nil mouse-face nil) t)
7462142d 396 (1 grep-error-face)
cf115520 397 (2 grep-error-face nil t))
7462142d 398 ("^.+?-[0-9]+-.*\n" (0 grep-context-face))
1bcd42e2 399 ;; Highlight grep matches and delete markers
983203ee 400 ("\\(\033\\[01;31m\\)\\(.*?\\)\\(\033\\[[0-9]*m\\)"
5ad24c0b
LK
401 ;; Refontification does not work after the markers have been
402 ;; deleted. So we use the font-lock-face property here as Font
403 ;; Lock does not clear that.
404 (2 (list 'face nil 'font-lock-face grep-match-face))
983203ee 405 ((lambda (bound))
1bcd42e2
JL
406 (progn
407 ;; Delete markers with `replace-match' because it updates
408 ;; the match-data, whereas `delete-region' would render it obsolete.
409 (replace-match "" t t nil 3)
983203ee
JL
410 (replace-match "" t t nil 1))))
411 ("\\(\033\\[[0-9;]*[mK]\\)"
412 ;; Delete all remaining escape sequences
413 ((lambda (bound))
414 (replace-match "" t t nil 1))))
ab55f76f
SM
415 "Additional things to highlight in grep output.
416This gets tacked on the end of the generated expressions.")
417
9b19858e 418;;;###autoload
6bdad9ae 419(defvar grep-program (purecopy "grep")
318e2976
KS
420 "The default grep program for `grep-command' and `grep-find-command'.
421This variable's value takes effect when `grep-compute-defaults' is called.")
422
9b19858e 423;;;###autoload
6bdad9ae 424(defvar find-program (purecopy "find")
318e2976
KS
425 "The default find program for `grep-find-command'.
426This variable's value takes effect when `grep-compute-defaults' is called.")
427
1571d112 428;;;###autoload
6bdad9ae 429(defvar xargs-program (purecopy "xargs")
1571d112
JB
430 "The default xargs program for `grep-find-command'.
431See `grep-find-use-xargs'.
432This variable's value takes effect when `grep-compute-defaults' is called.")
433
9b19858e 434;;;###autoload
318e2976 435(defvar grep-find-use-xargs nil
7f3afa3d
CY
436 "Non-nil means that `grep-find' uses the `xargs' utility by default.
437If `exec', use `find -exec'.
438If `gnu', use `find -print0' and `xargs -0'.
439Any other non-nil value means to use `find -print' and `xargs'.
318e2976
KS
440
441This variable's value takes effect when `grep-compute-defaults' is called.")
442
443;; History of grep commands.
9b19858e 444;;;###autoload
318e2976 445(defvar grep-history nil)
9b19858e 446;;;###autoload
318e2976
KS
447(defvar grep-find-history nil)
448
0acfb7ce
KS
449;; History of lgrep and rgrep regexp and files args.
450(defvar grep-regexp-history nil)
7ae62430 451(defvar grep-files-history nil)
0acfb7ce 452
9b19858e 453;;;###autoload
318e2976
KS
454(defun grep-process-setup ()
455 "Setup compilation variables and buffer for `grep'.
ab55f76f 456Set up `compilation-exit-message-function' and run `grep-setup-hook'."
a601d313 457 (when (eq grep-highlight-matches 'auto-detect)
a58c94a2 458 (grep-compute-defaults))
a601d313
JL
459 (unless (or (eq grep-highlight-matches 'auto-detect)
460 (null grep-highlight-matches))
6b8d0852
JL
461 ;; `setenv' modifies `process-environment' let-bound in `compilation-start'
462 ;; Any TERM except "dumb" allows GNU grep to use `--color=auto'
463 (setenv "TERM" "emacs-grep")
8f0e19af
EZ
464 (setenv "GREP_OPTIONS"
465 (concat (getenv "GREP_OPTIONS")
a601d313 466 " --color=" (if (eq grep-highlight-matches 'always)
8f0e19af 467 "always" "auto")))
6b8d0852 468 ;; GREP_COLOR is used in GNU grep 2.5.1, but deprecated in later versions
983203ee 469 (setenv "GREP_COLOR" "01;31")
6b8d0852 470 ;; GREP_COLORS is used in GNU grep 2.5.2 and later versions
983203ee 471 (setenv "GREP_COLORS" "mt=01;31:fn=:ln=:bn=:se=:ml=:cx=:ne"))
318e2976
KS
472 (set (make-local-variable 'compilation-exit-message-function)
473 (lambda (status code msg)
474 (if (eq status 'exit)
475 (cond ((zerop code)
476 '("finished (matches found)\n" . "matched"))
477 ((= code 1)
478 '("finished with no matches found\n" . "no match"))
479 (t
480 (cons msg code)))
481 (cons msg code))))
318e2976
KS
482 (run-hooks 'grep-setup-hook))
483
0acfb7ce 484(defun grep-probe (command args &optional func result)
458c8d31
MA
485 (let (process-file-side-effects)
486 (equal (condition-case nil
487 (apply (or func 'process-file) command args)
488 (error nil))
489 (or result 0))))
0acfb7ce 490
9b19858e 491;;;###autoload
318e2976 492(defun grep-compute-defaults ()
2e2eead3
MA
493 ;; Keep default values.
494 (unless grep-host-defaults-alist
495 (add-to-list
496 'grep-host-defaults-alist
497 (cons nil
498 `((grep-command ,grep-command)
499 (grep-template ,grep-template)
500 (grep-use-null-device ,grep-use-null-device)
501 (grep-find-command ,grep-find-command)
502 (grep-find-template ,grep-find-template)
503 (grep-find-use-xargs ,grep-find-use-xargs)
504 (grep-highlight-matches ,grep-highlight-matches)))))
505 (let* ((host-id
85e7298f 506 (intern (or (file-remote-p default-directory) "localhost")))
2e2eead3
MA
507 (host-defaults (assq host-id grep-host-defaults-alist))
508 (defaults (assq nil grep-host-defaults-alist)))
05c7c7b1 509 ;; There are different defaults on different hosts. They must be
2e2eead3 510 ;; computed for every host once.
d4bbd646
CY
511 (dolist (setting '(grep-command grep-template
512 grep-use-null-device grep-find-command
513 grep-find-template grep-find-use-xargs
514 grep-highlight-matches))
515 (set setting
516 (or (cadr (assq setting host-defaults))
517 (cadr (assq setting defaults)))))
05c7c7b1
MA
518
519 (unless (or (not grep-use-null-device) (eq grep-use-null-device t))
520 (setq grep-use-null-device
521 (with-temp-buffer
522 (let ((hello-file (expand-file-name "HELLO" data-directory)))
523 (not
524 (and (if grep-command
525 ;; `grep-command' is already set, so
526 ;; use that for testing.
527 (grep-probe grep-command
528 `(nil t nil "^English" ,hello-file)
529 #'call-process-shell-command)
530 ;; otherwise use `grep-program'
531 (grep-probe grep-program
532 `(nil t nil "-nH" "^English" ,hello-file)))
533 (progn
534 (goto-char (point-min))
535 (looking-at
536 (concat (regexp-quote hello-file)
537 ":[0-9]+:English")))))))))
538 (unless (and grep-command grep-find-command
539 grep-template grep-find-template)
540 (let ((grep-options
541 (concat (if grep-use-null-device "-n" "-nH")
542 (if (grep-probe grep-program
543 `(nil nil nil "-e" "foo" ,null-device)
544 nil 1)
545 " -e"))))
546 (unless grep-command
547 (setq grep-command
548 (format "%s %s " grep-program grep-options)))
549 (unless grep-template
550 (setq grep-template
7ae62430 551 (format "%s <X> <C> %s <R> <F>" grep-program grep-options)))
05c7c7b1
MA
552 (unless grep-find-use-xargs
553 (setq grep-find-use-xargs
554 (cond
555 ((and
556 (grep-probe find-program `(nil nil nil ,null-device "-print0"))
1571d112 557 (grep-probe xargs-program `(nil nil nil "-0" "-e" "echo")))
05c7c7b1
MA
558 'gnu)
559 (t
560 'exec))))
561 (unless grep-find-command
562 (setq grep-find-command
0acfb7ce 563 (cond ((eq grep-find-use-xargs 'gnu)
1571d112
JB
564 (format "%s . -type f -print0 | %s -0 -e %s"
565 find-program xargs-program grep-command))
84d797c9 566 ((eq grep-find-use-xargs 'exec)
05c7c7b1
MA
567 (let ((cmd0 (format "%s . -type f -exec %s"
568 find-program grep-command)))
569 (cons
570 (format "%s {} %s %s"
571 cmd0 null-device
572 (shell-quote-argument ";"))
573 (1+ (length cmd0)))))
84d797c9 574 (t
1571d112
JB
575 (format "%s . -type f -print | %s %s"
576 find-program xargs-program grep-command)))))
05c7c7b1
MA
577 (unless grep-find-template
578 (setq grep-find-template
579 (let ((gcmd (format "%s <C> %s <R>"
580 grep-program grep-options)))
581 (cond ((eq grep-find-use-xargs 'gnu)
1571d112
JB
582 (format "%s . <X> -type f <F> -print0 | %s -0 -e %s"
583 find-program xargs-program gcmd))
05c7c7b1
MA
584 ((eq grep-find-use-xargs 'exec)
585 (format "%s . <X> -type f <F> -exec %s {} %s %s"
586 find-program gcmd null-device
587 (shell-quote-argument ";")))
588 (t
1571d112
JB
589 (format "%s . <X> -type f <F> -print | %s %s"
590 find-program xargs-program gcmd))))))))
a601d313 591 (when (eq grep-highlight-matches 'auto-detect)
05c7c7b1
MA
592 (setq grep-highlight-matches
593 (with-temp-buffer
594 (and (grep-probe grep-program '(nil t nil "--help"))
595 (progn
596 (goto-char (point-min))
597 (search-forward "--color" nil t))
a601d313
JL
598 ;; Windows and DOS pipes fail `isatty' detection in Grep.
599 (if (memq system-type '(windows-nt ms-dos))
600 'always 'auto)))))
05c7c7b1
MA
601
602 ;; Save defaults for this host.
6e3aa3f5
MA
603 (setq grep-host-defaults-alist
604 (delete (assq host-id grep-host-defaults-alist)
605 grep-host-defaults-alist))
606 (add-to-list
607 'grep-host-defaults-alist
608 (cons host-id
609 `((grep-command ,grep-command)
610 (grep-template ,grep-template)
611 (grep-use-null-device ,grep-use-null-device)
612 (grep-find-command ,grep-find-command)
613 (grep-find-template ,grep-find-template)
614 (grep-find-use-xargs ,grep-find-use-xargs)
615 (grep-highlight-matches ,grep-highlight-matches))))))
318e2976 616
7cb0d0ef
KS
617(defun grep-tag-default ()
618 (or (and transient-mark-mode mark-active
619 (/= (point) (mark))
620 (buffer-substring-no-properties (point) (mark)))
621 (funcall (or find-tag-default-function
622 (get major-mode 'find-tag-default-function)
623 'find-tag-default))
624 ""))
625
318e2976 626(defun grep-default-command ()
1571d112 627 "Compute the default grep command for \\[universal-argument] \\[grep] to offer."
7cb0d0ef 628 (let ((tag-default (shell-quote-argument (grep-tag-default)))
13eb1bde
RS
629 ;; This a regexp to match single shell arguments.
630 ;; Could someone please add comments explaining it?
318e2976
KS
631 (sh-arg-re "\\(\\(?:\"\\(?:[^\"]\\|\\\\\"\\)+\"\\|'[^']+'\\|[^\"' \t\n]\\)+\\)")
632 (grep-default (or (car grep-history) grep-command)))
13eb1bde 633 ;; In the default command, find the arg that specifies the pattern.
318e2976
KS
634 (when (or (string-match
635 (concat "[^ ]+\\s +\\(?:-[^ ]+\\s +\\)*"
636 sh-arg-re "\\(\\s +\\(\\S +\\)\\)?")
637 grep-default)
638 ;; If the string is not yet complete.
639 (string-match "\\(\\)\\'" grep-default))
13eb1bde
RS
640 ;; Maybe we will replace the pattern with the default tag.
641 ;; But first, maybe replace the file name pattern.
642 (condition-case nil
643 (unless (or (not (stringp buffer-file-name))
644 (when (match-beginning 2)
645 (save-match-data
646 (string-match
647 (wildcard-to-regexp
648 (file-name-nondirectory
649 (match-string 3 grep-default)))
650 (file-name-nondirectory buffer-file-name)))))
651 (setq grep-default (concat (substring grep-default
652 0 (match-beginning 2))
653 " *."
654 (file-name-extension buffer-file-name))))
655 ;; In case wildcard-to-regexp gets an error
656 ;; from invalid data.
657 (error nil))
658 ;; Now replace the pattern with the default tag.
02b73b97 659 (replace-match tag-default t t grep-default 1))))
318e2976 660
0acfb7ce 661
318e2976 662;;;###autoload
0acfb7ce
KS
663(define-compilation-mode grep-mode "Grep"
664 "Sets `grep-last-buffer' and `compilation-window-height'."
665 (setq grep-last-buffer (current-buffer))
38cd9f17 666 (set (make-local-variable 'tool-bar-map) grep-mode-tool-bar-map)
0acfb7ce
KS
667 (set (make-local-variable 'compilation-error-face)
668 grep-hit-face)
669 (set (make-local-variable 'compilation-error-regexp-alist)
670 grep-regexp-alist)
671 (set (make-local-variable 'compilation-process-setup-function)
672 'grep-process-setup)
673 (set (make-local-variable 'compilation-disable-input) t))
674
675
676;;;###autoload
677(defun grep (command-args)
318e2976
KS
678 "Run grep, with user-specified args, and collect output in a buffer.
679While grep runs asynchronously, you can use \\[next-error] (M-x next-error),
c426452f 680or \\<grep-mode-map>\\[compile-goto-error] in the grep \
1571d112
JB
681output buffer, to go to the lines where grep
682found matches.
318e2976 683
215c3847 684For doing a recursive `grep', see the `rgrep' command. For running
9d4a3d39 685`grep' in a specific directory, see `lgrep'.
215c3847 686
1571d112
JB
687This command uses a special history list for its COMMAND-ARGS, so you
688can easily repeat a grep command.
318e2976
KS
689
690A prefix argument says to default the argument based upon the current
691tag the cursor is over, substituting it into the last grep command
1571d112
JB
692in the grep command history (or into `grep-command' if that history
693list is empty)."
318e2976
KS
694 (interactive
695 (progn
0acfb7ce 696 (grep-compute-defaults)
318e2976 697 (let ((default (grep-default-command)))
286d4b3b
SM
698 (list (read-shell-command "Run grep (like this): "
699 (if current-prefix-arg default grep-command)
700 'grep-history
701 (if current-prefix-arg nil default))))))
318e2976
KS
702
703 ;; Setting process-setup-function makes exit-message-function work
704 ;; even when async processes aren't supported.
19b3905f
JL
705 (compilation-start (if (and grep-use-null-device null-device)
706 (concat command-args " " null-device)
707 command-args)
0acfb7ce 708 'grep-mode))
ab55f76f 709
318e2976 710
318e2976
KS
711;;;###autoload
712(defun grep-find (command-args)
713 "Run grep via find, with user-specified args COMMAND-ARGS.
714Collect output in a buffer.
715While find runs asynchronously, you can use the \\[next-error] command
716to find the text that grep hits refer to.
717
718This command uses a special history list for its arguments, so you can
719easily repeat a find command."
720 (interactive
721 (progn
0acfb7ce 722 (grep-compute-defaults)
87f54c05 723 (if grep-find-command
286d4b3b
SM
724 (list (read-shell-command "Run find (like this): "
725 grep-find-command 'grep-find-history))
87f54c05
RS
726 ;; No default was set
727 (read-string
728 "compile.el: No `grep-find-command' command available. Press RET.")
729 (list nil))))
a34f7eb1 730 (when command-args
87f54c05
RS
731 (let ((null-device nil)) ; see grep
732 (grep command-args))))
318e2976 733
3d4d788a
DK
734;;;###autoload
735(defalias 'find-grep 'grep-find)
736
0acfb7ce
KS
737
738;; User-friendly interactive API.
739
740(defconst grep-expand-keywords
741 '(("<C>" . (and cf (isearch-no-upper-case-p regexp t) "-i"))
742 ("<D>" . dir)
743 ("<F>" . files)
744 ("<N>" . null-device)
745 ("<X>" . excl)
746 ("<R>" . (shell-quote-argument (or regexp ""))))
747 "List of substitutions performed by `grep-expand-template'.
748If car of an element matches, the cdr is evalled in to get the
749substitution string. Note dynamic scoping of variables.")
750
751(defun grep-expand-template (template &optional regexp files dir excl)
752 "Patch grep COMMAND string replacing <C>, <D>, <F>, <R>, and <X>."
753 (let ((command template)
754 (cf case-fold-search)
755 (case-fold-search nil))
756 (dolist (kw grep-expand-keywords command)
757 (if (string-match (car kw) command)
d0afff34
KS
758 (setq command
759 (replace-match
760 (or (if (symbolp (cdr kw))
41a2f8ba 761 (symbol-value (cdr kw))
d0afff34
KS
762 (save-match-data (eval (cdr kw))))
763 "")
764 t t command))))))
0acfb7ce
KS
765
766(defun grep-read-regexp ()
767 "Read regexp arg for interactive grep."
7cb0d0ef 768 (let ((default (grep-tag-default)))
0acfb7ce
KS
769 (read-string
770 (concat "Search for"
771 (if (and default (> (length default) 0))
7cb0d0ef 772 (format " (default \"%s\"): " default) ": "))
0acfb7ce
KS
773 nil 'grep-regexp-history default)))
774
775(defun grep-read-files (regexp)
776 "Read files arg for interactive grep."
6830f449
JL
777 (let* ((bn (or (buffer-file-name)
778 (replace-regexp-in-string "<[0-9]+>\\'" "" (buffer-name))))
208cc91f
KS
779 (fn (and bn
780 (stringp bn)
781 (file-name-nondirectory bn)))
7ae62430
JL
782 (default-alias
783 (and fn
784 (let ((aliases grep-files-aliases)
785 alias)
786 (while aliases
787 (setq alias (car aliases)
788 aliases (cdr aliases))
789 (if (string-match (wildcard-to-regexp (cdr alias)) fn)
790 (setq aliases nil)
791 (setq alias nil)))
792 (cdr alias))))
793 (default-extension
794 (and fn
795 (let ((ext (file-name-extension fn)))
796 (and ext (concat "*." ext)))))
208cc91f 797 (default
7ae62430
JL
798 (or default-alias
799 default-extension
bcdf86fb
KS
800 (car grep-files-history)
801 (car (car grep-files-aliases))))
9136e895 802 (files (completing-read
0acfb7ce 803 (concat "Search for \"" regexp
208cc91f
KS
804 "\" in files"
805 (if default (concat " (default " default ")"))
806 ": ")
9136e895
JL
807 'read-file-name-internal
808 nil nil nil 'grep-files-history
7ae62430
JL
809 (delete-dups
810 (delq nil (append (list default default-alias default-extension)
811 (mapcar 'car grep-files-aliases)))))))
0acfb7ce
KS
812 (and files
813 (or (cdr (assoc files grep-files-aliases))
814 files))))
318e2976
KS
815
816;;;###autoload
32a2cf25 817(defun lgrep (regexp &optional files dir confirm)
9d4a3d39 818 "Run grep, searching for REGEXP in FILES in directory DIR.
318e2976 819The search is limited to file names matching shell pattern FILES.
0acfb7ce 820FILES may use abbreviations defined in `grep-files-aliases', e.g.
318e2976
KS
821entering `ch' is equivalent to `*.[ch]'.
822
6e7c574f
KS
823With \\[universal-argument] prefix, you can edit the constructed shell command line
824before it is executed.
825With two \\[universal-argument] prefixes, directly edit and run `grep-command'.
318e2976 826
0acfb7ce 827Collect output in a buffer. While grep runs asynchronously, you
1571d112
JB
828can use \\[next-error] (M-x next-error), or \\<grep-mode-map>\\[compile-goto-error] \
829in the grep output buffer,
830to go to the lines where grep found matches.
318e2976 831
0acfb7ce 832This command shares argument histories with \\[rgrep] and \\[grep]."
318e2976 833 (interactive
0acfb7ce
KS
834 (progn
835 (grep-compute-defaults)
836 (cond
837 ((and grep-command (equal current-prefix-arg '(16)))
838 (list (read-from-minibuffer "Run: " grep-command
32a2cf25 839 nil nil 'grep-history)))
0acfb7ce 840 ((not grep-template)
5a0c3f56 841 (error "grep.el: No `grep-template' available"))
0acfb7ce 842 (t (let* ((regexp (grep-read-regexp))
9d4a3d39
KS
843 (files (grep-read-files regexp))
844 (dir (read-directory-name "In directory: "
32a2cf25
JL
845 nil default-directory t))
846 (confirm (equal current-prefix-arg '(4))))
847 (list regexp files dir confirm))))))
0acfb7ce 848 (when (and (stringp regexp) (> (length regexp) 0))
32a2cf25
JL
849 (unless (and dir (file-directory-p dir) (file-readable-p dir))
850 (setq dir default-directory))
0acfb7ce
KS
851 (let ((command regexp))
852 (if (null files)
853 (if (string= command grep-command)
854 (setq command nil))
9d4a3d39 855 (setq dir (file-name-as-directory (expand-file-name dir)))
0acfb7ce
KS
856 (setq command (grep-expand-template
857 grep-template
858 regexp
7ae62430
JL
859 files
860 nil
861 (and grep-find-ignored-files
862 (concat " --exclude="
863 (mapconcat
864 #'(lambda (ignore)
865 (cond ((stringp ignore)
866 (shell-quote-argument ignore))
867 ((consp ignore)
868 (and (funcall (car ignore) dir)
869 (shell-quote-argument
870 (cdr ignore))))))
871 grep-find-ignored-files
872 " --exclude=")))))
0acfb7ce 873 (when command
32a2cf25 874 (if confirm
0acfb7ce
KS
875 (setq command
876 (read-from-minibuffer "Confirm: "
877 command nil nil 'grep-history))
c3e9438b 878 (add-to-history 'grep-history command))))
0acfb7ce 879 (when command
32a2cf25 880 (let ((default-directory dir))
9d4a3d39
KS
881 ;; Setting process-setup-function makes exit-message-function work
882 ;; even when async processes aren't supported.
883 (compilation-start (if (and grep-use-null-device null-device)
884 (concat command " " null-device)
c60c6d91
RS
885 command)
886 'grep-mode))
9d4a3d39
KS
887 (if (eq next-error-last-buffer (current-buffer))
888 (setq default-directory dir))))))
889
0acfb7ce 890
da205913 891(defvar find-name-arg) ; not autoloaded but defined in find-dired
0acfb7ce
KS
892
893;;;###autoload
32a2cf25 894(defun rgrep (regexp &optional files dir confirm)
6e7c574f 895 "Recursively grep for REGEXP in FILES in directory tree rooted at DIR.
0acfb7ce
KS
896The search is limited to file names matching shell pattern FILES.
897FILES may use abbreviations defined in `grep-files-aliases', e.g.
898entering `ch' is equivalent to `*.[ch]'.
899
6e7c574f
KS
900With \\[universal-argument] prefix, you can edit the constructed shell command line
901before it is executed.
902With two \\[universal-argument] prefixes, directly edit and run `grep-find-command'.
0acfb7ce
KS
903
904Collect output in a buffer. While find runs asynchronously, you
1571d112
JB
905can use \\[next-error] (M-x next-error), or \\<grep-mode-map>\\[compile-goto-error] \
906in the grep output buffer,
907to go to the lines where grep found matches.
0acfb7ce
KS
908
909This command shares argument histories with \\[lgrep] and \\[grep-find]."
910 (interactive
911 (progn
912 (grep-compute-defaults)
913 (cond
914 ((and grep-find-command (equal current-prefix-arg '(16)))
915 (list (read-from-minibuffer "Run: " grep-find-command
32a2cf25 916 nil nil 'grep-find-history)))
0acfb7ce 917 ((not grep-find-template)
5a0c3f56 918 (error "grep.el: No `grep-find-template' available"))
0acfb7ce
KS
919 (t (let* ((regexp (grep-read-regexp))
920 (files (grep-read-files regexp))
921 (dir (read-directory-name "Base directory: "
32a2cf25
JL
922 nil default-directory t))
923 (confirm (equal current-prefix-arg '(4))))
924 (list regexp files dir confirm))))))
0acfb7ce 925 (when (and (stringp regexp) (> (length regexp) 0))
32a2cf25
JL
926 (unless (and dir (file-directory-p dir) (file-readable-p dir))
927 (setq dir default-directory))
0acfb7ce
KS
928 (if (null files)
929 (if (not (string= regexp grep-find-command))
930 (compilation-start regexp 'grep-mode))
c0b87c3f 931 (setq dir (file-name-as-directory (expand-file-name dir)))
da205913 932 (require 'find-dired) ; for `find-name-arg'
c0b87c3f
KS
933 (let ((command (grep-expand-template
934 grep-find-template
935 regexp
84d797c9 936 (concat (shell-quote-argument "(")
34fac407 937 " " find-name-arg " "
c0b87c3f
KS
938 (mapconcat #'shell-quote-argument
939 (split-string files)
ea7ac2eb 940 (concat " -o " find-name-arg " "))
84d797c9
KS
941 " "
942 (shell-quote-argument ")"))
7ae62430
JL
943 dir
944 (concat
0acfb7ce 945 (and grep-find-ignored-directories
84d797c9
KS
946 (concat (shell-quote-argument "(")
947 ;; we should use shell-quote-argument here
948 " -path "
49405d0e 949 (mapconcat
7ae62430
JL
950 #'(lambda (ignore)
951 (cond ((stringp ignore)
952 (shell-quote-argument
953 (concat "*/" ignore)))
954 ((consp ignore)
955 (and (funcall (car ignore) dir)
956 (shell-quote-argument
957 (concat "*/"
958 (cdr ignore)))))))
959 grep-find-ignored-directories
960 " -o -path ")
961 " "
962 (shell-quote-argument ")")
963 " -prune -o "))
964 (and grep-find-ignored-files
965 (concat (shell-quote-argument "(")
966 ;; we should use shell-quote-argument here
967 " -name "
968 (mapconcat
969 #'(lambda (ignore)
970 (cond ((stringp ignore)
971 (shell-quote-argument ignore))
972 ((consp ignore)
973 (and (funcall (car ignore) dir)
974 (shell-quote-argument
975 (cdr ignore))))))
976 grep-find-ignored-files
977 " -o -name ")
84d797c9
KS
978 " "
979 (shell-quote-argument ")")
7ae62430 980 " -prune -o "))))))
0acfb7ce 981 (when command
32a2cf25 982 (if confirm
0acfb7ce
KS
983 (setq command
984 (read-from-minibuffer "Confirm: "
985 command nil nil 'grep-find-history))
c3e9438b 986 (add-to-history 'grep-find-history command))
c0b87c3f
KS
987 (let ((default-directory dir))
988 (compilation-start command 'grep-mode))
989 ;; Set default-directory if we started rgrep in the *grep* buffer.
990 (if (eq next-error-last-buffer (current-buffer))
991 (setq default-directory dir)))))))
318e2976 992
a601d313
JL
993;;;###autoload
994(defun zrgrep (regexp &optional files dir confirm grep-find-template)
995 "Recursively grep for REGEXP in gzipped FILES in tree rooted at DIR.
996Like `rgrep' but uses `zgrep' for `grep-program', sets the default
997file name to `*.gz', and sets `grep-highlight-matches' to `always'."
998 (interactive
999 (let ((grep-program "zgrep")
1000 (grep-find-template nil) ; output of `grep-compute-defaults'
1001 (grep-find-command nil)
1002 (grep-host-defaults-alist nil)
1003 (grep-files-aliases '(("*.gz" . "*.gz") ; for `grep-read-files'
1004 ("all" . "* .*"))))
1005 ;; Recompute defaults using let-bound values above.
1006 (grep-compute-defaults)
1007 (cond
1008 ((and grep-find-command (equal current-prefix-arg '(16)))
1009 (list (read-from-minibuffer "Run: " grep-find-command
1010 nil nil 'grep-find-history)))
1011 ((not grep-find-template)
1012 (error "grep.el: No `grep-find-template' available"))
1013 (t (let* ((regexp (grep-read-regexp))
1014 (files (grep-read-files regexp))
1015 (dir (read-directory-name "Base directory: "
1016 nil default-directory t))
1017 (confirm (equal current-prefix-arg '(4))))
1018 (list regexp files dir confirm grep-find-template))))))
1019 ;; Set `grep-highlight-matches' to `always'
1020 ;; since `zgrep' puts filters in the grep output.
1021 (let ((grep-highlight-matches 'always))
1022 ;; `rgrep' uses the dynamically bound value `grep-find-template'
1023 ;; from the argument `grep-find-template' whose value is computed
1024 ;; in the `interactive' spec.
1025 (rgrep regexp files dir confirm)))
1026
1027;;;###autoload
1028(defalias 'rzgrep 'zrgrep)
318e2976
KS
1029
1030(provide 'grep)
1031
cbd5542a 1032;; arch-tag: 5a5b9169-a79d-4f38-9c38-f69615f39c4d
06626cf2 1033;;; grep.el ends here