Merge from emacs-24; up to 2014-05-29T17:16:00Z!dmantipov@yandex.ru
[bpt/emacs.git] / lisp / progmodes / grep.el
CommitLineData
f7993853 1;;; grep.el --- run `grep' and display the results -*- lexical-binding:t -*-
318e2976 2
1d75432d 3;; Copyright (C) 1985-1987, 1993-1999, 2001-2014 Free Software Foundation, Inc.
318e2976
KS
4
5;; Author: Roland McGrath <roland@gnu.org>
34dc21db 6;; Maintainer: emacs-devel@gnu.org
318e2976
KS
7;; Keywords: tools, processes
8
9;; This file is part of GNU Emacs.
10
b1fc2b50 11;; GNU Emacs is free software: you can redistribute it and/or modify
318e2976 12;; it under the terms of the GNU General Public License as published by
b1fc2b50
GM
13;; the Free Software Foundation, either version 3 of the License, or
14;; (at your option) any later version.
318e2976
KS
15
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
b1fc2b50 22;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
318e2976
KS
23
24;;; Commentary:
25
26;; This package provides the grep facilities documented in the Emacs
27;; user's manual.
28
29;;; Code:
30
31(require 'compile)
32
d70b5ac5 33
318e2976 34(defgroup grep nil
d9c54a06 35 "Run `grep' and display the results."
318e2976
KS
36 :group 'tools
37 :group 'processes)
38
d4bbd646
CY
39(defvar grep-host-defaults-alist nil
40 "Default values depending on target host.
41`grep-compute-defaults' returns default values for every local or
42remote host `grep' runs. These values can differ from host to
43host. Once computed, the default values are kept here in order
44to avoid computing them again.")
45
46(defun grep-apply-setting (symbol value)
47 "Set SYMBOL to VALUE, and update `grep-host-defaults-alist'.
48SYMBOL should be one of `grep-command', `grep-template',
49`grep-use-null-device', `grep-find-command',
50`grep-find-template', `grep-find-use-xargs', or
51`grep-highlight-matches'."
52 (when grep-host-defaults-alist
53 (let* ((host-id
54 (intern (or (file-remote-p default-directory) "localhost")))
55 (host-defaults (assq host-id grep-host-defaults-alist))
56 (defaults (assq nil grep-host-defaults-alist)))
57 (setcar (cdr (assq symbol host-defaults)) value)
58 (setcar (cdr (assq symbol defaults)) value)))
59 (set-default symbol value))
318e2976
KS
60
61;;;###autoload
62(defcustom grep-window-height nil
fb7ada5f 63 "Number of lines in a grep window. If nil, use `compilation-window-height'."
318e2976
KS
64 :type '(choice (const :tag "Default" nil)
65 integer)
bf247b6e 66 :version "22.1"
318e2976
KS
67 :group 'grep)
68
00889cf9 69(defcustom grep-highlight-matches 'auto-detect
a601d313 70 "Use special markers to highlight grep matches.
277df088
JL
71
72Some grep programs are able to surround matches with special
73markers in grep output. Such markers can be used to highlight
a915d7a1
GM
74matches in grep mode. This requires `font-lock-mode' to be active
75in grep buffers, so if you have globally disabled font-lock-mode,
76you will not get highlighting.
277df088 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 105(defcustom grep-scroll-output nil
fb7ada5f 106 "Non-nil to scroll the *grep* buffer window as output appears.
318e2976
KS
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
333f9019 110than the beginning."
318e2976 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]"))
fb7ada5f 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
fb7ada5f 211 "List of names of sub-directories which `rgrep' shall not recurse into.
49405d0e
SS
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)))
fb7ada5f 223 "List of file names which `rgrep' and `lgrep' shall exclude.
7ae62430
JL
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 230(defcustom grep-error-screen-columns nil
fb7ada5f 231 "If non-nil, column numbers in grep hits are screen columns.
bb72b9d0
DP
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)
ce3cefcc 247 (define-key map " " 'scroll-up-command)
958614cf 248 (define-key map [?\S-\ ] 'scroll-down-command)
ce3cefcc 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))
7a1d7ba7
JL
412 ;; "filename-linenumber-" format is used for context lines in GNU grep,
413 ;; "filename=linenumber=" for lines with function names in "git grep -p".
414 ("^.+?[-=][0-9]+[-=].*\n" (0 grep-context-face)))
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")
3ab49e2c
LI
425 "The default find program.
426This is used by commands like `grep-find-command', `find-dired'
427and others.")
318e2976 428
1571d112 429;;;###autoload
6bdad9ae 430(defvar xargs-program (purecopy "xargs")
1571d112
JB
431 "The default xargs program for `grep-find-command'.
432See `grep-find-use-xargs'.
433This variable's value takes effect when `grep-compute-defaults' is called.")
434
9b19858e 435;;;###autoload
318e2976 436(defvar grep-find-use-xargs nil
f3ca7378
GM
437 "How to invoke find and grep.
438If `exec', use `find -exec {} ;'.
439If `exec-plus' use `find -exec {} +'.
7f3afa3d 440If `gnu', use `find -print0' and `xargs -0'.
f3ca7378 441Any other value means to use `find -print' and `xargs'.
318e2976
KS
442
443This variable's value takes effect when `grep-compute-defaults' is called.")
444
445;; History of grep commands.
9b19858e 446;;;###autoload
3adbe224 447(defvar grep-history nil "History list for grep.")
9b19858e 448;;;###autoload
3adbe224 449(defvar grep-find-history nil "History list for grep-find.")
318e2976 450
0acfb7ce
KS
451;; History of lgrep and rgrep regexp and files args.
452(defvar grep-regexp-history nil)
7ae62430 453(defvar grep-files-history nil)
0acfb7ce 454
9b19858e 455;;;###autoload
318e2976
KS
456(defun grep-process-setup ()
457 "Setup compilation variables and buffer for `grep'.
ab55f76f 458Set up `compilation-exit-message-function' and run `grep-setup-hook'."
a601d313 459 (when (eq grep-highlight-matches 'auto-detect)
a58c94a2 460 (grep-compute-defaults))
a601d313 461 (unless (or (eq grep-highlight-matches 'auto-detect)
90439906
JL
462 (null grep-highlight-matches)
463 ;; Don't output color escapes if they can't be
464 ;; highlighted with `font-lock-face' by `grep-filter'.
465 (null font-lock-mode))
6b8d0852
JL
466 ;; `setenv' modifies `process-environment' let-bound in `compilation-start'
467 ;; Any TERM except "dumb" allows GNU grep to use `--color=auto'
468 (setenv "TERM" "emacs-grep")
8f0e19af
EZ
469 (setenv "GREP_OPTIONS"
470 (concat (getenv "GREP_OPTIONS")
a601d313 471 " --color=" (if (eq grep-highlight-matches 'always)
8f0e19af 472 "always" "auto")))
6b8d0852 473 ;; GREP_COLOR is used in GNU grep 2.5.1, but deprecated in later versions
983203ee 474 (setenv "GREP_COLOR" "01;31")
6b8d0852 475 ;; GREP_COLORS is used in GNU grep 2.5.2 and later versions
f62bd846 476 (setenv "GREP_COLORS" "mt=01;31:fn=:ln=:bn=:se=:sl=:cx=:ne"))
318e2976
KS
477 (set (make-local-variable 'compilation-exit-message-function)
478 (lambda (status code msg)
479 (if (eq status 'exit)
262a1439
JL
480 ;; This relies on the fact that `compilation-start'
481 ;; sets buffer-modified to nil before running the command,
482 ;; so the buffer is still unmodified if there is no output.
483 (cond ((and (zerop code) (buffer-modified-p))
318e2976 484 '("finished (matches found)\n" . "matched"))
46c5cf66 485 ((not (buffer-modified-p))
318e2976
KS
486 '("finished with no matches found\n" . "no match"))
487 (t
488 (cons msg code)))
489 (cons msg code))))
318e2976
KS
490 (run-hooks 'grep-setup-hook))
491
d9c54a06
CY
492(defun grep-filter ()
493 "Handle match highlighting escape sequences inserted by the grep process.
494This function is called from `compilation-filter-hook'."
495 (save-excursion
c89be45f 496 (forward-line 0)
f62bd846 497 (let ((end (point)) beg)
d9c54a06 498 (goto-char compilation-filter-start)
c89be45f 499 (forward-line 0)
f62bd846 500 (setq beg (point))
c89be45f
SM
501 ;; Only operate on whole lines so we don't get caught with part of an
502 ;; escape sequence in one chunk and the rest in another.
503 (when (< (point) end)
504 (setq end (copy-marker end))
505 ;; Highlight grep matches and delete marking sequences.
f62bd846 506 (while (re-search-forward "\033\\[0?1;31m\\(.*?\\)\033\\[[0-9]*m" end 1)
c89be45f
SM
507 (replace-match (propertize (match-string 1)
508 'face nil 'font-lock-face grep-match-face)
509 t t))
510 ;; Delete all remaining escape sequences
f62bd846 511 (goto-char beg)
c89be45f
SM
512 (while (re-search-forward "\033\\[[0-9;]*[mK]" end 1)
513 (replace-match "" t t))))))
d9c54a06 514
0acfb7ce 515(defun grep-probe (command args &optional func result)
458c8d31
MA
516 (let (process-file-side-effects)
517 (equal (condition-case nil
518 (apply (or func 'process-file) command args)
519 (error nil))
520 (or result 0))))
0acfb7ce 521
9b19858e 522;;;###autoload
318e2976 523(defun grep-compute-defaults ()
2e2eead3
MA
524 ;; Keep default values.
525 (unless grep-host-defaults-alist
526 (add-to-list
527 'grep-host-defaults-alist
528 (cons nil
529 `((grep-command ,grep-command)
530 (grep-template ,grep-template)
531 (grep-use-null-device ,grep-use-null-device)
532 (grep-find-command ,grep-find-command)
533 (grep-find-template ,grep-find-template)
534 (grep-find-use-xargs ,grep-find-use-xargs)
535 (grep-highlight-matches ,grep-highlight-matches)))))
536 (let* ((host-id
85e7298f 537 (intern (or (file-remote-p default-directory) "localhost")))
2e2eead3
MA
538 (host-defaults (assq host-id grep-host-defaults-alist))
539 (defaults (assq nil grep-host-defaults-alist)))
05c7c7b1 540 ;; There are different defaults on different hosts. They must be
2e2eead3 541 ;; computed for every host once.
d4bbd646
CY
542 (dolist (setting '(grep-command grep-template
543 grep-use-null-device grep-find-command
544 grep-find-template grep-find-use-xargs
545 grep-highlight-matches))
546 (set setting
7860d2e3
CY
547 (cadr (or (assq setting host-defaults)
548 (assq setting defaults)))))
05c7c7b1
MA
549
550 (unless (or (not grep-use-null-device) (eq grep-use-null-device t))
551 (setq grep-use-null-device
552 (with-temp-buffer
553 (let ((hello-file (expand-file-name "HELLO" data-directory)))
554 (not
555 (and (if grep-command
556 ;; `grep-command' is already set, so
557 ;; use that for testing.
558 (grep-probe grep-command
559 `(nil t nil "^English" ,hello-file)
560 #'call-process-shell-command)
561 ;; otherwise use `grep-program'
562 (grep-probe grep-program
563 `(nil t nil "-nH" "^English" ,hello-file)))
564 (progn
565 (goto-char (point-min))
566 (looking-at
567 (concat (regexp-quote hello-file)
568 ":[0-9]+:English")))))))))
569 (unless (and grep-command grep-find-command
570 grep-template grep-find-template)
571 (let ((grep-options
572 (concat (if grep-use-null-device "-n" "-nH")
573 (if (grep-probe grep-program
574 `(nil nil nil "-e" "foo" ,null-device)
575 nil 1)
576 " -e"))))
577 (unless grep-command
578 (setq grep-command
579 (format "%s %s " grep-program grep-options)))
580 (unless grep-template
581 (setq grep-template
7ae62430 582 (format "%s <X> <C> %s <R> <F>" grep-program grep-options)))
05c7c7b1
MA
583 (unless grep-find-use-xargs
584 (setq grep-find-use-xargs
585 (cond
f3ca7378
GM
586 ((grep-probe find-program
587 `(nil nil nil ,null-device "-exec" "echo"
588 "{}" "+"))
589 'exec-plus)
05c7c7b1
MA
590 ((and
591 (grep-probe find-program `(nil nil nil ,null-device "-print0"))
6fccd6e8 592 (grep-probe xargs-program `(nil nil nil "-0" "echo")))
05c7c7b1
MA
593 'gnu)
594 (t
595 'exec))))
596 (unless grep-find-command
597 (setq grep-find-command
0acfb7ce 598 (cond ((eq grep-find-use-xargs 'gnu)
362b9d48
GM
599 ;; Windows shells need the program file name
600 ;; after the pipe symbol be quoted if they use
601 ;; forward slashes as directory separators.
6fccd6e8 602 (format "%s . -type f -print0 | \"%s\" -0 %s"
1571d112 603 find-program xargs-program grep-command))
f3ca7378 604 ((memq grep-find-use-xargs '(exec exec-plus))
05c7c7b1 605 (let ((cmd0 (format "%s . -type f -exec %s"
f3ca7378
GM
606 find-program grep-command))
607 (null (if grep-use-null-device
608 (format "%s " null-device)
609 "")))
05c7c7b1 610 (cons
f3ca7378
GM
611 (if (eq grep-find-use-xargs 'exec-plus)
612 (format "%s %s{} +" cmd0 null)
613 (format "%s {} %s%s" cmd0 null
614 (shell-quote-argument ";")))
05c7c7b1 615 (1+ (length cmd0)))))
84d797c9 616 (t
362b9d48 617 (format "%s . -type f -print | \"%s\" %s"
1571d112 618 find-program xargs-program grep-command)))))
05c7c7b1
MA
619 (unless grep-find-template
620 (setq grep-find-template
621 (let ((gcmd (format "%s <C> %s <R>"
f3ca7378
GM
622 grep-program grep-options))
623 (null (if grep-use-null-device
624 (format "%s " null-device)
625 "")))
05c7c7b1 626 (cond ((eq grep-find-use-xargs 'gnu)
6fccd6e8 627 (format "%s . <X> -type f <F> -print0 | \"%s\" -0 %s"
1571d112 628 find-program xargs-program gcmd))
05c7c7b1 629 ((eq grep-find-use-xargs 'exec)
f3ca7378
GM
630 (format "%s . <X> -type f <F> -exec %s {} %s%s"
631 find-program gcmd null
05c7c7b1 632 (shell-quote-argument ";")))
f3ca7378
GM
633 ((eq grep-find-use-xargs 'exec-plus)
634 (format "%s . <X> -type f <F> -exec %s %s{} +"
635 find-program gcmd null))
05c7c7b1 636 (t
362b9d48 637 (format "%s . <X> -type f <F> -print | \"%s\" %s"
1571d112 638 find-program xargs-program gcmd))))))))
a601d313 639 (when (eq grep-highlight-matches 'auto-detect)
05c7c7b1
MA
640 (setq grep-highlight-matches
641 (with-temp-buffer
642 (and (grep-probe grep-program '(nil t nil "--help"))
643 (progn
644 (goto-char (point-min))
645 (search-forward "--color" nil t))
a601d313
JL
646 ;; Windows and DOS pipes fail `isatty' detection in Grep.
647 (if (memq system-type '(windows-nt ms-dos))
648 'always 'auto)))))
05c7c7b1
MA
649
650 ;; Save defaults for this host.
6e3aa3f5
MA
651 (setq grep-host-defaults-alist
652 (delete (assq host-id grep-host-defaults-alist)
653 grep-host-defaults-alist))
654 (add-to-list
655 'grep-host-defaults-alist
656 (cons host-id
657 `((grep-command ,grep-command)
658 (grep-template ,grep-template)
659 (grep-use-null-device ,grep-use-null-device)
660 (grep-find-command ,grep-find-command)
661 (grep-find-template ,grep-find-template)
662 (grep-find-use-xargs ,grep-find-use-xargs)
663 (grep-highlight-matches ,grep-highlight-matches))))))
318e2976 664
7cb0d0ef
KS
665(defun grep-tag-default ()
666 (or (and transient-mark-mode mark-active
667 (/= (point) (mark))
668 (buffer-substring-no-properties (point) (mark)))
669 (funcall (or find-tag-default-function
670 (get major-mode 'find-tag-default-function)
671 'find-tag-default))
672 ""))
673
318e2976 674(defun grep-default-command ()
1571d112 675 "Compute the default grep command for \\[universal-argument] \\[grep] to offer."
7cb0d0ef 676 (let ((tag-default (shell-quote-argument (grep-tag-default)))
13eb1bde
RS
677 ;; This a regexp to match single shell arguments.
678 ;; Could someone please add comments explaining it?
318e2976
KS
679 (sh-arg-re "\\(\\(?:\"\\(?:[^\"]\\|\\\\\"\\)+\"\\|'[^']+'\\|[^\"' \t\n]\\)+\\)")
680 (grep-default (or (car grep-history) grep-command)))
13eb1bde 681 ;; In the default command, find the arg that specifies the pattern.
318e2976
KS
682 (when (or (string-match
683 (concat "[^ ]+\\s +\\(?:-[^ ]+\\s +\\)*"
684 sh-arg-re "\\(\\s +\\(\\S +\\)\\)?")
685 grep-default)
686 ;; If the string is not yet complete.
687 (string-match "\\(\\)\\'" grep-default))
13eb1bde
RS
688 ;; Maybe we will replace the pattern with the default tag.
689 ;; But first, maybe replace the file name pattern.
690 (condition-case nil
691 (unless (or (not (stringp buffer-file-name))
692 (when (match-beginning 2)
693 (save-match-data
694 (string-match
695 (wildcard-to-regexp
696 (file-name-nondirectory
697 (match-string 3 grep-default)))
698 (file-name-nondirectory buffer-file-name)))))
699 (setq grep-default (concat (substring grep-default
700 0 (match-beginning 2))
701 " *."
702 (file-name-extension buffer-file-name))))
703 ;; In case wildcard-to-regexp gets an error
704 ;; from invalid data.
705 (error nil))
706 ;; Now replace the pattern with the default tag.
02b73b97 707 (replace-match tag-default t t grep-default 1))))
318e2976 708
0acfb7ce 709
318e2976 710;;;###autoload
0acfb7ce
KS
711(define-compilation-mode grep-mode "Grep"
712 "Sets `grep-last-buffer' and `compilation-window-height'."
713 (setq grep-last-buffer (current-buffer))
38cd9f17 714 (set (make-local-variable 'tool-bar-map) grep-mode-tool-bar-map)
0acfb7ce
KS
715 (set (make-local-variable 'compilation-error-face)
716 grep-hit-face)
717 (set (make-local-variable 'compilation-error-regexp-alist)
718 grep-regexp-alist)
b7cf2c79
SM
719 ;; compilation-directory-matcher can't be nil, so we set it to a regexp that
720 ;; can never match.
79106a44 721 (set (make-local-variable 'compilation-directory-matcher) '("\\`a\\`"))
0acfb7ce
KS
722 (set (make-local-variable 'compilation-process-setup-function)
723 'grep-process-setup)
d9c54a06 724 (set (make-local-variable 'compilation-disable-input) t)
0527e251
JL
725 (set (make-local-variable 'compilation-error-screen-columns)
726 grep-error-screen-columns)
d9c54a06 727 (add-hook 'compilation-filter-hook 'grep-filter nil t))
0acfb7ce
KS
728
729
730;;;###autoload
731(defun grep (command-args)
318e2976
KS
732 "Run grep, with user-specified args, and collect output in a buffer.
733While grep runs asynchronously, you can use \\[next-error] (M-x next-error),
d02766ab
CY
734or \\<grep-mode-map>\\[compile-goto-error] in the *grep* \
735buffer, to go to the lines where grep found
736matches. To kill the grep job before it finishes, type \\[kill-compilation].
318e2976 737
215c3847 738For doing a recursive `grep', see the `rgrep' command. For running
9d4a3d39 739`grep' in a specific directory, see `lgrep'.
215c3847 740
1571d112
JB
741This command uses a special history list for its COMMAND-ARGS, so you
742can easily repeat a grep command.
318e2976
KS
743
744A prefix argument says to default the argument based upon the current
745tag the cursor is over, substituting it into the last grep command
1571d112
JB
746in the grep command history (or into `grep-command' if that history
747list is empty)."
318e2976
KS
748 (interactive
749 (progn
0acfb7ce 750 (grep-compute-defaults)
318e2976 751 (let ((default (grep-default-command)))
286d4b3b
SM
752 (list (read-shell-command "Run grep (like this): "
753 (if current-prefix-arg default grep-command)
754 'grep-history
755 (if current-prefix-arg nil default))))))
318e2976
KS
756
757 ;; Setting process-setup-function makes exit-message-function work
758 ;; even when async processes aren't supported.
19b3905f
JL
759 (compilation-start (if (and grep-use-null-device null-device)
760 (concat command-args " " null-device)
761 command-args)
0acfb7ce 762 'grep-mode))
ab55f76f 763
318e2976 764
318e2976
KS
765;;;###autoload
766(defun grep-find (command-args)
767 "Run grep via find, with user-specified args COMMAND-ARGS.
768Collect output in a buffer.
769While find runs asynchronously, you can use the \\[next-error] command
770to find the text that grep hits refer to.
771
772This command uses a special history list for its arguments, so you can
773easily repeat a find command."
774 (interactive
775 (progn
0acfb7ce 776 (grep-compute-defaults)
87f54c05 777 (if grep-find-command
286d4b3b
SM
778 (list (read-shell-command "Run find (like this): "
779 grep-find-command 'grep-find-history))
87f54c05
RS
780 ;; No default was set
781 (read-string
782 "compile.el: No `grep-find-command' command available. Press RET.")
783 (list nil))))
a34f7eb1 784 (when command-args
87f54c05
RS
785 (let ((null-device nil)) ; see grep
786 (grep command-args))))
318e2976 787
3d4d788a
DK
788;;;###autoload
789(defalias 'find-grep 'grep-find)
790
0acfb7ce
KS
791
792;; User-friendly interactive API.
793
794(defconst grep-expand-keywords
795 '(("<C>" . (and cf (isearch-no-upper-case-p regexp t) "-i"))
796 ("<D>" . dir)
797 ("<F>" . files)
798 ("<N>" . null-device)
799 ("<X>" . excl)
800 ("<R>" . (shell-quote-argument (or regexp ""))))
801 "List of substitutions performed by `grep-expand-template'.
802If car of an element matches, the cdr is evalled in to get the
803substitution string. Note dynamic scoping of variables.")
804
805(defun grep-expand-template (template &optional regexp files dir excl)
806 "Patch grep COMMAND string replacing <C>, <D>, <F>, <R>, and <X>."
f7993853
SM
807 (let* ((command template)
808 (env `((cf . ,case-fold-search)
809 (excl . ,excl)
810 (dir . ,dir)
811 (files . ,files)
812 (regexp . ,regexp)))
813 (case-fold-search nil))
0acfb7ce
KS
814 (dolist (kw grep-expand-keywords command)
815 (if (string-match (car kw) command)
d0afff34
KS
816 (setq command
817 (replace-match
818 (or (if (symbolp (cdr kw))
f7993853
SM
819 (eval (cdr kw) env)
820 (save-match-data (eval (cdr kw) env)))
d0afff34
KS
821 "")
822 t t command))))))
0acfb7ce
KS
823
824(defun grep-read-regexp ()
b2bf2a25 825 "Read regexp arg for interactive grep using `read-regexp'."
24da7273 826 (read-regexp "Search for" 'grep-tag-default 'grep-regexp-history))
0acfb7ce
KS
827
828(defun grep-read-files (regexp)
829 "Read files arg for interactive grep."
6830f449
JL
830 (let* ((bn (or (buffer-file-name)
831 (replace-regexp-in-string "<[0-9]+>\\'" "" (buffer-name))))
208cc91f
KS
832 (fn (and bn
833 (stringp bn)
834 (file-name-nondirectory bn)))
7ae62430
JL
835 (default-alias
836 (and fn
df9db151
JL
837 (let ((aliases (remove (assoc "all" grep-files-aliases)
838 grep-files-aliases))
7ae62430
JL
839 alias)
840 (while aliases
841 (setq alias (car aliases)
842 aliases (cdr aliases))
df9db151
JL
843 (if (string-match (mapconcat
844 'wildcard-to-regexp
845 (split-string (cdr alias) nil t)
846 "\\|")
847 fn)
7ae62430
JL
848 (setq aliases nil)
849 (setq alias nil)))
850 (cdr alias))))
851 (default-extension
852 (and fn
853 (let ((ext (file-name-extension fn)))
854 (and ext (concat "*." ext)))))
208cc91f 855 (default
7ae62430
JL
856 (or default-alias
857 default-extension
bcdf86fb
KS
858 (car grep-files-history)
859 (car (car grep-files-aliases))))
9136e895 860 (files (completing-read
0acfb7ce 861 (concat "Search for \"" regexp
208cc91f
KS
862 "\" in files"
863 (if default (concat " (default " default ")"))
864 ": ")
9136e895
JL
865 'read-file-name-internal
866 nil nil nil 'grep-files-history
7ae62430
JL
867 (delete-dups
868 (delq nil (append (list default default-alias default-extension)
869 (mapcar 'car grep-files-aliases)))))))
0acfb7ce
KS
870 (and files
871 (or (cdr (assoc files grep-files-aliases))
872 files))))
318e2976
KS
873
874;;;###autoload
32a2cf25 875(defun lgrep (regexp &optional files dir confirm)
9d4a3d39 876 "Run grep, searching for REGEXP in FILES in directory DIR.
318e2976 877The search is limited to file names matching shell pattern FILES.
0acfb7ce 878FILES may use abbreviations defined in `grep-files-aliases', e.g.
318e2976
KS
879entering `ch' is equivalent to `*.[ch]'.
880
6e7c574f
KS
881With \\[universal-argument] prefix, you can edit the constructed shell command line
882before it is executed.
883With two \\[universal-argument] prefixes, directly edit and run `grep-command'.
318e2976 884
0acfb7ce 885Collect output in a buffer. While grep runs asynchronously, you
1571d112
JB
886can use \\[next-error] (M-x next-error), or \\<grep-mode-map>\\[compile-goto-error] \
887in the grep output buffer,
888to go to the lines where grep found matches.
318e2976 889
0acfb7ce 890This command shares argument histories with \\[rgrep] and \\[grep]."
318e2976 891 (interactive
0acfb7ce
KS
892 (progn
893 (grep-compute-defaults)
894 (cond
895 ((and grep-command (equal current-prefix-arg '(16)))
896 (list (read-from-minibuffer "Run: " grep-command
32a2cf25 897 nil nil 'grep-history)))
0acfb7ce 898 ((not grep-template)
5a0c3f56 899 (error "grep.el: No `grep-template' available"))
0acfb7ce 900 (t (let* ((regexp (grep-read-regexp))
9d4a3d39
KS
901 (files (grep-read-files regexp))
902 (dir (read-directory-name "In directory: "
32a2cf25
JL
903 nil default-directory t))
904 (confirm (equal current-prefix-arg '(4))))
905 (list regexp files dir confirm))))))
0acfb7ce 906 (when (and (stringp regexp) (> (length regexp) 0))
b722ea4e 907 (unless (and dir (file-accessible-directory-p dir))
32a2cf25 908 (setq dir default-directory))
0acfb7ce
KS
909 (let ((command regexp))
910 (if (null files)
911 (if (string= command grep-command)
912 (setq command nil))
9d4a3d39 913 (setq dir (file-name-as-directory (expand-file-name dir)))
0acfb7ce
KS
914 (setq command (grep-expand-template
915 grep-template
916 regexp
7ae62430
JL
917 files
918 nil
919 (and grep-find-ignored-files
920 (concat " --exclude="
921 (mapconcat
922 #'(lambda (ignore)
923 (cond ((stringp ignore)
924 (shell-quote-argument ignore))
925 ((consp ignore)
926 (and (funcall (car ignore) dir)
927 (shell-quote-argument
928 (cdr ignore))))))
929 grep-find-ignored-files
930 " --exclude=")))))
0acfb7ce 931 (when command
32a2cf25 932 (if confirm
0acfb7ce
KS
933 (setq command
934 (read-from-minibuffer "Confirm: "
935 command nil nil 'grep-history))
c3e9438b 936 (add-to-history 'grep-history command))))
0acfb7ce 937 (when command
32a2cf25 938 (let ((default-directory dir))
9d4a3d39
KS
939 ;; Setting process-setup-function makes exit-message-function work
940 ;; even when async processes aren't supported.
941 (compilation-start (if (and grep-use-null-device null-device)
942 (concat command " " null-device)
c60c6d91
RS
943 command)
944 'grep-mode))
9d4a3d39
KS
945 (if (eq next-error-last-buffer (current-buffer))
946 (setq default-directory dir))))))
947
0acfb7ce 948
da205913 949(defvar find-name-arg) ; not autoloaded but defined in find-dired
0acfb7ce
KS
950
951;;;###autoload
32a2cf25 952(defun rgrep (regexp &optional files dir confirm)
6e7c574f 953 "Recursively grep for REGEXP in FILES in directory tree rooted at DIR.
0acfb7ce
KS
954The search is limited to file names matching shell pattern FILES.
955FILES may use abbreviations defined in `grep-files-aliases', e.g.
956entering `ch' is equivalent to `*.[ch]'.
957
6e7c574f
KS
958With \\[universal-argument] prefix, you can edit the constructed shell command line
959before it is executed.
960With two \\[universal-argument] prefixes, directly edit and run `grep-find-command'.
0acfb7ce 961
d02766ab
CY
962Collect output in a buffer. While the recursive grep is running,
963you can use \\[next-error] (M-x next-error), or \\<grep-mode-map>\\[compile-goto-error] \
1571d112 964in the grep output buffer,
d02766ab
CY
965to visit the lines where matches were found. To kill the job
966before it finishes, type \\[kill-compilation].
0acfb7ce 967
1c4757d6
JL
968This command shares argument histories with \\[lgrep] and \\[grep-find].
969
970When called programmatically and FILES is nil, REGEXP is expected
971to specify a command to run."
0acfb7ce
KS
972 (interactive
973 (progn
974 (grep-compute-defaults)
975 (cond
976 ((and grep-find-command (equal current-prefix-arg '(16)))
977 (list (read-from-minibuffer "Run: " grep-find-command
32a2cf25 978 nil nil 'grep-find-history)))
0acfb7ce 979 ((not grep-find-template)
5a0c3f56 980 (error "grep.el: No `grep-find-template' available"))
0acfb7ce
KS
981 (t (let* ((regexp (grep-read-regexp))
982 (files (grep-read-files regexp))
983 (dir (read-directory-name "Base directory: "
32a2cf25
JL
984 nil default-directory t))
985 (confirm (equal current-prefix-arg '(4))))
986 (list regexp files dir confirm))))))
0acfb7ce 987 (when (and (stringp regexp) (> (length regexp) 0))
1d75432d 988 (unless (and dir (file-accessible-directory-p dir))
32a2cf25 989 (setq dir default-directory))
0acfb7ce 990 (if (null files)
02b404de
JL
991 (if (not (string= regexp (if (consp grep-find-command)
992 (car grep-find-command)
993 grep-find-command)))
12b16734 994 (compilation-start regexp 'grep-mode))
c0b87c3f 995 (setq dir (file-name-as-directory (expand-file-name dir)))
da205913 996 (require 'find-dired) ; for `find-name-arg'
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)
4803595d 1005 (concat " -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
4803595d 1026 " -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
4803595d 1044 " -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 1060;;;###autoload
f7993853 1061(defun zrgrep (regexp &optional files dir confirm template)
a601d313
JL
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.
f7993853
SM
1096 (let ((grep-find-template template)
1097 (grep-highlight-matches 'always))
a601d313
JL
1098 (rgrep regexp files dir confirm)))
1099
1100;;;###autoload
1101(defalias 'rzgrep 'zrgrep)
318e2976
KS
1102
1103(provide 'grep)
1104
06626cf2 1105;;; grep.el ends here