;;; grep.el --- run Grep as inferior of Emacs, parse match messages
-;; Copyright (C) 1985, 1986, 1987, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
-;; 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
+;; Copyright (C) 1985-1987, 1993-1999, 2001-2011
;; Free Software Foundation, Inc.
;; Author: Roland McGrath <roland@gnu.org>
:group 'grep)
(defcustom grep-highlight-matches 'auto-detect
- "If t, use special markers to highlight grep matches.
+ "Use special markers to highlight grep matches.
Some grep programs are able to surround matches with special
markers in grep output. Such markers can be used to highlight
-matches in grep mode.
+matches in grep mode. This requires `font-lock-mode' to be active
+in grep buffers, so if you have globally disabled font-lock-mode,
+you will not get highlighting.
-This option sets the environment variable GREP_COLOR to specify
+This option sets the environment variable GREP_COLORS to specify
markers for highlighting and GREP_OPTIONS to add the --color
option in front of any explicit grep options before starting
the grep.
+When this option is `auto', grep uses `--color=auto' to highlight
+matches only when it outputs to a terminal (when `grep' is the last
+command in the pipe), thus avoiding the use of any potentially-harmful
+escape sequences when standard output goes to a file or pipe.
+
+To make grep highlight matches even into a pipe, you need the option
+`always' that forces grep to use `--color=always' to unconditionally
+output escape sequences.
+
In interactive usage, the actual value of this variable is set up
-by `grep-compute-defaults'; to change the default value, use
-Customize or call the function `grep-apply-setting'."
+by `grep-compute-defaults' when the default value is `auto-detect'.
+To change the default value, use Customize or call the function
+`grep-apply-setting'."
:type '(choice (const :tag "Do not highlight matches with grep markers" nil)
(const :tag "Highlight matches with grep markers" t)
+ (const :tag "Use --color=always" always)
+ (const :tag "Use --color=auto" auto)
(other :tag "Not Set" auto-detect))
:set 'grep-apply-setting
:version "22.1"
The following place holders should be present in the string:
<C> - place to put -i if case insensitive grep.
<F> - file names and wildcards to search.
+ <X> - file names and wildcards to exclude.
<R> - the regular expression searched for.
<N> - place to insert null-device.
:group 'grep)
(defcustom grep-files-aliases
- '(("asm" . "*.[sS]")
+ '(("all" . "* .*")
+ ("el" . "*.el")
+ ("ch" . "*.[ch]")
("c" . "*.c")
("cc" . "*.cc *.cxx *.cpp *.C *.CC *.c++")
- ("cchh" . "*.cc *.[ch]xx *.[ch]pp *.[CHh] *.CC *.HH *.[ch]++")
+ ("cchh" . "*.cc *.[ch]xx *.[ch]pp *.[CHh] *.CC *.HH *.[ch]++")
("hh" . "*.hxx *.hpp *.[Hh] *.HH *.h++")
- ("ch" . "*.[ch]")
- ("el" . "*.el")
("h" . "*.h")
- ("l" . "[Cc]hange[Ll]og*")
+ ("l" . "[Cc]hange[Ll]og*")
("m" . "[Mm]akefile*")
- ("tex" . "*.tex")
- ("texi" . "*.texi"))
+ ("tex" . "*.tex")
+ ("texi" . "*.texi")
+ ("asm" . "*.[sS]"))
"*Alist of aliases for the FILES argument to `lgrep' and `rgrep'."
:type 'alist
:group 'grep)
"*List of names of sub-directories which `rgrep' shall not recurse into.
If an element is a cons cell, the car is called on the search directory
to determine whether cdr should not be recursed into."
- :type '(repeat string)
+ :type '(choice (repeat :tag "Ignored directories" string)
+ (const :tag "No ignored directories" nil))
+ :group 'grep)
+
+(defcustom grep-find-ignored-files
+ (cons ".#*" (delq nil (mapcar (lambda (s)
+ (unless (string-match-p "/\\'" s)
+ (concat "*" s)))
+ completion-ignored-extensions)))
+ "*List of file names which `rgrep' and `lgrep' shall exclude.
+If an element is a cons cell, the car is called on the search directory
+to determine whether cdr should not be excluded."
+ :type '(choice (repeat :tag "Ignored file" string)
+ (const :tag "No ignored files" nil))
:group 'grep)
(defcustom grep-error-screen-columns nil
`complation-last-buffer' rather than `grep-last-buffer'.")
;;;###autoload
-(defvar grep-regexp-alist
- '(("^\\(.+?\\)\\(:[ \t]*\\)\\([0-9]+\\)\\2"
+(defconst grep-regexp-alist
+ '(("^\\(.+?\\)\\(:[ \t]*\\)\\([1-9][0-9]*\\)\\2"
1 3)
;; Rule to match column numbers is commented out since no known grep
;; produces them
;; ("^\\(.+?\\)\\(:[ \t]*\\)\\([0-9]+\\)\\2\\(?:\\([0-9]+\\)\\(?:-\\([0-9]+\\)\\)?\\2\\)?"
;; 1 3 (4 . 5))
- ("^\\(\\(.+?\\):\\([0-9]+\\):\\).*?\
+ ;; Note that we want to use as tight a regexp as we can to try and
+ ;; handle weird file names (with colons in them) as well as possible.
+ ;; E.g. we use [1-9][0-9]* rather than [0-9]+ so as to accept ":034:" in
+ ;; file names.
+ ("^\\(\\(.+?\\):\\([1-9][0-9]*\\):\\).*?\
\\(\033\\[01;31m\\(?:\033\\[K\\)?\\)\\(.*?\\)\\(\033\\[[0-9]*m\\)"
2 3
;; Calculate column positions (beg . end) of first grep match on a line
(- (match-beginning 4) (match-end 1)))
.
(lambda () (- (match-end 5) (match-end 1)
- (- (match-end 4) (match-beginning 4)))))
+ (- (match-end 4) (match-beginning 4)))))
nil 1)
("^Binary file \\(.+\\) matches$" 1 nil nil 0 1))
"Regexp used to match grep hits. See `compilation-error-regexp-alist'.")
(defvar grep-mode-font-lock-keywords
'(;; Command output lines.
- ("^\\([A-Za-z_0-9/\.+-]+\\)[ \t]*:" 1 font-lock-function-name-face)
(": \\(.+\\): \\(?:Permission denied\\|No such \\(?:file or directory\\|device or address\\)\\)$"
1 grep-error-face)
;; remove match from grep-regexp-alist before fontifying
("^Grep[/a-zA-z]* started.*"
- (0 '(face nil message nil help-echo nil mouse-face nil) t))
+ (0 '(face nil compilation-message nil help-echo nil mouse-face nil) t))
("^Grep[/a-zA-z]* finished \\(?:(\\(matches found\\))\\|with \\(no matches found\\)\\).*"
- (0 '(face nil message nil help-echo nil mouse-face nil) t)
+ (0 '(face nil compilation-message nil help-echo nil mouse-face nil) t)
(1 compilation-info-face nil t)
(2 compilation-warning-face nil t))
("^Grep[/a-zA-z]* \\(exited abnormally\\|interrupt\\|killed\\|terminated\\)\\(?:.*with code \\([0-9]+\\)\\)?.*"
- (0 '(face nil message nil help-echo nil mouse-face nil) t)
+ (0 '(face nil compilation-message nil help-echo nil mouse-face nil) t)
(1 grep-error-face)
(2 grep-error-face nil t))
("^.+?-[0-9]+-.*\n" (0 grep-context-face))
- ;; Highlight grep matches and delete markers
+ ;; Highlight grep matches and delete markers.
+ ;; FIXME: Modifying the buffer text from font-lock is a bad idea!
("\\(\033\\[01;31m\\)\\(.*?\\)\\(\033\\[[0-9]*m\\)"
;; Refontification does not work after the markers have been
;; deleted. So we use the font-lock-face property here as Font
(progn
;; Delete markers with `replace-match' because it updates
;; the match-data, whereas `delete-region' would render it obsolete.
+ (syntax-ppss-flush-cache (match-beginning 0))
(replace-match "" t t nil 3)
(replace-match "" t t nil 1))))
- ("\\(\033\\[[0-9;]*[mK]\\)"
+ ("\033\\[[0-9;]*[mK]"
;; Delete all remaining escape sequences
((lambda (bound))
- (replace-match "" t t nil 1))))
+ (syntax-ppss-flush-cache (match-beginning 0))
+ (replace-match "" t t))))
"Additional things to highlight in grep output.
This gets tacked on the end of the generated expressions.")
;;;###autoload
-(defvar grep-program "grep"
+(defvar grep-program (purecopy "grep")
"The default grep program for `grep-command' and `grep-find-command'.
This variable's value takes effect when `grep-compute-defaults' is called.")
;;;###autoload
-(defvar find-program "find"
+(defvar find-program (purecopy "find")
"The default find program for `grep-find-command'.
This variable's value takes effect when `grep-compute-defaults' is called.")
;;;###autoload
-(defvar xargs-program "xargs"
+(defvar xargs-program (purecopy "xargs")
"The default xargs program for `grep-find-command'.
See `grep-find-use-xargs'.
This variable's value takes effect when `grep-compute-defaults' is called.")
;; History of lgrep and rgrep regexp and files args.
(defvar grep-regexp-history nil)
-(defvar grep-files-history '("ch" "el"))
+(defvar grep-files-history nil)
;;;###autoload
(defun grep-process-setup ()
"Setup compilation variables and buffer for `grep'.
Set up `compilation-exit-message-function' and run `grep-setup-hook'."
- (unless (or (not grep-highlight-matches) (eq grep-highlight-matches t))
+ (when (eq grep-highlight-matches 'auto-detect)
(grep-compute-defaults))
- (when (eq grep-highlight-matches t)
+ (unless (or (eq grep-highlight-matches 'auto-detect)
+ ;; Uses font-lock to parse color escapes. (Bug#8084)
+ (null font-lock-mode)
+ (null grep-highlight-matches))
;; `setenv' modifies `process-environment' let-bound in `compilation-start'
;; Any TERM except "dumb" allows GNU grep to use `--color=auto'
(setenv "TERM" "emacs-grep")
- ;; `--color=auto' emits escape sequences on a tty rather than on a pipe,
- ;; thus allowing to use multiple grep filters on the command line
- ;; and to output escape sequences only on the final grep output
(setenv "GREP_OPTIONS"
(concat (getenv "GREP_OPTIONS")
- ;; Windows and DOS pipes fail `isatty' detection in Grep.
- " --color=" (if (memq system-type '(windows-nt ms-dos))
+ " --color=" (if (eq grep-highlight-matches 'always)
"always" "auto")))
;; GREP_COLOR is used in GNU grep 2.5.1, but deprecated in later versions
(setenv "GREP_COLOR" "01;31")
grep-find-template grep-find-use-xargs
grep-highlight-matches))
(set setting
- (or (cadr (assq setting host-defaults))
- (cadr (assq setting defaults)))))
+ (cadr (or (assq setting host-defaults)
+ (assq setting defaults)))))
(unless (or (not grep-use-null-device) (eq grep-use-null-device t))
(setq grep-use-null-device
(format "%s %s " grep-program grep-options)))
(unless grep-template
(setq grep-template
- (format "%s <C> %s <R> <F>" grep-program grep-options)))
+ (format "%s <X> <C> %s <R> <F>" grep-program grep-options)))
(unless grep-find-use-xargs
(setq grep-find-use-xargs
(cond
(unless grep-find-command
(setq grep-find-command
(cond ((eq grep-find-use-xargs 'gnu)
- (format "%s . -type f -print0 | %s -0 -e %s"
+ ;; Windows shells need the program file name
+ ;; after the pipe symbol be quoted if they use
+ ;; forward slashes as directory separators.
+ (format "%s . -type f -print0 | \"%s\" -0 -e %s"
find-program xargs-program grep-command))
((eq grep-find-use-xargs 'exec)
(let ((cmd0 (format "%s . -type f -exec %s"
(shell-quote-argument ";"))
(1+ (length cmd0)))))
(t
- (format "%s . -type f -print | %s %s"
+ (format "%s . -type f -print | \"%s\" %s"
find-program xargs-program grep-command)))))
(unless grep-find-template
(setq grep-find-template
(let ((gcmd (format "%s <C> %s <R>"
grep-program grep-options)))
(cond ((eq grep-find-use-xargs 'gnu)
- (format "%s . <X> -type f <F> -print0 | %s -0 -e %s"
+ (format "%s . <X> -type f <F> -print0 | \"%s\" -0 -e %s"
find-program xargs-program gcmd))
((eq grep-find-use-xargs 'exec)
(format "%s . <X> -type f <F> -exec %s {} %s %s"
find-program gcmd null-device
(shell-quote-argument ";")))
(t
- (format "%s . <X> -type f <F> -print | %s %s"
+ (format "%s . <X> -type f <F> -print | \"%s\" %s"
find-program xargs-program gcmd))))))))
- (unless (or (not grep-highlight-matches) (eq grep-highlight-matches t))
+ (when (eq grep-highlight-matches 'auto-detect)
(setq grep-highlight-matches
(with-temp-buffer
(and (grep-probe grep-program '(nil t nil "--help"))
(progn
(goto-char (point-min))
(search-forward "--color" nil t))
- t))))
+ ;; Windows and DOS pipes fail `isatty' detection in Grep.
+ (if (memq system-type '(windows-nt ms-dos))
+ 'always 'auto)))))
;; Save defaults for this host.
(setq grep-host-defaults-alist
(defun grep-read-files (regexp)
"Read files arg for interactive grep."
- (let* ((bn (or (buffer-file-name) (buffer-name)))
+ (let* ((bn (or (buffer-file-name)
+ (replace-regexp-in-string "<[0-9]+>\\'" "" (buffer-name))))
(fn (and bn
(stringp bn)
(file-name-nondirectory bn)))
+ (default-alias
+ (and fn
+ (let ((aliases (remove (assoc "all" grep-files-aliases)
+ grep-files-aliases))
+ alias)
+ (while aliases
+ (setq alias (car aliases)
+ aliases (cdr aliases))
+ (if (string-match (mapconcat
+ 'wildcard-to-regexp
+ (split-string (cdr alias) nil t)
+ "\\|")
+ fn)
+ (setq aliases nil)
+ (setq alias nil)))
+ (cdr alias))))
+ (default-extension
+ (and fn
+ (let ((ext (file-name-extension fn)))
+ (and ext (concat "*." ext)))))
(default
- (or (and fn
- (let ((aliases grep-files-aliases)
- alias)
- (while aliases
- (setq alias (car aliases)
- aliases (cdr aliases))
- (if (string-match (wildcard-to-regexp (cdr alias)) fn)
- (setq aliases nil)
- (setq alias nil)))
- (cdr alias)))
- (and fn
- (let ((ext (file-name-extension fn)))
- (and ext (concat "*." ext))))
+ (or default-alias
+ default-extension
(car grep-files-history)
(car (car grep-files-aliases))))
- (files (read-string
+ (files (completing-read
(concat "Search for \"" regexp
"\" in files"
(if default (concat " (default " default ")"))
": ")
- nil 'grep-files-history default)))
+ 'read-file-name-internal
+ nil nil nil 'grep-files-history
+ (delete-dups
+ (delq nil (append (list default default-alias default-extension)
+ (mapcar 'car grep-files-aliases)))))))
(and files
(or (cdr (assoc files grep-files-aliases))
files))))
(list (read-from-minibuffer "Run: " grep-command
nil nil 'grep-history)))
((not grep-template)
- (error "grep.el: No `grep-template' available."))
+ (error "grep.el: No `grep-template' available"))
(t (let* ((regexp (grep-read-regexp))
(files (grep-read-files regexp))
(dir (read-directory-name "In directory: "
(setq command (grep-expand-template
grep-template
regexp
- files))
+ files
+ nil
+ (and grep-find-ignored-files
+ (concat " --exclude="
+ (mapconcat
+ #'(lambda (ignore)
+ (cond ((stringp ignore)
+ (shell-quote-argument ignore))
+ ((consp ignore)
+ (and (funcall (car ignore) dir)
+ (shell-quote-argument
+ (cdr ignore))))))
+ grep-find-ignored-files
+ " --exclude=")))))
(when command
(if confirm
(setq command
(setq default-directory dir))))))
-(defvar find-name-arg) ; autoloaded
+(defvar find-name-arg) ; not autoloaded but defined in find-dired
;;;###autoload
(defun rgrep (regexp &optional files dir confirm)
(list (read-from-minibuffer "Run: " grep-find-command
nil nil 'grep-find-history)))
((not grep-find-template)
- (error "grep.el: No `grep-find-template' available."))
+ (error "grep.el: No `grep-find-template' available"))
(t (let* ((regexp (grep-read-regexp))
(files (grep-read-files regexp))
(dir (read-directory-name "Base directory: "
(if (not (string= regexp grep-find-command))
(compilation-start regexp 'grep-mode))
(setq dir (file-name-as-directory (expand-file-name dir)))
+ (require 'find-dired) ; for `find-name-arg'
(let ((command (grep-expand-template
grep-find-template
regexp
(concat " -o " find-name-arg " "))
" "
(shell-quote-argument ")"))
- dir
+ dir
+ (concat
(and grep-find-ignored-directories
(concat (shell-quote-argument "(")
;; we should use shell-quote-argument here
" -path "
(mapconcat
- #'(lambda (ignore)
- (cond ((stringp ignore)
- (shell-quote-argument
- (concat "*/" ignore)))
- ((consp ignore)
- (and (funcall (car ignore) dir)
- (shell-quote-argument
- (concat "*/"
- (cdr ignore)))))))
- grep-find-ignored-directories
- " -o -path ")
+ #'(lambda (ignore)
+ (cond ((stringp ignore)
+ (shell-quote-argument
+ (concat "*/" ignore)))
+ ((consp ignore)
+ (and (funcall (car ignore) dir)
+ (shell-quote-argument
+ (concat "*/"
+ (cdr ignore)))))))
+ grep-find-ignored-directories
+ " -o -path ")
" "
(shell-quote-argument ")")
- " -prune -o ")))))
+ " -prune -o "))
+ (and grep-find-ignored-files
+ (concat (shell-quote-argument "(")
+ ;; we should use shell-quote-argument here
+ " -name "
+ (mapconcat
+ #'(lambda (ignore)
+ (cond ((stringp ignore)
+ (shell-quote-argument ignore))
+ ((consp ignore)
+ (and (funcall (car ignore) dir)
+ (shell-quote-argument
+ (cdr ignore))))))
+ grep-find-ignored-files
+ " -o -name ")
+ " "
+ (shell-quote-argument ")")
+ " -prune -o "))))))
(when command
(if confirm
(setq command
(if (eq next-error-last-buffer (current-buffer))
(setq default-directory dir)))))))
+;;;###autoload
+(defun zrgrep (regexp &optional files dir confirm grep-find-template)
+ "Recursively grep for REGEXP in gzipped FILES in tree rooted at DIR.
+Like `rgrep' but uses `zgrep' for `grep-program', sets the default
+file name to `*.gz', and sets `grep-highlight-matches' to `always'."
+ (interactive
+ (progn
+ ;; Compute standard default values.
+ (grep-compute-defaults)
+ ;; Compute the default zrgrep command by running `grep-compute-defaults'
+ ;; for grep program "zgrep", but not changing global values.
+ (let ((grep-program "zgrep")
+ ;; Don't change global values for variables computed
+ ;; by `grep-compute-defaults'.
+ (grep-find-template nil)
+ (grep-find-command nil)
+ (grep-host-defaults-alist nil)
+ ;; Use for `grep-read-files'
+ (grep-files-aliases '(("all" . "* .*")
+ ("gz" . "*.gz"))))
+ ;; Recompute defaults using let-bound values above.
+ (grep-compute-defaults)
+ (cond
+ ((and grep-find-command (equal current-prefix-arg '(16)))
+ (list (read-from-minibuffer "Run: " grep-find-command
+ nil nil 'grep-find-history)))
+ ((not grep-find-template)
+ (error "grep.el: No `grep-find-template' available"))
+ (t (let* ((regexp (grep-read-regexp))
+ (files (grep-read-files regexp))
+ (dir (read-directory-name "Base directory: "
+ nil default-directory t))
+ (confirm (equal current-prefix-arg '(4))))
+ (list regexp files dir confirm grep-find-template)))))))
+ ;; Set `grep-highlight-matches' to `always'
+ ;; since `zgrep' puts filters in the grep output.
+ (let ((grep-highlight-matches 'always))
+ ;; `rgrep' uses the dynamically bound value `grep-find-template'
+ ;; from the argument `grep-find-template' whose value is computed
+ ;; in the `interactive' spec.
+ (rgrep regexp files dir confirm)))
+
+;;;###autoload
+(defalias 'rzgrep 'zrgrep)
(provide 'grep)
-;; arch-tag: 5a5b9169-a79d-4f38-9c38-f69615f39c4d
;;; grep.el ends here