Fix Xcode warnings, part I
[texmacs.git] / src / src / System / Link / connection.cpp
blob6c0f501355ff9893084b5a5e04755e01e587a062
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"
23 #include "scheme.hpp"
24 #include "resource.hpp"
25 #include "Generic/input.hpp"
26 #include "gui.hpp"
28 static tree connection_retrieve (string name, string session);
30 /******************************************************************************
31 * The connection resource
32 ******************************************************************************/
34 RESOURCE(connection);
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
45 public:
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);
50 void stop ();
51 void interrupt ();
52 void listen ();
54 RESOURCE_CODE(connection);
56 /******************************************************************************
57 * Routines for connections
58 ******************************************************************************/
60 static void
61 connection_callback (void *obj, void* info) {
62 (void) info;
63 //cout << "connection callback " << obj << LF;
64 connection_rep *con = (connection_rep*) obj;
65 con->listen ();
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),
73 forced_eval (false),
74 tm_in ("output"), tm_err ("error") {}
76 string
77 connection_rep::start (bool again) {
78 string message;
79 if (ln->alive) {
80 message= "Continuation of#" * name * "#session";
81 status = WAITING_FOR_INPUT;
83 else {
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")) {
89 beep ();
90 (void) connection_retrieve (name, session);
93 tm_in ->bof ();
94 tm_err->bof ();
95 ln->set_command (command (connection_callback, this));
96 return message;
99 void
100 connection_rep::write (string s) {
101 ln->write (s, LINK_IN);
102 tm_in ->bof ();
103 tm_err->bof ();
104 status= WAITING_FOR_OUTPUT;
107 void
108 connection_rep::read (int channel) {
109 if (channel == LINK_OUT) {
110 string s= ln->read (LINK_OUT);
111 int i, n= N(s);
112 for (i=0; i<n; i++)
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);
120 int i, n= N(s);
121 for (i=0; i<n; i++)
122 (void) tm_err->put (s[i]);
124 if (!ln->alive) {
125 tm_in ->eof ();
126 tm_err->eof ();
127 status= CONNECTION_DEAD;
131 void
132 connection_rep::stop () {
133 if (ln->alive) {
134 ln->stop ();
135 tm_in ->eof ();
136 tm_err->eof ();
137 if (status == WAITING_FOR_OUTPUT)
138 status= CONNECTION_DYING;
142 void
143 connection_rep::interrupt () {
144 if (ln->alive) {
145 ln->interrupt ();
146 if (status == WAITING_FOR_OUTPUT)
147 status= CONNECTION_DYING;
151 /******************************************************************************
152 * Handle output from extern applications
153 ******************************************************************************/
155 void
156 connection_notify (connection con, string ch, tree t) {
157 if (t == "") return;
158 call ("connection-notify",
159 object (con->name),
160 object (con->session),
161 object (ch),
162 object (t));
165 void
166 connection_notify_status (connection con) {
167 int status=
168 (con->status == CONNECTION_DYING? WAITING_FOR_OUTPUT: con->status);
169 if (status == con->prev_status) return;
170 call ("connection-notify-status",
171 object (con->name),
172 object (con->session),
173 object (status));
174 con->prev_status= status;
177 void
178 connection_rep::listen () {
179 if (forced_eval) return;
180 connection_notify_status (this);
181 if (status != CONNECTION_DEAD) {
182 read (LINK_ERR);
183 connection_notify (this, "error", tm_err->get ("error"));
184 read (LINK_OUT);
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);
189 int i, n= N(t);
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 ******************************************************************************/
204 bool
205 connection_declared (string name) {
206 return as_bool (call ("connection-defined?", name));
209 tree
210 connection_info (string name, string session) {
211 return stree_to_tree (call ("connection-info", name, session));
214 tree
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 ******************************************************************************/
226 string
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);
233 if (is_nil (con)) {
234 if (DEBUG_VERBOSE)
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)) {
246 tm_link ln=
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);
255 void
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;
260 con->write (s);
263 void
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);
270 tree
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);
277 if (t == "") {
278 con->read (LINK_OUT);
279 t= con->tm_in->get (channel);
281 // cout << "Result " << t << "\n";
282 return t;
285 void
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;
290 con->interrupt ();
291 con->listen ();
294 void
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;
299 con->stop ();
300 con->listen ();
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";
311 return con->status;
314 /******************************************************************************
315 * Evaluation interface (using a specific connection)
316 ******************************************************************************/
318 static connection
319 connection_get (string name, string session) {
320 connection con= connection (name * "-" * session);
321 if (is_nil (con)) {
322 if (connection_start (name, session, true) != "ok") return con;
323 con= connection (name * "-" * session);
325 return con;
328 static tree
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 "";
333 tree doc (DOCUMENT);
334 while (true) {
335 con->forced_eval= true;
336 perform_select ();
337 con->forced_eval= false;
338 tree next= connection_read (name, session);
339 if (next == "");
340 else if (is_document (next)) doc << A (next);
341 else doc << next;
342 if (con->status == WAITING_FOR_INPUT) break;
344 if (N(doc) == 0) return "";
345 // cout << "Retrieved " << doc << "\n";
346 return doc;
349 tree
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);
358 tree
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);
367 tree
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];
373 return r;