Back to index

texmacs  1.0.7.15
tm_maple_5.cpp
Go to the documentation of this file.
00001 
00002 /******************************************************************************
00003 * MODULE     : maple_filter.cpp
00004 * DESCRIPTION: Filter for Maple sessions
00005 * COPYRIGHT  : (C) 2005 Joris van der Hoeven
00006 *******************************************************************************
00007 * This software falls under the GNU general public license version 3 or later.
00008 * It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE
00009 * in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>.
00010 ******************************************************************************/
00011 
00012 #include <stdio.h>
00013 #include <stdlib.h>
00014 #include <string.h>
00015 #include <signal.h>
00016 #include <unistd.h>
00017 #include <sys/time.h>
00018 #include <sys/wait.h>
00019 #include <iostream>
00020 using std::istream;
00021 using std::ostream;
00022 using std::cin;
00023 using std::cout;
00024 using std::cerr;
00025 
00026 typedef char* charp;
00027 extern charp* environ;
00028 
00029 #define ERROR (-1)
00030 #define STDIN 0
00031 #define STDOUT 1
00032 #define STDERR 2
00033 #define IN 0
00034 #define OUT 1
00035 
00036 #define DATA_BEGIN   ((char) 2)
00037 #define DATA_END     ((char) 5)
00038 #define DATA_ESCAPE  ((char) 27)
00039 //#define DATA_BEGIN   "[BEGIN]"
00040 //#define DATA_END     "[END]"
00041 //#define DATA_ESCAPE  "[ESCAPE]"
00042 
00043 int   pid;          // process identifier of the child
00044 int   tochild[2];     // for data going to the child
00045 int   fromchild[2];   // for data coming from the child
00046 int   in;           // file descriptor for data going to the child
00047 int   out;          // file descriptor for data coming from the child
00048 
00049 /******************************************************************************
00050 * A simple but portable string class
00051 ******************************************************************************/
00052 
00053 class string {
00054 public:
00055   int   l; // reserved number of bytes
00056   char* s; // the string
00057   int   n; // length
00058   string (int reserve= 7):
00059     l (reserve+1), s (new char[l]), n (0) { s[0]='\0'; }
00060   string (const char* s2):
00061     l (strlen(s2)+1), s (new char[l]), n (l-1) { strcpy (s, s2); }
00062   string (const string& s2):
00063     l (s2.n+1), s (new char[l]), n (l-1) { strcpy (s, s2.s); }
00064   ~string () { delete[] s; }
00065   string& operator = (const string& s2) {
00066     if (this != &s2) {
00067       delete[] s;
00068       l= s2.n + 1;
00069       s= new char[l];
00070       n= l - 1;
00071     }
00072     return *this;
00073   }
00074   inline char& operator [] (int i) { return s[i]; }
00075 };
00076 
00077 inline bool
00078 operator == (const string& s1, const char* s2) {
00079   return strcmp (s1.s, s2) == 0;
00080 }
00081 
00082 inline bool
00083 ends (const string& s1, const char* s2) {
00084   int n= strlen (s2);
00085   return s1.n >= n && strcmp (s1.s + s1.n - n, s2) == 0;
00086 }
00087 
00088 inline void
00089 shorten (string& s, int n) {
00090   if (s.n >= n) {
00091     s.n -= n;
00092     s.s[s.n]= '\0';
00093   }
00094 }
00095 
00096 inline ostream&
00097 operator << (ostream& out, const string& s) {
00098   return out << s.s;
00099 }
00100 
00101 string
00102 operator * (const string& s1, const string& s2) {
00103   string s (s1.n + s2.n);
00104   strcpy (s.s, s1.s);
00105   strcpy (s.s + s1.n, s2.s);
00106   s.n= s1.n + s2.n;
00107   return s;
00108 }
00109 
00110 string&
00111 operator << (string& s, const string& s2) {
00112   if (s.n + s2.n >= s.l) {
00113     char* old= s.s;
00114     s.l= ((s.n + s2.n) << 1) + 1;
00115     s.s= new char[s.l];
00116     strcpy (s.s, old);
00117     delete[] old;
00118   }
00119   strcpy (s.s + s.n, s2.s);
00120   s.n += s2.n;
00121   return s;
00122 }
00123 
00124 string&
00125 operator << (string& s, char c) {
00126   char s2[2];
00127   s2[0]= c; s2[1]= '\0';
00128   return s << string (s2);
00129 }
00130 
00131 string
00132 get_line () {
00133   string input;
00134   while (true) {
00135     char c;
00136     cin.get (c);
00137     if (c == '\n') break;
00138     input << c; 
00139   }
00140   return input;
00141 }
00142 
00143 void
00144 write (int channel, const string& s) {
00145   write (channel, s.s, s.n);
00146 }
00147 
00148 /******************************************************************************
00149 * Handling maple output
00150 ******************************************************************************/
00151 
00152 static int counter= 0;
00153 
00154 void
00155 next_input () {
00156   counter++;
00157   cout << DATA_BEGIN << "channel:prompt" << DATA_END;
00158   cout << DATA_BEGIN << "scheme:(with \"color\" \"brown\" \"";
00159   cout << "Maple " << counter << "] ";
00160   cout << "\")" << DATA_END;
00161 }
00162 
00163 void
00164 maple_output () {
00165   bool show_flag= false;
00166   string output;
00167   cout << DATA_BEGIN << "verbatim:";
00168 
00169   while (true) {
00170     fd_set rfds;
00171     FD_ZERO (&rfds);
00172     FD_SET (out, &rfds);
00173     int max_fd= out+1;
00174     struct timeval tv;
00175     tv.tv_sec  = 0;
00176     tv.tv_usec = 100;
00177     int nr= select (max_fd, &rfds, NULL, NULL, &tv);
00178     if (nr==0) {
00179       cout.flush ();
00180       continue;
00181     }
00182 
00183     if (FD_ISSET (out, &rfds)) {
00184       int i, r;
00185       char outbuf[1024];
00186       bool error_flag= false;
00187       r = read (out, outbuf, 1024);
00188       if (r == ERROR) {
00189        next_input ();
00190        cout << DATA_END << DATA_END;
00191        cout.flush ();
00192        return;
00193       }
00194       else if (r == 0) {
00195        killpg (pid, SIGKILL);
00196        cout << DATA_END;
00197        cout.flush ();
00198        exit (0);
00199       }
00200       else for (i=0; i<r; i++) {
00201        output << outbuf[i];
00202        if (ends (output, "error"))
00203          error_flag= true;
00204        if (outbuf[i]=='\n') {
00205          if (output == "tmstart\n")
00206            show_flag= true;
00207          else if (output == "tmend\n") {
00208            next_input ();
00209            cout << DATA_END;
00210            cout.flush ();
00211            return;
00212          }
00213          else if (show_flag) {
00214            cout << output;
00215            cout.flush ();
00216          }
00217          output= string ();
00218        }
00219       }
00220       if (error_flag) {
00221        next_input ();
00222        cout << DATA_END;
00223        cout.flush ();
00224        return;
00225       }
00226     }
00227   }
00228 }
00229 
00230 /******************************************************************************
00231 * Handling maple input
00232 ******************************************************************************/
00233 
00234 void
00235 send (const string& s) {
00236   write (in, s);
00237 }
00238 
00239 void
00240 strip (string& s, char c1, char c2) {
00241   while (s.n>0 && (s[s.n-1] == c1 || s[s.n-1] == c2))
00242     shorten (s, 1);
00243 }
00244 
00245 void
00246 maple_input () {
00247   string input= get_line ();
00248   send ("printf(`tmstart\\n`):\n");
00249   strip (input, ' ', '\n');
00250   if (input.n == 0 || input[0] == '?')
00251     send (input * "\n");
00252   else if (input[input.n-1] == ':') {
00253     strip (input, ':', ';');
00254     send (input * ":\n");
00255   }
00256   else {
00257     strip (input, ':', ';');
00258     send ("tmresult := tmdummy:\n");
00259     send (input * ":\n");
00260     send ("if \" <> tmdummy then tmprint(\") fi:\n");
00261   }
00262   send ("printf(`tmend\\n`):\n");
00263 }
00264 
00265 /******************************************************************************
00266 * Interrupting maple
00267 ******************************************************************************/
00268 
00269 void
00270 maple_interrupt (int sig) {
00271   killpg (pid, sig);
00272   cout << DATA_END; // << DATA_END << DATA_END;
00273   signal (sig, maple_interrupt);
00274   siginterrupt (sig, 1);
00275 }
00276 
00277 /******************************************************************************
00278 * Launching maple
00279 ******************************************************************************/
00280 
00281 volatile void
00282 invoke_maple () {
00283   charp argv[3];
00284   argv[0] = const_cast<char*> ("maple");
00285   argv[1] = const_cast<char*> ("-q");
00286   argv[2] = 0;
00287   char* maple_bin= getenv ("TEXMACS_MAPLE_BIN");
00288   execve (maple_bin, argv, environ);
00289   exit(127);
00290 }
00291 
00292 void
00293 init_maple () {
00294   cout << DATA_BEGIN << "verbatim:";
00295   cout << "Maple session inside TeXmacs";
00296   send ("tmmaple:=5:\n");
00297   send ("interface(errorbreak=0,screenheight=9999):\n");
00298   char* tm_path= getenv ("TEXMACS_PATH");
00299   send ("read (`" * string (tm_path) *
00300        "/plugins/maple/maple/init-maple.mpl`):\n");
00301   next_input ();
00302   cout << DATA_END;
00303   cout.flush ();
00304 }
00305 
00306 int
00307 main () {
00308   pipe (tochild);
00309   pipe (fromchild);
00310   pid= fork ();
00311   if (pid==0) { // the child
00312     setsid();
00313     dup2 (tochild [IN], STDIN);
00314     close (tochild [IN]);
00315     close (fromchild [IN]);
00316     close (tochild [OUT]);
00317     dup2 (fromchild [OUT], STDOUT);
00318     dup2 (STDOUT, STDERR);
00319     close (fromchild [OUT]);
00320     invoke_maple ();
00321     exit (127);
00322   }
00323   else { // the main process
00324     int sig;
00325     out= fromchild [IN];
00326     close (fromchild [OUT]);
00327     in= tochild [OUT];
00328     close (tochild [IN]);
00329     signal (SIGINT, maple_interrupt);
00330     siginterrupt (SIGINT, 1);
00331     init_maple ();
00332     while (true) {
00333       maple_input ();
00334       maple_output ();
00335     }
00336   }
00337   return 0;
00338 }