Initial Commit
[packages] / xemacs-packages / igrep / igrep.el
1 ;;; igrep.el --- An improved interface to `grep` and `find`
2 ;;; -*-coding: iso-latin-1;-*-
3
4 ;; Copyright © 1993-1998,2000-2005 Kevin Rodgers
5
6 ;; Author: Kevin Rodgers <ihs_4664@yahoo.com>
7 ;; Created:  22 Jun 1993
8 ;; Version: 2.113
9 ;; Keywords: tools, processes, search
10 ;; SCCS: @(#)igrep.el   2.113
11
12 ;; This program is free software; you can redistribute it and/or
13 ;; modify it under the terms of the GNU General Public License as
14 ;; published by the Free Software Foundation; either version 2 of
15 ;; the License, or (at your option) any later version.
16
17 ;; This program is distributed in the hope that it will be
18 ;; useful, but WITHOUT ANY WARRANTY; without even the implied
19 ;; warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
20 ;; PURPOSE.  See the GNU General Public License for more details.
21
22 ;; You should have received a copy of the GNU General Public
23 ;; License along with this program; if not, write to the Free
24 ;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
25 ;; MA 02111-1307 USA
26
27 ;;; Commentary:
28
29 ;; The `igrep' command is like `grep' except that it takes three
30 ;; required arguments (PROGRAM, REGEX, and FILES) and an optional
31 ;; argument (OPTIONS) instead of just one argument (COMMAND).  The
32 ;; analogous `egrep' and `fgrep' commands are also defined for
33 ;; convenience.
34 ;; 
35 ;; The `igrep-find' command is like `igrep' except that it uses `find`
36 ;; to recursively `grep` a directory.  The analogous `egrep-find' and
37 ;; `fgrep-find' commands are also defined for convenience.
38 ;; 
39 ;; When called interactively, `igrep' and `igrep-find' (and their
40 ;; analogues) provide defaults for the REGEX and FILES arguments based
41 ;; on the current word and the visited file name (if the `igrep-regex-
42 ;; default' and `igrep-files-default' options are set, respectively).
43 ;; The `igrep-insert-default-key' option allows the default value to be
44 ;; inserted into the minibuffer for editing; since Emacs 20 provides
45 ;; that via the minibuffer history, it's only enabled for older
46 ;; versions by default. Other options that control the user interface
47 ;; are `igrep-insert-default-directory', `igrep-read-options', `igrep-
48 ;; read-multiple-files', `igrep-verbose-prompts', `igrep-save-buffers',
49 ;; and `igrep-menu-bar'.
50 ;; 
51 ;; Besides the basic `igrep-program' and `igrep-find-program' global
52 ;; variables, other variables control the syntax of the `grep` and
53 ;; `find` shell commands that are executed: `igrep-options', `igrep-
54 ;; regex-option', `igrep-case-fold-search', `igrep-find-prune-clause',
55 ;; `igrep-find-file-clause', and `igrep-find-use-xargs'.
56 ;; 
57 ;; The `igrep-use-zgrep' user option controls whether the corresponding
58 ;; GNU (gzip) "zPROGRAM" script is used, to `grep` compressed files.
59 ;; Special minibuffer history lists are maintained for the REGEX and
60 ;; FILES arguments.
61 ;; 
62 ;; The `agrep' and `agrep-find' commands are interfaces to the
63 ;; approximate `grep` utility, which is distributed with the `glimpse'
64 ;; indexing and query tool (available from http://www.tgries.de/agrep/).
65 ;; 
66 ;; `grep' itself can be advised to provide the `igrep' interface when
67 ;; it is invoked interactively (but still use the original argument
68 ;; list when it is called from Emacs Lisp), via the `igrep-insinuate'
69 ;; command.  `igrep-insinuate' also defines `grep-find' as an alias for
70 ;; `igrep-find', `dired-do-grep' and `dired-do-grep-find' as aliases
71 ;; for `dired-do-igrep' and `dired-do-igrep-find', and `Buffer-menu-
72 ;; grep' as an alias for `Buffer-menu-igrep'.
73 ;; 
74 ;; When run interactively from Dired mode, the various `igrep' commands
75 ;; provide defaults for the REGEX and FILES arguments that are based on
76 ;; the visited directory (including any inserted subdirectories) and
77 ;; the current file.  The alternative `dired-do-igrep' and `dired-do-
78 ;; igrep-find' commands respect the `dired-do-*' command conventions: a
79 ;; prefix argument is interpreted as the number of succeeding files to
80 ;; `grep`, otherwise all the marked files are `grep`ed.
81 ;; 
82 ;; The `igrep-visited-files' command provides a simple way to `grep`
83 ;; just those files that are being visited in buffers.  The `Buffer-
84 ;; menu-igrep' command does the same thing, for buffers marked for
85 ;; selection in Buffer Menu mode.
86
87 ;; Installation:
88 ;; 
89 ;; 1. Put this file in a directory that is a member of load-path, and
90 ;;    byte-compile it (e.g. with `M-x byte-compile-file') for better
91 ;;    performance.  You can ignore any warnings about references to free
92 ;;    variables and "not known to be defined" functions.
93 ;; 2. Put these forms in default.el or ~/.emacs:
94 ;;    (autoload 'igrep "igrep"
95 ;;       "*Run `grep` PROGRAM to match REGEX in FILES..." t)
96 ;;    (autoload 'igrep-find "igrep"
97 ;;       "*Run `grep` via `find`..." t)
98 ;;    (autoload 'igrep-visited-files "igrep"
99 ;;       "*Run `grep` ... on all visited files." t)
100 ;;    (autoload 'dired-do-igrep "igrep"
101 ;;       "*Run `grep` on the marked (or next prefix ARG) files." t)
102 ;;    (autoload 'dired-do-igrep-find "igrep"
103 ;;       "*Run `grep` via `find` on the marked (or next prefix ARG) directories." t)
104 ;;    (autoload 'Buffer-menu-igrep "igrep"
105 ;;      "*Run `grep` on the files visited in buffers marked with '>'." t)
106 ;;    (autoload 'igrep-insinuate "igrep"
107 ;;      "Define `grep' aliases for the corresponding `igrep' commands." t)
108 ;; 2. a. For completeness, you can add these forms as well:
109 ;;    (autoload 'grep "igrep"
110 ;;       "*Run `grep` PROGRAM to match REGEX in FILES..." t)
111 ;;    (autoload 'egrep "igrep"
112 ;;       "*Run `egrep`..." t)
113 ;;    (autoload 'fgrep "igrep"
114 ;;       "*Run `fgrep`..." t)
115 ;;    (autoload 'agrep "igrep"
116 ;;       "*Run `agrep`..." t)
117 ;;    (autoload 'grep-find "igrep"
118 ;;       "*Run `grep` via `find`..." t)
119 ;;    (autoload 'egrep-find "igrep"
120 ;;       "*Run `egrep` via `find`..." t)
121 ;;    (autoload 'fgrep-find "igrep"
122 ;;       "*Run `fgrep` via `find`..." t)
123 ;;    (autoload 'agrep-find "igrep"
124 ;;       "*Run `agrep` via `find`..." t)
125 ;; 3. If you are running Windows 95/NT, you should install findutils
126 ;;    and grep from release 17.1 (or higher) of the Cygnus GNU-Win32
127 ;;    distribution (http://www.cygnus.com/misc/gnu-win32/).
128
129 ;; Usage:
130 ;; 
131 ;; These igrep commands accept 1, 2, or 3 `C-u' prefix arguments:
132 ;;      M-x igrep               M-x igrep-find
133 ;;      M-x  grep               M-x  grep-find  [after `M-x igrep-insinuate']
134 ;;      M-x egrep               M-x egrep-find
135 ;;      M-x fgrep               M-x fgrep-find
136 ;;      M-x agrep               M-x agrep-find
137 ;; 
138 ;; These igrep commands accept a single `C-u' prefix argument:
139 ;;      M-x igrep-visited-files
140 ;;      M-x Buffer-menu-igrep   [in the *Buffer List* buffer]
141 ;; 
142 ;; These igrep commands interpret a prefix argument like the Emacs
143 ;; `dired-do-*' commands:
144 ;;      M-x dired-do-igrep      M-x dired-do-igrep-find
145 ;;      M-x  dired-do-grep      M-x  dired-do-grep-find [after `M-x
146 ;;                                                       igrep-insinuate']
147 ;; 
148 ;; These Emacs commands can be used after any igrep command:
149 ;;      C-x ` (M-x next-error)
150 ;;      C-c C-c (M-x compile-goto-error)        [in the *igrep* buffer]
151
152 ;; Customization examples:
153 ;; 
154 ;; To ignore case by default:
155 ;;      (setq igrep-options "-i")
156 ;; or:
157 ;;      (setq igrep-case-fold-search t)
158 ;; To search subdirectories by default:
159 ;;      (setq igrep-find t)
160 ;; To search files with the GNU (gzip) zgrep script:
161 ;;      (setq igrep-use-zgrep t)
162 ;; or define new igrep commands (this works for zegrep and zfgrep as well):
163 ;;      (igrep-define zgrep)            ; M-x zgrep
164 ;;      (igrep-find-define zgrep)       ; M-x zgrep-find
165 ;; To search "*.[ch]" files by default in C mode:
166 ;;      (put 'igrep-files-default 'c-mode
167 ;;           (lambda () "*.[ch]"))
168 ;; To disable the default search regex and/or files pattern, except for
169 ;; specific modes:
170 ;;      (setq igrep-regex-default 'ignore)
171 ;;      (setq igrep-files-default 'ignore)
172 ;; To avoid exceeding some shells' limit on command argument length
173 ;; (this only searches files in the current directory):
174 ;;      (setq igrep-find t
175 ;;            igrep-find-prune-clause "-type d \\! -name .")
176
177 ;; To do:
178 ;; 1. Replace igrep-options with a table that maps igrep-program
179 ;;    to the appropriate options, and/or support POSIX (egrep -> `grep -E`).
180 ;; 2. Generalize support for the -prune find clause (e.g. -fstype nfs).
181 ;; 3. Provide support for `glimpse`.
182 \f
183 ;;; Code:
184
185 ;; Package interface:
186
187 (require 'custom)                       ; defgroup, defcustom
188
189 (require 'easymenu)                     ; easy-menu-define, easy-menu-add-item
190
191 (or (condition-case nil
192         (require 'grep)                 ; Emacs 22
193       (error nil))
194     (require 'compile))                 ; compile-internal, grep-regexp-alist,
195                                         ; grep-null-device
196
197 (eval-when-compile
198   (require 'dired)                      ; dired-directory,
199                                         ; dired-get-filename,
200                                         ; dired-current-directory,
201                                         ; dired-get-marked-files,
202                                         ; dired-mark-get-files
203   (or (featurep 'ange-ftp)
204       (featurep 'efs)
205       (condition-case nil
206           (load-library "ange-ftp")     ; ange-ftp-ftp-name
207         (error nil))
208       (condition-case nil
209           (load-library "efs")          ; efs-ftp-path
210         (error nil)))
211   )
212
213 (defconst igrep-version "2.113"
214   "This version of igrep.el.")
215
216 (defgroup igrep nil
217   "An improved interface to `grep` and `find`."
218   :group 'compilation)
219 \f
220 ;; User options:
221
222 (defcustom igrep-options nil
223   "*The options passed by `\\[igrep]' to `igrep-program', or nil.
224
225 \"-n\" will automatically be passed to `igrep-program', to generate the
226 output expected by `\\[next-error]' and `\\[compile-goto-error]'.
227 \"-e\" will automatically be passed to `igrep-program', if it supports
228 that option."
229   :group 'igrep
230   :type '(choice (const nil) (string)))
231 (put 'igrep-options 'variable-interactive
232      "xOptions (\"-xyz\" or nil): ")
233
234 (defcustom igrep-case-fold-search nil
235   "*If non-nil, `\\[igrep]' ignores case unless REGEX has uppercase letters."
236   :group 'igrep
237   :type '(boolean))
238 (put 'igrep-case-fold-search 'variable-interactive
239      "XIgnore case? (t or nil): ")
240
241 (defcustom igrep-read-options nil
242   "*If non-nil, `\\[igrep]' always prompts for options;
243 otherwise, it only prompts when 1 or 3 `C-u's are given as a prefix arg."
244   :group 'igrep
245   :type '(boolean))
246 (put 'igrep-read-options 'variable-interactive
247      "XAlways prompt for options? (t or nil): ")
248
249 (defcustom igrep-read-multiple-files nil
250   "*If non-nil, `\\[igrep]' always prompts for multiple-files;
251 otherwise, it only prompts when 2 or 3 `C-u's are given as a prefix arg."
252   :group 'igrep
253   :type '(boolean))
254 (put 'igrep-read-multiple-files 'variable-interactive
255      "XAlways prompt for multiple files? (t or nil): ")
256
257 (defcustom igrep-regex-default 'current-word
258   "*If non-nil, a function that returns a default REGEX for `\\[igrep]'.
259 The function is called with no arguments and should return a string (or nil).
260
261 A different function can be specified for any particular mode by specifying
262 a value for that `major-mode' property; for example:
263         (put 'igrep-regex-default 'dired-mode
264              'igrep-dired-file-current-word)"
265   :group 'igrep
266   :type '(choice (const nil) (function)))
267 (put 'igrep-regex-default 'variable-interactive
268      "SProvide a default regex? (function or nil): ")
269 (put 'igrep-regex-default 'dired-mode
270      'igrep-dired-file-current-word)
271
272 (defcustom igrep-files-default 'igrep-buffer-file-name-pattern
273   "*If non-nil, a function that returns the default FILES for `\\[igrep]'.
274 The function is called with no arguments and should return a string,
275 or a list of strings (or nil).
276
277 A different function can be specified for any particular mode by specifying
278 a value for that `major-mode' property; for example:
279         (put 'igrep-files-default 'dired-mode
280              'igrep-dired-directory-file-pattern)"
281   :group 'igrep
282   :type '(choice (const nil) (function)))
283 (put 'igrep-files-default 'variable-interactive
284      "SProvide a default file name pattern? (function or nil): ")
285 (put 'igrep-files-default 'dired-mode
286      'igrep-dired-directory-file-pattern)
287
288 (defcustom igrep-verbose-prompts t
289   "*If t, `\\[igrep]' prompts for arguments verbosely;
290 if not t but non-nil, `\\[igrep]' prompts for arguments semi-verbosely;
291 if nil, `\\[igrep]' prompts for arguments tersely."
292   :group 'igrep
293   :type '(choice (const :tag "Verbose" t)
294                  (other :tag "Semi-verbose" semi)
295                  (const :tag "Terse" nil)))
296 (put 'igrep-verbose-prompts 'variable-interactive
297      "XPrompt verbosely? (t, 'semi, or nil): ")
298
299 (defcustom igrep-insert-default-directory nil
300   "*The value of `insert-default-directory' for `\\[igrep]'."
301   :group 'igrep
302   :type '(boolean))
303 (put 'igrep-insert-default-directory 'variable-interactive
304      "XPrompt with directory in the minibuffer? (t or nil): ")
305
306 (defcustom igrep-insert-default-key
307   (if (< emacs-major-version 20) "\C-c\C-e")
308   "*The key used to insert the default argument in the minibuffer.
309 In Emacs 20, the default is available via the minibuffer history \
310 \(\\<minibuffer-local-map>\\[next-history-element])."
311   :group 'igrep
312   :type '(choice (const nil) (string) (vector))) ; key-binding
313 (put 'igrep-insert-default-key 'variable-interactive
314      "kSet key to insert the default `\\[igrep]' argument in the minibuffer: ")
315
316 (defcustom igrep-save-buffers 'query
317   "*If t, `\\[igrep]' first saves each modified file buffer;
318 if not t but non-nil, `\\[igrep]' offers to save each modified file buffer."
319   :group 'igrep
320   :type '(choice (const :tag "Save" t)
321                  (other :tag "Query" query)
322                  (const :tag "Don't Save" nil)))
323 (put 'igrep-save-buffers 'variable-interactive
324      "XSave modified buffers? (t, 'query, or nil): ")
325
326 (defcustom igrep-menu-bar t
327   "*If non-nil, enable the `igrep-menu' submenu on the \"Tools\" menu bar."
328   :group 'igrep
329   :type '(boolean))
330 (put 'igrep-menu-bar 'variable-interactive
331      "XEnable menu bar? (t or nil): ")
332 \f
333 ;; User variables:
334
335 (defsubst igrep-easy-menu-item (name callback help-keyword help-text)
336   "Return a [NAME CALLBACK HELP-KEYWORD HELP-TEXT] menu item.
337 See `easy-menu-define'."
338   (if (featurep 'xemacs)                ; no :help keyword
339       (vector name callback)
340     (vector name callback help-keyword help-text)))
341
342 (eval-when-compile
343   (unless (and (fboundp 'keywordp) (keywordp :help))
344     (defvar :help ':help)))             ; Emacs 19
345
346 (defvar igrep-easy-menu
347   `("Search Files and Directories (igrep)"
348     ,@(cond ((featurep 'xemacs) '(:included igrep-menu-bar))
349             ((>= emacs-major-version 20) '(:active igrep-menu-bar))
350             (t ()))
351     ("Search files"
352      ,(igrep-easy-menu-item "`grep` files..." 'igrep
353                             :help "Search files for basic regex(5)s")
354      ,(igrep-easy-menu-item "`egrep` files..." 'egrep
355                             :help "Search files for extended regex(5)s")
356      ,(igrep-easy-menu-item "`fgrep` files..." 'fgrep
357                             :help "Search files for strings"))
358     ("Search directories"
359      ,(igrep-easy-menu-item "`find | grep` directories..." 'igrep-find
360                             :help "Search directories for basic regex(5)s")
361      ,(igrep-easy-menu-item "`find | egrep` directories..." 'egrep-find
362                             :help "Search directories for extended regex(5)s")
363      ,(igrep-easy-menu-item "`find | fgrep` directories..." 'fgrep-find
364                             :help "Search directories for strings"))
365     "--"
366     ,(igrep-easy-menu-item "Search visited files..." 'igrep-visited-files
367                            :help "Search visited files for basic regex(5)s"))
368   "If non-nil, the menu bar submenu of `igrep' commands.
369 See `easy-menu-define'.")
370
371 (defvar igrep-null-device
372   (cond ((boundp 'null-device) null-device) ; Emacs 20
373         ((boundp 'grep-null-device) grep-null-device)) ; Emacs 19
374   "The system null device.")
375
376 (defvar igrep-program "grep"
377   "The default program run by `\\[igrep]' and `\\[igrep-find]'.
378 It must accept a `grep` regex argument and one or more file names, plus
379 the \"-n\" option.  If nil, `\\[igrep]' prompts for the program to run.")
380
381 (defvar igrep-regex-option
382   (if (equal (call-process igrep-program nil nil nil
383                            "-e" "foo" igrep-null-device)
384              1)
385       "-e")
386   "If non-nil, the option used to specify the REGEX argument to `\\[igrep]'.
387 This protects an initial \"-\" from option processing.")
388
389 (defvar igrep-program-table             ; referenced by igrep-use-zgrep
390   (let ((exec-directories exec-path)
391         (program-obarray (make-vector 11 0)))
392     (while exec-directories
393       (if (and (car exec-directories)
394                (file-directory-p (car exec-directories))
395                (file-readable-p (car exec-directories)))
396           (let ((grep-programs
397                  (directory-files (car exec-directories)
398                                   nil "grep\\(\\.exe\\)?\\'")))
399             (while grep-programs
400               ;; Check `(file-executable-p (car grep-programs))'?
401               (if (save-match-data
402                     (string-match "\\.exe\\'" (car grep-programs)))
403                   (intern (substring (car grep-programs) 0 -4) program-obarray)
404                 (intern (car grep-programs) program-obarray))
405               (setq grep-programs (cdr grep-programs)))))
406       (setq exec-directories (cdr exec-directories)))
407     program-obarray)
408   "An obarray of available `grep` programs.
409 This is passed by `igrep-read-program' to `completing-read' when
410 `igrep-program' is nil.")
411
412 (defvar igrep-use-zgrep
413   (if (intern-soft "zgrep" igrep-program-table)
414       'files)
415   "If t, `\\[igrep]' searches files using the GNU (gzip) `zPROGRAM` script;
416 If not t but non-nil, `\\[igrep]' searches compressed FILES using `zPROGRAM`;
417 if nil, `\\[igrep]' searches files with `PROGRAM`.")
418
419 (defvar igrep-find nil
420   "If non-nil, `\\[igrep]' searches directories using `find`.
421 See `igrep-find'.")
422
423 (defvar igrep-find-program "find"
424   "The program run by `\\[igrep-find]'.")
425
426 (defvar igrep-find-prune-clause
427   (if (equal (call-process igrep-find-program nil nil nil
428                            igrep-null-device "-prune")
429              0)
430       (format "-type d %s -name RCS -o -name CVS -o -name SCCS -o -name .svn -o -name .hg -o -name .git %s"
431               (shell-quote-argument "(")
432               (shell-quote-argument ")")))
433   "The `find` clause used to prune directories, or nil;
434 see `igrep-find'.")
435
436 (defvar igrep-find-file-clause
437   (format "-type f %s -name %s %s -name %s %s -name %s %s -name %s" ; -type l
438           (shell-quote-argument "!")
439           (shell-quote-argument "*~")   ; Emacs backup
440           (shell-quote-argument "!")
441           (shell-quote-argument "*,v")  ; RCS file
442           (shell-quote-argument "!")
443           (shell-quote-argument "s.*")  ; SCCS file
444           (shell-quote-argument "!")
445           (shell-quote-argument ".#*")) ; CVS file
446   "The `find` clause used to filter files passed to `grep`, or nil;
447 see `igrep-find'.")
448
449 (defvar igrep-find-use-xargs
450   (cond ((not (equal (call-process "xargs" nil nil nil "-e") 0))
451          nil)
452         ((equal (call-process igrep-find-program nil nil nil
453                               igrep-null-device "-print0")
454                 0)
455          'gnu)
456         (t t))
457   "Whether `\\[igrep-find]' uses the `xargs` program or not.
458 If `gnu', it executes
459         `find ... -print0 | xargs -0 -e grep ...`;
460 if not `gnu' but non-nil, it executes
461         `find ... -print | xargs -e grep ...`;
462 if nil, it executes
463         `find ... -exec grep ...`.")
464
465 (defvar igrep-program-default "grep"
466   "The default `grep` program.
467 This is passed by `igrep-read-program' to `completing-read' when
468 `igrep-program' is nil.")
469 \f
470 ;; Internal variables:
471
472 (defvar igrep-regex-history '()
473   "The minibuffer history list for `\\[igrep]'s REGEX argument.")
474
475 (defvar igrep-files-history '()
476   "The minibuffer history list for `\\[igrep]'s FILES argument.")
477 \f
478 ;; Commands:
479
480 ;;;###autoload
481 (defun igrep-insinuate (&optional override)
482   "Define `grep' aliases for the corresponding `igrep' commands.
483 With a prefix arg, OVERRIDE the current `grep' command definitions."
484   (interactive "P")
485   (if override
486       (defalias 'grep 'igrep)
487     (defadvice grep (around igrep-interactive first (&rest command-args)
488                             activate)
489       "If called interactively, use the `\\[igrep]' interface instead,
490 where COMMAND-ARGS is (PROGRAM REGEX FILES [OPTIONS]); if called
491 programmatically, COMMAND-ARGS is still (COMMAND)."
492       (interactive (igrep-read-args))
493       (if (interactive-p)
494           (apply 'igrep command-args)
495         ad-do-it)))
496   (if (or (not (fboundp 'grep-find))
497           override)
498       (defalias 'grep-find 'igrep-find))
499   (if (or (not (fboundp 'dired-do-grep))
500           override)
501       (defalias 'dired-do-grep 'dired-do-igrep))
502   (if (or (not (fboundp 'dired-do-grep-find))
503           override)
504       (defalias 'dired-do-grep-find 'dired-do-igrep-find))
505   (if (or (not (fboundp 'Buffer-menu-grep))
506           override)
507       (defalias 'Buffer-menu-grep 'Buffer-menu-igrep)))
508
509 (defsubst igrep-quote-file-name (file)
510   "Quote FILE name pattern for `shell-file-name'."
511   (if (fboundp 'shell-quote-wildcard-pattern) ; Emacs 21
512       (shell-quote-wildcard-pattern file)
513     (shell-quote-argument file)))
514
515 ;;;###autoload
516 (defun igrep (program regex files &optional options)
517   "*Run `grep` PROGRAM to match REGEX in FILES.
518 The output is displayed in the *igrep* buffer, which `\\[next-error]' and
519 `\\[compile-goto-error]' parse to find each line of matched text.
520
521 PROGRAM may be nil, in which case it defaults to `igrep-program'.
522
523 REGEX is automatically quoted by `shell-quote-argument'.
524
525 FILES is either a file name pattern (automatically quoted by
526 `shell-quote-wildcard-pattern', then expanded by the `shell-file-name' shell),
527 or a list of file name patterns.
528
529 Optional OPTIONS is also passed to PROGRAM; it defaults to `igrep-options'.
530
531 If a prefix argument \
532 \(`\\[universal-argument]') \
533 is given when called interactively,
534 or if `igrep-read-options' is set, OPTIONS is read from the minibuffer.
535
536 If two prefix arguments \
537 \(`\\[universal-argument] \\[universal-argument]') \
538 are given when called interactively,
539 or if `igrep-read-multiple-files' is set, FILES is read from the minibuffer
540 multiple times.
541
542 If three prefix arguments \
543 \(`\\[universal-argument] \\[universal-argument] \\[universal-argument]') \
544 are given when called interactively,
545 or if `igrep-read-options' and `igrep-read-multiple-files' are set,
546 OPTIONS is read and FILES is read multiple times.
547
548 If `igrep-find' is non-nil, the directory or directories
549 containing FILES is recursively searched for files whose name matches
550 the file name component of FILES (and whose contents match REGEX)."
551   (interactive
552    (igrep-read-args))
553   (if (null program)
554       (setq program (or igrep-program "grep")))
555   (if (null options)
556       (setq options igrep-options))
557   (if (not (listp files))               ; (stringp files)
558       (setq files (list files)))
559   (if (and (member ?~ (mapcar 'string-to-char files))
560            (save-match-data
561              (string-match "\\`[rj]?sh\\(\\.exe\\)?\\'"
562                            (file-name-nondirectory shell-file-name))))
563       ;; (restricted, job-control, or standard) Bourne shell doesn't expand ~:
564       (setq files
565             (mapcar 'expand-file-name files)))
566   (let* ((use-zgrep (cond ((eq igrep-use-zgrep t))
567                           (igrep-use-zgrep
568                            (let ((files files)
569                                  (compressed-p nil))
570                              (while (and files (not compressed-p))
571                                (if (save-match-data
572                                      (string-match "\\.g?[zZ]\\'" (car files)))
573                                    (setq compressed-p t))
574                                (setq files (cdr files)))
575                              compressed-p))
576                           (t nil)))
577          (command (format "%s -n %s %s %s %s %s"
578                           (if (and use-zgrep
579                                    (save-match-data
580                                      (not (string-match "\\`z" program))))
581                               (setq program (concat "z" program))
582                             program)
583                           (or options
584                               (and igrep-case-fold-search
585                                    (equal regex (downcase regex))
586                                    "-i")
587                               "")
588                           (or igrep-regex-option
589                               (progn
590                                 (if (save-match-data
591                                       (string-match "\\`-" regex))
592                                     (setq regex (concat "\\" regex)))
593                                 ""))
594                           (shell-quote-argument regex)
595                           (if igrep-find
596                               (if igrep-find-use-xargs
597                                   ""
598                                 (shell-quote-argument "{}"))
599                             (mapconcat (lambda (file)
600                                          (let ((dir (file-name-directory file)))
601                                            (if dir
602                                                (expand-file-name
603                                                 (file-name-nondirectory file)
604                                                 (igrep-quote-file-name dir))
605                                              file)))
606                                        files " "))
607                           igrep-null-device)))
608     (if igrep-find
609         (setq command
610               (igrep-format-find-command command files)))
611     (cond ((eq igrep-save-buffers t) (save-some-buffers t))
612           (igrep-save-buffers (save-some-buffers)))
613     (if (fboundp 'compilation-start)    ; Emacs 22
614         (let ((compilation-process-setup-function 'grep-process-setup))
615           (or (fboundp 'igrep-mode)
616               (define-derived-mode igrep-mode grep-mode "Igrep"))
617           (compilation-start command
618                              'igrep-mode
619                              nil
620                              (cond ((eq compilation-highlight-regexp t))
621                                    (compilation-highlight-regexp
622                                     (if (eq program "fgrep")
623                                         (regexp-quote regex)
624                                       regex)))))
625       (compile-internal command (format "No more %s matches" program)
626                         "Igrep" nil grep-regexp-alist))))
627
628 ;; Analogue commands:
629
630 (defmacro igrep-define (analogue-command &rest igrep-bindings)
631   "Define ANALOGUE-COMMAND as an `igrep' analogue command.
632 Optional (VARIABLE VALUE) arguments specify the temporary IGREP-BINDINGS
633 for the command."
634   ;; (interactive "SCommand: ") ; C-u => read bindings?
635   (let ((analogue-program (symbol-name analogue-command)))
636     `(defun ,analogue-command (&rest igrep-args)
637        ,(format "*Run `%s` via `\\[igrep]'.
638 All arguments (including prefix arguments, when called interactively)
639 are handled by `igrep'."
640                 analogue-program)
641        (interactive
642         (let ((igrep-program (if igrep-program ,analogue-program))
643               (igrep-program-default ,analogue-program))
644           (igrep-read-args)))
645        (let (,@ igrep-bindings)
646          (apply 'igrep
647                 (cond ((interactive-p) (car igrep-args))
648                       ((car igrep-args))
649                       (t ,analogue-program))
650                 (cdr igrep-args))))))
651
652 (igrep-define egrep)
653 (igrep-define fgrep)
654 (igrep-define agrep
655   (igrep-use-zgrep nil)
656   (igrep-regex-option "-e"))
657
658 ;; Recursive (`find`) commands:
659
660 ;;;###autoload
661 (defun igrep-find (&rest igrep-args)
662   "*Run `grep` via `find`; see `igrep' and `igrep-find'.
663 All IGREP-ARGS (including prefix arguments, when called interactively)
664 are handled by `igrep'."
665   (interactive
666    (let ((igrep-find t))
667      (igrep-read-args)))
668   (let ((igrep-find t))
669     (apply 'igrep igrep-args)))
670
671 ;; Analogue recursive (`find`) commands:
672
673 (defmacro igrep-find-define (analogue-command &rest igrep-bindings)
674   "Define ANALOGUE-COMMAND-find as an `igrep' analogue `find` command.
675 Optional (VARIABLE VALUE) arguments specify the temporary IGREP-BINDINGS
676 for the command."
677   ;; (interactive "SCommand: ") ; C-u => read bindings?
678   (let ((analogue-program (symbol-name analogue-command)))
679     (setq analogue-command
680           (intern (format "%s-find" analogue-command)))
681     `(defun ,analogue-command (&rest igrep-args)
682        ,(format "*Run `%s` via `\\[igrep-find]'.
683 All arguments (including prefix arguments, when called interactively)
684 are handled by `igrep'."
685                 analogue-program)
686        (interactive
687         (let ((igrep-program (if igrep-program ,analogue-program))
688               (igrep-program-default ,analogue-program)
689               (igrep-find t))
690           (igrep-read-args)))
691        (let (,@ igrep-bindings)
692          (apply 'igrep-find
693                 (cond ((interactive-p) (car igrep-args))
694                       ((car igrep-args))
695                       (t ,analogue-program))
696                 (cdr igrep-args))))))
697
698 (igrep-find-define egrep)
699 (igrep-find-define fgrep)
700 (igrep-find-define agrep
701   (igrep-use-zgrep nil)
702   (igrep-regex-option "-e"))
703
704 ;;;###autoload
705 (defun igrep-visited-files (program regex &optional options)
706   "*Run `grep` PROGRAM to match REGEX (with optional OPTIONS) \
707 on all visited files.
708 See `\\[igrep]'."
709   (interactive
710    (let ((igrep-args (igrep-read-args 'no-files)))
711      ;; Delete FILES:
712      (setcdr (nthcdr 1 igrep-args) (nthcdr 3 igrep-args))
713      igrep-args))
714   (igrep program regex
715          (let ((directory-abbrev-alist
716                 (cons (cons (regexp-quote (expand-file-name default-directory))
717                             "./")       ; or even ""
718                       directory-abbrev-alist)))
719            (mapcar 'abbreviate-file-name
720                    (apply 'nconc
721                           (mapcar (lambda (buffer)
722                                     (let ((file (buffer-file-name buffer)))
723                                       (if (and file
724                                                (cond ((featurep 'ange-ftp)
725                                                       (not (ange-ftp-ftp-name file)))
726                                                      ((featurep 'efs)
727                                                       (not (efs-ftp-path file)))
728                                                      (t t))
729                                                ;; (file-exists-p file)
730                                                )
731                                           (list file))))
732                                   (buffer-list)))))
733          options))
734
735 ;; Dired commands:
736
737 ;;;###autoload
738 (defun dired-do-igrep (program regex &optional options arg)
739   "*Search the marked (or next prefix ARG) files.
740 See `\\[igrep]' for a description of PROGRAM, REGEX, and OPTIONS."
741   (interactive
742    (let ((igrep-args
743           (let ((current-prefix-arg nil))
744             (igrep-read-args 'no-files))))
745      ;; Delete FILES:
746      (setcdr (nthcdr 1 igrep-args) (nthcdr 3 igrep-args))
747      ;; Append ARG:
748      (nconc igrep-args (list current-prefix-arg))))
749   (igrep program regex
750          (funcall (cond ((fboundp 'dired-get-marked-files) ; GNU Emacs
751                          'dired-get-marked-files)
752                         ((fboundp 'dired-mark-get-files) ; XEmacs
753                          'dired-mark-get-files))
754                   t arg)
755          options))
756
757 ;; Dired recursive (`find`) commands:
758
759 ;;;###autoload
760 (defun dired-do-igrep-find (program regex &optional options arg)
761   "*Run `grep` on the marked (or next prefix ARG) directories.
762 See `\\[igrep]' for a description of PROGRAM, REGEX, and OPTIONS."
763   (interactive
764    (let ((igrep-args
765           (let ((current-prefix-arg nil)
766                 (igrep-find t))
767             (igrep-read-args 'no-files))))
768      ;; Delete FILES:
769      (setcdr (nthcdr 1 igrep-args) (nthcdr 3 igrep-args))
770      ;; Append ARG:
771      (nconc igrep-args (list current-prefix-arg))))
772   (let ((igrep-find t))
773     (dired-do-igrep program regex options arg)))
774
775 ;; Buffer menu commands:
776
777 ;;;###autoload
778 (defun Buffer-menu-igrep (program regex &optional options)
779   "*Run `grep` on the files visited in buffers marked with '>'.
780 See `\\[igrep]' for a description of PROGRAM, REGEX, and OPTIONS."
781   (interactive
782    (let ((igrep-args (igrep-read-args 'no-files)))
783      ;; Delete FILES:
784      (setcdr (nthcdr 1 igrep-args) (nthcdr 3 igrep-args))
785      igrep-args))
786   ;; See Buffer-menu-select:
787   (let ((marked-files '())
788         marked-buffer
789         file)
790     (goto-char (point-min))
791     (while (search-forward "\n>" nil t)
792       (setq marked-buffer (Buffer-menu-buffer t)
793             file (buffer-file-name marked-buffer))
794       (if (and file
795                ;; local:
796                (cond ((featurep 'ange-ftp)
797                       (not (ange-ftp-ftp-name file)))
798                      ((featurep 'efs)
799                       (not (efs-ftp-path file)))
800                      (t t)))
801           (setq marked-files (cons file marked-files)))
802 ;;;    (let ((buffer-read-only nil))
803 ;;;      (delete-char -1)
804 ;;;      (insert ?\ ))
805       )
806     (setq marked-files (nreverse marked-files))
807     (igrep program regex
808            (let ((directory-abbrev-alist
809                   (cons (cons (regexp-quote (expand-file-name default-directory))
810                               "./")     ; or even ""
811                         directory-abbrev-alist)))
812              (mapcar 'abbreviate-file-name marked-files))
813            options)))
814 \f
815 ;; User functions:
816
817 (defun igrep-dired-file-current-word ()
818   "Return the current word in the file on this line, if it is visible;
819 else, return the file name on this line, if there is one;
820 otherwise, return the current word."
821   (let* ((dired-file
822           (dired-get-filename t t))
823          (dired-file-buffer
824           (if dired-file
825               (get-file-buffer (expand-file-name dired-file))))
826          (dired-file-buffer-window
827           (if dired-file-buffer
828               (get-buffer-window dired-file-buffer))))
829     (cond (dired-file-buffer-window (save-excursion
830                                       (set-buffer dired-file-buffer)
831                                       (current-word)))
832           (dired-file)
833           (t (current-word)))))
834
835 (defun igrep-buffer-file-name-pattern ()
836   "Return a shell file name pattern based on the visited file name.
837 If the `buffer-file-name' variable is nil, return \"*\"."
838   ;; (Based on other-possibly-interesting-files in ~/as-is/unix.el, by
839   ;; Wolfgang Rupprecht <wolfgang@mgm.mit.edu>.)
840   (if buffer-file-name
841       (let ((file-name (file-name-nondirectory buffer-file-name)))
842         (concat "*"
843                 (save-match-data
844                   (if (string-match "\\.[^.]+\\(\\.g?[zZ]\\)?\\'"
845                                     file-name)
846                       (substring file-name (match-beginning 0)
847                                  (match-end 0))))))
848     "*"))
849
850 (defun igrep-dired-directory-file-pattern ()
851   "Return a shell file name pattern based on `dired-directory', or \"*\"."
852   (cond ((stringp dired-directory)
853          (if (file-directory-p dired-directory)
854              "*"
855            (file-name-nondirectory dired-directory))) ; wildcard
856         ((consp dired-directory)        ; (DIR FILE ...)
857          (mapconcat 'identity (cdr dired-directory) " "))))
858 \f
859 ;; Utilities:
860
861 (defsubst igrep-file-directory (name)
862   "Return the directory component of NAME, or \".\" if it has none."
863   (directory-file-name (or (file-name-directory name)
864                            (file-name-as-directory "."))))
865
866 (defsubst igrep-file-pattern (name)
867   "Return the file component of NAME, or \"*\" if it has none."
868   (let ((pattern (file-name-nondirectory name)))
869        (if (string= pattern "")
870            "*"
871          pattern)))
872
873 (defun igrep-format-find-command (command files)
874   "Format `grep` COMMAND to be invoked via `find` on FILES."
875   (let ((directories '())
876         (patterns '()))
877     (while files
878       (let ((dir (igrep-file-directory (car files)))
879             (pat (igrep-file-pattern (car files))))
880         (if (and (not (string= dir "."))
881                  (file-symlink-p dir))
882             (setq dir (concat dir "/.")))
883         (if (not (member dir directories))
884             (setq directories (cons dir directories)))
885         (cond ((equal pat "*")
886                (setq patterns t))
887               ((and (listp patterns)
888                     (not (member pat patterns)))
889                (setq patterns (cons pat patterns)))))
890       (setq files (cdr files)))
891     (format (cond ((eq igrep-find-use-xargs 'gnu)
892                    ;; | \\\n
893                    "%s %s %s %s %s -print0 | xargs -0 -e %s")
894                   (igrep-find-use-xargs
895                    ;; | \\\n
896                    "%s %s %s %s %s -print | xargs -e %s")
897                   (t
898                    "%s %s %s %s %s -exec %s %s"))
899             igrep-find-program
900             (mapconcat 'igrep-quote-file-name (nreverse directories)
901                        " ")
902             (if igrep-find-prune-clause
903                 (format "%s -prune -o" igrep-find-prune-clause)
904               "")
905             (or igrep-find-file-clause "")
906             (if (listp patterns)
907                 (if (cdr patterns)      ; (> (length patterns) 1)
908                     (format "%s %s %s"
909                             (shell-quote-argument "(")
910                             (mapconcat (lambda (pat)
911                                          (format "-name %s"
912                                                  (shell-quote-argument pat)))
913                                        (nreverse patterns)
914                                        " -o ")
915                             (shell-quote-argument ")"))
916                   (format "-name %s" (shell-quote-argument (car patterns))))
917               "")
918             command
919             (shell-quote-argument ";")
920             )))
921
922 (defmacro igrep-default-arg (variable)
923   "Return the default arg based on VARIABLE."
924   `(if ,variable
925        (cond ((get (quote ,variable) major-mode)
926               (funcall (get (quote ,variable) major-mode)))
927              (t (funcall ,variable)))))
928
929 (defun igrep-default-regex ()
930   "Return the default REGEX for `\\[igrep]'."
931   (let ((default-regex (igrep-default-arg igrep-regex-default)))
932     (if (not (equal default-regex ""))
933         default-regex)))
934
935 (defun igrep-default-files ()
936   "Return the default FILES for `\\[igrep]'."
937   (let* ((dired-subdirectory (if (cond ((fboundp 'derived-mode-p) ; Emacs 21
938                                         (derived-mode-p 'dired-mode))
939                                        (t (eq major-mode 'dired-mode)))
940                                  (dired-current-directory t)))
941          (default-files (igrep-default-arg igrep-files-default)))
942     (if (not (listp default-files))     ; stringp
943         (setq default-files (list default-files)))
944     (if dired-subdirectory
945         (mapcar (lambda (file)
946                   (concat dired-subdirectory file))
947                 default-files)
948       default-files)))
949
950 (defsubst igrep-prefix (prefix string &rest strings)
951   "Concatenate PREFIX (if non-nil), STRING, and any other STRINGS."
952   (if (or prefix strings)
953       (apply 'concat prefix string strings)
954     string))
955
956 (defun igrep-read-args (&optional no-files)
957   "Read and return a list: (PROGRAM REGEX FILES OPTIONS).
958 If NO-FILES is non-nil, then FILES is not read and nil is returned
959 in its place."
960   (let* ((pre-prefix (if (and igrep-find (eq igrep-verbose-prompts t))
961                          "[find] "))
962          (program
963           (igrep-read-program pre-prefix))
964          (prefix (if (and program (eq igrep-verbose-prompts t))
965                      (igrep-prefix pre-prefix program " ")
966                    pre-prefix))
967          (options
968           (igrep-read-options prefix))
969          (post-prefix (if (and options (eq igrep-verbose-prompts t))
970                             (igrep-prefix prefix options " ")
971                           prefix)))
972     (list program
973           (igrep-read-regex post-prefix)
974           (if (not no-files)
975               (igrep-read-files post-prefix))
976           options)))
977
978 (defun igrep-read-program (&optional prompt-prefix)
979   "Read and return a `grep` program name from the minibuffer.
980 If `igrep-program' is non-nil, it.
981
982 Optional PROMPT-PREFIX is prepended to the \"Program: \" prompt."
983   (or igrep-program
984       (let ((prompt "Program: "))
985         (completing-read (igrep-prefix prompt-prefix prompt) igrep-program-table
986                          nil t igrep-program-default))))
987
988 (defun igrep-read-options (&optional prompt-prefix)
989   "Read and return an options string from the minibuffer.
990 If `current-prefix-arg' is '(4) or '(64), return `igrep-options'.
991
992 Optional PROMPT-PREFIX is prepended to the \"Options: \" prompt."
993   (if (or igrep-read-options
994           (and (consp current-prefix-arg)
995                (memq (prefix-numeric-value current-prefix-arg)
996                      '(4 64))))
997       (let ((prompt "Options: "))
998         (read-string (igrep-prefix prompt-prefix prompt)
999                      (or igrep-options "-")))
1000     igrep-options))
1001
1002 (defun igrep-read-regex (&optional prompt-prefix)
1003   "Read and return a `grep` regex(5) string from the minibuffer.
1004 Optional PROMPT-PREFIX is prepended to the \"Regex: \" prompt."
1005   (if igrep-insert-default-key
1006       (define-key minibuffer-local-map igrep-insert-default-key
1007         'igrep-insert-default-regex))
1008   (let* ((default-regex (igrep-default-regex))
1009          (prompt (igrep-prefix prompt-prefix
1010                                (if default-regex
1011                                    (format "Regex [default: %s]: "
1012                                            default-regex)
1013                                  "Regex: ")))
1014          (regex (cond ((featurep 'xemacs) ; incompatible
1015                        ;; DEFAULT-VALUE is the 7th arg in 21.4 (but 21.1
1016                        ;; only accepts 6 args):
1017                        (read-from-minibuffer prompt
1018                                              nil nil nil
1019                                              'igrep-regex-history
1020                                              nil)) ; ABBREV-TABLE
1021                       ((>= emacs-major-version 20)
1022                        (read-from-minibuffer prompt
1023                                              nil nil nil
1024                                              'igrep-regex-history
1025                                              default-regex))
1026                       (t
1027                        (read-from-minibuffer prompt
1028                                              nil nil nil
1029                                              'igrep-regex-history)))))
1030     (if (equal regex "")
1031         (progn
1032           (or (equal default-regex (car igrep-regex-history))
1033               (setq igrep-regex-history
1034                     (cons default-regex igrep-regex-history)))
1035           default-regex)
1036       regex)))
1037
1038 (defun igrep-insert-default-regex (&optional clear-minibuffer)
1039   "*Insert the default regex in the minibuffer.
1040 If a prefix argument is specified, CLEAR-MINIBUFFER contents first."
1041   (interactive "P")
1042   (if clear-minibuffer
1043       (delete-region (if (fboundp 'minibuffer-prompt-end) ; Emacs 21
1044                          (minibuffer-prompt-end)
1045                        (point-min))
1046                      (point-max)))
1047   (insert (or (save-excursion
1048                 (set-buffer (window-buffer minibuffer-scroll-window))
1049                 (igrep-default-regex))
1050               "")))
1051
1052 (defun igrep-insert-default-files (&optional clear-minibuffer)
1053   "*Insert the default files in the minibuffer.
1054 If a prefix argument is specified, CLEAR-MINIBUFFER contents first."
1055   (interactive "P")
1056   (if clear-minibuffer
1057       (delete-region (if (fboundp 'minibuffer-prompt-end) ; Emacs 21
1058                          (minibuffer-prompt-end)
1059                        (point-min))
1060                      (point-max)))
1061   (insert (mapconcat 'identity
1062                      (save-excursion
1063                        (set-buffer (window-buffer minibuffer-scroll-window))
1064                        (igrep-default-files))
1065                      " ")))
1066
1067 (defsubst igrep-default-key (command &optional keymap key)
1068   "Return the key bound to COMMAND in KEYMAP, preferably KEY."
1069   (if (null keymap)
1070       (setq keymap (current-global-map)))
1071   (if (and key
1072            (eq (lookup-key keymap key) command))
1073       key
1074     (where-is-internal command keymap t)))
1075
1076 (defun igrep-read-files (&optional prompt-prefix)
1077   "Read and return a file name pattern from the minibuffer.
1078 If `current-prefix-arg' is '(16) or '(64), read multiple file name
1079 patterns and return them in a list.  Optional PROMPT-PREFIX is
1080 prepended to the \"File(s): \" prompt."
1081   (let* ((default-files (igrep-default-files))
1082          (default-files-string (mapconcat 'identity default-files " "))
1083          (insert-default-directory igrep-insert-default-directory)
1084          (file (igrep-read-file-name
1085                 (igrep-prefix prompt-prefix
1086                               (if default-files
1087                                   (format "File(s) [default: %s]: "
1088                                           default-files-string)
1089                                 "File(s): "))
1090                 nil (if default-files default-files-string "") nil nil
1091                 'igrep-files-history))
1092          (files (list file)))
1093     (if (or igrep-read-multiple-files
1094             (and (consp current-prefix-arg)
1095                  (memq (prefix-numeric-value current-prefix-arg)
1096                        '(16 64))))
1097         (let* ((key (igrep-default-key 'exit-minibuffer
1098                                        minibuffer-local-completion-map
1099                                        "\r"))
1100                (prompt
1101                 (igrep-prefix prompt-prefix
1102                               (if igrep-verbose-prompts
1103                                   (format "File(s): [Type `%s' when done] "
1104                                           (key-description key))
1105                                 "File(s): "))))
1106           (while (and (setq file
1107                             (igrep-read-file-name prompt
1108                                                   nil "" nil nil
1109                                                   'igrep-files-history))
1110                       (not (equal file "")))
1111             (setq files (cons file files)))))
1112     (mapcar (lambda (file)
1113               (if (file-directory-p file)
1114                   ;; really should map expand-file-name over default-files:
1115                   (expand-file-name (if default-files default-files-string "*")
1116                                     file)
1117                 file))
1118             (nreverse files))))
1119
1120 (defun igrep-read-file-name (prompt
1121   &optional directory default existing initial history)
1122   "Just like `read-file-name,' but with optional HISTORY."
1123   (if igrep-insert-default-key
1124       (define-key minibuffer-local-completion-map igrep-insert-default-key
1125         'igrep-insert-default-files))
1126   (if history
1127       (let ((file-name-history (symbol-value history)))
1128         (prog1 (read-file-name prompt directory default existing initial)
1129           (set history file-name-history)))
1130     (read-file-name prompt directory default existing initial)))
1131 \f
1132 ;; Menu bar:
1133
1134 (if igrep-easy-menu
1135     (progn
1136       (easy-menu-define igrep-menu nil
1137         "Menu keymap for igrep."
1138         igrep-easy-menu)
1139       (cond ((fboundp 'add-submenu)     ; XEmacs
1140              (add-submenu '("Tools") igrep-menu "Grep..."))
1141             ((fboundp 'easy-menu-add-item) ; Emacs 20
1142              (easy-menu-add-item menu-bar-tools-menu nil igrep-menu
1143                                  'grep))
1144             (t                          ; Emacs 19
1145              (define-key-after menu-bar-tools-menu [igrep]
1146                (cons (car igrep-easy-menu) igrep-menu)
1147                (and (lookup-key menu-bar-tools-menu [grep]) 'grep))))))
1148 \f
1149 ;;; Local Variables:
1150 ;;; eval: (put 'igrep-define 'lisp-indent-function 1)
1151 ;;; eval: (put 'igrep-find-define 'lisp-indent-function 1)
1152 ;;; eval: (put 'easy-menu-define 'lisp-indent-function 'defun)
1153 ;;; End:
1154
1155 (provide 'igrep)
1156
1157 ;;; igrep.el ends here