|
|
1.1 root 1: ;;
2: ;; copyright (C) 1987, 1988 Franz Inc, Berkeley, Ca.
3: ;;
4: ;; The software, data and information contained herein are the property
5: ;; of Franz, Inc.
6: ;;
7: ;; This file (or any derivation of it) may be distributed without
8: ;; further permission from Franz Inc. as long as:
9: ;;
10: ;; * it is not part of a product for sale,
11: ;; * no charge is made for the distribution, other than a tape
12: ;; fee, and
13: ;; * all copyright notices and this notice are preserved.
14: ;;
15: ;; If you have any comments or questions on this interface, please feel
16: ;; free to contact Franz Inc. at
17: ;; Franz Inc.
18: ;; Attn: Kevin Layer
19: ;; 1995 University Ave
20: ;; Suite 275
21: ;; Berkeley, CA 94704
22: ;; (415) 548-3600
23: ;; or
24: ;; emacs-info%[email protected]
25: ;; ucbvax!franz!emacs-info
26: ;;
27: ;; $Header: tcplisp.el,v 1.11 88/11/21 13:37:02 layer Exp $
28: ;;
29: ;; Description:
30: ;; Implemented in this file are the backdoor lisp listener and lisp
31: ;; evalserver.
32:
33: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
34: ;;;;;;;;;;;;;;;;;;;;; The Backdoor Lisp Listener ;;;;;;;;;;;;;;;;;;;;;;;;;;;
35: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
36:
37: ;;;;
38: ;;; User Visibles
39: ;;;;
40:
41: (defvar fi:unix-domain t
42: "*If non-nil, then `fi:unix-domain-socket' specifies the name of the
43: socket file. It is recommended that this interface be used, and not
44: internet ports, because when internet ports are used only one process on a
45: machine may use this interface (it is a global resource). When using UNIX
46: domain sockets, communication is done through a socket file in the user's
47: home directory. But, if you really want to use internet ports, here are
48: the steps to take:
49:
50: 1. Set this variable to nil.
51: 2. Add the following line to /etc/services:
52: excl 6789/tcp
53: 3. Make sure `fi:local-host-name' is in /etc/hosts and points to the local
54: or loopback host.
55: 4. On the Common Lisp side, put the following in you .clinit.cl file:
56: (setq ipc:*inet-port* 6789) ; the number from /etc/services
57: (setq ipc:*unix-domain* nil)
58:
59: The problem with this, is that people can then use `telnet' to get a
60: listener on your lisp!")
61:
62: (defvar fi:unix-domain-socket "~/.excl_to_emacs"
63: "*The name of the socket file that lisp and emacs use to communicate.
64: This is used when fi:unix-domain is non-nil.")
65:
66: (defvar fi:local-host-name "localhost"
67: "*On 4.2 BSD the name of 127.1--usually localhost or loopback.
68: This is only used when fi:unix-domain is nil.")
69:
70: (defvar fi:excl-service-name "excl"
71: "*The service name from /etc/services (`tcp' type). This is only used
72: when fi:unix-domain is nil.")
73:
74: (defvar fi:source-info-not-found-hook 'find-tag
75: "*The value of this variable is funcalled when source information is not
76: present in Lisp for a symbol. The function is given one argument, the name
77: for which source is desired (a string). The null string means use the word
78: at the point as the search word. This allows the GNU Emacs tags facility
79: to be used when the information is not present in Lisp.")
80:
81: ;;;;
82: ;;; Internals
83: ;;;;
84:
85: (defvar fi::lisp-macroexpand-command
86: "(progn
87: (errorset
88: (let ((*print-pretty* t)(excl::*print-nickname* t)(*package* %s))
89: (with-open-file (*standard-input* \"%s\")
90: (lisp:prin1 (%s (lisp:read)))))
91: t)
92: (values))\n")
93:
94: (defvar fi::backdoor-process nil
95: "Process connected to sublist socket for fi:lisp-arglist and friends.")
96:
97: (defvar fi::backdoor-read-eval-loop
98: "(progn
99: (setf (getf (mp:process-property-list mp:*current-process*)
100: ':no-interrupts)
101: t)
102: (loop
103: (princ \"&\n\")
104: (errorset (eval (read)) t)))\n"
105: "The program executed by the backdoor lisp listener.")
106:
107: (defun fi:backdoor-eval (string &rest args)
108: "Evaluate apply format to STRING and ARGS and evaluate this in Common
109: Lisp at the other end of our socket."
110: (if (fi::background-sublisp-process)
111: (process-send-string
112: fi::backdoor-process
113: (format "(progn (format t \"\1\") %s)\n"
114: (apply 'format string args)))
115: (error "The backdoor listener to Lisp is not responding.")))
116:
117: (defun fi::background-sublisp-process ()
118: (if (or (null fi::backdoor-process)
119: (not (eq (process-status fi::backdoor-process) 'open)))
120: (progn
121: (and fi::backdoor-process
122: (delete-process fi::backdoor-process))
123: (setq fi::backdoor-process
124: (condition-case ()
125: (if fi:unix-domain
126: (open-network-stream "lisp-backdoor" nil
127: (expand-file-name fi:unix-domain-socket)
128: 0)
129: (open-network-stream "lisp-backdoor" nil fi:local-host-name
130: fi:excl-service-name))
131: (error nil)))
132: (if fi::backdoor-process
133: (progn
134: (setq fi::sublisp-returns-state nil)
135: (process-send-string ; first send the process name
136: fi::backdoor-process
137: (format "\"%s\"" "GNU Listener"))
138: (process-send-string fi::backdoor-process
139: fi::backdoor-read-eval-loop)
140: (set-process-filter fi::backdoor-process
141: 'fi::backdoor-filter)))))
142: fi::backdoor-process)
143:
144: ;; This is the filter for the back door lisp process.
145: ;; It collects output until it sees a ctl-A\n, then prints the preceding
146: ;; collected text. If the text fits on one line, it is printed to the message
147: ;; area. Otherwise it goes to a temporary pop up buffer.
148:
149: (defvar fi::sublisp-returns "")
150: (defvar fi::sublisp-returns-state nil)
151:
152: (defun fi::backdoor-filter (proc string)
153: ;; This collects everything returned until a ^A\n prompt is seen,
154: ;; then displays it. The first time is special cased to throw away
155: ;; the initial prompt without display. Someday we should use the state
156: ;; variable for detecting screwups and coordinating reset.
157: ;; The \n is part of the prompt so that a subsequent prettyprint isn't
158: ;; confused about the starting column.
159: ;;
160: ;; The first character has special meaning:
161: ;; ^A toss the output from lisp after eval (for fi:backdoor-eval)
162: ;; ^B for fi:lisp-find-tag
163: ;; ^C for fi:lisp-find-tag-other-window
164: (setq fi::sublisp-returns (concat fi::sublisp-returns string))
165: (let ((len (length fi::sublisp-returns)))
166: (if (and (= 10 (aref fi::sublisp-returns (- len 1))); newline
167: (= 1 (aref fi::sublisp-returns (- len 2))))
168: (if (eq fi::sublisp-returns-state nil); ignore the startup response
169: (setq fi::sublisp-returns-state t
170: fi::sublisp-returns "")
171: (progn (setq fi::sublisp-returns
172: (substring fi::sublisp-returns
173: (progn (string-match "\n*" fi::sublisp-returns)
174: (match-end 0))
175: -2))
176: (let ((first-char (elt fi::sublisp-returns 0)))
177: (cond
178: ((= first-char 1)
179: ;;throw away the result
180: (setq fi::sublisp-returns ""))
181: ((or (= first-char 2) (= first-char 3))
182: (fi::backdoor-find-tag-request (= first-char 3)))
183: (t
184: (if (or (> (length fi::sublisp-returns) 78)
185: ;; should be mbuf width
186: (string-match "\n" fi::sublisp-returns nil))
187: (with-output-to-temp-buffer "*CL-Help*"
188: (princ fi::sublisp-returns))
189: (message fi::sublisp-returns))
190: (setq fi::sublisp-returns "")))))))))
191:
192: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
193: ;;;;;;;;;;;;;;;;;;;;;;;; The Lisp Eval Server ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
194: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
195:
196: ;;;;
197: ;;; User Visibles
198: ;;;;
199:
200: (defconst fi:lisp-evalserver-timeout 5
201: "The time which fi:eval-in-lisp will wait before timing out and
202: signalling an error. Without a timeout Emacs would potentially be locked
203: out if Lisp did not `return' a result.")
204:
205: (defconst fi:lisp-evalserver-number-reads 20
206: "The number of times the Lisp eval server tries to read from the
207: lisp-evalserver process before giving up. Without this feature Emacs would
208: hang if Lisp got into an infinite loop while printing. If the size of the
209: values returned to Emacs is large, then the value of this variable should
210: be increased.")
211:
212: (defun fi:eval-in-lisp (string &rest args)
213: "Apply format (in Emacs) to STRING and ARGS and evaluate the result
214: in the Common Lisp to which we are connected. If a lisp-eval-server has
215: not been started, then this function starts it."
216: (if (not (fi::lisp-evalserver-process))
217: (error "The Lisp Eval Server is not responding."))
218: (setq fi::lisp-evalserver-response nil)
219: (setq fi::lisp-evalserver-returns "")
220: (process-send-string fi::lisp-evalserver-process
221: (format "%s\n" (apply 'format string args)))
222: (accept-process-output fi::lisp-evalserver-process
223: fi:lisp-evalserver-timeout)
224: (let ((i 0))
225: (while (and (< i fi:lisp-evalserver-number-reads)
226: fi::lisp-evalserver-collecting)
227: (accept-process-output fi::lisp-evalserver-process
228: fi:lisp-evalserver-timeout)
229: (setq i (+ i 1))))
230: (if (null fi::lisp-evalserver-response)
231: (error "timeout (%d secs) on response from lisp!"
232: fi:lisp-evalserver-timeout))
233: (condition-case ()
234: (car (read-from-string fi::lisp-evalserver-returns))
235: (error (error "parse error: %s" fi::lisp-evalserver-returns))))
236:
237: ;;;;
238: ;;; Internals
239: ;;;;
240:
241: (defconst fi::lisp-evalserver-read-eval-loop
242: "(progn
243: (setf (getf (mp:process-property-list mp:*current-process*)
244: ':no-interrupts)
245: t)
246: (setq tpl::*prompt* \"\")
247: (loop (errorset (princ (with-output-to-string (*standard-output*)
248: (princ \"&\")
249: (prin1 (eval (read)))
250: (princ \"\")))
251: t)))\n")
252:
253: (defvar fi::lisp-evalserver-process nil)
254:
255: (defun fi::lisp-evalserver-process ()
256: (if (or (null fi::lisp-evalserver-process)
257: (not (eq (process-status fi::lisp-evalserver-process) 'open)))
258: (progn
259: (and fi::lisp-evalserver-process
260: (delete-process fi::lisp-evalserver-process))
261: (setq fi::lisp-evalserver-process
262: (condition-case ()
263: (if fi:unix-domain
264: (open-network-stream "lisp-evalserver" nil
265: (expand-file-name fi:unix-domain-socket)
266: 0)
267: (open-network-stream "lisp-evalserver" nil fi:local-host-name
268: fi:excl-service-name))
269: (error nil)))
270: (if fi::lisp-evalserver-process
271: (progn
272: (setq fi::sublisp-returns-state nil)
273: (process-send-string ; first send the process name
274: fi::lisp-evalserver-process
275: (format "\"%s\"" "Lisp Eval Server"))
276: (process-send-string fi::lisp-evalserver-process
277: fi::lisp-evalserver-read-eval-loop)
278: ;; wait for the prompt
279: (accept-process-output fi::lisp-evalserver-process)
280: (set-process-filter fi::lisp-evalserver-process
281: 'fi::lisp-evalserver-filter)))))
282: fi::lisp-evalserver-process)
283:
284: (defconst fi::lisp-evalserver-returns "")
285: (defconst fi::lisp-evalserver-response nil)
286: (defconst fi::lisp-evalserver-counter 0)
287: (defconst fi::lisp-evalserver-collecting nil)
288:
289: (defun fi::lisp-evalserver-filter (proc string)
290: (setq fi::lisp-evalserver-counter (+ 1 fi::lisp-evalserver-counter))
291: (let ((len (length string)))
292: (cond
293: ((> len 0)
294: (if (/= (elt string 0) ?&)
295: (setq string (concat fi::lisp-evalserver-returns string)))
296:
297: (setq len (length string))
298: (if (and (= (elt string 0) ?&)
299: (= (elt string (- len 1)) ?))
300: (setq fi::lisp-evalserver-returns (substring string 1 (- len 1))
301: fi::lisp-evalserver-collecting nil
302: fi::lisp-evalserver-response t)
303: (setq fi::lisp-evalserver-returns string
304: fi::lisp-evalserver-collecting t))))))
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.