Download
(require 'custom)
(require 'easymenu)
(or (condition-case nil
(require 'grep)
(error nil))
(require 'compile))
(eval-when-compile
(require 'dired)
(or (featurep 'ange-ftp)
(featurep 'efs)
(condition-case nil
(load-library "ange-ftp")
(error nil))
(condition-case nil
(load-library "efs")
(error nil)))
)
(defconst igrep-version "2.113"
"This version of igrep.el.")
(defgroup igrep nil
"An improved interface to `grep` and `find`."
:group 'compilation)
(defcustom igrep-options nil
"*The options passed by `\\[igrep]' to `igrep-program', or nil.
\"-n\" will automatically be passed to `igrep-program', to generate the
output expected by `\\[next-error]' and `\\[compile-goto-error]'.
\"-e\" will automatically be passed to `igrep-program', if it supports
that option."
:group 'igrep
:type '(choice (const nil) (string)))
(put 'igrep-options 'variable-interactive
"xOptions (\"-xyz\" or nil): ")
(defcustom igrep-case-fold-search nil
"*If non-nil, `\\[igrep]' ignores case unless REGEX has uppercase letters."
:group 'igrep
:type '(boolean))
(put 'igrep-case-fold-search 'variable-interactive
"XIgnore case? (t or nil): ")
(defcustom igrep-read-options nil
"*If non-nil, `\\[igrep]' always prompts for options
otherwise, it only prompts when 1 or 3 `C-u's are given as a prefix arg."
:group 'igrep
:type '(boolean))
(put 'igrep-read-options 'variable-interactive
"XAlways prompt for options? (t or nil): ")
(defcustom igrep-read-multiple-files nil
"*If non-nil, `\\[igrep]' always prompts for multiple-files
otherwise, it only prompts when 2 or 3 `C-u's are given as a prefix arg."
:group 'igrep
:type '(boolean))
(put 'igrep-read-multiple-files 'variable-interactive
"XAlways prompt for multiple files? (t or nil): ")
(defcustom igrep-regex-default 'current-word
"*If non-nil, a function that returns a default REGEX for `\\[igrep]'.
The function is called with no arguments and should return a string (or nil).
A different function can be specified for any particular mode by specifying
a value for that `major-mode' property
(put 'igrep-regex-default 'dired-mode
'igrep-dired-file-current-word)"
:group 'igrep
:type '(choice (const nil) (function)))
(put 'igrep-regex-default 'variable-interactive
"SProvide a default regex? (function or nil): ")
(put 'igrep-regex-default 'dired-mode
'igrep-dired-file-current-word)
(defcustom igrep-files-default 'igrep-buffer-file-name-pattern
"*If non-nil, a function that returns the default FILES for `\\[igrep]'.
The function is called with no arguments and should return a string,
or a list of strings (or nil).
A different function can be specified for any particular mode by specifying
a value for that `major-mode' property
(put 'igrep-files-default 'dired-mode
'igrep-dired-directory-file-pattern)"
:group 'igrep
:type '(choice (const nil) (function)))
(put 'igrep-files-default 'variable-interactive
"SProvide a default file name pattern? (function or nil): ")
(put 'igrep-files-default 'dired-mode
'igrep-dired-directory-file-pattern)
(defcustom igrep-verbose-prompts t
"*If t, `\\[igrep]' prompts for arguments verbosely
if not t but non-nil, `\\[igrep]' prompts for arguments semi-verbosely
if nil, `\\[igrep]' prompts for arguments tersely."
:group 'igrep
:type '(choice (const :tag "Verbose" t)
(other :tag "Semi-verbose" semi)
(const :tag "Terse" nil)))
(put 'igrep-verbose-prompts 'variable-interactive
"XPrompt verbosely? (t, 'semi, or nil): ")
(defcustom igrep-insert-default-directory nil
"*The value of `insert-default-directory' for `\\[igrep]'."
:group 'igrep
:type '(boolean))
(put 'igrep-insert-default-directory 'variable-interactive
"XPrompt with directory in the minibuffer? (t or nil): ")
(defcustom igrep-insert-default-key
(if (< emacs-major-version 20) "\C-c\C-e")
"*The key used to insert the default argument in the minibuffer.
In Emacs 20, the default is available via the minibuffer history \
\(\\<minibuffer-local-map>\\[next-history-element])."
:group 'igrep
:type '(choice (const nil) (string) (vector)))
(put 'igrep-insert-default-key 'variable-interactive
"kSet key to insert the default `\\[igrep]' argument in the minibuffer: ")
(defcustom igrep-save-buffers 'query
"*If t, `\\[igrep]' first saves each modified file buffer
if not t but non-nil, `\\[igrep]' offers to save each modified file buffer."
:group 'igrep
:type '(choice (const :tag "Save" t)
(other :tag "Query" query)
(const :tag "Don't Save" nil)))
(put 'igrep-save-buffers 'variable-interactive
"XSave modified buffers? (t, 'query, or nil): ")
(defcustom igrep-menu-bar t
"*If non-nil, enable the `igrep-menu' submenu on the \"Tools\" menu bar."
:group 'igrep
:type '(boolean))
(put 'igrep-menu-bar 'variable-interactive
"XEnable menu bar? (t or nil): ")
(defsubst igrep-easy-menu-item (name callback help-keyword help-text)
"Return a [NAME CALLBACK HELP-KEYWORD HELP-TEXT] menu item.
See `easy-menu-define'."
(if (featurep 'xemacs)
(vector name callback)
(vector name callback help-keyword help-text)))
(eval-when-compile
(unless (and (fboundp 'keywordp) (keywordp :help))
(defvar :help ':help)))
(defvar igrep-easy-menu
`("Search Files and Directories (igrep)"
,@(cond ((featurep 'xemacs) '(:included igrep-menu-bar))
((>= emacs-major-version 20) '(:active igrep-menu-bar))
(t ()))
("Search files"
,(igrep-easy-menu-item "`grep` files..." 'igrep
:help "Search files for basic regex(5)s")
,(igrep-easy-menu-item "`egrep` files..." 'egrep
:help "Search files for extended regex(5)s")
,(igrep-easy-menu-item "`fgrep` files..." 'fgrep
:help "Search files for strings"))
("Search directories"
,(igrep-easy-menu-item "`find | grep` directories..." 'igrep-find
:help "Search directories for basic regex(5)s")
,(igrep-easy-menu-item "`find | egrep` directories..." 'egrep-find
:help "Search directories for extended regex(5)s")
,(igrep-easy-menu-item "`find | fgrep` directories..." 'fgrep-find
:help "Search directories for strings"))
"--"
,(igrep-easy-menu-item "Search visited files..." 'igrep-visited-files
:help "Search visited files for basic regex(5)s"))
"If non-nil, the menu bar submenu of `igrep' commands.
See `easy-menu-define'.")
(defvar igrep-null-device
(cond ((boundp 'null-device) null-device)
((boundp 'grep-null-device) grep-null-device))
"The system null device.")
(defvar igrep-program "grep"
"The default program run by `\\[igrep]' and `\\[igrep-find]'.
It must accept a `grep` regex argument and one or more file names, plus
the \"-n\" option. If nil, `\\[igrep]' prompts for the program to run.")
(defvar igrep-regex-option
(if (equal (call-process igrep-program nil nil nil
"-e" "foo" igrep-null-device)
1)
"-e")
"If non-nil, the option used to specify the REGEX argument to `\\[igrep]'.
This protects an initial \"-\" from option processing.")
(defvar igrep-program-table
(let ((exec-directories exec-path)
(program-obarray (make-vector 11 0)))
(while exec-directories
(if (and (car exec-directories)
(file-directory-p (car exec-directories))
(file-readable-p (car exec-directories)))
(let ((grep-programs
(directory-files (car exec-directories)
nil "grep\\(\\.exe\\)?\\'")))
(while grep-programs
(if (save-match-data
(string-match "\\.exe\\'" (car grep-programs)))
(intern (substring (car grep-programs) 0 -4) program-obarray)
(intern (car grep-programs) program-obarray))
(setq grep-programs (cdr grep-programs)))))
(setq exec-directories (cdr exec-directories)))
program-obarray)
"An obarray of available `grep` programs.
This is passed by `igrep-read-program' to `completing-read' when
`igrep-program' is nil.")
(defvar igrep-use-zgrep
(if (intern-soft "zgrep" igrep-program-table)
'files)
"If t, `\\[igrep]' searches files using the GNU (gzip) `zPROGRAM` script
If not t but non-nil, `\\[igrep]' searches compressed FILES using `zPROGRAM`
if nil, `\\[igrep]' searches files with `PROGRAM`.")
(defvar igrep-find nil
"If non-nil, `\\[igrep]' searches directories using `find`.
See `igrep-find'.")
(defvar igrep-find-program "find"
"The program run by `\\[igrep-find]'.")
(defvar igrep-find-prune-clause
(if (equal (call-process igrep-find-program nil nil nil
igrep-null-device "-prune")
0)
(format "-type d %s -name RCS -o -name CVS -o -name SCCS %s"
(shell-quote-argument "(")
(shell-quote-argument ")")))
"The `find` clause used to prune directories, or nil
see `igrep-find'.")
(defvar igrep-find-file-clause
(format "-type f %s -name %s %s -name %s %s -name %s %s -name %s"
(shell-quote-argument "!")
(shell-quote-argument "*~")
(shell-quote-argument "!")
(shell-quote-argument "*,v")
(shell-quote-argument "!")
(shell-quote-argument "s.*")
(shell-quote-argument "!")
(shell-quote-argument ".#*"))
"The `find` clause used to filter files passed to `grep`, or nil
see `igrep-find'.")
(defvar igrep-find-use-xargs
(cond ((equal (call-process igrep-find-program nil nil nil
igrep-null-device "-print0")
0)
'gnu)
((equal (call-process "xargs" nil nil nil "-e") 0)))
"Whether `\\[igrep-find]' uses the `xargs` program or not.
If `gnu', it executes
`find ... -print0 | xargs -0 -e grep ...`
if not `gnu' but non-nil, it executes
`find ... -print | xargs -e grep ...`
if nil, it executes
`find ... -exec grep ...`.")
(defvar igrep-program-default "grep"
"The default `grep` program.
This is passed by `igrep-read-program' to `completing-read' when
`igrep-program' is nil.")
(defvar igrep-regex-history '()
"The minibuffer history list for `\\[igrep]'s REGEX argument.")
(defvar igrep-files-history '()
"The minibuffer history list for `\\[igrep]'s FILES argument.")
(defun igrep-insinuate (&optional override)
"Define `grep' aliases for the corresponding `igrep' commands.
With a prefix arg, OVERRIDE the current `grep' command definitions."
(interactive "P")
(if override
(defalias 'grep 'igrep)
(defadvice grep (around igrep-interactive first (&rest command-args)
activate)
"If called interactively, use the `\\[igrep]' interface instead,
where COMMAND-ARGS is (PROGRAM REGEX FILES [OPTIONS])
programmatically, COMMAND-ARGS is still (COMMAND)."
(interactive (igrep-read-args))
(if (interactive-p)
(apply 'igrep command-args)
ad-do-it)))
(if (or (not (fboundp 'grep-find))
override)
(defalias 'grep-find 'igrep-find))
(if (or (not (fboundp 'dired-do-grep))
override)
(defalias 'dired-do-grep 'dired-do-igrep))
(if (or (not (fboundp 'dired-do-grep-find))
override)
(defalias 'dired-do-grep-find 'dired-do-igrep-find))
(if (or (not (fboundp 'Buffer-menu-grep))
override)
(defalias 'Buffer-menu-grep 'Buffer-menu-igrep)))
(defsubst igrep-quote-file-name (file)
"Quote FILE name pattern for `shell-file-name'."
(if (fboundp 'shell-quote-wildcard-pattern)
(shell-quote-wildcard-pattern file)
(shell-quote-argument file)))
(defun igrep (program regex files &optional options)
"*Run `grep` PROGRAM to match REGEX in FILES.
The output is displayed in the *igrep* buffer, which `\\[next-error]' and
`\\[compile-goto-error]' parse to find each line of matched text.
PROGRAM may be nil, in which case it defaults to `igrep-program'.
REGEX is automatically quoted by `shell-quote-argument'.
FILES is either a file name pattern (automatically quoted by
`shell-quote-wildcard-pattern', then expanded by the `shell-file-name' shell),
or a list of file name patterns.
Optional OPTIONS is also passed to PROGRAM
If a prefix argument \
\(`\\[universal-argument]') \
is given when called interactively,
or if `igrep-read-options' is set, OPTIONS is read from the minibuffer.
If two prefix arguments \
\(`\\[universal-argument] \\[universal-argument]') \
are given when called interactively,
or if `igrep-read-multiple-files' is set, FILES is read from the minibuffer
multiple times.
If three prefix arguments \
\(`\\[universal-argument] \\[universal-argument] \\[universal-argument]') \
are given when called interactively,
or if `igrep-read-options' and `igrep-read-multiple-files' are set,
OPTIONS is read and FILES is read multiple times.
If `igrep-find' is non-nil, the directory or directories
containing FILES is recursively searched for files whose name matches
the file name component of FILES (and whose contents match REGEX)."
(interactive
(igrep-read-args))
(if (null program)
(setq program (or igrep-program "grep")))
(if (null options)
(setq options igrep-options))
(if (not (listp files))
(setq files (list files)))
(if (and (member ?~ (mapcar 'string-to-char files))
(save-match-data
(string-match "\\`[rj]?sh\\(\\.exe\\)?\\'"
(file-name-nondirectory shell-file-name))))
(setq files
(mapcar 'expand-file-name files)))
(let* ((use-zgrep (cond ((eq igrep-use-zgrep t))
(igrep-use-zgrep
(let ((files files)
(compressed-p nil))
(while (and files (not compressed-p))
(if (save-match-data
(string-match "\\.g?[zZ]\\'" (car files)))
(setq compressed-p t))
(setq files (cdr files)))
compressed-p))
(t nil)))
(command (format "%s -n %s %s %s %s %s"
(if (and use-zgrep
(save-match-data
(not (string-match "\\`z" program))))
(setq program (concat "z" program))
program)
(or options
(and igrep-case-fold-search
(equal regex (downcase regex))
"-i")
"")
(or igrep-regex-option
(progn
(if (save-match-data
(string-match "\\`-" regex))
(setq regex (concat "\\" regex)))
""))
(shell-quote-argument regex)
(if igrep-find
(if igrep-find-use-xargs
""
(shell-quote-argument "{}"))
(mapconcat (lambda (file)
(let ((dir (file-name-directory file)))
(if dir
(expand-file-name
(file-name-nondirectory file)
(igrep-quote-file-name dir))
file)))
files " "))
igrep-null-device)))
(if igrep-find
(setq command
(igrep-format-find-command command files)))
(cond ((eq igrep-save-buffers t) (save-some-buffers t))
(igrep-save-buffers (save-some-buffers)))
(if (fboundp 'compilation-start)
(let ((compilation-process-setup-function 'grep-process-setup))
(or (fboundp 'igrep-mode)
(define-derived-mode igrep-mode grep-mode "Igrep"))
(compilation-start command
'igrep-mode
nil
(cond ((eq compilation-highlight-regexp t))
(compilation-highlight-regexp
(if (eq program "fgrep")
(regexp-quote regex)
regex)))))
(compile-internal command (format "No more %s matches" program)
"Igrep" nil grep-regexp-alist))))
(defmacro igrep-define (analogue-command &rest igrep-bindings)
"Define ANALOGUE-COMMAND as an `igrep' analogue command.
Optional (VARIABLE VALUE) arguments specify the temporary IGREP-BINDINGS
for the command."
SCommand: ") ; C-u => read bindings?
(let ((analogue-program (symbol-name analogue-command)))
`(defun ,analogue-command (&rest igrep-args)
,(format "*Run `%s` via `\\[igrep]'.
All arguments (including prefix arguments, when called interactively)
are handled by `igrep'."
analogue-program)
(interactive
(let ((igrep-program (if igrep-program ,analogue-program))
(igrep-program-default ,analogue-program))
(igrep-read-args)))
(let (,@ igrep-bindings)
(apply 'igrep
(cond ((interactive-p) (car igrep-args))
((car igrep-args))
(t ,analogue-program))
(cdr igrep-args))))))
(igrep-define egrep)
(igrep-define fgrep)
(igrep-define agrep
(igrep-use-zgrep nil)
(igrep-regex-option "-e"))
(defun igrep-find (&rest igrep-args)
"*Run `grep` via `find`
All IGREP-ARGS (including prefix arguments, when called interactively)
are handled by `igrep'."
(interactive
(let ((igrep-find t))
(igrep-read-args)))
(let ((igrep-find t))
(apply 'igrep igrep-args)))
(defmacro igrep-find-define (analogue-command &rest igrep-bindings)
"Define ANALOGUE-COMMAND-find as an `igrep' analogue `find` command.
Optional (VARIABLE VALUE) arguments specify the temporary IGREP-BINDINGS
for the command."
SCommand: ") ; C-u => read bindings?
(let ((analogue-program (symbol-name analogue-command)))
(setq analogue-command
(intern (format "%s-find" analogue-command)))
`(defun ,analogue-command (&rest igrep-args)
,(format "*Run `%s` via `\\[igrep-find]'.
All arguments (including prefix arguments, when called interactively)
are handled by `igrep'."
analogue-program)
(interactive
(let ((igrep-program (if igrep-program ,analogue-program))
(igrep-program-default ,analogue-program)
(igrep-find t))
(igrep-read-args)))
(let (,@ igrep-bindings)
(apply 'igrep-find
(cond ((interactive-p) (car igrep-args))
((car igrep-args))
(t ,analogue-program))
(cdr igrep-args))))))
(igrep-find-define egrep)
(igrep-find-define fgrep)
(igrep-find-define agrep
(igrep-use-zgrep nil)
(igrep-regex-option "-e"))
(defun igrep-visited-files (program regex &optional options)
"*Run `grep` PROGRAM to match REGEX (with optional OPTIONS) \
on all visited files.
See `\\[igrep]'."
(interactive
(let ((igrep-args (igrep-read-args 'no-files)))
(setcdr (nthcdr 1 igrep-args) (nthcdr 3 igrep-args))
igrep-args))
(igrep program regex
(let ((directory-abbrev-alist
(cons (cons (regexp-quote (expand-file-name default-directory))
"./") "
directory-abbrev-alist)))
(mapcar 'abbreviate-file-name
(apply 'nconc
(mapcar (lambda (buffer)
(let ((file (buffer-file-name buffer)))
(if (and file
(cond ((featurep 'ange-ftp)
(not (ange-ftp-ftp-name file)))
((featurep 'efs)
(not (efs-ftp-path file)))
(t t))
)
(list file))))
(buffer-list)))))
options))
(defun dired-do-igrep (program regex &optional options arg)
"*Search the marked (or next prefix ARG) files.
See `\\[igrep]' for a description of PROGRAM, REGEX, and OPTIONS."
(interactive
(let ((igrep-args
(let ((current-prefix-arg nil))
(igrep-read-args 'no-files))))
(setcdr (nthcdr 1 igrep-args) (nthcdr 3 igrep-args))
(nconc igrep-args (list current-prefix-arg))))
(igrep program regex
(funcall (cond ((fboundp 'dired-get-marked-files)
'dired-get-marked-files)
((fboundp 'dired-mark-get-files)
'dired-mark-get-files))
t arg)
options))
(defun dired-do-igrep-find (program regex &optional options arg)
"*Run `grep` on the marked (or next prefix ARG) directories.
See `\\[igrep]' for a description of PROGRAM, REGEX, and OPTIONS."
(interactive
(let ((igrep-args
(let ((current-prefix-arg nil)
(igrep-find t))
(igrep-read-args 'no-files))))
(setcdr (nthcdr 1 igrep-args) (nthcdr 3 igrep-args))
(nconc igrep-args (list current-prefix-arg))))
(let ((igrep-find t))
(dired-do-igrep program regex options arg)))
(defun Buffer-menu-igrep (program regex &optional options)
"*Run `grep` on the files visited in buffers marked with '>'.
See `\\[igrep]' for a description of PROGRAM, REGEX, and OPTIONS."
(interactive
(let ((igrep-args (igrep-read-args 'no-files)))
(setcdr (nthcdr 1 igrep-args) (nthcdr 3 igrep-args))
igrep-args))
(let ((marked-files '())
marked-buffer
file)
(goto-char (point-min))
(while (search-forward "\n>" nil t)
(setq marked-buffer (Buffer-menu-buffer t)
file (buffer-file-name marked-buffer))
(if (and file
(cond ((featurep 'ange-ftp)
(not (ange-ftp-ftp-name file)))
((featurep 'efs)
(not (efs-ftp-path file)))
(t t)))
(setq marked-files (cons file marked-files)))
)
(setq marked-files (nreverse marked-files))
(igrep program regex
(let ((directory-abbrev-alist
(cons (cons (regexp-quote (expand-file-name default-directory))
"./") "
directory-abbrev-alist)))
(mapcar 'abbreviate-file-name marked-files))
options)))
(defun igrep-dired-file-current-word ()
"Return the current word in the file on this line, if it is visible
else, return the file name on this line, if there is one
otherwise, return the current word."
(let* ((dired-file
(dired-get-filename t t))
(dired-file-buffer
(if dired-file
(get-file-buffer (expand-file-name dired-file))))
(dired-file-buffer-window
(if dired-file-buffer
(get-buffer-window dired-file-buffer))))
(cond (dired-file-buffer-window (save-excursion
(set-buffer dired-file-buffer)
(current-word)))
(dired-file)
(t (current-word)))))
(defun igrep-buffer-file-name-pattern ()
"Return a shell file name pattern based on the visited file name.
If the `buffer-file-name' variable is nil, return \"*\"."
(if buffer-file-name
(let ((file-name (file-name-nondirectory buffer-file-name)))
(concat "*"
(save-match-data
(if (string-match "\\.[^.]+\\(\\.g?[zZ]\\)?\\'"
file-name)
(substring file-name (match-beginning 0)
(match-end 0))))))
"*"))
(defun igrep-dired-directory-file-pattern ()
"Return a shell file name pattern based on `dired-directory', or \"*\"."
(cond ((stringp dired-directory)
(if (file-directory-p dired-directory)
"*"
(file-name-nondirectory dired-directory)))
((consp dired-directory)
(mapconcat 'identity (cdr dired-directory) " "))))
(defsubst igrep-file-directory (name)
"Return the directory component of NAME, or \".\" if it has none."
(directory-file-name (or (file-name-directory name)
(file-name-as-directory "."))))
(defsubst igrep-file-pattern (name)
"Return the file component of NAME, or \"*\" if it has none."
(let ((pattern (file-name-nondirectory name)))
(if (string= pattern "")
"*"
pattern)))
(defun igrep-format-find-command (command files)
"Format `grep` COMMAND to be invoked via `find` on FILES."
(let ((directories '())
(patterns '()))
(while files
(let ((dir (igrep-file-directory (car files)))
(pat (igrep-file-pattern (car files))))
(if (and (not (string= dir "."))
(file-symlink-p dir))
(setq dir (concat dir "/.")))
(if (not (member dir directories))
(setq directories (cons dir directories)))
(cond ((equal pat "*")
(setq patterns t))
((and (listp patterns)
(not (member pat patterns)))
(setq patterns (cons pat patterns)))))
(setq files (cdr files)))
(format (cond ((eq igrep-find-use-xargs 'gnu)
"%s %s %s %s %s -print0 | xargs -0 -e %s")
(igrep-find-use-xargs
"%s %s %s %s %s -print | xargs -e %s")
(t
"%s %s %s %s %s -exec %s %s"))
igrep-find-program
(mapconcat 'igrep-quote-file-name (nreverse directories)
" ")
(if igrep-find-prune-clause
(format "%s -prune -o" igrep-find-prune-clause)
"")
(or igrep-find-file-clause "")
(if (listp patterns)
(if (cdr patterns)
(format "%s %s %s"
(shell-quote-argument "(")
(mapconcat (lambda (pat)
(format "-name %s"
(shell-quote-argument pat)))
(nreverse patterns)
" -o ")
(shell-quote-argument ")"))
(format "-name %s" (shell-quote-argument (car patterns))))
"")
command
(shell-quote-argument "Return the default arg based on VARIABLE."
`(if ,variable
(cond ((get (quote ,variable) major-mode)
(funcall (get (quote ,variable) major-mode)))
(t (funcall ,variable)))))
(defun igrep-default-regex ()
"Return the default REGEX for `\\[igrep]'."
(let ((default-regex (igrep-default-arg igrep-regex-default)))
(if (not (equal default-regex ""))
default-regex)))
(defun igrep-default-files ()
"Return the default FILES for `\\[igrep]'."
(let* ((dired-subdirectory (if (cond ((fboundp 'derived-mode-p)
(derived-mode-p 'dired-mode))
(t (eq major-mode 'dired-mode)))
(dired-current-directory t)))
(default-files (igrep-default-arg igrep-files-default)))
(if (not (listp default-files))
(setq default-files (list default-files)))
(if dired-subdirectory
(mapcar (lambda (file)
(concat dired-subdirectory file))
default-files)
default-files)))
(defsubst igrep-prefix (prefix string &rest strings)
"Concatenate PREFIX (if non-nil), STRING, and any other STRINGS."
(if (or prefix strings)
(apply 'concat prefix string strings)
string))
(defun igrep-read-args (&optional no-files)
"Read and return a list: (PROGRAM REGEX FILES OPTIONS).
If NO-FILES is non-nil, then FILES is not read and nil is returned
in its place."
(let* ((pre-prefix (if (and igrep-find (eq igrep-verbose-prompts t))
"[find] "))
(program
(igrep-read-program pre-prefix))
(prefix (if (and program (eq igrep-verbose-prompts t))
(igrep-prefix pre-prefix program " ")
pre-prefix))
(options
(igrep-read-options prefix))
(post-prefix (if (and options (eq igrep-verbose-prompts t))
(igrep-prefix prefix options " ")
prefix)))
(list program
(igrep-read-regex post-prefix)
(if (not no-files)
(igrep-read-files post-prefix))
options)))
(defun igrep-read-program (&optional prompt-prefix)
"Read and return a `grep` program name from the minibuffer.
If `igrep-program' is non-nil, it.
Optional PROMPT-PREFIX is prepended to the \"Program: \" prompt."
(or igrep-program
(let ((prompt "Program: "))
(completing-read (igrep-prefix prompt-prefix prompt) igrep-program-table
nil t igrep-program-default))))
(defun igrep-read-options (&optional prompt-prefix)
"Read and return an options string from the minibuffer.
If `current-prefix-arg' is '(4) or '(64), return `igrep-options'.
Optional PROMPT-PREFIX is prepended to the \"Options: \" prompt."
(if (or igrep-read-options
(and (consp current-prefix-arg)
(memq (prefix-numeric-value current-prefix-arg)
'(4 64))))
(let ((prompt "Options: "))
(read-string (igrep-prefix prompt-prefix prompt)
(or igrep-options "-")))
igrep-options))
(defun igrep-read-regex (&optional prompt-prefix)
"Read and return a `grep` regex(5) string from the minibuffer.
Optional PROMPT-PREFIX is prepended to the \"Regex: \" prompt."
(if igrep-insert-default-key
(define-key minibuffer-local-map igrep-insert-default-key
'igrep-insert-default-regex))
(let* ((default-regex (igrep-default-regex))
(prompt (igrep-prefix prompt-prefix
(if default-regex
(format "Regex [default: %s]: "
default-regex)
"Regex: ")))
(regex (cond ((featurep 'xemacs)
(read-from-minibuffer prompt
nil nil nil
'igrep-regex-history
nil))
((>= emacs-major-version 20)
(read-from-minibuffer prompt
nil nil nil
'igrep-regex-history
default-regex))
(t
(read-from-minibuffer prompt
nil nil nil
'igrep-regex-history)))))
(if (equal regex "")
(progn
(or (equal default-regex (car igrep-regex-history))
(setq igrep-regex-history
(cons default-regex igrep-regex-history)))
default-regex)
regex)))
(defun igrep-insert-default-regex (&optional clear-minibuffer)
"*Insert the default regex in the minibuffer.
If a prefix argument is specified, CLEAR-MINIBUFFER contents first."
(interactive "P")
(if clear-minibuffer
(delete-region (if (fboundp 'minibuffer-prompt-end)
(minibuffer-prompt-end)
(point-min))
(point-max)))
(insert (or (save-excursion
(set-buffer (window-buffer minibuffer-scroll-window))
(igrep-default-regex))
"")))
(defun igrep-insert-default-files (&optional clear-minibuffer)
"*Insert the default files in the minibuffer.
If a prefix argument is specified, CLEAR-MINIBUFFER contents first."
(interactive "P")
(if clear-minibuffer
(delete-region (if (fboundp 'minibuffer-prompt-end)
(minibuffer-prompt-end)
(point-min))
(point-max)))
(insert (mapconcat 'identity
(save-excursion
(set-buffer (window-buffer minibuffer-scroll-window))
(igrep-default-files))
" ")))
(defsubst igrep-default-key (command &optional keymap key)
"Return the key bound to COMMAND in KEYMAP, preferably KEY."
(if (null keymap)
(setq keymap (current-global-map)))
(if (and key
(eq (lookup-key keymap key) command))
key
(where-is-internal command keymap t)))
(defun igrep-read-files (&optional prompt-prefix)
"Read and return a file name pattern from the minibuffer.
If `current-prefix-arg' is '(16) or '(64), read multiple file name
patterns and return them in a list. Optional PROMPT-PREFIX is
prepended to the \"File(s): \" prompt."
(let* ((default-files (igrep-default-files))
(default-files-string (mapconcat 'identity default-files " "))
(insert-default-directory igrep-insert-default-directory)
(file (igrep-read-file-name
(igrep-prefix prompt-prefix
(if default-files
(format "File(s) [default: %s]: "
default-files-string)
"File(s): "))
nil (if default-files default-files-string "") nil nil
'igrep-files-history))
(files (list file)))
(if (or igrep-read-multiple-files
(and (consp current-prefix-arg)
(memq (prefix-numeric-value current-prefix-arg)
'(16 64))))
(let* ((key (igrep-default-key 'exit-minibuffer
minibuffer-local-completion-map
"\r"))
(prompt
(igrep-prefix prompt-prefix
(if igrep-verbose-prompts
(format "File(s): [Type `%s' when done] "
(key-description key))
"File(s): "))))
(while (and (setq file
(igrep-read-file-name prompt
nil "" nil nil
'igrep-files-history))
(not (equal file "")))
(setq files (cons file files)))))
(mapcar (lambda (file)
(if (file-directory-p file)
(expand-file-name (if default-files default-files-string "*")
file)
file))
(nreverse files))))
(defun igrep-read-file-name (prompt
&optional directory default existing initial history)
"Just like `read-file-name,' but with optional HISTORY."
(if igrep-insert-default-key
(define-key minibuffer-local-completion-map igrep-insert-default-key
'igrep-insert-default-files))
(if history
(let ((file-name-history (symbol-value history)))
(prog1 (read-file-name prompt directory default existing initial)
(set history file-name-history)))
(read-file-name prompt directory default existing initial)))
(if igrep-easy-menu
(progn
(easy-menu-define igrep-menu nil
"Menu keymap for igrep."
igrep-easy-menu)
(cond ((fboundp 'add-submenu)
(add-submenu '("Tools") igrep-menu "Grep..."))
((fboundp 'easy-menu-add-item)
(easy-menu-add-item menu-bar-tools-menu nil igrep-menu
'grep))
(t
(define-key-after menu-bar-tools-menu [igrep]
(cons (car igrep-easy-menu) igrep-menu)
(and (lookup-key menu-bar-tools-menu [grep]) 'grep))))))
(provide 'igrep)