2 /******************************************************************************
3 * MODULE : connection.cpp
4 * DESCRIPTION: TeXmacs connections
5 * COPYRIGHT : (C) 2000 Joris van der Hoeven
6 *******************************************************************************
7 * When the underlying link of a connection is "alive",
8 * then the status of the connection is either WAITING_FOR_OUTPUT
9 * (when waiting for output from the plugin) or WAITING_FOR_INPUT.
10 * If the underlying link is "dead", then the status is either
11 * CONNECTION_DEAD (usually) or CONNECTION_DYING (if we are still
12 * waiting for some residual output from the plugin).
13 *******************************************************************************
14 * This software falls under the GNU general public license version 3 or later.
15 * It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE
16 * in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>.
17 ******************************************************************************/
19 #include "connect.hpp"
20 #include "socket_notifier.hpp"
21 #include "iterator.hpp"
22 #include "convert.hpp"
24 #include "resource.hpp"
25 #include "Generic/input.hpp"
28 static tree
connection_retrieve (string name
, string session
);
30 /******************************************************************************
31 * The connection resource
32 ******************************************************************************/
35 struct connection_rep
: rep
<connection
> {
36 string name
; // name of the pipe type
37 string session
; // name of the session
38 tm_link ln
; // the underlying link
39 int status
; // status of the connection
40 int prev_status
; // last notified status
41 bool forced_eval
; // forced input evaluation without call backs
42 texmacs_input tm_in
; // texmacs input handler for data from child
43 texmacs_input tm_err
; // texmacs input handler for errors from child
46 connection_rep (string name
, string session
, tm_link ln
);
47 string
start (bool again
);
48 void write (string s
);
49 void read (int channel
);
54 RESOURCE_CODE(connection
);
56 /******************************************************************************
57 * Routines for connections
58 ******************************************************************************/
61 connection_callback (void *obj
, void* info
) {
63 //cout << "connection callback " << obj << LF;
64 connection_rep
*con
= (connection_rep
*) obj
;
69 connection_rep::connection_rep (string name2
, string session2
, tm_link ln2
):
70 rep
<connection
> (name2
* "-" * session2
),
71 name (name2
), session (session2
), ln (ln2
),
72 status (CONNECTION_DEAD
), prev_status (CONNECTION_DEAD
),
74 tm_in ("output"), tm_err ("error") {}
77 connection_rep::start (bool again
) {
80 message
= "Continuation of#" * name
* "#session";
81 status
= WAITING_FOR_INPUT
;
84 message
= ln
->start ();
85 tm_in
= texmacs_input ("output");
86 tm_err
= texmacs_input ("error");
87 status
= WAITING_FOR_OUTPUT
;
88 if (again
&& (message
== "ok")) {
90 (void) connection_retrieve (name
, session
);
95 ln
->set_command (command (connection_callback
, this));
100 connection_rep::write (string s
) {
101 ln
->write (s
, LINK_IN
);
104 status
= WAITING_FOR_OUTPUT
;
108 connection_rep::read (int channel
) {
109 if (channel
== LINK_OUT
) {
110 string s
= ln
->read (LINK_OUT
);
113 if (tm_in
->put (s
[i
])) {
114 status
= WAITING_FOR_INPUT
;
115 if (DEBUG_IO
) cout
<< LF
<< HRULE
;
118 else if (channel
== LINK_ERR
) {
119 string s
= ln
->read (LINK_ERR
);
122 (void) tm_err
->put (s
[i
]);
127 status
= CONNECTION_DEAD
;
132 connection_rep::stop () {
137 if (status
== WAITING_FOR_OUTPUT
)
138 status
= CONNECTION_DYING
;
143 connection_rep::interrupt () {
146 if (status
== WAITING_FOR_OUTPUT
)
147 status
= CONNECTION_DYING
;
151 /******************************************************************************
152 * Handle output from extern applications
153 ******************************************************************************/
156 connection_notify (connection con
, string ch
, tree t
) {
158 call ("connection-notify",
160 object (con
->session
),
166 connection_notify_status (connection con
) {
168 (con
->status
== CONNECTION_DYING
? WAITING_FOR_OUTPUT
: con
->status
);
169 if (status
== con
->prev_status
) return;
170 call ("connection-notify-status",
172 object (con
->session
),
174 con
->prev_status
= status
;
178 connection_rep::listen () {
179 if (forced_eval
) return;
180 connection_notify_status (this);
181 if (status
!= CONNECTION_DEAD
) {
183 connection_notify (this, "error", tm_err
->get ("error"));
185 connection_notify (this, "output", tm_in
->get ("output"));
186 connection_notify (this, "prompt", tm_in
->get ("prompt"));
187 connection_notify (this, "input", tm_in
->get ("input"));
188 tree t
= connection_handlers (name
);
190 for (i
=0; i
<n
; i
++) {
191 tree doc
= tm_in
->get (t
[i
][0]->label
);
192 if (doc
!= "") call (t
[i
][1]->label
, doc
);
193 doc
= tm_err
->get (t
[i
][0]->label
);
194 if (doc
!= "") call (t
[i
][1]->label
, doc
);
197 connection_notify_status (this);
200 /******************************************************************************
201 * Connection type information
202 ******************************************************************************/
205 connection_declared (string name
) {
206 return as_bool (call ("connection-defined?", name
));
210 connection_info (string name
, string session
) {
211 return stree_to_tree (call ("connection-info", name
, session
));
215 connection_handlers (string name
) {
216 static hashmap
<string
,tree
> handlers (tuple ());
217 if (!handlers
->contains (name
))
218 handlers (name
)= stree_to_tree (call ("connection-get-handlers", name
));
219 return handlers
[name
];
222 /******************************************************************************
223 * First part of interface (using a specific connection)
224 ******************************************************************************/
227 connection_start (string name
, string session
, bool again
) {
228 // cout << "Start " << name << ", " << session << "\n";
229 if (!connection_declared (name
))
230 return "Error: connection " * name
* " has not been declared";
232 connection con
= connection (name
* "-" * session
);
235 cout
<< "TeXmacs] Starting session '" << session
<< "'\n";
236 tree t
= connection_info (name
, session
);
237 if (is_tuple (t
, "pipe", 1)) {
238 tm_link ln
= make_pipe_link (t
[1]->label
);
239 con
= tm_new
<connection_rep
> (name
, session
, ln
);
241 else if (is_tuple (t
, "socket", 2)) {
242 tm_link ln
= make_socket_link (t
[1]->label
, as_int (t
[2]->label
));
243 con
= tm_new
<connection_rep
> (name
, session
, ln
);
245 else if (is_tuple (t
, "dynlink", 3)) {
247 make_dynamic_link (t
[1]->label
, t
[2]->label
, t
[3]->label
, session
);
248 con
= tm_new
<connection_rep
> (name
, session
, ln
);
252 return con
->start (again
);
256 connection_write (string name
, string session
, string s
) {
257 // cout << "Write " << name << ", " << session << ", " << s << "\n";
258 connection con
= connection (name
* "-" * session
);
259 if (is_nil (con
)) return;
264 connection_write (string name
, string session
, tree t
) {
265 // cout << "Write " << name << ", " << session << ", " << t << "\n";
266 string s
= as_string (call ("plugin-serialize", name
, tree_to_stree (t
)));
267 connection_write (name
, session
, s
);
271 connection_read (string name
, string session
, string channel
) {
272 // cout << "Read " << name << ", " << session << ", " << channel << "\n";
273 connection con
= connection (name
* "-" * session
);
274 if (is_nil (con
)) return "";
275 con
->read (LINK_ERR
);
276 tree t
= con
->tm_err
->get (channel
);
278 con
->read (LINK_OUT
);
279 t
= con
->tm_in
->get (channel
);
281 // cout << "Result " << t << "\n";
286 connection_interrupt (string name
, string session
) {
287 // cout << "Interrupt " << name << ", " << session << "\n";
288 connection con
= connection (name
* "-" * session
);
289 if (is_nil (con
)) return;
295 connection_stop (string name
, string session
) {
296 // cout << "Stop " << name << ", " << session << "\n";
297 connection con
= connection (name
* "-" * session
);
298 if (is_nil (con
)) return;
304 connection_status (string name
, string session
) {
305 // cout << "Status " << name << ", " << session << " -> ";
306 connection con
= connection (name
* "-" * session
);
307 if ((!is_nil (con
)) && (con
->status
== CONNECTION_DYING
))
308 return WAITING_FOR_OUTPUT
;
309 if (is_nil (con
) || (!con
->ln
->alive
)) return CONNECTION_DEAD
;
310 // cout << con->ln->status << "\n";
314 /******************************************************************************
315 * Evaluation interface (using a specific connection)
316 ******************************************************************************/
319 connection_get (string name
, string session
) {
320 connection con
= connection (name
* "-" * session
);
322 if (connection_start (name
, session
, true) != "ok") return con
;
323 con
= connection (name
* "-" * session
);
329 connection_retrieve (string name
, string session
) {
330 // cout << "Retrieve " << name << ", " << session << "\n";
331 connection con
= connection (name
* "-" * session
);
332 if (is_nil (con
)) return "";
335 con
->forced_eval
= true;
337 con
->forced_eval
= false;
338 tree next
= connection_read (name
, session
);
340 else if (is_document (next
)) doc
<< A (next
);
342 if (con
->status
== WAITING_FOR_INPUT
) break;
344 if (N(doc
) == 0) return "";
345 // cout << "Retrieved " << doc << "\n";
350 connection_eval (string name
, string session
, tree t
) {
351 // cout << "Evaluating " << name << ", " << session << ", " << t << LF;
352 connection con
= connection_get (name
, session
);
353 if (is_nil (con
)) return "";
354 connection_write (name
, session
, t
);
355 return connection_retrieve (name
, session
);
359 connection_eval (string name
, string session
, string s
) {
360 // cout << "Evaluating " << name << ", " << session << ", " << s << LF;
361 connection con
= connection_get (name
, session
);
362 if (is_nil (con
)) return "";
363 connection_write (name
, session
, s
);
364 return connection_retrieve (name
, session
);
368 connection_cmd (string name
, string session
, string cmd
) {
369 // cout << "Command " << name << ", " << session << ", " << cmd << LF;
370 string s
= as_string (call ("format-command", name
, cmd
));
371 tree r
= connection_eval (name
, session
, s
);
372 if (is_func (r
, DOCUMENT
, 1)) r
= r
[0];