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