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