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