2 ;;;; DDE Support for XLISP-STAT
3 ;;;; Copyright (c) 1999, by Luke Tierney
4 ;;;; You may give out copies of this software; for conditions see the file
5 ;;;; COPYING included with this distribution.
14 (export '(dde-request dde-poke dde-execute
))
16 (defun dde-execute (con cmd
&key timeout
)
18 (dde-client-transaction con
:data cmd
:timeout timeout
)
19 (dde-client-transaction con
:data cmd
)))
21 (defun dde-request (con item
&key binary timeout
)
23 (dde-client-transaction con
:type
:request
:item item
:binary binary
25 (dde-client-transaction con
:type
:request
:item item
:binary binary
)))
27 (defun dde-poke (con item value
&key timeout
)
28 (let ((vstring (if (stringp value
) value
(format nil
"~s" value
))))
30 (dde-client-transaction con
:type
:poke
:item item
:data vstring
32 (dde-client-transaction con
:type
:poke
:item item
:data vstring
))))
34 ;;**** A little example:
36 (let* ((c (dde-connect "XLISP-STAT"))
37 (success (if c
(dde-execute c
(format nil
"~s" e
)) nil
))
38 (v (if success
(dde-request c
"value") nil
)))
39 (when c
(dde-disconnect c
))
42 (error "evaluation failed"))))
49 (defconstant *dde-servers
* (make-hash-table :test
'equal
))
50 (defconstant *dde-conversations
* (make-hash-table))
51 (defparameter *dde-debug
* nil
)
53 ;;**** need to be able to remove service too
54 (defun dde-add-server (server)
55 (let ((service (string-upcase (send server
:name
)))
56 (old (gethash service
*dde-servers
*)))
57 (when (or old
(dde-name-service service
))
58 (setf (gethash service
*dde-servers
*) server
)
61 (defun dde-find-server (name)
62 (values (gethash (string-upcase name
) *dde-servers
*)))
64 ;;**** could use a convention about getting back error info from executes
65 (defun dde-server-callback (type fmt hconv hsz1 hsz2 data dw1 dw1
)
66 (dde-debug "Server args: ~s~%" (list type fmt hconv hsz1 hsz2 data dw1 dw1
))
70 (let ((server (gethash hsz2
*dde-servers
*)))
71 (and server
(send server
:has-topic hsz1
))))
73 (let* ((server (gethash hsz2
*dde-servers
*))
74 (conv (send server
:make-conversation hsz1
)))
75 (setf (gethash hconv
*dde-conversations
*) conv
)))
78 (flet ((servs (servname server
)
79 (let ((topics (send server
:topics
)))
80 (dolist (topic topics
)
81 (push (list servname topic
) val
)))))
82 (maphash #'servs
*dde-servers
*)
84 (t (let ((conv (gethash hconv
*dde-conversations
*)))
86 (:execute
(send conv
:execute data
))
87 (:request
(send conv
:request hsz2
))
88 (:poke
(send conv
:poke hsz2 data
))
90 (remhash hconv
*dde-conversations
*)
91 (send conv
:disconnect
))))))))
93 (defun dde-debug (fmt &rest args
)
94 (when *dde-debug
* (apply #'format
*debug-io
* fmt args
)))
101 (defproto dde-server-proto
'(name topics
))
103 (defmeth dde-server-proto
:isnew
(name)
104 (setf (slot-value 'name
) name
))
106 (defmeth dde-server-proto
:name
() (slot-value 'name
))
107 (defmeth dde-server-proto
:has-topic
(topic)
108 (if (assoc topic
(slot-value 'topics
) :test
#'equal
) t nil
))
110 (defmeth dde-server-proto
:topics
()
111 (mapcar #'first
(slot-value 'topics
)))
113 (defmeth dde-server-proto
:add-topic
(topic factory
)
114 (let* ((topic (string-upcase topic
))
115 (entry (assoc topic
(slot-value 'topics
) :test
#'equal
)))
117 (setf (second entry
) factory
)
118 (push (list topic factory
) (slot-value 'topics
)))))
120 (defmeth dde-server-proto
:make-conversation
(topic)
121 (let ((confac (second (assoc topic
(slot-value 'topics
) :test
#'equal
))))
123 (send confac
:new self topic
)
124 (funcall confac self topic
))))
128 ;;;; Standard Conversation
131 (defproto dde-conversation-proto
'(server topic value
))
133 (defmeth dde-conversation-proto
:isnew
(server topic
)
134 (setf (slot-value 'server
) server
)
135 (setf (slot-value 'topic
) topic
))
137 ;; Using the following modified readtable allows commands to be
138 ;; enclosed in [...]. This seems to be necessary to properly handle
139 ;; execute transactions sent by Excel.
140 (defconstant *dde-readtable
* (copy-readtable nil
))
141 (set-macro-character #\
[ #'(lambda (x y
) (values)) t
*dde-readtable
*)
142 (set-macro-character #\
] #'(lambda (x y
) (values)) t
*dde-readtable
*)
144 (defmeth dde-conversation-proto
:execute
(cmd)
145 (let ((*readtable
* *dde-readtable
*)
146 (eof (cons nil nil
)))
147 (with-input-from-string (s cmd
)
148 (do ((expr (read s nil eof
) (read s nil eof
)))
150 (setf (slot-value 'value
) (eval expr
))))
153 (defmeth dde-conversation-proto
:request
(item)
154 (when (equal item
"VALUE")
155 (format nil
"~s" (slot-value 'value
))))
157 (defmeth dde-conversation-proto
:poke
(item data
) nil
)
159 (defmeth dde-conversation-proto
:disconnect
() nil
)
163 ;;;; Initialize the Standard Server
166 (let ((server (send dde-server-proto
:new
"XLISP-STAT")))
167 (send server
:add-topic
"XLISP-STAT" dde-conversation-proto
)
168 (send server
:add-topic
"SYSTEM" dde-conversation-proto
)
169 (setf (gethash "XLISP-STAT" system
::*dde-servers
*) server
))
173 ;;;; Command Line Conversation Prototype
176 (defproto cmdline-conversation-proto nil nil dde-conversation-proto
)
178 ;;**** get this internally?
179 (defconstant *banner
*
180 (format nil
"XLISP-PLUS version 3.04~%~
181 Portions Copyright (c) 1988, by David Betz.~%~
182 Modified by Thomas Almy and others.~%~
183 XLISP-STAT Release ~d.~d.~d.~%~
184 Copyright (c) 1989-1999, by Luke Tierney.~%"
187 xls-subminor-release
))
189 (defmeth cmdline-conversation-proto
:isnew
(server topic
)
190 (call-next-method server topic
)
191 (setf (slot-value 'value
)
192 (format nil
"~a~%~a" *banner
* (make-prompt-string))))
194 (defun make-prompt-string ()
195 (if (eq (find-package "USER") *package
*)
197 (format nil
"~a> " (package-name *package
*))))
199 (defun read-eval-print-from-string (string)
200 (with-input-from-string (*standard-input
* string
)
201 (with-output-to-string (*standard-output
*)
202 (let ((*debug-io
* *standard-output
*))
203 (let ((eof (cons nil nil
)))
204 (do ((expr (read *standard-input
* nil eof
)
205 (read *standard-input
* nil eof
)))
207 (setf +++ ++ ++ + + - - expr
)
208 (multiple-value-bind (values error
)
210 (multiple-value-list (eval expr
)))
212 (error (format t
"~&Error: ~a~%" error
))
213 (t (setf *** ** ** * * (first values
))
214 (format t
"~{~&~s~%~}" values
))))
215 (format t
"~&~a" (make-prompt-string))))))))
217 (defmeth cmdline-conversation-proto
:execute
(cmd)
218 (setf (slot-value 'value
) (read-eval-print-from-string cmd
)))
220 (defmeth cmdline-conversation-proto
:request
(item)
221 (when (equal item
"VALUE")
222 (let ((value (slot-value 'value
)))
223 (setf (slot-value 'value
) "")
228 ;;;; Add Command Line Handler to Server
231 (send (dde-find-server "XLISP-STAT")
232 :add-topic
"CMDLINE" cmdline-conversation-proto
)