24
|
1 ;;;; igrep.el --- An improved interface to `grep`.
|
|
2
|
|
3 ;;; Description:
|
|
4 ;;;
|
|
5 ;;; Define the \[igrep] command, which is like \[grep] except that it
|
|
6 ;;; takes three required arguments (PROGRAM, EXPRESSION, and FILES) and
|
|
7 ;;; an optional argument (OPTIONS) instead of just one argument (COMMAND).
|
|
8 ;;; Also define the analogous \[egrep] and \[fgrep] commands for convenience.
|
|
9 ;;;
|
|
10 ;;; Define the \[igrep-find] command, which is like \[igrep] except that
|
|
11 ;;; it uses `find` to recursively `grep` a directory. Also define the
|
|
12 ;;; analogous \[egrep-find] and \[fgrep-find] commands for convenience.
|
|
13 ;;;
|
|
14 ;;; \[igrep] and \[igrep-find] (and their analogues) provide defaults
|
|
15 ;;; for the EXPRESSION and FILES arguments when called interactively,
|
|
16 ;;; and there are global variables that control the syntax of the `grep`
|
|
17 ;;; and `find` shell commands that are executed. A user option controls
|
|
18 ;;; whether the corresponding GNU (gzip) "zPROGRAM" script is used, to
|
|
19 ;;; `grep` compressed files.
|
|
20 ;;;
|
|
21 ;;; \[agrep] and \[agrep-find] are also defined as convenient interfaces
|
|
22 ;;; to the approximate `grep` utility, which is distributed with the
|
|
23 ;;; `glimpse' indexing and query tool (available from
|
|
24 ;;; <URL:http://glimpse.cs.arizona.edu:1994/>).
|
|
25 ;;;
|
|
26 ;;; \[grep] itself is advised to provide the \[igrep] interface when
|
|
27 ;;; called interactively (when called programmatically, it still uses
|
|
28 ;;; the original argument list). \[grep-find] is defined as an alias
|
|
29 ;;; for \[igrep-find].
|
|
30 ;;;
|
|
31 ;;; When run interactively from Dired mode, the various \[igrep]
|
|
32 ;;; commands provide defaults for the EXPRESSION and FILES arguments
|
|
33 ;;; that are based on the visited directory (including any inserted
|
|
34 ;;; subdirectories) and the current file. In addition, the
|
|
35 ;;; \[dired-do-igrep] and \[dired-do-igrep-find] commands are defined
|
|
36 ;;; that respect the `dired-do-*' command calling conventions: a prefix
|
|
37 ;;; argument is interpreted as the number of succeeding files to `grep`,
|
|
38 ;;; otherwise all the marked files are `grep`ed. \[dired-do-grep] and
|
|
39 ;;; \[dired-do-grep-find] are defined as aliases for \[dired-do-igrep]
|
|
40 ;;; and \[dired-do-igrep-find].
|
|
41
|
|
42 ;;; Copyright:
|
|
43 ;;;
|
|
44 ;;; Copyright © 1994,1995,1996,1997 Kevin Rodgers
|
|
45 ;;;
|
|
46 ;;; This program is free software; you can redistribute it and/or modify
|
|
47 ;;; it under the terms of the GNU General Public License as published by
|
|
48 ;;; the Free Software Foundation; either version 2 of the License, or
|
|
49 ;;; at your option) any later version.
|
|
50 ;;;
|
|
51 ;;; This program is distributed in the hope that it will be useful,
|
|
52 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
53 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
54 ;;; GNU General Public License for more details.
|
|
55 ;;;
|
|
56 ;;; You should have received a copy of the GNU General Public License
|
|
57 ;;; along with this program; if not, write to the Free Software
|
|
58 ;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
59 ;;;
|
|
60 ;;; Neither my former nor current employer (Martin Marietta and
|
|
61 ;;; Information Handling Services, respectively) has disclaimed any
|
|
62 ;;; copyright interest in igrep.el.
|
|
63 ;;;
|
|
64 ;;; Kevin Rodgers <kevinr@ihs.com> Project Engineer
|
|
65 ;;; Information Handling Services Electronic Systems Development
|
|
66 ;;; 15 Inverness Way East, M/S A201
|
|
67 ;;; Englewood CO 80112 USA (303)397-2807[voice]/-2779[fax]
|
|
68
|
|
69 ;;; Installation:
|
|
70 ;;;
|
|
71 ;;; 1. Put this file in a directory that is a member of load-path, and
|
|
72 ;;; byte-compile it (e.g. with `M-x byte-compile-file') for better
|
|
73 ;;; performance. You can ignore any warnings about references to free
|
|
74 ;;; variables and "not known to be defined" functions.
|
|
75 ;;; 2. Put these forms in default.el or ~/.emacs:
|
|
76 ;;; (autoload (function igrep) "igrep"
|
|
77 ;;; "*Run `grep` PROGRAM to match EXPRESSION in FILES..." t)
|
|
78 ;;; (autoload (function igrep-find) "igrep"
|
|
79 ;;; "*Run `grep` via `find`..." t)
|
|
80 ;;; (autoload (function dired-do-igrep) "igrep"
|
|
81 ;;; "*Run `grep` on the marked (or next prefix ARG) files." t)
|
|
82 ;;; (autoload (function dired-do-igrep-find) "igrep"
|
|
83 ;;; "*Run `grep` via `find` on the marked (or next prefix ARG) directories." t)
|
|
84 ;;; 2. a. For completeness, you can add these forms as well:
|
|
85 ;;; (autoload (function grep) "igrep"
|
|
86 ;;; "*Run `grep` PROGRAM to match EXPRESSION in FILES..." t)
|
|
87 ;;; (autoload (function egrep) "igrep"
|
|
88 ;;; "*Run `egrep`..." t)
|
|
89 ;;; (autoload (function fgrep) "igrep"
|
|
90 ;;; "*Run `fgrep`..." t)
|
|
91 ;;; (autoload (function agrep) "igrep"
|
|
92 ;;; "*Run `agrep`..." t)
|
|
93 ;;; (autoload (function grep-find) "igrep"
|
|
94 ;;; "*Run `grep` via `find`..." t)
|
|
95 ;;; (autoload (function egrep-find) "igrep"
|
|
96 ;;; "*Run `egrep` via `find`..." t)
|
|
97 ;;; (autoload (function fgrep-find) "igrep"
|
|
98 ;;; "*Run `fgrep` via `find`..." t)
|
|
99 ;;; (autoload (function agrep-find) "igrep"
|
|
100 ;;; "*Run `agrep` via `find`..." t)
|
|
101 ;;; (autoload (function dired-do-grep) "igrep"
|
|
102 ;;; "*Run `grep` on the marked (or next prefix ARG) files." t)
|
|
103 ;;; (autoload (function dired-do-grep-find) "igrep"
|
|
104 ;;; "*Run `grep` via `find` on the marked (or next prefix ARG) directories." t)
|
|
105 ;;; 3. If you are running Windows 95/NT, you should install findutils
|
|
106 ;;; and grep from release 17.1 (or higher) of the Cygnus GNU-Win32
|
|
107 ;;; distribution. See <URL:http://www.cygnus.com/misc/gnu-win32/>.
|
|
108
|
|
109 ;;; Usage:
|
|
110 ;;;
|
|
111 ;;; M-x igrep M-x igrep-find
|
|
112 ;;; M-x grep M-x grep-find
|
|
113 ;;; M-x egrep M-x egrep-find
|
|
114 ;;; M-x fgrep M-x fgrep-find
|
|
115 ;;; M-x agrep M-x agrep-find
|
|
116 ;;; M-x dired-do-igrep M-x dired-do-igrep-find
|
|
117 ;;; M-x dired-do-grep M-x dired-do-grep-find
|
|
118 ;;; (Each of the 10 igrep commands accepts 1, 2, or 3 `C-u' prefix arguments.
|
|
119 ;;; The 2 Dired commands interpret a prefix argument like the regular `dired-do'
|
|
120 ;;; commands.)
|
|
121 ;;; C-x ` (M-x next-error) C-c C-c (M-x compile-goto-error) [in *igrep*]
|
|
122
|
|
123 ;;; Customization examples:
|
|
124 ;;;
|
|
125 ;;; To ignore case by default:
|
|
126 ;;; (setq igrep-options "-i")
|
|
127 ;;; To search subdirectories by default:
|
|
128 ;;; (setq igrep-find t)
|
|
129 ;;; To search files with the GNU (gzip) zgrep script:
|
|
130 ;;; (setq igrep-use-zgrep t)
|
|
131 ;;; or define new igrep commands (this works for zegrep and zfgrep as well):
|
|
132 ;;; (igrep-define zgrep) ; M-x zgrep
|
|
133 ;;; (igrep-find-define zgrep) ; M-x zgrep-find
|
|
134 ;;; To avoid exceeding some shells' limit on command argument length
|
|
135 ;;; (this only works when grep'ing files in the current directory):
|
|
136 ;;; (setq igrep-find t
|
|
137 ;;; igrep-find-prune-option "\\! -name .")
|
|
138
|
|
139 ;;; To do:
|
|
140 ;;;
|
|
141 ;;; 1. Delete the *-program variables (except for igrep-program) and
|
|
142 ;;; replace igrep-options with a table that maps igrep-program to the
|
|
143 ;;; appropriate options.
|
|
144 ;;; 2. Generalize support for the -prune find clause (e.g. -fstype nfs).
|
|
145 ;;; 3. Provide support for `glimpse`.
|
|
146 ;;; 4. Add a menu interface.
|
|
147
|
|
148 ;;; LCD Archive Entry:
|
|
149 ;;;
|
|
150 ;;; igrep|Kevin Rodgers|kevinr@ihs.com|
|
|
151 ;;; An improved interface to grep/egrep/fgrep; plus recursive `find` versions.|
|
|
152 ;;; 97/02/10|2.56|~/misc/igrep.el.Z|
|
|
153
|
|
154
|
|
155 ;;; Package interface:
|
|
156
|
|
157 (provide 'igrep)
|
|
158
|
|
159 (require 'backquote) ; igrep-with-default-in-history
|
|
160 (require 'compile) ; compile-internal and grep-regexp-
|
|
161 ; alist
|
|
162
|
|
163 (defconst igrep-version "2.56"
|
|
164 "Version of igrep.el")
|
|
165
|
|
166
|
|
167 ;;; User options:
|
|
168
|
|
169 (defvar igrep-options nil
|
|
170 "*The options passed by \\[igrep] to `igrep-program', or nil.
|
|
171
|
|
172 `-n' will automatically be passed to `igrep-program', to generate the
|
|
173 output expected by \\[next-error] and \\[compile-goto-error].
|
|
174 `-e' will automatically be passed to `igrep-program', if it supports
|
|
175 that option.")
|
|
176
|
|
177 (defvar igrep-read-options nil
|
|
178 "*If non-nil, `\\[igrep]' always prompts for options;
|
|
179 otherwise, it only prompts when 1 or 3 `C-u's are given as a prefix arg.")
|
|
180
|
|
181 (defvar igrep-read-multiple-files nil
|
|
182 "*If non-nil, `\\[igrep]' always prompts for multiple-files;
|
|
183 otherwise, it only prompts when 2 or 3 `C-u's are given as a prefix arg.")
|
|
184
|
|
185 (defvar igrep-verbose-prompts t
|
|
186 "*If t, \\[igrep] prompts for arguments verbosely;
|
|
187 if not t but non-nil, \\[igrep] prompts for arguments semi-verbosely;
|
|
188 if nil, \\[igrep] prompts for arguments tersely.")
|
|
189
|
|
190 (defvar igrep-save-buffers 'query
|
|
191 "*If t, \\[igrep] first saves each modified file buffer;
|
|
192 if not t but non-nil, \\[igrep] offers to save each modified file buffer.")
|
|
193
|
|
194 (defvar igrep-program-table ; referenced by igrep-use-zgrep
|
|
195 (let ((exec-directories exec-path)
|
|
196 (program-obarray (make-vector 11 0)))
|
|
197 (while exec-directories
|
|
198 (if (and (car exec-directories)
|
|
199 (file-directory-p (car exec-directories)))
|
|
200 (let ((grep-programs
|
|
201 (directory-files (car exec-directories)
|
|
202 nil "grep\\(\\.exe\\)?\\'")))
|
|
203 (while grep-programs
|
|
204 ;; Check `(file-executable-p (car grep-programs))'?
|
|
205 (if (save-match-data
|
|
206 (string-match "\\.exe\\'" (car grep-programs)))
|
|
207 (intern (substring (car grep-programs) 0 -4) program-obarray)
|
|
208 (intern (car grep-programs) program-obarray))
|
|
209 (setq grep-programs (cdr grep-programs)))))
|
|
210 (setq exec-directories (cdr exec-directories)))
|
|
211 program-obarray)
|
|
212 "An obarray of available `grep` programs, passed by `igrep-read-program'
|
|
213 to `completing-read' when `igrep-program' is nil.")
|
|
214
|
|
215 (defvar igrep-use-zgrep
|
|
216 (if (intern-soft "zgrep" igrep-program-table)
|
|
217 'files)
|
|
218 "If t, \\[igrep] searches files using the GNU (gzip) `zPROGRAM` script;
|
|
219 If not t but non-nil, \\[igrep] searches compressed FILES using `zPROGRAM`;
|
|
220 if nil, \\[igrep] searches files with `PROGRAM`.")
|
|
221
|
|
222
|
|
223 ;;; User variables:
|
|
224
|
|
225 (defvar igrep-program "grep"
|
|
226 "The default shell program run by \\[igrep] and \\[igrep-find].
|
|
227 It must take a `grep` expression argument and one or more file names.
|
|
228 If nil, \\[igrep] prompts for the program to run.")
|
|
229
|
|
230 (defvar igrep-expression-quote-char
|
|
231 (if (memq system-type '(ms-dos windows-95 windows-nt emx))
|
|
232 ?\"
|
|
233 ?')
|
|
234 "The character used to delimit the EXPRESSION argument to \\[igrep],
|
|
235 to protect it from shell filename expansion.")
|
|
236
|
|
237 (defvar igrep-expression-option
|
|
238 (if (or (eq system-type 'berkeley-unix)
|
|
239 (save-match-data
|
|
240 (string-match "-sco" system-configuration)))
|
|
241 "-e")
|
|
242 "If non-nil, the option used to specify the EXPRESSION argument to \\[igrep],
|
|
243 to protect an initial `-' from option processing.")
|
|
244
|
|
245 (defvar igrep-parenthesis-escape-char
|
|
246 (if (memq system-type '(ms-dos windows-95 windows-nt emx))
|
|
247 nil
|
|
248 ?\\)
|
|
249 "If non-nil, the character used by \\[igrep] to escape parentheses,
|
|
250 to protect them from shell interpretation.")
|
|
251
|
|
252 (defvar igrep-find nil
|
|
253 "If non-nil, \\[igrep] searches directories using `find`.
|
|
254 See \\[igrep-find].")
|
|
255
|
|
256 (defvar igrep-find-prune-options
|
|
257 (if (not (save-match-data
|
|
258 (string-match "-sco" system-configuration)))
|
|
259 "-name SCCS -o -name RCS")
|
|
260 "The `find` clause used to prune directories, or nil;
|
|
261 see \\[igrep-find].")
|
|
262
|
|
263 (defvar igrep-find-file-options "-type f -o -type l"
|
|
264 "The `find` clause used to filter files passed to `grep`, or nil;
|
|
265 see \\[igrep-find].")
|
|
266
|
|
267 (defvar igrep-find-use-xargs
|
|
268 (if (equal (call-process "find" nil nil nil
|
|
269 (if (boundp 'grep-null-device)
|
|
270 grep-null-device
|
|
271 "/dev/null")
|
|
272 "-print0")
|
|
273 0)
|
|
274 'gnu)
|
|
275 "If `gnu', \\[igrep-find] executes
|
|
276 `find ... -print0 | xargs -0 -e grep ...`;
|
|
277 if not `gnu' but non-nil, \\[igrep-find] executes
|
|
278 `find ... -print | xargs -e grep ...`;
|
|
279 if nil, \\[igrep-find] executes
|
|
280 `find ... -exec grep ...`.")
|
|
281
|
|
282 (defvar igrep-program-default nil
|
|
283 "The default `grep` program, passed by `igrep-read-program'
|
|
284 to `completing-read' when `igrep-program' is nil.")
|
|
285
|
|
286 (defvar igrep-expression-history '()
|
|
287 "The minibuffer history list for \\[igrep]'s EXPRESSION argument.")
|
|
288
|
|
289 (defvar igrep-files-history '()
|
|
290 "The minibuffer history list for \\[igrep]'s FILES argument.")
|
|
291
|
|
292
|
|
293 ;;; Commands:
|
|
294
|
|
295 ;; ;;;###autoload Not ready to replace compile.el's grep yet. -sb
|
|
296 (defadvice grep (around igrep-interface first (&rest grep-args) activate)
|
|
297 "If called interactively, use the \\[igrep] interface instead,
|
|
298 where GREP-ARGS is (PROGRAM EXPRESSION FILES OPTIONS);
|
|
299 if called programmatically, GREP-ARGS is still (COMMAND)."
|
|
300 (interactive (igrep-read-args))
|
|
301 (if (interactive-p)
|
|
302 (apply (function igrep) grep-args)
|
|
303 ad-do-it))
|
|
304
|
|
305 (defalias 'grep-find 'igrep-find)
|
|
306
|
|
307 ;;;###autoload
|
|
308 (defun igrep (program expression files &optional options)
|
|
309 "*Run `grep` PROGRAM to match EXPRESSION in FILES.
|
|
310 The output is displayed in the *igrep* buffer, which \\[next-error] and
|
|
311 \\[compile-goto-error] parse to find each line of matched text.
|
|
312
|
|
313 PROGRAM may be nil, in which case it defaults to `igrep-program'.
|
|
314
|
|
315 EXPRESSION is automatically delimited by `igrep-expression-quote-char'.
|
|
316
|
|
317 FILES is either a file name pattern (expanded by the shell named by
|
|
318 `shell-file-name') or a list of file name patterns.
|
|
319
|
|
320 Optional OPTIONS is also passed to PROGRAM; it defaults to `igrep-options'.
|
|
321
|
|
322 If a prefix argument \
|
|
323 \(\\[universal-argument]\) \
|
|
324 is given when called interactively,
|
|
325 or if `igrep-read-options' is set, OPTIONS is read from the minibuffer.
|
|
326
|
|
327 If two prefix arguments \
|
|
328 \(\\[universal-argument] \\[universal-argument]\) \
|
|
329 are given when called interactively,
|
|
330 or if `igrep-read-multiple-files' is set, FILES is read from the minibuffer
|
|
331 multiple times.
|
|
332
|
|
333 If three prefix arguments \
|
|
334 \(\\[universal-argument] \\[universal-argument] \\[universal-argument]\) \
|
|
335 are given when called interactively,
|
|
336 or if `igrep-read-options' and `igrep-read-multiple-files' are set,
|
|
337 OPTIONS is read and FILES is read multiple times.
|
|
338
|
|
339 If `igrep-find' is non-nil, the directory or directories
|
|
340 containing FILES is recursively searched for files whose name matches
|
|
341 the file name component of FILES \(and whose contents match
|
|
342 EXPRESSION\)."
|
|
343 (interactive
|
|
344 (igrep-read-args))
|
|
345 (if (null program)
|
|
346 (setq program (or igrep-program "grep")))
|
|
347 (if (null options)
|
|
348 (setq options igrep-options))
|
|
349 (if (not (listp files)) ; (stringp files)
|
|
350 (setq files (list files)))
|
|
351 (if (string-match "^[rj]?sh$" (file-name-nondirectory shell-file-name))
|
|
352 ;; (restricted, job-control, or standard) Bourne shell doesn't expand ~:
|
|
353 (setq files
|
|
354 (mapcar 'expand-file-name files)))
|
|
355 (let* ((win32-quote-process-args nil) ; work around NT Emacs hack
|
|
356 (use-zgrep (cond ((eq igrep-use-zgrep t))
|
|
357 (igrep-use-zgrep
|
|
358 (let ((files files)
|
|
359 (compressed-p nil))
|
|
360 (while (and files (not compressed-p))
|
|
361 (if (save-match-data
|
|
362 (string-match "\\.g?[zZ]\\'" (car files)))
|
|
363 (setq compressed-p t))
|
|
364 (setq files (cdr files)))
|
|
365 compressed-p))
|
|
366 (t nil)))
|
|
367 (command (format "%s -n %s %s %c%s%c %s %s"
|
|
368 (if (and use-zgrep
|
|
369 (save-match-data
|
|
370 (not (string-match "\\`z" program))))
|
|
371 (setq program (concat "z" program))
|
|
372 program)
|
|
373 (or options "")
|
|
374 (or igrep-expression-option
|
|
375 (progn
|
|
376 (if (save-match-data
|
|
377 (string-match "\\`-" expression))
|
|
378 (setq expression (concat "\\" expression)))
|
|
379 ""))
|
|
380 igrep-expression-quote-char
|
|
381 expression
|
|
382 igrep-expression-quote-char
|
|
383 (if igrep-find
|
|
384 (if igrep-find-use-xargs
|
|
385 ""
|
|
386 "\"{}\"")
|
|
387 (mapconcat (function identity) files " "))
|
|
388 (if (boundp 'grep-null-device)
|
|
389 grep-null-device
|
|
390 "/dev/null"))))
|
|
391 (if igrep-find
|
|
392 (setq command
|
|
393 (igrep-format-find-command command files)))
|
|
394 (cond ((eq igrep-save-buffers t) (save-some-buffers t))
|
|
395 (igrep-save-buffers (save-some-buffers)))
|
|
396 (compile-internal command
|
|
397 (format "No more %c%s%c matches"
|
|
398 igrep-expression-quote-char
|
|
399 program
|
|
400 igrep-expression-quote-char)
|
|
401 "igrep" nil grep-regexp-alist)))
|
|
402
|
|
403 ;; Analogue commands:
|
|
404
|
|
405 ;;;###autoload
|
|
406 (defmacro igrep-define (analogue-command &rest igrep-bindings)
|
|
407 "Define ANALOGUE-COMMAND as an `igrep' analogue command.
|
|
408 Optional (VARIABLE VALUE) arguments specify temporary bindings for the command."
|
|
409 ;;; (interactive "SCommand: ") ; C-u => read bindings?
|
|
410 (let ((analogue-program (symbol-name analogue-command)))
|
|
411 (` (defun (, analogue-command) (&rest igrep-args)
|
|
412 (, (format "*Run `%s` via \\[igrep].
|
|
413 All arguments \(including prefix arguments, when called interactively\)
|
|
414 are handled by `igrep'."
|
|
415 analogue-program))
|
|
416 (interactive
|
|
417 (let ((igrep-program (if igrep-program (, analogue-program)))
|
|
418 (igrep-program-default (, analogue-program)))
|
|
419 (igrep-read-args)))
|
|
420 (let ( (,@ igrep-bindings))
|
|
421 (apply (function igrep)
|
|
422 (cond ((interactive-p) (car igrep-args))
|
|
423 ((car igrep-args))
|
|
424 (t (, analogue-program)))
|
|
425 (cdr igrep-args)))))))
|
|
426
|
|
427 (igrep-define egrep)
|
|
428 (igrep-define fgrep)
|
|
429 (igrep-define agrep
|
|
430 (igrep-use-zgrep nil)
|
|
431 (igrep-expression-option "-e"))
|
|
432
|
|
433
|
|
434 ;; Recursive (`find`) commands:
|
|
435
|
|
436 ;;;###autoload
|
|
437 (defun igrep-find (&rest igrep-args)
|
|
438 "*Run `grep` via `find`; see \\[igrep] and `igrep-find'.
|
|
439 All arguments \(including prefix arguments, when called interactively\)
|
|
440 are handled by `igrep'."
|
|
441 (interactive
|
|
442 (let ((igrep-find t))
|
|
443 (igrep-read-args)))
|
|
444 (let ((igrep-find t))
|
|
445 (apply (function igrep) igrep-args)))
|
|
446
|
|
447 ;; Analogue recursive (`find`) commands:
|
|
448
|
|
449 ;;;###autoload
|
|
450 (defmacro igrep-find-define (analogue-command &rest igrep-bindings)
|
|
451 "Define ANALOGUE-COMMAND-find as an `igrep' analogue `find` command.
|
|
452 Optional (VARIABLE VALUE) arguments specify temporary bindings for the command."
|
|
453 ;;; (interactive "SCommand: ") ; C-u => read bindings?
|
|
454 (let ((analogue-program (symbol-name analogue-command)))
|
|
455 (setq analogue-command
|
|
456 (intern (format "%s-find" analogue-command)))
|
|
457 (` (defun (, analogue-command) (&rest igrep-args)
|
|
458 (, (format "*Run `%s` via \\[igrep-find].
|
|
459 All arguments \(including prefix arguments, when called interactively\)
|
|
460 are handled by `igrep'."
|
|
461 analogue-program))
|
|
462 (interactive
|
|
463 (let ((igrep-program (if igrep-program (, analogue-program)))
|
|
464 (igrep-program-default (, analogue-program))
|
|
465 (igrep-find t))
|
|
466 (igrep-read-args)))
|
|
467 (let ( (,@ igrep-bindings))
|
|
468 (apply (function igrep-find)
|
|
469 (cond ((interactive-p) (car igrep-args))
|
|
470 ((car igrep-args))
|
|
471 (t (, analogue-program)))
|
|
472 (cdr igrep-args)))))))
|
|
473
|
|
474 (igrep-find-define egrep)
|
|
475 (igrep-find-define fgrep)
|
|
476 (igrep-find-define agrep
|
|
477 (igrep-use-zgrep nil)
|
|
478 (igrep-expression-option "-e"))
|
|
479
|
|
480
|
|
481 ;; Dired commands:
|
|
482
|
|
483 ;;;###autoload
|
|
484 (defun dired-do-igrep (program expression &optional options arg)
|
|
485 "*Run `grep` PROGRAM to match EXPRESSION (with optional OPTIONS)
|
|
486 on the marked (or next prefix ARG) files."
|
|
487 (interactive
|
|
488 (let* ((current-prefix-arg nil)
|
|
489 (igrep-args (igrep-read-args t)))
|
|
490 ;; Delete FILES:
|
|
491 (setcdr (nthcdr 1 igrep-args) (nthcdr 3 igrep-args))
|
|
492 ;; Append ARG:
|
|
493 (nconc igrep-args (list current-prefix-arg))))
|
|
494 (igrep program
|
|
495 expression
|
|
496 (funcall (cond ((fboundp 'dired-get-marked-files) ; GNU Emacs
|
|
497 'dired-get-marked-files)
|
|
498 ((fboundp 'dired-mark-get-files) ; XEmacs
|
|
499 'dired-mark-get-files))
|
|
500 t arg)
|
|
501 options))
|
|
502
|
|
503 ;;;###autoload
|
|
504 (defalias 'dired-do-grep 'dired-do-igrep)
|
|
505
|
|
506 ;; Dired recursive (`find`) commands:
|
|
507
|
|
508 ;;;###autoload
|
|
509 (defun dired-do-igrep-find (program expression &optional options arg)
|
|
510 "*Run `grep` PROGRAM to match EXPRESSION (with optional OPTIONS)
|
|
511 on the marked (or next prefix ARG) directories."
|
|
512 (interactive
|
|
513 (let* ((current-prefix-arg nil)
|
|
514 (igrep-find t)
|
|
515 (igrep-args (igrep-read-args t)))
|
|
516 ;; Delete FILES:
|
|
517 (setcdr (nthcdr 1 igrep-args) (nthcdr 3 igrep-args))
|
|
518 ;; Append ARG:
|
|
519 (nconc igrep-args (list current-prefix-arg))))
|
|
520 (let ((igrep-find t))
|
|
521 (dired-do-igrep program expression options arg)))
|
|
522
|
|
523 ;;;###autoload
|
|
524 (defalias 'dired-do-grep-find 'dired-do-igrep-find)
|
|
525
|
|
526
|
|
527 ;;; Utilities:
|
|
528
|
|
529 (defsubst igrep-file-directory (name)
|
|
530 ;; Return the directory component of NAME, or "." if it has no
|
|
531 ;; directory component.
|
|
532 (directory-file-name (or (file-name-directory name)
|
|
533 (file-name-as-directory "."))))
|
|
534
|
|
535 (defsubst igrep-file-pattern (name)
|
|
536 ;; Return the file component of NAME, or "*" if it has no file
|
|
537 ;; component.
|
|
538 (let ((pattern (file-name-nondirectory name)))
|
|
539 (if (string= pattern "")
|
|
540 "*"
|
|
541 pattern)))
|
|
542
|
|
543 (defun igrep-format-find-command (command files)
|
|
544 ;; Format `grep` COMMAND to be invoked via `find` on FILES.
|
|
545 (let ((directories '())
|
|
546 (patterns '()))
|
|
547 (while files
|
|
548 (let ((dir (igrep-file-directory (car files)))
|
|
549 (pat (igrep-file-pattern (car files))))
|
|
550 (if (and (not (string= dir "."))
|
|
551 (file-symlink-p dir))
|
|
552 (setq dir (concat dir "/.")))
|
|
553 (if (not (member dir directories))
|
|
554 (setq directories (cons dir directories)))
|
|
555 (cond ((equal pat "*")
|
|
556 (setq patterns t))
|
|
557 ((and (listp patterns)
|
|
558 (not (member pat patterns)))
|
|
559 (setq patterns (cons pat patterns)))))
|
|
560 (setq files (cdr files)))
|
|
561 (format (cond ((eq igrep-find-use-xargs 'gnu)
|
|
562 ;; | \\\n
|
|
563 "find %s %s %s %s -print0 | xargs -0 -e %s")
|
|
564 (igrep-find-use-xargs
|
|
565 ;; | \\\n
|
|
566 "find %s %s %s %s -print | xargs -e %s")
|
|
567 ;;; ((memq system-type '(ms-dos windows-95 windows-nt emx))
|
|
568 ;;; "find %s %s %s %s -exec %s ;")
|
|
569 (t
|
|
570 "find %s %s %s %s -exec %s \\;"))
|
|
571 (mapconcat (function identity) (nreverse directories)
|
|
572 " ")
|
|
573 (if igrep-find-prune-options
|
|
574 (format "-type d %c( %s %c) -prune -o"
|
|
575 (or igrep-parenthesis-escape-char ? )
|
|
576 igrep-find-prune-options
|
|
577 (or igrep-parenthesis-escape-char ? ))
|
|
578 "")
|
|
579 (if igrep-find-file-options
|
|
580 (format "%c( %s %c)"
|
|
581 (or igrep-parenthesis-escape-char ? )
|
|
582 igrep-find-file-options
|
|
583 (or igrep-parenthesis-escape-char ? ))
|
|
584 "")
|
|
585 (if (listp patterns)
|
|
586 (if (cdr patterns) ; (> (length patterns) 1)
|
|
587 (format "%c( %s %c)"
|
|
588 (or igrep-parenthesis-escape-char " ")
|
|
589 (mapconcat (function (lambda (pat)
|
|
590 (format "-name \"%s\"" pat)))
|
|
591 (nreverse patterns)
|
|
592 " -o ")
|
|
593 (or igrep-parenthesis-escape-char " "))
|
|
594 (format "-name \"%s\"" (car patterns)))
|
|
595 "")
|
|
596 command)))
|
|
597
|
|
598 (defun igrep-read-args (&optional no-files)
|
|
599 ;; Read and return a list: (PROGRAM EXPRESSION FILES OPTIONS).
|
|
600 ;; If NO-FILES is non-nil, then FILES is not read and nil is returned
|
|
601 ;; in its place.
|
|
602 (let* ((program (igrep-read-program (if igrep-verbose-prompts
|
|
603 (if igrep-find
|
|
604 "[find] "))))
|
|
605 (prompt-prefix (if igrep-verbose-prompts
|
|
606 (apply (function concat)
|
|
607 (if igrep-find
|
|
608 "[find] ")
|
|
609 (if (eq igrep-verbose-prompts t)
|
|
610 (list program " ")))))
|
|
611 (options (igrep-read-options prompt-prefix)))
|
|
612 (if (eq igrep-verbose-prompts t)
|
|
613 (setq prompt-prefix
|
|
614 (concat prompt-prefix options " ")))
|
|
615 (list program
|
|
616 (igrep-read-expression prompt-prefix)
|
|
617 (if (not no-files)
|
|
618 (igrep-read-files prompt-prefix))
|
|
619 options)))
|
|
620
|
|
621 (defsubst igrep-prefix (prefix string)
|
|
622 ;; If PREFIX is non-nil, concatenate it and STRING; otherwise, return STRING.
|
|
623 (if prefix
|
|
624 (concat prefix string)
|
|
625 string))
|
|
626
|
|
627 (defun igrep-read-program (&optional prompt-prefix)
|
|
628 ;; If igrep-program is nil, read and return a program name from the
|
|
629 ;; minibuffer; otherwise, return igrep-program.
|
|
630 ;; Optional PROMPT-PREFIX is prepended to the "Program: " prompt.
|
|
631 (or igrep-program
|
|
632 (let ((prompt "Program: "))
|
|
633 (completing-read (igrep-prefix prompt-prefix prompt) igrep-program-table
|
|
634 nil t (or igrep-program-default "grep")))))
|
|
635
|
|
636 (defun igrep-read-options (&optional prompt-prefix)
|
|
637 ;; If current-prefix-arg is '(4) or '(64), read and return an options
|
|
638 ;; string from the minibuffer; otherwise, return igrep-options.
|
|
639 ;; Optional PROMPT-PREFIX is prepended to the "Options: " prompt.
|
|
640 (if (or igrep-read-options
|
|
641 (and (consp current-prefix-arg)
|
|
642 (memq (prefix-numeric-value current-prefix-arg)
|
|
643 '(4 64))))
|
|
644 (let ((prompt "Options: "))
|
|
645 (read-string (igrep-prefix prompt-prefix prompt)
|
|
646 (or igrep-options "-")))
|
|
647 igrep-options))
|
|
648
|
|
649 (defun igrep-read-expression (&optional prompt-prefix)
|
|
650 ;; Read and return a `grep` expression string from the minibuffer.
|
|
651 ;; Optional PROMPT-PREFIX is prepended to the "Expression: " prompt.
|
|
652 (let ((default-expression (igrep-default-expression)))
|
|
653 (if (string= default-expression "")
|
|
654 (read-from-minibuffer (igrep-prefix prompt-prefix "Expression: ")
|
|
655 nil nil nil 'igrep-expression-history)
|
|
656 (let ((expression
|
|
657 (igrep-read-string-with-default-in-history
|
|
658 (igrep-prefix prompt-prefix (format "Expression (default %s): "
|
|
659 default-expression))
|
|
660 default-expression
|
|
661 'igrep-expression-history)))
|
|
662 (if (string= expression "")
|
|
663 default-expression
|
|
664 expression)))))
|
|
665
|
|
666 (defun igrep-default-expression ()
|
|
667 (if (eq major-mode 'dired-mode)
|
|
668 (let ((dired-file (dired-get-filename nil t)))
|
|
669 (save-excursion
|
|
670 (set-buffer (or (and dired-file (get-file-buffer dired-file))
|
|
671 (other-buffer (current-buffer) t)))
|
|
672 (current-word)))
|
|
673 (current-word)))
|
|
674
|
|
675 (defsubst igrep-default-key ()
|
|
676 ;; Return the key bound to `exit-minibuffer', preferably "\r".
|
|
677 (if (eq (lookup-key minibuffer-local-completion-map "\r")
|
|
678 (function exit-minibuffer))
|
|
679 "\r"
|
|
680 (where-is-internal (function exit-minibuffer)
|
|
681 minibuffer-local-completion-map
|
|
682 t)))
|
|
683
|
|
684 (defun igrep-read-files (&optional prompt-prefix)
|
|
685 ;; Read and return a file name pattern from the minibuffer. If
|
|
686 ;; current-prefix-arg is '(16) or '(64), read multiple file name
|
|
687 ;; patterns and return them in a list. Optional PROMPT-PREFIX is
|
|
688 ;; prepended to the "File(s): " prompt.
|
|
689 (let* ((dired-subdirectory (if (eq major-mode 'dired-mode)
|
|
690 (dired-current-directory t)))
|
|
691 (default-files (concat dired-subdirectory
|
|
692 (igrep-default-file-pattern)))
|
|
693 (prompt (format "File(s) (default %s): " default-files))
|
|
694 (insert-default-directory nil) ; use relative path names
|
|
695 (file (igrep-read-file-name-with-default-in-history
|
|
696 (igrep-prefix prompt-prefix prompt)
|
|
697 default-files
|
|
698 nil
|
|
699 'igrep-files-history)))
|
|
700 (if (or igrep-read-multiple-files
|
|
701 (and (consp current-prefix-arg)
|
|
702 (memq (prefix-numeric-value current-prefix-arg)
|
|
703 '(16 64))))
|
|
704 (let ((files (list file)))
|
|
705 (setq prompt
|
|
706 (igrep-prefix prompt-prefix
|
|
707 (if igrep-verbose-prompts
|
|
708 (format "File(s): [Type `%s' when done] "
|
|
709 (key-description (igrep-default-key)))
|
|
710 "File(s): ")))
|
|
711 (while (not (string= (setq file
|
|
712 (igrep-read-file-name prompt
|
|
713 nil "" nil nil
|
|
714 'igrep-files-history))
|
|
715 ""))
|
|
716 (setq files (cons file files)))
|
|
717 (nreverse files))
|
|
718 file)))
|
|
719
|
|
720 (defmacro igrep-with-default-in-history (default history &rest body)
|
|
721 ;; Temporarily append DEFAULT to HISTORY, and execute BODY forms.
|
|
722 (` (progn
|
|
723 ;; Append default to history:
|
|
724 (set history
|
|
725 (cons (, default)
|
|
726 (if (boundp (, history))
|
|
727 (symbol-value (, history))
|
|
728 '())))
|
|
729 (unwind-protect ; Make sure the history is restored.
|
|
730 ;; Execute body:
|
|
731 (progn (,@ body))
|
|
732 ;; Delete default from history (undo the append above):
|
|
733 (setcdr (symbol-value (, history))
|
|
734 (nthcdr 2 (symbol-value (, history))))))))
|
|
735
|
|
736 (defun igrep-read-string-with-default-in-history (prompt default history)
|
|
737 ;; Read a string from the minibuffer, prompting with string PROMPT.
|
|
738 ;; DEFAULT can be inserted into the minibuffer with `previous-
|
|
739 ;; history-element'; HISTORY is a symbol whose value (if bound) is a
|
|
740 ;; list of previous results, most recent first.
|
|
741 (let ((string (igrep-with-default-in-history default history
|
|
742 (read-from-minibuffer prompt nil nil nil history))))
|
|
743 ;; Replace empty string in history with default:
|
|
744 (if (string= string "")
|
|
745 (setcar (symbol-value history) default))
|
|
746 string))
|
|
747
|
|
748 (defun igrep-read-file-name-with-default-in-history
|
|
749 (prompt &optional default initial history)
|
|
750 ;; Read a file name from the minibuffer, prompting with string PROMPT.
|
|
751 ;; DEFAULT can be inserted into the minibuffer with `previous-
|
|
752 ;; history-element'; HISTORY is a symbol whose value (if any) is a
|
|
753 ;; list of previous results, most recent first.
|
|
754 (igrep-with-default-in-history default history
|
|
755 (igrep-read-file-name prompt nil default nil initial history)))
|
|
756
|
|
757 (defun igrep-read-file-name (prompt
|
|
758 &optional directory default existing initial history)
|
|
759 ;; Just like read-file-name, but with optional HISTORY.
|
|
760 ;; Also: convert DIRECTORY to DIRECTORY/* file name pattern.
|
|
761 (let ((file-name
|
|
762 (if history
|
|
763 (let ((file-name-history (symbol-value history)))
|
|
764 (prog1 (read-file-name prompt directory default existing initial)
|
|
765 (set history file-name-history)))
|
|
766 (read-file-name prompt directory default existing initial))))
|
|
767 (if (and (not (string-equal file-name ""))
|
|
768 (file-directory-p file-name))
|
|
769 (expand-file-name "*" file-name)
|
|
770 file-name)))
|
|
771
|
|
772 (defun igrep-default-file-pattern ()
|
|
773 ;; Return a shell file name pattern that matches files with the same
|
|
774 ;; extension as the file being visited in the current buffer.
|
|
775 ;; (Based on other-possibly-interesting-files in ~/as-is/unix.el, by
|
|
776 ;; Wolfgang Rupprecht <wolfgang@mgm.mit.edu>.)
|
|
777 (if (eq major-mode 'dired-mode)
|
|
778 (cond ((stringp dired-directory)
|
|
779 (if (file-directory-p dired-directory)
|
|
780 "*"
|
|
781 (file-name-nondirectory dired-directory))) ; wildcard
|
|
782 ((consp dired-directory) ; (DIR FILE ...)
|
|
783 (mapconcat 'identity (cdr dired-directory) " ")))
|
|
784 (if buffer-file-name
|
|
785 (let ((file-name (file-name-nondirectory buffer-file-name)))
|
|
786 (concat "*"
|
|
787 (save-match-data
|
|
788 (if (string-match "\\.[^.]+\\(\\.g?[zZ]\\)?$" file-name)
|
|
789 (substring file-name
|
|
790 (match-beginning 0) (match-end 0))))))
|
|
791 "*")))
|
|
792
|
|
793 ;;; Local Variables:
|
|
794 ;;; eval: (put 'igrep-with-default-in-history 'lisp-indent-hook 2)
|
|
795 ;;; eval: (put 'igrep-define 'lisp-indent-hook 1)
|
|
796 ;;; eval: (put 'igrep-find-define 'lisp-indent-hook 1)
|
|
797 ;;; End:
|
|
798
|
|
799 ;;;; igrep.el ends here
|
|
800
|