Mercurial > hg > xemacs-beta
comparison lisp/ilisp/bridge.el @ 0:376386a54a3c r19-14
Import from CVS: tag r19-14
author | cvs |
---|---|
date | Mon, 13 Aug 2007 08:45:50 +0200 |
parents | |
children | b82b59fe008d |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:376386a54a3c |
---|---|
1 ;;; -*-Emacs-Lisp-*- | |
2 ;;;%Header | |
3 ;;; Bridge process filter, V1.0 | |
4 ;;; Copyright (C) 1991 Chris McConnell, ccm@cs.cmu.edu | |
5 ;;; | |
6 ;;; Send mail to ilisp@lehman.com if you have problems. | |
7 ;;; | |
8 ;;; Send mail to ilisp-request@lehman.com if you want to be on the | |
9 ;;; ilisp mailing list. | |
10 | |
11 ;;; This file is part of GNU Emacs. | |
12 | |
13 ;;; GNU Emacs is distributed in the hope that it will be useful, | |
14 ;;; but WITHOUT ANY WARRANTY. No author or distributor | |
15 ;;; accepts responsibility to anyone for the consequences of using it | |
16 ;;; or for whether it serves any particular purpose or works at all, | |
17 ;;; unless he says so in writing. Refer to the GNU Emacs General Public | |
18 ;;; License for full details. | |
19 | |
20 ;;; Everyone is granted permission to copy, modify and redistribute | |
21 ;;; GNU Emacs, but only under the conditions described in the | |
22 ;;; GNU Emacs General Public License. A copy of this license is | |
23 ;;; supposed to have been given to you along with GNU Emacs so you | |
24 ;;; can know your rights and responsibilities. It should be in a | |
25 ;;; file named COPYING. Among other things, the copyright notice | |
26 ;;; and this notice must be preserved on all copies. | |
27 | |
28 ;;; Send any bugs or comments. Thanks to Todd Kaufmann for rewriting | |
29 ;;; the process filter for continuous handlers. | |
30 | |
31 ;;; USAGE: M-x install-bridge will add a process output filter to the | |
32 ;;; current buffer. Any output that the process does between | |
33 ;;; bridge-start-regexp and bridge-end-regexp will be bundled up and | |
34 ;;; passed to the first handler on bridge-handlers that matches the | |
35 ;;; output using string-match. If bridge-prompt-regexp shows up | |
36 ;;; before bridge-end-regexp, the bridge will be cancelled. If no | |
37 ;;; handler matches the output, the first symbol in the output is | |
38 ;;; assumed to be a buffer name and the rest of the output will be | |
39 ;;; sent to that buffer's process. This can be used to communicate | |
40 ;;; between processes or to set up two way interactions between Emacs | |
41 ;;; and an inferior process. | |
42 | |
43 ;;; You can write handlers that process the output in special ways. | |
44 ;;; See bridge-send-handler for the default handler. The command | |
45 ;;; hand-bridge is useful for testing. Keep in mind that all | |
46 ;;; variables are buffer local. | |
47 | |
48 ;;; YOUR .EMACS FILE: | |
49 ;;; | |
50 ;;; ;;; Set up load path to include bridge | |
51 ;;; (setq load-path (cons "/bridge-directory/" load-path)) | |
52 ;;; (autoload 'install-bridge "bridge" "Install a process bridge." t) | |
53 ;;; (setq bridge-hook | |
54 ;;; '(lambda () | |
55 ;;; ;; Example options | |
56 ;;; (setq bridge-source-insert nil) ;Don't insert in source buffer | |
57 ;;; (setq bridge-destination-insert nil) ;Don't insert in dest buffer | |
58 ;;; ;; Handle copy-it messages yourself | |
59 ;;; (setq bridge-handlers | |
60 ;;; '(("copy-it" . my-copy-handler))))) | |
61 | |
62 ;;; EXAMPLE: | |
63 ;;; # This pipes stdin to the named buffer in a Unix shell | |
64 ;;; alias devgnu '(echo -n "\!* "; cat -; echo -n "")' | |
65 ;;; | |
66 ;;; ls | devgnu *scratch* | |
67 | |
68 ;;;%Parameters | |
69 (defvar bridge-hook nil | |
70 "Hook called when a bridge is installed by install-hook.") | |
71 | |
72 (defvar bridge-start-regexp "" | |
73 "*Regular expression to match the start of a process bridge in | |
74 process output. It should be followed by a buffer name, the data to | |
75 be sent and a bridge-end-regexp.") | |
76 | |
77 (defvar bridge-end-regexp "" | |
78 "*Regular expression to match the end of a process bridge in process | |
79 output.") | |
80 | |
81 (defvar bridge-prompt-regexp nil | |
82 "*Regular expression for detecting a prompt. If there is a | |
83 comint-prompt-regexp, it will be initialized to that. A prompt before | |
84 a bridge-end-regexp will stop the process bridge.") | |
85 | |
86 (defvar bridge-handlers nil | |
87 "Alist of (regexp . handler) for handling process output delimited | |
88 by bridge-start-regexp and bridge-end-regexp. The first entry on the | |
89 list whose regexp matches the output will be called on the process and | |
90 the delimited output.") | |
91 | |
92 (defvar bridge-source-insert t | |
93 "*T to insert bridge input in the source buffer minus delimiters.") | |
94 | |
95 (defvar bridge-destination-insert t | |
96 "*T for bridge-send-handler to insert bridge input into the | |
97 destination buffer minus delimiters.") | |
98 | |
99 (defvar bridge-chunk-size 512 | |
100 "*Long inputs send to comint processes are broken up into chunks of | |
101 this size. If your process is choking on big inputs, try lowering the | |
102 value.") | |
103 | |
104 ;;;%Internal variables | |
105 (defvar bridge-old-filter nil | |
106 "Old filter for a bridged process buffer.") | |
107 | |
108 (defvar bridge-string nil | |
109 "The current output in the process bridge.") | |
110 | |
111 (defvar bridge-in-progress nil | |
112 "The current handler function, if any, that bridge passes strings on to, | |
113 or nil if none.") | |
114 | |
115 (defvar bridge-send-to-buffer nil | |
116 "The buffer that the default bridge-handler (bridge-send-handler) is | |
117 currently sending to, or nil if it hasn't started yet. Your handler | |
118 function can use this variable also.") | |
119 | |
120 (defvar bridge-last-failure () | |
121 "Last thing that broke the bridge handler. First item is function call | |
122 (eval'able); last item is error condition which resulted. This is provided | |
123 to help handler-writers in their debugging.") | |
124 | |
125 ;;;%Utilities | |
126 (defun bridge-insert (output) | |
127 "Insert process OUTPUT into the current buffer." | |
128 (if output | |
129 (let* ((buffer (current-buffer)) | |
130 (process (get-buffer-process buffer)) | |
131 (mark (process-mark process)) | |
132 (window (selected-window)) | |
133 (at-end nil)) | |
134 (if (eq (window-buffer window) buffer) | |
135 (setq at-end (= (point) mark)) | |
136 (setq window (get-buffer-window buffer))) | |
137 (save-excursion | |
138 (goto-char mark) | |
139 (insert output) | |
140 (set-marker mark (point))) | |
141 (if window | |
142 (progn | |
143 (if at-end (goto-char mark)) | |
144 (if (not (pos-visible-in-window-p (point) window)) | |
145 (let ((original (selected-window))) | |
146 (save-excursion | |
147 (select-window window) | |
148 (recenter '(center)) | |
149 (select-window original))))))))) | |
150 | |
151 ;;; | |
152 (defun bridge-send-string (process string) | |
153 "Send PROCESS the contents of STRING as input. | |
154 This is equivalent to process-send-string, except that long input strings | |
155 are broken up into chunks of size comint-input-chunk-size. Processes | |
156 are given a chance to output between chunks. This can help prevent processes | |
157 from hanging when you send them long inputs on some OS's." | |
158 (let* ((len (length string)) | |
159 (i (min len bridge-chunk-size))) | |
160 (process-send-string process (substring string 0 i)) | |
161 (while (< i len) | |
162 (let ((next-i (+ i bridge-chunk-size))) | |
163 (accept-process-output) | |
164 (process-send-string process (substring string i (min len next-i))) | |
165 (setq i next-i))))) | |
166 | |
167 ;;; | |
168 (defun bridge-call-handler (handler proc string) | |
169 "Funcall HANDLER on PROC, STRING carefully. Error is caught if happens, | |
170 and user is signaled. State is put in bridge-last-failure. Returns t if | |
171 handler executed without error." | |
172 (let ((inhibit-quit nil) | |
173 (failed nil)) | |
174 (condition-case err | |
175 (funcall handler proc string) | |
176 (error | |
177 (ding) | |
178 (setq failed t) | |
179 (message "bridge-handler \"%s\" failed %s (see bridge-last-failure)" | |
180 handler err) | |
181 (setq bridge-last-failure | |
182 (` ((funcall '(, handler) '(, proc) (, string)) | |
183 "Caused: " | |
184 (, err)))))) | |
185 (not failed))) | |
186 | |
187 ;;;%Handlers | |
188 (defun bridge-send-handler (process input) | |
189 "Send PROCESS INPUT to the buffer name found at the start of the | |
190 input. The input after the buffer name is sent to the buffer's | |
191 process if it has one. If bridge-destination-insert is T, the input | |
192 will be inserted into the buffer. If it does not have a process, it | |
193 will be inserted at the end of the buffer." | |
194 (if (null input) | |
195 (setq bridge-send-to-buffer nil) ; end of bridge | |
196 (let (buffer-and-start buffer-name dest to) | |
197 ;; if this is first time, get the buffer out of the first line | |
198 (cond ((not bridge-send-to-buffer) | |
199 (setq buffer-and-start (read-from-string input) | |
200 buffer-name (format "%s" (car (read-from-string input))) | |
201 dest (get-buffer buffer-name) | |
202 to (get-buffer-process dest) | |
203 input (substring input (cdr buffer-and-start))) | |
204 (setq bridge-send-to-buffer dest)) | |
205 (t | |
206 (setq buffer-name bridge-send-to-buffer | |
207 dest (get-buffer buffer-name) | |
208 to (get-buffer-process dest) | |
209 ))) | |
210 (if dest | |
211 (let ((buffer (current-buffer))) | |
212 (if bridge-destination-insert | |
213 (unwind-protect | |
214 (progn | |
215 (set-buffer dest) | |
216 (if to | |
217 (bridge-insert input) | |
218 (goto-char (point-max)) | |
219 (insert input))) | |
220 (set-buffer buffer))) | |
221 (if to (bridge-send-string to input))) | |
222 (error "%s is not a buffer" buffer-name))))) | |
223 | |
224 ;;;%Filter | |
225 (defun bridge-filter (process output) | |
226 "Given PROCESS and some OUTPUT, check for the presence of | |
227 bridge-start-regexp. Everything prior to this will be passed to the | |
228 normal filter function or inserted in the buffer if it is nil. The | |
229 output up to bridge-end-regexp will be sent to the first handler on | |
230 bridge-handlers that matches the string. If no handlers match, the | |
231 input will be sent to bridge-send-handler. If bridge-prompt-regexp is | |
232 encountered before the bridge-end-regexp, the bridge will be cancelled." | |
233 (let ((inhibit-quit t) | |
234 (match-data (match-data)) | |
235 (buffer (current-buffer)) | |
236 (process-buffer (process-buffer process)) | |
237 (case-fold-search t) | |
238 (start 0) (end 0) | |
239 function | |
240 b-start b-start-end b-end) | |
241 (set-buffer process-buffer) ;; access locals | |
242 (setq function bridge-in-progress) | |
243 | |
244 ;; How it works: | |
245 ;; | |
246 ;; start, end delimit the part of string we are interested in; | |
247 ;; initially both 0; after an iteration we move them to next string. | |
248 | |
249 ;; b-start, b-end delimit part of string to bridge (possibly whole string); | |
250 ;; this will be string between corresponding regexps. | |
251 | |
252 ;; There are two main cases when we come into loop: | |
253 | |
254 ;; bridge in progress | |
255 ;;0 setq b-start = start | |
256 ;;1 setq b-end (or end-pattern end) | |
257 ;;4 process string | |
258 ;;5 remove handler if end found | |
259 | |
260 ;; no bridge in progress | |
261 ;;0 setq b-start if see start-pattern | |
262 ;;1 setq b-end if bstart to (or end-pattern end) | |
263 ;;2 send (substring start b-start) to normal place | |
264 ;;3 find handler (in b-start, b-end) if not set | |
265 ;;4 process string | |
266 ;;5 remove handler if end found | |
267 | |
268 ;; equivalent sections have the same numbers here; | |
269 ;; we fold them together in this code. | |
270 | |
271 (unwind-protect | |
272 (while (< end (length output)) | |
273 | |
274 ;;0 setq b-start if find | |
275 (setq b-start | |
276 (cond (bridge-in-progress | |
277 (setq b-start-end start) | |
278 start) | |
279 ((string-match bridge-start-regexp output start) | |
280 (setq b-start-end (match-end 0)) | |
281 (match-beginning 0)) | |
282 (t nil))) | |
283 ;;1 setq b-end | |
284 (setq b-end | |
285 (if b-start | |
286 (let ((end-seen (string-match bridge-end-regexp | |
287 output b-start-end))) | |
288 (if end-seen (setq end (match-end 0))) | |
289 end-seen))) | |
290 (if (not b-end) (setq end (length output) | |
291 b-end (length output))) | |
292 | |
293 ;;1.5 - if see prompt before end, remove current | |
294 (if b-start | |
295 (let ((prompt (string-match bridge-prompt-regexp | |
296 output b-start-end))) | |
297 (if (and prompt (<= (match-end 0) b-end)) | |
298 (setq b-start nil ; b-start-end start | |
299 b-end start | |
300 end (match-end 0) | |
301 bridge-in-progress nil | |
302 )))) | |
303 | |
304 ;;2 send (substring start b-start) to old filter, if any | |
305 (if (/= start (or b-start end)) ; don't bother on empty string | |
306 (let ((pass-on (substring output start (or b-start end)))) | |
307 (if bridge-old-filter | |
308 (let ((old bridge-old-filter)) | |
309 (store-match-data match-data) | |
310 (funcall old process pass-on) | |
311 ;; if filter changed, re-install ourselves | |
312 (let ((new (process-filter process))) | |
313 (if (not (eq new 'bridge-filter)) | |
314 (progn (setq bridge-old-filter new) | |
315 (set-process-filter process 'bridge-filter))))) | |
316 (set-buffer process-buffer) | |
317 (bridge-insert pass-on)))) | |
318 | |
319 ;;3 find handler (in b-start, b-end) if none current | |
320 (if (and b-start (not bridge-in-progress)) | |
321 (let ((handlers bridge-handlers)) | |
322 (while (and handlers (not function)) | |
323 (let* ((handler (car handlers)) | |
324 (m (string-match (car handler) output b-start-end))) | |
325 (if (and m (< m b-end)) | |
326 (setq function (cdr handler)) | |
327 (setq handlers (cdr handlers))))) | |
328 ;; Set default handler if none | |
329 (if (null function) | |
330 (setq function 'bridge-send-handler)) | |
331 (setq bridge-in-progress function))) | |
332 ;;4 process string | |
333 (if function | |
334 (let ((ok t)) | |
335 (if (/= b-start-end b-end) | |
336 (let ((send (substring output b-start-end b-end))) | |
337 ;; also, insert the stuff in buffer between | |
338 ;; iff bridge-source-insert. | |
339 (if bridge-source-insert (bridge-insert send)) | |
340 ;; call handler on string | |
341 (setq ok (bridge-call-handler function process send)))) | |
342 ;;5 remove handler if end found | |
343 ;; if function removed then tell it that's all | |
344 (if (or (not ok) (/= b-end end));; saw end before end-of-string | |
345 (progn | |
346 (bridge-call-handler function process nil) | |
347 ;; have to remove function too for next time around | |
348 (setq function nil | |
349 bridge-in-progress nil) | |
350 )) | |
351 )) | |
352 | |
353 ;; continue looping, in case there's more string | |
354 (setq start end) | |
355 )) | |
356 ;; protected forms: restore buffer, match-data | |
357 (set-buffer buffer) | |
358 (store-match-data match-data) | |
359 )) | |
360 | |
361 ;;;%Interface | |
362 (defun install-bridge () | |
363 "Set up a process bridge in the current buffer." | |
364 (interactive) | |
365 (if (not (get-buffer-process (current-buffer))) | |
366 (error "%s does not have a process" (buffer-name (current-buffer))) | |
367 (make-local-variable 'bridge-start-regexp) | |
368 (make-local-variable 'bridge-end-regexp) | |
369 (make-local-variable 'bridge-prompt-regexp) | |
370 (make-local-variable 'bridge-handlers) | |
371 (make-local-variable 'bridge-source-insert) | |
372 (make-local-variable 'bridge-destination-insert) | |
373 (make-local-variable 'bridge-chunk-size) | |
374 (make-local-variable 'bridge-old-filter) | |
375 (make-local-variable 'bridge-string) | |
376 (make-local-variable 'bridge-in-progress) | |
377 (make-local-variable 'bridge-send-to-buffer) | |
378 (setq bridge-string nil bridge-in-progress nil | |
379 bridge-send-to-buffer nil) | |
380 (if (boundp 'comint-prompt-regexp) | |
381 (setq bridge-prompt-regexp comint-prompt-regexp)) | |
382 (let ((process (get-buffer-process (current-buffer)))) | |
383 (if process | |
384 (if (not (eq (process-filter process) 'bridge-filter)) | |
385 (progn | |
386 (setq bridge-old-filter (process-filter process)) | |
387 (set-process-filter process 'bridge-filter))) | |
388 (error "%s does not have a process" | |
389 (buffer-name (current-buffer))))) | |
390 (run-hooks 'bridge-hook) | |
391 (message "Process bridge is installed"))) | |
392 | |
393 ;;; | |
394 (defun reset-bridge () | |
395 "Must be called from the process's buffer. Removes any active bridge." | |
396 (interactive) | |
397 ;; for when things get wedged | |
398 (if bridge-in-progress | |
399 (unwind-protect | |
400 (funcall bridge-in-progress (get-buffer-process | |
401 (current-buffer)) | |
402 nil) | |
403 (setq bridge-in-progress nil)) | |
404 (message "No bridge in progress."))) | |
405 | |
406 ;;; | |
407 (defun remove-bridge () | |
408 "Remove bridge from the current buffer." | |
409 (interactive) | |
410 (let ((process (get-buffer-process (current-buffer)))) | |
411 (if (or (not process) (not (eq (process-filter process) 'bridge-filter))) | |
412 (error "%s has no bridge" (buffer-name (current-buffer))) | |
413 ;; remove any bridge-in-progress | |
414 (reset-bridge) | |
415 (set-process-filter process bridge-old-filter) | |
416 (funcall bridge-old-filter process bridge-string) | |
417 (message "Process bridge is removed.")))) | |
418 | |
419 ;;;% Utility for testing | |
420 (defun hand-bridge (start end) | |
421 "With point at bridge-start, sends bridge-start + string + | |
422 bridge-end to bridge-filter. With prefix, use current region to send." | |
423 (interactive "r") | |
424 (let ((p0 (if current-prefix-arg (min start end) | |
425 (if (looking-at bridge-start-regexp) (point) | |
426 (error "Not looking at bridge-start-regexp")))) | |
427 (p1 (if current-prefix-arg (max start end) | |
428 (if (re-search-forward bridge-end-regexp nil t) | |
429 (point) (error "Didn't see bridge-end-regexp"))))) | |
430 | |
431 (bridge-filter (get-buffer-process (current-buffer)) | |
432 (buffer-substring p0 p1)) | |
433 )) | |
434 | |
435 (provide 'bridge) |