Revert inadvertently commited changes in grep.el.
[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 248 (define-key map " " 'scroll-up-command)
958614cf 249 (define-key map [?\S-\ ] 'scroll-down-command)
ce3cefcc 250 (define-key map "\^?" 'scroll-down-command)
0443d889 251 (define-key map "\C-c\C-f" 'next-error-follow-minor-mode)
318e2976 252
318e2976
KS
253 (define-key map "\r" 'compile-goto-error) ;; ?
254 (define-key map "n" 'next-error-no-select)
255 (define-key map "p" 'previous-error-no-select)
256 (define-key map "{" 'compilation-previous-file)
257 (define-key map "}" 'compilation-next-file)
94c713b2
JL
258 (define-key map "\t" 'compilation-next-error)
259 (define-key map [backtab] 'compilation-previous-error)
318e2976
KS
260
261 ;; Set up the menu-bar
262 (define-key map [menu-bar grep]
263 (cons "Grep" (make-sparse-keymap "Grep")))
264
265 (define-key map [menu-bar grep compilation-kill-compilation]
38805987
DN
266 '(menu-item "Kill Grep" kill-compilation
267 :help "Kill the currently running grep process"))
268 (define-key map [menu-bar grep compilation-separator2] '("----"))
318e2976 269 (define-key map [menu-bar grep compilation-compile]
38805987
DN
270 '(menu-item "Compile..." compile
271 :help "Compile the program including the current buffer. Default: run `make'"))
5d91db30
JL
272 (define-key map [menu-bar grep compilation-rgrep]
273 '(menu-item "Recursive grep..." rgrep
274 :help "User-friendly recursive grep in directory tree"))
275 (define-key map [menu-bar grep compilation-lgrep]
276 '(menu-item "Local grep..." lgrep
277 :help "User-friendly grep in a directory"))
278 (define-key map [menu-bar grep compilation-grep-find]
279 '(menu-item "Grep via Find..." grep-find
280 :help "Run grep via find, with user-specified args"))
318e2976 281 (define-key map [menu-bar grep compilation-grep]
38805987
DN
282 '(menu-item "Another grep..." grep
283 :help "Run grep, with user-specified args, and collect output in a buffer."))
318e2976 284 (define-key map [menu-bar grep compilation-recompile]
38805987
DN
285 '(menu-item "Repeat grep" recompile
286 :help "Run grep again"))
287 (define-key map [menu-bar grep compilation-separator2] '("----"))
318e2976 288 (define-key map [menu-bar grep compilation-first-error]
38805987
DN
289 '(menu-item "First Match" first-error
290 :help "Restart at the first match, visit corresponding location"))
318e2976 291 (define-key map [menu-bar grep compilation-previous-error]
38805987
DN
292 '(menu-item "Previous Match" previous-error
293 :help "Visit the previous match and corresponding location"))
318e2976 294 (define-key map [menu-bar grep compilation-next-error]
38805987
DN
295 '(menu-item "Next Match" next-error
296 :help "Visit the next match and corresponding location"))
318e2976
KS
297 map)
298 "Keymap for grep buffers.
299`compilation-minor-mode-map' is a cdr of this.")
300
38cd9f17 301(defvar grep-mode-tool-bar-map
30b72491
CY
302 ;; When bootstrapping, tool-bar-map is not properly initialized yet,
303 ;; so don't do anything.
304 (when (keymapp (butlast tool-bar-map))
305 (let ((map (butlast (copy-keymap tool-bar-map)))
306 (help (last tool-bar-map))) ;; Keep Help last in tool bar
307 (tool-bar-local-item
308 "left-arrow" 'previous-error-no-select 'previous-error-no-select map
309 :rtl "right-arrow"
310 :help "Goto previous match")
311 (tool-bar-local-item
312 "right-arrow" 'next-error-no-select 'next-error-no-select map
313 :rtl "left-arrow"
314 :help "Goto next match")
315 (tool-bar-local-item
316 "cancel" 'kill-compilation 'kill-compilation map
317 :enable '(let ((buffer (compilation-find-buffer)))
318 (get-buffer-process buffer))
319 :help "Stop grep")
320 (tool-bar-local-item
321 "refresh" 'recompile 'recompile map
322 :help "Restart grep")
323 (append map help))))
38cd9f17 324
ab55f76f
SM
325(defalias 'kill-grep 'kill-compilation)
326
318e2976
KS
327;;;; TODO --- refine this!!
328
6b3a84f2
SM
329;; (defcustom grep-use-compilation-buffer t
330;; "When non-nil, grep specific commands update `compilation-last-buffer'.
331;; This means that standard compile commands like \\[next-error] and \\[compile-goto-error]
332;; can be used to navigate between grep matches (the default).
333;; Otherwise, the grep specific commands like \\[grep-next-match] must
334;; be used to navigate between grep matches."
335;; :type 'boolean
336;; :group 'grep)
318e2976
KS
337
338;; override compilation-last-buffer
339(defvar grep-last-buffer nil
340 "The most recent grep buffer.
f911a445 341A grep buffer becomes most recent when you select Grep mode in it.
318e2976 342Notice that using \\[next-error] or \\[compile-goto-error] modifies
da6062e6 343`compilation-last-buffer' rather than `grep-last-buffer'.")
318e2976 344
9b19858e 345;;;###autoload
2f7f4bee 346(defconst grep-regexp-alist
bfe5d7f0
JL
347 '(
348 ;; Rule to match column numbers is commented out since no known grep
349 ;; produces them
350 ;; ("^\\(.+?\\)\\(:[ \t]*\\)\\([1-9][0-9]*\\)\\2\\(?:\\([1-9][0-9]*\\)\\(?:-\\([1-9][0-9]*\\)\\)?\\2\\)?"
351 ;; 1 3 (4 . 5))
352 ;; Note that we want to use as tight a regexp as we can to try and
353 ;; handle weird file names (with colons in them) as well as possible.
354 ;; E.g. we use [1-9][0-9]* rather than [0-9]+ so as to accept ":034:"
355 ;; in file names.
356 ("^\\(.+?\\)\\(:[ \t]*\\)\\([1-9][0-9]*\\)\\2"
0527e251
JL
357 1 3
358 ;; Calculate column positions (col . end-col) of first grep match on a line
359 ((lambda ()
360 (when grep-highlight-matches
361 (let* ((beg (match-end 0))
362 (end (save-excursion (goto-char beg) (line-end-position)))
98da8c0f 363 (mbeg (text-property-any beg end 'font-lock-face grep-match-face)))
0527e251
JL
364 (when mbeg
365 (- mbeg beg)))))
366 .
367 (lambda ()
368 (when grep-highlight-matches
369 (let* ((beg (match-end 0))
370 (end (save-excursion (goto-char beg) (line-end-position)))
98da8c0f 371 (mbeg (text-property-any beg end 'font-lock-face grep-match-face))
0527e251
JL
372 (mend (and mbeg (next-single-property-change mbeg 'font-lock-face nil end))))
373 (when mend
374 (- mend beg)))))))
ba65be24 375 ("^Binary file \\(.+\\) matches$" 1 nil nil 0 1))
318e2976
KS
376 "Regexp used to match grep hits. See `compilation-error-regexp-alist'.")
377
5ddce96c
GM
378(defvar grep-first-column 0 ; bug#10594
379 "Value to use for `compilation-first-column' in grep buffers.")
380
ab55f76f
SM
381(defvar grep-error "grep hit"
382 "Message to print when no matches are found.")
383
384;; Reverse the colors because grep hits are not errors (though we jump there
385;; with `next-error'), and unreadable files can't be gone to.
386(defvar grep-hit-face compilation-info-face
387 "Face name to use for grep hits.")
388
7462142d 389(defvar grep-error-face 'compilation-error
ab55f76f
SM
390 "Face name to use for grep error messages.")
391
f30c45e1
JL
392(defvar grep-match-face 'match
393 "Face name to use for grep matches.")
394
35952129
JL
395(defvar grep-context-face 'shadow
396 "Face name to use for grep context lines.")
397
ab55f76f
SM
398(defvar grep-mode-font-lock-keywords
399 '(;; Command output lines.
ab55f76f
SM
400 (": \\(.+\\): \\(?:Permission denied\\|No such \\(?:file or directory\\|device or address\\)\\)$"
401 1 grep-error-face)
402 ;; remove match from grep-regexp-alist before fontifying
c2c04c28 403 ("^Grep[/a-zA-z]* started.*"
1a0a6cef 404 (0 '(face nil compilation-message nil help-echo nil mouse-face nil) t))
c2c04c28 405 ("^Grep[/a-zA-z]* finished \\(?:(\\(matches found\\))\\|with \\(no matches found\\)\\).*"
1a0a6cef 406 (0 '(face nil compilation-message nil help-echo nil mouse-face nil) t)
7462142d
JL
407 (1 compilation-info-face nil t)
408 (2 compilation-warning-face nil t))
c2c04c28 409 ("^Grep[/a-zA-z]* \\(exited abnormally\\|interrupt\\|killed\\|terminated\\)\\(?:.*with code \\([0-9]+\\)\\)?.*"
1a0a6cef 410 (0 '(face nil compilation-message nil help-echo nil mouse-face nil) t)
7462142d 411 (1 grep-error-face)
cf115520 412 (2 grep-error-face nil t))
7a1d7ba7
JL
413 ;; "filename-linenumber-" format is used for context lines in GNU grep,
414 ;; "filename=linenumber=" for lines with function names in "git grep -p".
415 ("^.+?[-=][0-9]+[-=].*\n" (0 grep-context-face)))
ab55f76f
SM
416 "Additional things to highlight in grep output.
417This gets tacked on the end of the generated expressions.")
418
9b19858e 419;;;###autoload
6bdad9ae 420(defvar grep-program (purecopy "grep")
318e2976
KS
421 "The default grep program for `grep-command' and `grep-find-command'.
422This variable's value takes effect when `grep-compute-defaults' is called.")
423
9b19858e 424;;;###autoload
6bdad9ae 425(defvar find-program (purecopy "find")
318e2976
KS
426 "The default find program for `grep-find-command'.
427This variable's value takes effect when `grep-compute-defaults' is called.")
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>."
807 (let ((command template)
808 (cf case-fold-search)
809 (case-fold-search nil))
810 (dolist (kw grep-expand-keywords command)
811 (if (string-match (car kw) command)
d0afff34
KS
812 (setq command
813 (replace-match
814 (or (if (symbolp (cdr kw))
41a2f8ba 815 (symbol-value (cdr kw))
d0afff34
KS
816 (save-match-data (eval (cdr kw))))
817 "")
818 t t command))))))
0acfb7ce
KS
819
820(defun grep-read-regexp ()
821 "Read regexp arg for interactive grep."
7cb0d0ef 822 (let ((default (grep-tag-default)))
eb2deaff 823 (read-regexp
0acfb7ce
KS
824 (concat "Search for"
825 (if (and default (> (length default) 0))
7cb0d0ef 826 (format " (default \"%s\"): " default) ": "))
eb2deaff 827 default 'grep-regexp-history)))
0acfb7ce
KS
828
829(defun grep-read-files (regexp)
830 "Read files arg for interactive grep."
6830f449
JL
831 (let* ((bn (or (buffer-file-name)
832 (replace-regexp-in-string "<[0-9]+>\\'" "" (buffer-name))))
208cc91f
KS
833 (fn (and bn
834 (stringp bn)
835 (file-name-nondirectory bn)))
7ae62430
JL
836 (default-alias
837 (and fn
df9db151
JL
838 (let ((aliases (remove (assoc "all" grep-files-aliases)
839 grep-files-aliases))
7ae62430
JL
840 alias)
841 (while aliases
842 (setq alias (car aliases)
843 aliases (cdr aliases))
df9db151
JL
844 (if (string-match (mapconcat
845 'wildcard-to-regexp
846 (split-string (cdr alias) nil t)
847 "\\|")
848 fn)
7ae62430
JL
849 (setq aliases nil)
850 (setq alias nil)))
851 (cdr alias))))
852 (default-extension
853 (and fn
854 (let ((ext (file-name-extension fn)))
855 (and ext (concat "*." ext)))))
208cc91f 856 (default
7ae62430
JL
857 (or default-alias
858 default-extension
bcdf86fb
KS
859 (car grep-files-history)
860 (car (car grep-files-aliases))))
9136e895 861 (files (completing-read
0acfb7ce 862 (concat "Search for \"" regexp
208cc91f
KS
863 "\" in files"
864 (if default (concat " (default " default ")"))
865 ": ")
9136e895
JL
866 'read-file-name-internal
867 nil nil nil 'grep-files-history
7ae62430
JL
868 (delete-dups
869 (delq nil (append (list default default-alias default-extension)
870 (mapcar 'car grep-files-aliases)))))))
0acfb7ce
KS
871 (and files
872 (or (cdr (assoc files grep-files-aliases))
873 files))))
318e2976
KS
874
875;;;###autoload
32a2cf25 876(defun lgrep (regexp &optional files dir confirm)
9d4a3d39 877 "Run grep, searching for REGEXP in FILES in directory DIR.
318e2976 878The search is limited to file names matching shell pattern FILES.
0acfb7ce 879FILES may use abbreviations defined in `grep-files-aliases', e.g.
318e2976
KS
880entering `ch' is equivalent to `*.[ch]'.
881
6e7c574f
KS
882With \\[universal-argument] prefix, you can edit the constructed shell command line
883before it is executed.
884With two \\[universal-argument] prefixes, directly edit and run `grep-command'.
318e2976 885
0acfb7ce 886Collect output in a buffer. While grep runs asynchronously, you
1571d112
JB
887can use \\[next-error] (M-x next-error), or \\<grep-mode-map>\\[compile-goto-error] \
888in the grep output buffer,
889to go to the lines where grep found matches.
318e2976 890
0acfb7ce 891This command shares argument histories with \\[rgrep] and \\[grep]."
318e2976 892 (interactive
0acfb7ce
KS
893 (progn
894 (grep-compute-defaults)
895 (cond
896 ((and grep-command (equal current-prefix-arg '(16)))
897 (list (read-from-minibuffer "Run: " grep-command
32a2cf25 898 nil nil 'grep-history)))
0acfb7ce 899 ((not grep-template)
5a0c3f56 900 (error "grep.el: No `grep-template' available"))
0acfb7ce 901 (t (let* ((regexp (grep-read-regexp))
9d4a3d39
KS
902 (files (grep-read-files regexp))
903 (dir (read-directory-name "In directory: "
32a2cf25
JL
904 nil default-directory t))
905 (confirm (equal current-prefix-arg '(4))))
906 (list regexp files dir confirm))))))
0acfb7ce 907 (when (and (stringp regexp) (> (length regexp) 0))
32a2cf25
JL
908 (unless (and dir (file-directory-p dir) (file-readable-p dir))
909 (setq dir default-directory))
0acfb7ce
KS
910 (let ((command regexp))
911 (if (null files)
912 (if (string= command grep-command)
913 (setq command nil))
9d4a3d39 914 (setq dir (file-name-as-directory (expand-file-name dir)))
0acfb7ce
KS
915 (setq command (grep-expand-template
916 grep-template
917 regexp
7ae62430
JL
918 files
919 nil
920 (and grep-find-ignored-files
921 (concat " --exclude="
922 (mapconcat
923 #'(lambda (ignore)
924 (cond ((stringp ignore)
925 (shell-quote-argument ignore))
926 ((consp ignore)
927 (and (funcall (car ignore) dir)
928 (shell-quote-argument
929 (cdr ignore))))))
930 grep-find-ignored-files
931 " --exclude=")))))
0acfb7ce 932 (when command
32a2cf25 933 (if confirm
0acfb7ce
KS
934 (setq command
935 (read-from-minibuffer "Confirm: "
936 command nil nil 'grep-history))
c3e9438b 937 (add-to-history 'grep-history command))))
0acfb7ce 938 (when command
32a2cf25 939 (let ((default-directory dir))
9d4a3d39
KS
940 ;; Setting process-setup-function makes exit-message-function work
941 ;; even when async processes aren't supported.
942 (compilation-start (if (and grep-use-null-device null-device)
943 (concat command " " null-device)
c60c6d91
RS
944 command)
945 'grep-mode))
9d4a3d39
KS
946 (if (eq next-error-last-buffer (current-buffer))
947 (setq default-directory dir))))))
948
0acfb7ce 949
da205913 950(defvar find-name-arg) ; not autoloaded but defined in find-dired
0acfb7ce
KS
951
952;;;###autoload
32a2cf25 953(defun rgrep (regexp &optional files dir confirm)
6e7c574f 954 "Recursively grep for REGEXP in FILES in directory tree rooted at DIR.
0acfb7ce
KS
955The search is limited to file names matching shell pattern FILES.
956FILES may use abbreviations defined in `grep-files-aliases', e.g.
957entering `ch' is equivalent to `*.[ch]'.
958
6e7c574f
KS
959With \\[universal-argument] prefix, you can edit the constructed shell command line
960before it is executed.
961With two \\[universal-argument] prefixes, directly edit and run `grep-find-command'.
0acfb7ce 962
d02766ab
CY
963Collect output in a buffer. While the recursive grep is running,
964you can use \\[next-error] (M-x next-error), or \\<grep-mode-map>\\[compile-goto-error] \
1571d112 965in the grep output buffer,
d02766ab
CY
966to visit the lines where matches were found. To kill the job
967before it finishes, type \\[kill-compilation].
0acfb7ce 968
1c4757d6
JL
969This command shares argument histories with \\[lgrep] and \\[grep-find].
970
971When called programmatically and FILES is nil, REGEXP is expected
972to specify a command to run."
0acfb7ce
KS
973 (interactive
974 (progn
975 (grep-compute-defaults)
976 (cond
977 ((and grep-find-command (equal current-prefix-arg '(16)))
978 (list (read-from-minibuffer "Run: " grep-find-command
32a2cf25 979 nil nil 'grep-find-history)))
0acfb7ce 980 ((not grep-find-template)
5a0c3f56 981 (error "grep.el: No `grep-find-template' available"))
0acfb7ce
KS
982 (t (let* ((regexp (grep-read-regexp))
983 (files (grep-read-files regexp))
984 (dir (read-directory-name "Base directory: "
32a2cf25
JL
985 nil default-directory t))
986 (confirm (equal current-prefix-arg '(4))))
987 (list regexp files dir confirm))))))
0acfb7ce 988 (when (and (stringp regexp) (> (length regexp) 0))
32a2cf25
JL
989 (unless (and dir (file-directory-p dir) (file-readable-p dir))
990 (setq dir default-directory))
0acfb7ce 991 (if (null files)
02b404de
JL
992 (if (not (string= regexp (if (consp grep-find-command)
993 (car grep-find-command)
994 grep-find-command)))
12b16734 995 (compilation-start regexp 'grep-mode))
c0b87c3f 996 (setq dir (file-name-as-directory (expand-file-name dir)))
da205913 997 (require 'find-dired) ; for `find-name-arg'
91b982a0
MA
998 ;; In Tramp, there could be problems if the command line is too
999 ;; long. We escape it, therefore.
c0b87c3f
KS
1000 (let ((command (grep-expand-template
1001 grep-find-template
1002 regexp
84d797c9 1003 (concat (shell-quote-argument "(")
34fac407 1004 " " find-name-arg " "
91b982a0
MA
1005 (mapconcat
1006 #'shell-quote-argument
1007 (split-string files)
a59d76e0 1008 (concat "\\\n" " -o " find-name-arg " "))
84d797c9
KS
1009 " "
1010 (shell-quote-argument ")"))
7ae62430
JL
1011 dir
1012 (concat
0acfb7ce 1013 (and grep-find-ignored-directories
453de99f
OG
1014 (concat "-type d "
1015 (shell-quote-argument "(")
84d797c9
KS
1016 ;; we should use shell-quote-argument here
1017 " -path "
49405d0e 1018 (mapconcat
7ae62430
JL
1019 #'(lambda (ignore)
1020 (cond ((stringp ignore)
1021 (shell-quote-argument
1022 (concat "*/" ignore)))
1023 ((consp ignore)
1024 (and (funcall (car ignore) dir)
1025 (shell-quote-argument
1026 (concat "*/"
1027 (cdr ignore)))))))
1028 grep-find-ignored-directories
a59d76e0 1029 "\\\n -o -path ")
7ae62430
JL
1030 " "
1031 (shell-quote-argument ")")
1032 " -prune -o "))
1033 (and grep-find-ignored-files
49a2697c
WJ
1034 (concat (shell-quote-argument "!") " -type d "
1035 (shell-quote-argument "(")
7ae62430
JL
1036 ;; we should use shell-quote-argument here
1037 " -name "
1038 (mapconcat
1039 #'(lambda (ignore)
1040 (cond ((stringp ignore)
1041 (shell-quote-argument ignore))
1042 ((consp ignore)
1043 (and (funcall (car ignore) dir)
1044 (shell-quote-argument
1045 (cdr ignore))))))
1046 grep-find-ignored-files
a59d76e0 1047 "\\\n -o -name ")
84d797c9
KS
1048 " "
1049 (shell-quote-argument ")")
7ae62430 1050 " -prune -o "))))))
0acfb7ce 1051 (when command
32a2cf25 1052 (if confirm
0acfb7ce
KS
1053 (setq command
1054 (read-from-minibuffer "Confirm: "
1055 command nil nil 'grep-find-history))
c3e9438b 1056 (add-to-history 'grep-find-history command))
ac8cf6e6 1057 (let ((default-directory dir))
c0b87c3f
KS
1058 (compilation-start command 'grep-mode))
1059 ;; Set default-directory if we started rgrep in the *grep* buffer.
1060 (if (eq next-error-last-buffer (current-buffer))
1061 (setq default-directory dir)))))))
318e2976 1062
a601d313
JL
1063;;;###autoload
1064(defun zrgrep (regexp &optional files dir confirm grep-find-template)
1065 "Recursively grep for REGEXP in gzipped FILES in tree rooted at DIR.
1066Like `rgrep' but uses `zgrep' for `grep-program', sets the default
1067file name to `*.gz', and sets `grep-highlight-matches' to `always'."
1068 (interactive
ad6fc8f4
JL
1069 (progn
1070 ;; Compute standard default values.
a601d313 1071 (grep-compute-defaults)
ad6fc8f4
JL
1072 ;; Compute the default zrgrep command by running `grep-compute-defaults'
1073 ;; for grep program "zgrep", but not changing global values.
1074 (let ((grep-program "zgrep")
1075 ;; Don't change global values for variables computed
1076 ;; by `grep-compute-defaults'.
1077 (grep-find-template nil)
1078 (grep-find-command nil)
1079 (grep-host-defaults-alist nil)
1080 ;; Use for `grep-read-files'
1081 (grep-files-aliases '(("all" . "* .*")
1082 ("gz" . "*.gz"))))
1083 ;; Recompute defaults using let-bound values above.
1084 (grep-compute-defaults)
1085 (cond
1086 ((and grep-find-command (equal current-prefix-arg '(16)))
1087 (list (read-from-minibuffer "Run: " grep-find-command
1088 nil nil 'grep-find-history)))
1089 ((not grep-find-template)
1090 (error "grep.el: No `grep-find-template' available"))
1091 (t (let* ((regexp (grep-read-regexp))
1092 (files (grep-read-files regexp))
1093 (dir (read-directory-name "Base directory: "
1094 nil default-directory t))
1095 (confirm (equal current-prefix-arg '(4))))
1096 (list regexp files dir confirm grep-find-template)))))))
a601d313
JL
1097 ;; Set `grep-highlight-matches' to `always'
1098 ;; since `zgrep' puts filters in the grep output.
1099 (let ((grep-highlight-matches 'always))
1100 ;; `rgrep' uses the dynamically bound value `grep-find-template'
1101 ;; from the argument `grep-find-template' whose value is computed
1102 ;; in the `interactive' spec.
1103 (rgrep regexp files dir confirm)))
1104
1105;;;###autoload
1106(defalias 'rzgrep 'zrgrep)
318e2976
KS
1107
1108(provide 'grep)
1109
06626cf2 1110;;; grep.el ends here