Back to index

texmacs  1.0.7.15
connection.cpp
Go to the documentation of this file.
00001 
00002 /******************************************************************************
00003 * MODULE     : connection.cpp
00004 * DESCRIPTION: TeXmacs connections
00005 * COPYRIGHT  : (C) 2000  Joris van der Hoeven
00006 *******************************************************************************
00007 * When the underlying link of a connection is "alive",
00008 * then the status of the connection is either WAITING_FOR_OUTPUT
00009 * (when waiting for output from the plugin) or WAITING_FOR_INPUT.
00010 * If the underlying link is "dead", then the status is either
00011 * CONNECTION_DEAD (usually) or CONNECTION_DYING (if we are still
00012 * waiting for some residual output from the plugin).
00013 *******************************************************************************
00014 * This software falls under the GNU general public license version 3 or later.
00015 * It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE
00016 * in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>.
00017 ******************************************************************************/
00018 
00019 #include "connect.hpp"
00020 #include "socket_notifier.hpp"
00021 #include "iterator.hpp"
00022 #include "convert.hpp"
00023 #include "scheme.hpp"
00024 #include "resource.hpp"
00025 #include "Generic/input.hpp"
00026 #include "gui.hpp"
00027 
00028 static tree connection_retrieve (string name, string session);
00029 
00030 /******************************************************************************
00031 * The connection resource
00032 ******************************************************************************/
00033 
00034 RESOURCE(connection);
00035 struct connection_rep: rep<connection> {
00036   string  name;          // name of the pipe type
00037   string  session;       // name of the session
00038   tm_link ln;            // the underlying link
00039   int     status;        // status of the connection
00040   int     prev_status;   // last notified status
00041   bool    forced_eval;   // forced input evaluation without call backs
00042   texmacs_input tm_in;   // texmacs input handler for data from child
00043   texmacs_input tm_err;  // texmacs input handler for errors from child
00044 
00045 public:
00046   connection_rep (string name, string session, tm_link ln);
00047   string start (bool again);
00048   void   write (string s);
00049   void   read (int channel);
00050   void   stop ();
00051   void   interrupt ();
00052   void   listen ();
00053 };
00054 RESOURCE_CODE(connection);
00055 
00056 /******************************************************************************
00057 * Routines for connections
00058 ******************************************************************************/
00059 
00060 static void 
00061 connection_callback (void *obj, void* info) {
00062   (void) info;
00063   //cout << "connection callback " << obj << LF;
00064   connection_rep *con = (connection_rep*) obj;
00065   con->listen ();
00066 }
00067 
00068 
00069 connection_rep::connection_rep (string name2, string session2, tm_link ln2):
00070   rep<connection> (name2 * "-" * session2),
00071   name (name2), session (session2), ln (ln2),
00072   status (CONNECTION_DEAD), prev_status (CONNECTION_DEAD),
00073   forced_eval (false),
00074   tm_in ("output"), tm_err ("error") {}
00075 
00076 string
00077 connection_rep::start (bool again) {
00078   string message;
00079   if (ln->alive) {
00080     message= "Continuation of '" * name * "' session";
00081     status = WAITING_FOR_INPUT;
00082   }
00083   else {
00084     message= ln->start ();
00085     tm_in  = texmacs_input ("output");
00086     tm_err = texmacs_input ("error");
00087     status = WAITING_FOR_OUTPUT;
00088     if (again && (message == "ok")) {
00089       beep ();
00090       (void) connection_retrieve (name, session);
00091     }
00092   }
00093   tm_in ->bof ();
00094   tm_err->bof ();
00095   ln->set_command (command (connection_callback, this));
00096   return message;
00097 }
00098 
00099 void
00100 connection_rep::write (string s) {
00101   ln->write (s, LINK_IN);
00102   tm_in ->bof ();
00103   tm_err->bof ();
00104   status= WAITING_FOR_OUTPUT;
00105 }
00106 
00107 void
00108 connection_rep::read (int channel) {
00109   if (channel == LINK_OUT) {
00110     string s= ln->read (LINK_OUT);
00111     int i, n= N(s);
00112     for (i=0; i<n; i++)
00113       if (tm_in->put (s[i])) {
00114        status= WAITING_FOR_INPUT;
00115        if (DEBUG_IO) cout << LF << HRULE;
00116       }
00117   }
00118   else if (channel == LINK_ERR) {
00119     string s= ln->read (LINK_ERR);
00120     int i, n= N(s);
00121     for (i=0; i<n; i++)
00122       (void) tm_err->put (s[i]);
00123   }
00124   if (!ln->alive) {
00125     tm_in ->eof ();
00126     tm_err->eof ();
00127     status= CONNECTION_DEAD;
00128   }
00129 }
00130 
00131 void
00132 connection_rep::stop () {
00133   if (ln->alive) {
00134     ln->stop ();
00135     tm_in ->eof ();
00136     tm_err->eof ();
00137     if (status == WAITING_FOR_OUTPUT)
00138       status= CONNECTION_DYING;
00139   }
00140 }
00141 
00142 void
00143 connection_rep::interrupt () {
00144   if (ln->alive) {
00145     ln->interrupt ();
00146     if (status == WAITING_FOR_OUTPUT)
00147       status= CONNECTION_DYING;
00148   }
00149 }
00150 
00151 /******************************************************************************
00152 * Handle output from extern applications
00153 ******************************************************************************/
00154 
00155 void
00156 connection_notify (connection con, string ch, tree t) {
00157   if (t == "") return;
00158   call ("connection-notify",
00159        object (con->name),
00160        object (con->session),
00161        object (ch),
00162        object (t));
00163 }
00164 
00165 void
00166 connection_notify_status (connection con) {
00167   int status=
00168     (con->status == CONNECTION_DYING? WAITING_FOR_OUTPUT: con->status);
00169   if (status == con->prev_status) return;
00170   call ("connection-notify-status",
00171        object (con->name),
00172        object (con->session),
00173        object (status));
00174   con->prev_status= status;
00175 }
00176 
00177 void
00178 connection_rep::listen () {
00179   if (forced_eval) return;
00180   connection_notify_status (this);
00181   if (status != CONNECTION_DEAD) {
00182     read (LINK_ERR);
00183     connection_notify (this, "error", tm_err->get ("error"));
00184     read (LINK_OUT);
00185     connection_notify (this, "output", tm_in->get ("output"));
00186     connection_notify (this, "prompt", tm_in->get ("prompt"));
00187     connection_notify (this, "input", tm_in->get ("input"));
00188     tree t= connection_handlers (name);
00189     int i, n= N(t);
00190     for (i=0; i<n; i++) {
00191       tree doc= tm_in->get (t[i][0]->label);
00192       if (doc != "") call (t[i][1]->label, doc);
00193       doc= tm_err->get (t[i][0]->label);
00194       if (doc != "") call (t[i][1]->label, doc);
00195     }
00196   }
00197   connection_notify_status (this);  
00198 }
00199 
00200 /******************************************************************************
00201 * Connection type information
00202 ******************************************************************************/
00203 
00204 bool
00205 connection_declared (string name) {
00206   return as_bool (call ("connection-defined?", name));
00207 }
00208 
00209 tree
00210 connection_info (string name, string session) {
00211   return stree_to_tree (call ("connection-info", name, session));
00212 }
00213 
00214 tree
00215 connection_handlers (string name) {
00216   static hashmap<string,tree> handlers (tuple ());
00217   if (!handlers->contains (name))
00218     handlers (name)= stree_to_tree (call ("connection-get-handlers", name));
00219   return handlers[name];
00220 }
00221 
00222 /******************************************************************************
00223 * First part of interface (using a specific connection)
00224 ******************************************************************************/
00225 
00226 string
00227 connection_start (string name, string session, bool again) {
00228   // cout << "Start " << name << ", " << session << "\n";
00229   if (!connection_declared (name))
00230     return "Error: connection " * name * " has not been declared";
00231 
00232   connection con= connection (name * "-" * session);
00233   if (is_nil (con)) {
00234     if (DEBUG_VERBOSE)
00235       cout << "TeXmacs] Starting session '" << session << "'\n";
00236     tree t= connection_info (name, session);
00237     if (is_tuple (t, "pipe", 1)) {
00238       tm_link ln= make_pipe_link (t[1]->label);
00239       con= tm_new<connection_rep> (name, session, ln);
00240     }
00241     else if (is_tuple (t, "socket", 2)) {
00242       tm_link ln= make_socket_link (t[1]->label, as_int (t[2]->label));
00243       con= tm_new<connection_rep> (name, session, ln);
00244     }
00245     else if (is_tuple (t, "dynlink", 3)) {
00246       tm_link ln=
00247        make_dynamic_link (t[1]->label, t[2]->label, t[3]->label, session);
00248       con= tm_new<connection_rep> (name, session, ln);
00249     }
00250   }
00251 
00252   return con->start (again);
00253 }
00254 
00255 void
00256 connection_write (string name, string session, string s) {
00257   // cout << "Write " << name << ", " << session << ", " << s << "\n";
00258   connection con= connection (name * "-" * session);
00259   if (is_nil (con)) return;
00260   con->write (s);
00261 }
00262 
00263 void
00264 connection_write (string name, string session, tree t) {
00265   // cout << "Write " << name << ", " << session << ", " << t << "\n";
00266   string s= as_string (call ("plugin-serialize", name, tree_to_stree (t)));
00267   connection_write (name, session, s);
00268 }
00269 
00270 tree
00271 connection_read (string name, string session, string channel) {
00272   // cout << "Read " << name << ", " << session << ", " << channel << "\n";
00273   connection con= connection (name * "-" * session);
00274   if (is_nil (con)) return "";
00275   con->read (LINK_ERR);
00276   tree t= con->tm_err->get (channel);
00277   if (t == "") {
00278     con->read (LINK_OUT);
00279     t= con->tm_in->get (channel);
00280   }
00281   // cout << "Result " << t << "\n";
00282   return t;
00283 }
00284 
00285 void
00286 connection_interrupt (string name, string session) {
00287   // cout << "Interrupt " << name << ", " << session << "\n";
00288   connection con= connection (name * "-" * session);
00289   if (is_nil (con)) return;
00290   con->interrupt ();
00291   con->listen ();
00292 }
00293 
00294 void
00295 connection_stop (string name, string session) {
00296   // cout << "Stop " << name << ", " << session << "\n";
00297   connection con= connection (name * "-" * session);
00298   if (is_nil (con)) return;
00299   con->stop ();
00300   con->listen ();
00301 }
00302 
00303 int
00304 connection_status (string name, string session) {
00305   // cout << "Status " << name << ", " << session << " -> ";
00306   connection con= connection (name * "-" * session);
00307   if ((!is_nil (con)) && (con->status == CONNECTION_DYING))
00308     return WAITING_FOR_OUTPUT;
00309   if (is_nil (con) || (!con->ln->alive)) return CONNECTION_DEAD;
00310   // cout << con->ln->status << "\n";
00311   return con->status;
00312 }
00313 
00314 /******************************************************************************
00315 * Evaluation interface (using a specific connection)
00316 ******************************************************************************/
00317 
00318 static connection
00319 connection_get (string name, string session) {
00320   connection con= connection (name * "-" * session);
00321   if (is_nil (con)) {
00322     if (connection_start (name, session, true) != "ok") return con;
00323     con= connection (name * "-" * session);
00324   }
00325   return con;
00326 }
00327 
00328 static tree
00329 connection_retrieve (string name, string session) {
00330   // cout << "Retrieve " << name << ", " << session << "\n";
00331   connection con= connection (name * "-" * session);
00332   if (is_nil (con)) return "";
00333   tree doc (DOCUMENT);
00334   while (true) {
00335     con->forced_eval= true;
00336     perform_select ();
00337     con->forced_eval= false;
00338     tree next= connection_read (name, session);
00339     if (next == "");
00340     else if (is_document (next)) doc << A (next);
00341     else doc << next;
00342     if (con->status == WAITING_FOR_INPUT) break;
00343   }
00344   if (N(doc) == 0) return "";
00345   // cout << "Retrieved " << doc << "\n";
00346   return doc;
00347 }
00348 
00349 tree
00350 connection_eval (string name, string session, tree t) {
00351   // cout << "Evaluating " << name << ", " << session << ", " << t << LF;
00352   connection con= connection_get (name, session);
00353   if (is_nil (con)) return "";
00354   connection_write (name, session, t);
00355   return connection_retrieve (name, session);
00356 }
00357 
00358 tree
00359 connection_eval (string name, string session, string s) {
00360   // cout << "Evaluating " << name << ", " << session << ", " << s << LF;
00361   connection con= connection_get (name, session);
00362   if (is_nil (con)) return "";
00363   connection_write (name, session, s);
00364   return connection_retrieve (name, session);
00365 }
00366 
00367 tree
00368 connection_cmd (string name, string session, string cmd) {
00369   // cout << "Command " << name << ", " << session << ", " << cmd << LF;
00370   string s= as_string (call ("format-command", name, cmd));
00371   tree r= connection_eval (name, session, s);
00372   if (is_func (r, DOCUMENT, 1)) r= r[0];
00373   return r;
00374 }