Back to index

texmacs  1.0.7.15
tm_shell.cpp
Go to the documentation of this file.
00001 
00002 /******************************************************************************
00003 * MODULE     : tm_shell.cpp
00004 * DESCRIPTION: TeXmacs shell
00005 * COPYRIGHT  : (C) 2000  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 "config.h"
00013 #include <iostream>
00014 #include <stdlib.h>
00015 #include <stdio.h>
00016 #include <string.h>
00017 #include <stdio.h>
00018 #include <signal.h>
00019 #include <unistd.h>
00020 #include <sys/time.h>
00021 #include <sys/wait.h>
00022 #include <termios.h>
00023 
00024 #if HAVE_PTY_H
00025 #include <pty.h>
00026 #endif
00027 #if HAVE_UTIL_H
00028 #include <util.h>
00029 #endif
00030 
00031 using namespace std;
00032 
00033 typedef char* charp;
00034 extern "C" charp* environ;
00035 
00036 #define ERROR (-1)
00037 #define STDIN 0
00038 #define STDOUT 1
00039 #define STDERR 2
00040 #define IN 0
00041 #define OUT 1
00042 #define DATA_BEGIN   ((char) 2)
00043 #define DATA_END     ((char) 5)
00044 #define DATA_ESCAPE  ((char) 27)
00045 //#define DATA_BEGIN   "[BEGIN]"
00046 //#define DATA_END     "[END]"
00047 //#define DATA_ESCAPE  "[ESCAPE]"
00048 
00049 static int pid;
00050 static int master;
00051 static char prompt[] = "tmshell$ ";
00052 static const int promptlen = sizeof(prompt)-1;
00053 static void append (charp &s, char c, int& pos, int& max) {
00054   if (pos == max) {
00055     int i;
00056     charp r= s;
00057     max <<= 1;
00058     s= (charp) malloc (max);
00059     for (i=0; i<pos; i++) s[i]=r[i];
00060     free (r);
00061   }
00062   s[pos++]= c;
00063 }
00064 
00065 int output_pos;
00066 charp output;
00067 
00068 static void
00069 shell_interrupt (int sig) {
00070   if (output != NULL) {
00071     int i;
00072     for (i=0; i<output_pos; i++) cout << output[i];
00073     cout << endl;
00074   }
00075   cout << DATA_BEGIN << "scheme:(with \"color\" \"red\" \""
00076        << "Interrupted TeXmacs shell"
00077        << "\")" << DATA_END
00078        << DATA_END
00079        << flush ;
00080   killpg(pid, SIGTERM);
00081   sleep(1);
00082   killpg(pid, SIGKILL);
00083   wait (NULL);
00084   exit (0);
00085 }
00086 
00087 static void
00088 shell_output (bool hide= false) {
00089   static struct timeval tv;
00090   int output_max= 1024;
00091   output = (charp) malloc (output_max);
00092   output_pos= 0;
00093   cout << DATA_BEGIN << "verbatim:" << flush;
00094   while (true) {
00095     fd_set rfds;
00096     FD_ZERO (&rfds);
00097     FD_SET (master, &rfds);
00098     tv.tv_sec = 0;
00099     tv.tv_usec = 10000;
00100     int r = select (master+1, &rfds, NULL, NULL, &tv);
00101     if (r == 0) continue;
00102     if (r == -1) continue;
00103 
00104     if (FD_ISSET (master, &rfds)) {
00105       int i, r;
00106       char outbuf[1024];
00107       r = read (master, outbuf, 1024);
00108       if (r == ERROR) {
00109        cerr << "TeXmacs shell] read failed\n" << flush;
00110        killpg(pid, SIGTERM);
00111        sleep(1);
00112        killpg(pid, SIGKILL);
00113        wait (NULL);
00114        exit (1);
00115       }
00116       else if (r == 0) {
00117        wait (NULL);
00118        cout << DATA_END << flush;
00119        exit (0);
00120       }
00121       else for (i=0; i<r; i++) {
00122        append (output, outbuf[i], output_pos, output_max);
00123        if (outbuf[i]=='\n') {
00124          append (output, '\0', output_pos, output_max);
00125          if (hide) hide= false; 
00126          else cout << output << flush;
00127          output_pos= 0;
00128        }
00129       }
00130     }
00131 
00132     if (output_pos >= promptlen &&
00133        strncmp(output + output_pos - promptlen, prompt, promptlen) == 0)
00134       {
00135        output[output_pos-promptlen]=0;
00136        if (hide) hide= false; 
00137        else cout << output << flush;
00138        output_pos= 0;
00139        break;
00140       }
00141   }
00142 
00143   cout << DATA_END << flush;
00144   free (output);
00145 }
00146 
00147 static void
00148 shell_input () {
00149   char input [10000];
00150   cin.getline (input, 10000, '\n');
00151   strcat (input, "\n");
00152   write (master, input, strlen (input));
00153 }
00154 
00155 static void
00156 child() {
00157   struct termios t;
00158   tcgetattr(STDIN, &t);
00159   t.c_lflag &= ~ECHO;
00160   tcsetattr(STDIN, TCSANOW, &t);
00161   setenv ("PS1", prompt, true);
00162   const charp argv[] = {
00163     const_cast<charp> ("sh"),
00164     const_cast<charp> ("-i"),
00165     NULL };
00166   execve("/bin/sh", argv, environ);
00167   exit(127);
00168 }
00169 
00170 static void
00171 parent() {
00172   signal (SIGINT, shell_interrupt);
00173   shell_output (true);
00174   cout << DATA_END << flush;
00175   while (true) {
00176     shell_input ();
00177     shell_output ();
00178   }
00179 }
00180 
00181 int
00182 main () {
00183   cout << DATA_BEGIN << "verbatim:" << "Shell session inside TeXmacs" << flush;
00184   pid = forkpty(&master,NULL,NULL,NULL);
00185   if (pid==0) child();
00186   cout << " pid = " << pid << "\n" << flush;
00187   parent();
00188 }