comparison lisp/packages/igrep.el @ 24:4103f0995bd7 r19-15b95

Import from CVS: tag r19-15b95
author cvs
date Mon, 13 Aug 2007 08:51:03 +0200
parents
children 34a5b81f86ba
comparison
equal deleted inserted replaced
23:0edd3412f124 24:4103f0995bd7
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