1
/******************************************************************************
3
* DESCRIPTION: Glue between TeXmacs and R
4
* COPYRIGHT : (C) 2003 Michael Lachmann Tamarlin
5
*******************************************************************************
6
* This software falls under the GNU general public license and comes WITHOUT
7
* ANY WARRANTY WHATSOEVER. See the file $TEXMACS_PATH/LICENSE for more details.
8
* If you don't have this file, write to the Free Software Foundation, Inc.,
9
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
10
******************************************************************************/
13
#include <sys/select.h>
23
#include <sys/types.h>
27
#include <sys/types.h>
31
/* do we want to talk with texmacs, or with a human? */
32
#define INTERFACE_TEXMACS
35
#ifdef INTERFACE_TEXMACS
36
#define DATA_BEGIN (2)
38
#define DATA_COMMAND (16)
40
#define DATA_BEGIN '{'
42
#define DATA_COMMAND '@'
45
#define IN_VERBATIM( TXB ) { if( in_data == 0 ) { in_data=1 ; copy_to_B( TXB, st, sprintf(st,"%cverbatim:",DATA_BEGIN) ) ; } }
46
#define END_VERBATIM( TXB ) { while( in_data>0 ) { copy_to_B( TXB, st, sprintf(st,"%c",DATA_END) ) ; in_data-- ;}}
47
#define B_DATA_BEGIN( TXB ) { copy_to_B( TXB, st, sprintf(st,"%c",DATA_BEGIN) ); in_data++; }
48
#define B_DATA_END( TXB ) { copy_to_B( TXB, st, sprintf(st,"%c",DATA_END) ); in_data--; }
55
/* #define VERBOSE_TERM */
58
#define WRITELOG(s,n) {if( DEBUG ) write(LOG,s,n);}
65
tcflag_t prompt_flags[][4] = {
66
{ 00500, 000005, 0277, 0005021},
67
{00000, 000005, 0277, 0105061},
68
{00000, 000004, 0277, 0005061},
69
{02400, 014005, 0277, 0105001},
70
{0, 05, 0277, 037777763425},
76
int check_terminal( int f )
79
int got_prompt = (1==0) ;
80
struct termios termi ;
81
tcgetattr(f, &termi ) ;
82
WRITELOG(st,sprintf(st,"term:i:%o o:%o c:%o l:%o\n",
88
/* check if we got the prompt from R */
89
for( p_i = 0; prompt_flags[p_i][0]!=-1; p_i++) { /* check terminal flags */
90
WRITELOG(st,sprintf(st,"prompt:i:%o o:%o c:%o l:%o\n",
96
if( (termi.c_iflag == prompt_flags[p_i][0]) &&
97
(termi.c_oflag == prompt_flags[p_i][1]) &&
98
(termi.c_cflag == prompt_flags[p_i][2]) &&
99
(termi.c_lflag = prompt_flags[p_i][3]) )
100
got_prompt = (1==1) ;
107
char *prompt_string[] = {
111
"Type <Return>\t to start : ",
112
"Save workspace image? [y/n/c]: ",
113
"Hit <Return> to see next plot: ",
124
int compare_end_B( struct my_buffer *b, char *string )
128
n = strlen( string ) ;
129
if( n > b->put - b->get )
132
ret = strncmp( b->buf+b->put-n, string,n ) ;
136
int check_prompt_strings_B( struct my_buffer *B )
139
int got_prompt = (1==0) ;
140
for( p_i=0; prompt_string[p_i]!=0; p_i++)
141
if( compare_end_B( B, prompt_string[p_i] ) ) {
142
WRITELOG(st,sprintf(st,"Found string:%s\n",prompt_string[p_i]) ) ;
153
int not_done=(1==1) ;
157
void child_died(int x) ;
159
void signal_int(int x)
162
WRITELOG(st,sprintf(st,"got signal %d\n",childpid) ) ;
164
kill(childpid,SIGINT) ;
165
signal (SIGINT, signal_int);
166
signal (SIGCHLD, child_died);
169
void child_died(int x)
175
void copy_to_B( struct my_buffer *b, char *data, size_t count )
177
while( b->put + count > b->size ) {
179
b->buf = (char *)realloc( b->buf, b->size ) ;
181
memcpy(b->buf+b->put,data,count) ;
185
void copy_B_to_B( struct my_buffer *b, struct my_buffer *c )
187
ssize_t count = c->put - c->get ;
188
while( b->put + count > b->size ) {
190
b->buf = (char *)realloc( b->buf, b->size ) ;
192
memcpy(b->buf+b->put, c->buf + c->get,count) ;
194
c->put = c->get = 0 ;
197
void ncopy_B_to_B( struct my_buffer *b, struct my_buffer *c, ssize_t count )
199
while( b->put + count > b->size ) {
201
b->buf = (char *)realloc( b->buf, b->size ) ;
203
memcpy(b->buf+b->put, c->buf + c->get,count) ;
206
if( c->put == c->get )
207
c->get = c->put = 0 ; /* reset buffer */
210
void rem_nl_B( struct my_buffer *b)
213
for( i=b->get,j=b->get; i<b->put-1; i++) {
214
if( b->buf[i] == 13 ) {
215
if( b->buf[i+1] != 10 ) {
220
b->buf[j] = b->buf[i] ;
224
if( b->buf[i] == 13 )
227
b->buf[j] = b->buf[i] ;
233
int del_last_nl_B( struct my_buffer *b)
236
char *p = b->buf + b->put - 1 ;
237
while( (b->put != b->get) && ((*p == 13) || (*p == 10)) ) {
245
int del_first_nl_B( struct my_buffer *b)
248
char *p = b->buf + b->get ;
249
while( (b->put != b->get) && ((*p == 13) || (*p == 10)) ) {
258
int copy_to_B_del_nl( struct my_buffer *b, char *data, size_t count )
261
while( b->put + count > b->size ) {
263
b->buf = (char *)realloc( b->buf, b->size ) ;
265
while( ((data[count-1] == 13) || (data[count-1]==10))&&(count>0) ) {
269
memcpy(b->buf+b->put,data,count) ;
274
ssize_t read_B( int fd, struct my_buffer *b, ssize_t count )
276
while( b->put + count > b->size ) {
278
b->buf = (char *)realloc( b->buf, b->size ) ;
280
count = read( fd, b->buf+b->put, count ) ;
287
int data_available_B( struct my_buffer *b)
289
return (b->put - b->get) ;
292
int write_B( int fd, struct my_buffer *b )
296
if( b->put > b->get ) {
297
/* we have data available */
298
WRITELOG(big,snprintf(big,1000,"wrote to %d:\n",fd) ) ;
299
WRITELOG(b->buf+b->get, b->put - b->get ) ;
300
WRITELOG(big,snprintf(big,1000,"<<\n") ) ;
302
nwrite = write( fd, b->buf+b->get, b->put - b->get ) ;
304
if( b->put == b->get )
305
b->get = b->put = 0 ; /* reset buffer */
312
struct my_buffer *init_buffer( int size )
314
struct my_buffer *b = (struct my_buffer *)malloc( sizeof( struct my_buffer ) ) ;
315
b->buf = (char *)malloc( size ) ;
317
b->get = b->put = 0 ;
322
int main(int arc, char *argv[])
330
*TXB, /* buffer for interface with texmacs */
331
*RB ; /* buffer for interface with R */
333
ssize_t nread, more_nread;
344
char *TEXMACS_PATH, *TEXMACS_R, *TEXMACS_SEND ;
345
struct termios termi ;
346
sigset_t sigmask, orig_sigmask;
351
LOG = open("/tmp/log",O_CREAT|O_WRONLY) ;
355
TXB = init_buffer( 4096 ) ;
356
RB = init_buffer( 4096 ) ;
357
TEMPB = init_buffer( 4096 ) ;
359
TEXMACS_PATH = getenv("TEXMACS_PATH") ;
360
if( TEXMACS_PATH == NULL )
361
TEXMACS_PATH = "/home/dirk/work";
363
TEXMACS_R = getenv("TEXMACS_CMD") ;
364
if( TEXMACS_R == NULL )
367
TEXMACS_SEND = getenv("TEXMACS_SEND") ;
370
if( TEXMACS_SEND == NULL ) {
371
TEXMACS_SEND = (char *)malloc( 4096 ) ;
372
snprintf(TEXMACS_SEND,4096,"library(TeXmacs,lib.loc=\"%s/plugins/r/r/\")\n",TEXMACS_PATH) ;
376
/* prepare for the string to be sent to the process */
377
copy_to_B( RB, st, snprintf( st,4096, "%s",TEXMACS_SEND) ) ;
379
/* ignore 1 input request - i.e. do not generate a prompt channel in
383
if( (childpid=forkpty( &master, name, NULL, NULL ))==0 ) {
384
/* I'm the child - I'll run the command */
387
m = strlen( TEXMACS_R ) ;
388
for( i=0,n=0; i<m; i++)
389
if( TEXMACS_R[i] == ' ' )
392
exec_argv = (char **) malloc( (n+2)*sizeof( char * ) ) ;
394
/* split TEXMACS_R into arguments into exec_argv,
395
at each " " that doesn't have a \ to escape it */
396
exec_argv[0] = TEXMACS_R ;
397
for( i=0,n=0; i<m; i++)
398
if( (TEXMACS_R[i] == ' ') && (i>0) && (TEXMACS_R[i-1]!='\\') ) {
400
exec_argv[n] = TEXMACS_R+i+1 ;
403
exec_argv[n+1] = NULL ;
404
execvp(TEXMACS_R,exec_argv) ;
406
/* I'm the parent - I'll handle input and output and watch the child.*/
408
/* This is for pselect. Supposedly if pselect doesn't know what signals
409
we are waiting for, it will get confused. */
410
sigemptyset (&sigmask);
411
sigaddset (&sigmask, SIGCHLD);
412
sigaddset (&sigmask, SIGINT);
413
sigprocmask (SIG_BLOCK, &sigmask,
416
signal (SIGINT, signal_int);
417
signal (SIGCHLD, child_died);
419
fcntl(master, F_SETFL, O_NONBLOCK) ;
421
/* send the initial string */
422
write_B( master, RB ) ;
424
/* get terminal settings */
425
tcgetattr(master, &termi ) ;
427
printf("%x %x %x %x\n",termi.c_iflag,termi.c_oflag,termi.c_lflag,termi.c_lflag&ECHO) ;
429
termi.c_lflag &= ~ECHO ; /* no echo */
430
tcsetattr(master,TCSANOW, &termi ) ;
433
printf("%x %x %x %x\n",termi.c_iflag,termi.c_oflag,termi.c_lflag,termi.c_lflag&ECHO) ;
439
/* prepare the file sets for select to watch */
441
WRITELOG("<<<\n",4) ;
446
FD_SET (master, &rd);
449
if( data_available_B( RB ) ) /* if we have data available to send, */
450
FD_SET (master, &wr); /* then also wait to send. */
452
FD_SET (STDIN_FILENO, &rd ) ;
456
if( data_available_B( TXB ) )
457
FD_SET (STDOUT_FILENO, &wr ) ;
460
/* Main pselect switch --------------- */
461
if( pselect( master+1, &rd, &wr, &er, NULL, &orig_sigmask ) > 0 ) {
462
if( FD_ISSET( STDIN_FILENO, &rd ) ) {
463
/* =============== read input from TeXmacs */
464
nread = read_B( STDIN_FILENO, RB, 1000 ) ;
466
(RB->buf[RB->put-nread] == '@' ) &&
467
(RB->buf[RB->put+1-nread] == '@' ) &&
468
(RB->buf[RB->put+2-nread] == '@' )
472
for(i=0; i<nread-1; i++) {
473
WRITELOG(st,sprintf(st,":%d:%c", RB->buf[RB->put+i-nread], RB->buf[RB->put+i-nread]) ) ;
474
if( (RB->buf[RB->put+i-nread]==';') && (RB->buf[RB->put+i-nread+1]==';') ) {
475
RB->buf[RB->put+i-nread] = ' ' ;
476
RB->buf[RB->put+i-nread+1] = 10 ;
477
} else if( RB->buf[RB->put+i-nread]==DATA_COMMAND) {
479
WRITELOG(st,sprintf(st,"TeXmacs completion!!!\n") ) ;
480
strncpy(RB->buf+RB->put+i-nread,"t.tab.comp",10) ;
482
RB->buf[RB->put+(i)-nread] = '(' ;
483
for(i++;i<nread-1;i++)
484
if( RB->buf[RB->put+(i)-nread]==' ') {
485
RB->buf[RB->put+(i)-nread] = ',' ;
495
if( FD_ISSET( master, &rd ) ) {
496
/* =================== read input from sub process (R) */
497
while( read_B( master, TEMPB, 4096) > 0 )
500
got_prompt = check_terminal( master ) || check_prompt_strings_B( TEMPB ) ;
502
if( (got_prompt ) && (ignore==0) ) { /* just a trick to sub 1 from ignore */
503
WRITELOG("PPP\n",4) ;
504
/* find the previous end-of-line, and use that for the
506
for( i= TEMPB->put - TEMPB->get; i > 0; i--)
507
if( (TEMPB->buf[i-1]==13) || (TEMPB->buf[i-1]==10) )
511
if( TEMPB->buf[TEMPB->get] == DATA_BEGIN ) {
512
ncopy_B_to_B( TXB, TEMPB, i ) ;
517
/* don't forget the last nl */
518
while( last_nl > 0 ) {
519
copy_to_B( TXB, "\n", 1 ) ;
522
/* print everything before the pervious end-of-line */
523
ncopy_B_to_B( TXB, TEMPB, i ) ;
524
del_last_nl_B( TXB ) ;
527
del_first_nl_B( TEMPB ) ;
530
if( data_available_B( TEMPB) ) {
533
B_DATA_BEGIN( TXB) ; {
534
copy_to_B( TXB, st, sprintf(st,"prompt#") ) ;
535
B_DATA_BEGIN( TXB ) ; {
536
copy_to_B( TXB, st, sprintf(st,"latex:\\red ") ) ;
537
copy_B_to_B( TXB, TEMPB ) ;
538
copy_to_B( TXB, st, sprintf(st,"\\black") ) ;
544
END_VERBATIM( TXB ) ;
546
if( got_prompt && (ignore > 0) )
548
/* terminal is not waiting for user - just print data. */
550
if( TEMPB->buf[TEMPB->get] == DATA_BEGIN ) {
551
copy_B_to_B( TXB, TEMPB ) ;
556
while( last_nl > 0 ) {
557
copy_to_B( TXB, "\n", 1 ) ;
560
copy_B_to_B( TXB, TEMPB ) ;
561
last_nl = del_last_nl_B( TXB ) ;
567
if( FD_ISSET( STDOUT_FILENO, &wr ) ) {
569
/* ================= TeXmacs is ready to receive data */
571
write_B( STDOUT_FILENO, TXB ) ;
575
if( FD_ISSET( master, &wr ) ) {
576
/* ================= Terminal is ready to receive data */
577
if( RB->put > RB->get ) {
578
tcgetattr(master, &termi ) ;
579
termi.c_lflag |= ECHO ; /* no echo */
580
termi.c_lflag ^= ECHO ; /* no echo */
581
/* set tserminal settings */
582
tcsetattr(master,TCSANOW, &termi ) ;
584
write_B( master, RB ) ;