/****************************************************************************
**
*W pty.c XGAP source Frank Celler
**
**
*Y Copyright 1995-1997, Lehrstuhl D fuer Mathematik, RWTH Aachen, Germany
*Y Copyright 1997, Frank Celler, Huerth, Germany
**
** This file contains all the code for handling pseudo ttys. 'GetMasterPty'
** is based on code from 'xterm'.
**
** GAP is started in a special mode that will mask special characters. The
** following '@' sequences produced by GAP are recoginzed:
**
** 'pX.' package mode version X
** '@' a single '@'
** 'A'..'Z' a control character
** '1','2','3','4','5','6' full garbage collection information
** '!','"','#','$','%','&' partial garbage collection information
** 'e' gap is waiting for error input
** 'c' completion started
** 'f' error output
** 'h' help started
** 'i' gap is waiting for input
** 'm' end of 'Exec'
** 'n' normal output
** 'r' the current input line follows
** 'sN' ACK for '@yN'
** 'w' a window command follows
** 'x' the current input line is empty
** 'z' start of 'Exec'
*/
#include "utils.h"
#include "gaptext.h"
#include "xcmds.h"
#include "xgap.h"
#include "pty.h"
#ifdef HAVE_OPENPTY
#if defined(HAVE_UTIL_H)
#include <util.h>
/* for openpty() on Mac OS X, OpenBSD and NetBSD */
#elif defined(HAVE_LIBUTIL_H)
#include <libutil.h>
/* for openpty() on FreeBSD */
#elif defined(HAVE_PTY_H)
#include <pty.h>
/* for openpty() on Cygwin, Interix, OSF/1 4 and 5 */
#endif
#endif
/****************************************************************************
**
*F * * * * * * * * * * * * * * local variables * * * * * * * * * * * * * * *
*/
/****************************************************************************
**
*V GapPID . . . . . . . . . . . . . . . . . . . . . . . . gap subprocess id
*/
static int GapPID = -1;
/****************************************************************************
**
*V FromGap . . . . . . . . . . . . . . . . . . . . . . for messages from gap
*/
static int FromGap;
/****************************************************************************
**
*V ToGap . . . . . . . . . . . . . . . . . . . . . . . . for messages to gap
*/
static int ToGap;
/* * * * * * * * * * * * * * global variables * * * * * * * * * * * * * * */
/****************************************************************************
**
*V QuitGapCtrlD . . . . . . . . . . . . . . . . . . . . . . . quit on CTR-D
*/
Boolean QuitGapCtrlD =
FALSE;
/****************************************************************************
**
*V ScreenSizeBuffer . . . . . . . . . . . . . . screen size change command
*/
char ScreenSizeBuffer[1024] = { 0 };
/****************************************************************************
**
*V ExecRunning . . . . . . . . . . . . . . . . . . external program running
*/
Boolean ExecRunning =
False;
/****************************************************************************
**
*F * * * * * * * * * * * * communication with GAP * * * * * * * * * * * * *
*/
/****************************************************************************
**
*F ReadGap( <line>, <len> ) . . . . . . . . . . . . . . . . read gap output
*/
#ifdef DEBUG_ON
Int READ_GAP (
String file,
Int where,
String line,
Int len)
{
Int n;
Int old;
if ( Debug & D_COMM )
{
printf(
"%04d:%s: ReadGap( buf, %d ) = ", where, file, len );
fflush( stdout );
}
if ( len < 0 )
{
len = read( FromGap, line, -len );
if ( Debug & D_COMM )
{
if ( len == -1 )
fprintf( stdout,
"-1: no input\n" );
else
{
fprintf( stdout,
"%d: '", len );
fwrite( line, 1, len, stdout );
fprintf( stdout,
"'\n" );
}
fflush( stdout );
}
return len;
}
else
{
old = len;
while ( 0 < len )
{
while ( ( n = read( FromGap, line, len ) ) < 0 )
;
line = line + n;
len = len - n;
}
if ( Debug & D_COMM )
{
fprintf( stdout,
"%d: '", old );
fwrite( line-old, 1, old, stdout );
fprintf( stdout,
"'\n" );
fflush( stdout );
}
return old;
}
}
#else
Int ReadGap (
String line,
Int len)
{
Int n;
Int old;
if ( len < 0 )
return read( FromGap, line, -len );
else
{
old = len;
while ( 0 < len )
{
while ( ( n = read( FromGap, line, len ) ) < 0 )
;
line = line + n;
len = len - n;
}
return old;
}
}
#endif
/****************************************************************************
**
*F WriteGap( <line>, <len> ) . . . . . . . . . . . . . . . . write gap input
*/
#include <errno.h>
#ifdef DEBUG_ON
void WRITE_GAP (
String file,
Int where,
String line,
Int len)
{
Int res;
if ( Debug & D_COMM )
{
printf(
"%04d:%s: WriteGap( %d ) = '", where, file, len );
fwrite( line, 1, len, stdout );
fprintf( stdout,
"'\n" );
fflush( stdout );
}
while ( 0 < len )
{
res = write( ToGap, line, len );
if ( res < 0 )
{
if ( errno == EAGAIN )
continue;
perror(
"WriteGap" );
KillGap();
exit(1);
}
len -= res;
line += res;
}
}
#else
void WriteGap (
String line,
Int len)
{
Int res;
while ( 0 < len )
{
res = write( ToGap, line, len );
if ( res < 0 )
{
if ( errno == EAGAIN )
continue;
perror(
"WriteGap" );
KillGap();
exit(1);
}
len -= res;
line += res;
}
}
#endif
/****************************************************************************
**
*V InBuffer . . . . . . . . . . . . . . . . . . . . . buffer of gap output
*/
#define SIZE_BUFFER 16000
static struct _in_buf
{
char buffer[SIZE_BUFFER];
Int pos;
Int len;
}
InBuffer;
/****************************************************************************
**
*V GapBuffer . . . . . . . . . . . . . . . . temporary buffer for 'ReadLine'
*/
static char GapBuffer[SIZE_BUFFER];
/****************************************************************************
**
*V LastLine . . . . . . . . . . . . . . . . . . . . beginning of last line
*/
static Int LastLine;
/****************************************************************************
**
*D CURRENT( <buf> ) . . . . . . . . . . . . . . . . . . . . current symbol
*/
#define CURRENT(buf) ((buf).buffer[(buf).pos])
/****************************************************************************
**
*D READ_CURRENT( <buf> ) . . . . . . . . . . . . . . consume current symbol
*/
#define READ_CURRENT(buf) ((buf).buffer[(buf).pos++])
/****************************************************************************
**
*D HAS_INPUT( <buf> ) . . . . . . . . . . . . . . . . . . . check for input
*/
#define HAS_INPUT(buf) ( ((buf).len <= (buf).pos) ? (buf).pos = 0, \
((buf).len=ReadGap((buf).buffer,-SIZE_BUFFER))>0 :\
1 )
/****************************************************************************
**
*D HAS_BUFFERED( <buf> ) . . . . . . . . . . . . . check for buffered input
*/
#define HAS_BUFFERED(buf) ( (buf).pos < (buf).len )
/****************************************************************************
**
*D LOOK_AHEAD( <buf> ) . . . look ahead if there is enough input, dont check
*/
#define LOOK_AHEAD(buf) ( ((buf).pos+1 < (buf).len ) ? \
((buf).buffer)[(buf).pos+1] :
'\0' )
/****************************************************************************
**
*F WaitInput( <buf> ) . . . . . . . . . . . . . . . wait for one character
*/
void WaitInput (
struct _in_buf * buf)
{
Int len;
if ( buf->len <= buf->pos )
{
buf->pos = 0;
ReadGap( buf->buffer, 1 );
len = ReadGap( buf->buffer+1, -(SIZE_BUFFER-1) );
buf->len = (len < 0) ? 1 : len+1;
}
}
/****************************************************************************
**
*F WaitInput2( <buf> ) . . . . . . . . . . . . . . . wait for two characters
*/
void WaitInput2 (
struct _in_buf * buf)
{
Int len;
if ( buf->len <= 1 + buf->pos )
{
if ( buf->pos+1 == buf->len )
{
*buf->buffer = buf->buffer[buf->pos];
buf->pos = 0;
ReadGap( buf->buffer+1, 1 );
len = ReadGap( buf->buffer+2, -(SIZE_BUFFER-2) );
buf->len = (len < 0) ? 2 : len+2;
}
else
{
buf->pos = 0;
ReadGap( buf->buffer, 2 );
len = ReadGap( buf->buffer+2, -(SIZE_BUFFER-2) );
buf->len = (len < 0) ? 2 : len+2;
}
}
}
/****************************************************************************
**
*F ReadLine( <buf> ) . . . . . . . . . . . . . . . . . . . . . . read a line
*/
void ReadLine (
struct _in_buf * buf)
{
String ptr = GapBuffer;
do
{
WaitInput(buf);
if ( CURRENT(*buf) ==
'\n' )
{
*ptr++ = READ_CURRENT(*buf);
*ptr = 0;
return;
}
else if ( CURRENT(*buf) ==
'\r' )
(
void) READ_CURRENT(*buf);
else if ( CURRENT(*buf) ==
'@' )
{
(
void) READ_CURRENT(*buf);
WaitInput(buf);
if ( CURRENT(*buf) ==
'J' )
{
*ptr++ =
'\n';
*ptr = 0;
(
void) READ_CURRENT(*buf);
return;
}
else if ( CURRENT(*buf) !=
'@' )
*ptr++ =
'^';
*ptr++ = READ_CURRENT(*buf);
}
else
*ptr++ = READ_CURRENT(*buf);
}
while ( 1 );
}
/****************************************************************************
**
*F StoreInput( <str>, <len> ) . . . . . . . . . store input for later usage
*/
static struct _storage
{
String buffer;
Int size;
Int len;
}
Storage = { 0, 0, 0 };
void StoreInput (
String str,
Int len)
{
if ( Storage.buffer == 0 )
{
Storage.buffer = XtMalloc(4096);
Storage.size = 4096;
}
if ( Storage.size <= Storage.len + len )
{
Storage.size += ((len/4096+1) * 4096);
Storage.buffer = XtRealloc( Storage.buffer, Storage.size );
}
memcpy( Storage.buffer+Storage.len, str, len );
Storage.len += len;
}
/****************************************************************************
**
*F ProcessStoredInput( <state> ) . . . . . . . . . feed stored input to gap
*/
static Char InputCookie =
'A';
void ProcessStoredInput (
Int state)
{
String ptr;
String free;
Int len;
static Boolean inProgress =
False;
/* if we are already processing input do not start again */
if ( inProgress || state != 0 )
return;
/* if gap is not accepting input return */
if ( GapState != GAP_INPUT && GapState != GAP_ERROR )
return;
/* if no input is waiting return */
if ( Storage.len == 0 && *ScreenSizeBuffer == 0 )
return;
/* otherwise make sure that gap does not want to tell use something */
again:
if ( HAS_INPUT(InBuffer) )
GapOutput( 0, 0, 0 );
if ( GapState != GAP_INPUT && GapState != GAP_ERROR )
return;
/* send '@yN' and wait for ACK '@sN' */
if ( InputCookie++ ==
'Z' ) InputCookie =
'A';
WriteGap(
"@y", 2 );
WriteGap( &InputCookie, 1 );
WaitInput(&InBuffer);
if ( CURRENT(InBuffer) !=
'@' )
goto again;
WaitInput2(&InBuffer);
if ( LOOK_AHEAD(InBuffer) !=
's' )
goto again;
(
void)READ_CURRENT(InBuffer);
(
void)READ_CURRENT(InBuffer);
WaitInput(&InBuffer);
if ( READ_CURRENT(InBuffer) != InputCookie )
goto again;
/* if the screen was resized, process resize command first */
if ( *ScreenSizeBuffer != 0 )
{
WriteGap( ScreenSizeBuffer, strlen(ScreenSizeBuffer) );
*ScreenSizeBuffer = 0;
return;
}
/* start processing input, check reaction of gap */
inProgress =
True;
len = Storage.len;
free = ptr = Storage.buffer;
while ( 0 < len )
{
WriteGap( ptr, 1 ); len--; ptr++;
if ( ptr[-1] ==
'\n'
|| ptr[-1] ==
'\r'
|| (free<ptr-1 && ptr[-2]==
'@' && ptr[-1]==
'M')
|| (free<ptr-1 && ptr[-2]==
'@' && ptr[-1]==
'J')
)
break;
if ( ! QuitGapCtrlD && GapState == GAP_INPUT
&& ptr[-1] ==
'@' && ptr[0] ==
'D' )
{
WriteGap(
"F@H", 3 );
len--;
ptr++;
}
}
/* create new buffer, store remaining input, and free old */
inProgress =
False;
if ( len <= Storage.size )
{
Storage.len = len;
for ( ; 0 < len; len-- )
*free++ = *ptr++;
}
else
{
Storage.size = ( 4096 < len ) ? len : 4096;
Storage.buffer = XtMalloc(Storage.size);
memcpy( Storage.buffer, ptr, len );
Storage.len = len;
XtFree(free);
}
if ( GapState == GAP_HELP )
ProcessStoredInput(0);
}
/****************************************************************************
**
*F SimulateInput( <str> ) . . . . . . . . . . enter a line as command line
*/
void SimulateInput (
String str)
{
Int pos;
/* if <GAP> is not accepting input, discard line */
if ( GapState != GAP_INPUT && GapState != GAP_ERROR )
return;
/* ok, do it. get current cursor position */
pos = GTPosition(GapTalk) - LastLine;
StoreInput(
"@A@K", 4 );
StoreInput( str, strlen(str) );
StoreInput(
"@Y@A", 4 );
while ( 0 < pos-- )
StoreInput(
"@F", 2 );
ProcessStoredInput(0);
}
/****************************************************************************
**
*F KeyboardInput( <str>, <len> ) . . . . . . . . . . process keyboard input
*/
Boolean PlayingBack =
False;
FILE * Playback = 0;
int PlaybackFile (
String str)
{
if ( Playback != 0 ) {
fclose(Playback);
}
Playback = fopen( str,
"r" );
if ( Playback != 0 ) {
PlayingBack =
True;
}
else {
PlayingBack =
False;
}
return PlayingBack;
}
int ResumePlayback (
void )
{
if ( PlayingBack || Playback == 0 )
return False;
PlayingBack =
True;
return True;
}
void KeyboardInput (
String str,
Int len)
{
char buf[1025];
#ifndef EXIT_ON_DOUBLE_CTR_C
static Int ltime = 0;
Int ntime;
#endif
/* read playback file */
if ( PlayingBack && GapState == GAP_INPUT ) {
if ( *str ==
'q' || *str ==
'Q' ) {
fclose(Playback);
PlayingBack =
False;
Playback = 0;
StoreInput(
"\"Playback STOPPED\
";;\n", 21 );
}
else if ( *str==
'z' || *str==
'Z' || *str==
'y' || *str==
'Y' ) {
PlayingBack =
False;
StoreInput(
"\"Playback SUPENDED\
";;\n", 22 );
}
else {
if ( fgets( buf, 1024, Playback ) == 0 ) {
fclose(Playback);
PlayingBack =
False;
Playback = 0;
}
else {
StoreInput( buf, strlen(buf) );
if ( feof(Playback) ) {
fclose(Playback);
PlayingBack =
False;
Playback = 0;
}
}
if ( ! PlayingBack )
StoreInput(
"\"Playback ENDED\
";;\n", 19 );
}
}
/* handle help mode directly */
else if ( GapState == GAP_HELP )
{
if ( HAS_INPUT(InBuffer) || HAS_INPUT(InBuffer) )
GapOutput( 0, 0, 0 );
if ( GapState != GAP_HELP )
{
KeyboardInput( str, len );
return;
}
/* send '@yN' and wait for ACK '@sN' */
if ( InputCookie++ ==
'Z' ) InputCookie =
'A';
WriteGap(
"@y", 2 );
WriteGap( &InputCookie, 1 );
WaitInput(&InBuffer);
if ( CURRENT(InBuffer) !=
'@' )
{
GapOutput( 0, 0, 0 );
KeyboardInput( str, len );
return;
}
WaitInput2(&InBuffer);
if ( LOOK_AHEAD(InBuffer) !=
's' )
{
GapOutput( 0, 0, 0 );
KeyboardInput( str, len );
return;
}
(
void)READ_CURRENT(InBuffer);
(
void)READ_CURRENT(InBuffer);
if ( READ_CURRENT(InBuffer) != InputCookie ) {
GapOutput( 0, 0, 0 );
KeyboardInput( str, len );
return;
}
/* write a character and start again */
WriteGap( str, 1 );
if ( *str ==
'@' && 1 < len )
{
WriteGap( str+1, 1 );
str++;
len--;
}
str++;
len--;
if ( 0 < len )
KeyboardInput( str, len );
return;
}
/* consume input */
else if ( PlayingBack && GapState == GAP_RUNNING ) {
;
}
else {
while ( 0 < len )
{
/* handle <CTR-C> */
if ( 2 <= len && *str ==
'@' && str[1] ==
'C' )
{
# ifndef EXIT_ON_DOUBLE_CTR_C
while ( 2 <= len && *str ==
'@' && str[1] ==
'C' )
{
str += 2;
len -= 2;
}
ntime = (
int) time(0);
if ( 2 < ntime - ltime )
InterruptGap();
ltime = ntime;
# else
InterruptGap();
str += 2;
len -= 2;
# endif
}
/* otherwise store it */
else
{
StoreInput( str, 1 );
len--;
str++;
}
}
}
/* try to process input */
ProcessStoredInput(0);
}
/****************************************************************************
**
*F CheckCaretPos( <new>, <old> ) . . . . . . . . . . . check caret movement
*/
Int CheckCaretPos (
Int new,
Int old)
{
/* if <LastLine> is -1, then gap is running, ignore move */
if ( LastLine < 0 )
return 0;
/* if the new position is before the last line, ignore move */
else if (
new < LastLine )
return 0;
/* otherwise move in the correct direction */
else if (
new < old )
{
while (
new++ < old )
WriteGap(
"@B", 2 );
return 0;
}
else if ( old <
new )
{
while ( old++ <
new )
WriteGap(
"@F", 2 );
return 0;
}
else
return 0;
}
/****************************************************************************
**
*F ParseInt( <buf>, <val> ) . . . . . . . . . . . . . . . get a long value
*/
static Boolean ParseInt (
struct _in_buf * buf,
Int * val )
{
Int mult;
*val = 0;
mult = 1;
do
{
WaitInput(buf);
if ( CURRENT(*buf) ==
'+' )
{
(
void) READ_CURRENT(*buf);
return True;
}
else if ( CURRENT(*buf) ==
'-' )
{
(
void) READ_CURRENT(*buf);
*val = -*val;
return True;
}
else if (
'0' <= CURRENT(*buf) && CURRENT(*buf) <=
'9' )
*val += mult * (READ_CURRENT(*buf)-
'0');
else
return False;
mult = mult * 10;
}
while (1);
}
/****************************************************************************
**
*F GapOutput( <cld>, <fid>, <id> ) . . . . . . . . . . . . handle gap output
*/
#undef CTR
#define CTR(a) ( a & 0x1f )
static char TBuf[SIZE_BUFFER];
void GapOutput (
XtPointer cld,
int * fid,
XtInputId id)
{
char ch;
Int special;
Int len;
/* wait a while for input */
HAS_INPUT(InBuffer);
HAS_INPUT(InBuffer);
HAS_INPUT(InBuffer);
/* special code for 'Exec' */
if ( ExecRunning )
{
DEBUG( D_COMM, (
"GapOutput: exec still active\n") );
len = 0;
while ( HAS_BUFFERED(InBuffer) && len < SIZE_BUFFER-3 )
{
/* '@' is special */
if ( CURRENT(InBuffer) ==
'@' )
{
(
void) READ_CURRENT(InBuffer);
WaitInput(&InBuffer);
if ( CURRENT(InBuffer) ==
'm' )
{
/* get ride of any output left over */
if ( 0 < len )
{
TBuf[len] = 0;
GTReplaceText( GapTalk, TBuf, len );
if ( SpyMode )
fwrite( TBuf, 1, len, stderr );
}
/* collect output 'TBuf' in case it is not "mAgIc" */
len = 0;
TBuf[len++] =
'@';
TBuf[len++] =
'm';
(
void)READ_CURRENT(InBuffer);
WaitInput(&InBuffer);
if ( CURRENT(InBuffer) !=
'A' )
continue;
(
void)READ_CURRENT(InBuffer);
TBuf[len++] =
'A';
WaitInput(&InBuffer);
if ( CURRENT(InBuffer) !=
'g' )
continue;
(
void)READ_CURRENT(InBuffer);
TBuf[len++] =
'g';
WaitInput(&InBuffer);
if ( CURRENT(InBuffer) !=
'I' )
continue;
(
void)READ_CURRENT(InBuffer);
TBuf[len++] =
'I';
WaitInput(&InBuffer);
if ( CURRENT(InBuffer) !=
'c' )
continue;
(
void)READ_CURRENT(InBuffer);
len = 0;
ExecRunning =
False;
DEBUG( D_COMM, (
"GapOutput: %s: '%s'\n",
"leaving exec loop, input remaining",
InBuffer.buffer+InBuffer.pos) );
goto end_exec_loop;
}
else
{
TBuf[len++] =
'@';
continue;
}
}
/* store input */
else
TBuf[len++] = READ_CURRENT(InBuffer);
}
TBuf[len] = 0;
GTReplaceText( GapTalk, TBuf, len );
if ( SpyMode )
fwrite( TBuf, 1, len, stderr );
return;
}
end_exec_loop:
/* process gap output */
while ( HAS_BUFFERED(InBuffer) )
{
/* '@' is special */
if ( CURRENT(InBuffer) ==
'@' )
{
(
void) READ_CURRENT(InBuffer);
WaitInput(&InBuffer);
if (
'A' <= CURRENT(InBuffer) && CURRENT(InBuffer) <=
'Z' )
{
special = 0;
ch = READ_CURRENT(InBuffer);
ch = CTR(ch);
}
else if ( CURRENT(InBuffer) ==
'@' )
{
special = 0;
ch = READ_CURRENT(InBuffer);
}
else if ( CURRENT(InBuffer) ==
'z' )
{
(
void)READ_CURRENT(InBuffer);
ExecRunning =
True;
DEBUG( D_COMM, (
"GapOutput: entering exec loop\n") );
GapOutput( cld, fid, id );
return;
}
else
{
special = 1;
ch = READ_CURRENT(InBuffer);
}
}
else
{
special = 0;
ch = READ_CURRENT(InBuffer);
}
/* process window commands */
if ( special )
{
/* '1' to '6' are garbage */
if (
'1' <= ch && ch <=
'6' )
{
Int size;
ParseInt( &InBuffer, &size );
UpdateMemoryInfo( (
int) (ch-
'0'), size );
}
/* '!','"','#','$','%','&' are garbage */
else if (
'!' <= ch && ch <=
'&' ) {
Int size;
ParseInt( &InBuffer, &size );
}
/* 'i' means gap is waiting for input */
else if ( ch ==
'i' )
{
LastLine = GTPosition(GapTalk);
GapState = GAP_INPUT;
UpdateMenus(GapState);
UpdateXCMDS(
True);
ProcessStoredInput(0);
}
/* 'e' means gap is waiting for error input */
else if ( ch ==
'e' )
{
LastLine = GTPosition(GapTalk);
GapState = GAP_ERROR;
UpdateMenus(GapState);
UpdateXCMDS(
True);
ProcessStoredInput(0);
}
/* 'r' is the current input line */
else if ( ch ==
'r' )
{
ReadLine(&InBuffer);
GTSetPosition( GapTalk, LastLine );
GTReplaceText( GapTalk, GapBuffer, strlen(GapBuffer) );
if ( SpyMode )
{
fwrite( GapBuffer, 1, strlen(GapBuffer), stderr );
}
GapState = GAP_RUNNING;
UpdateMenus(GapState);
UpdateXCMDS(
False);
ProcessStoredInput(1);
LastLine = -1;
}
/* 'x' no text at current line */
else if ( ch ==
'x' )
{
GTSetPosition( GapTalk, LastLine );
GTReplaceText( GapTalk,
"", 0 );
GapState = GAP_RUNNING;
UpdateMenus(GapState);
UpdateXCMDS(
True);
LastLine = -1;
}
/* 'c' completion output started */
else if ( ch ==
'c' )
{
GapState = GAP_RUNNING;
UpdateMenus(GapState);
UpdateXCMDS(
True);
LastLine = -1;
}
/* 'h' help output started */
else if ( ch ==
'h' )
{
GapState = GAP_HELP;
UpdateMenus(GapState);
UpdateXCMDS(
False);
LastLine = -1;
}
/* 'w' is a window command */
else if ( ch ==
'w' )
{
long i;
long m;
char * ptr;
char * cmd;
len = 0;
WaitInput(&InBuffer);
ch = READ_CURRENT(InBuffer);
for ( len = 0, m = 1;
'0' <= ch && ch <=
'9'; m *= 10 ) {
len += (ch-
'0') * m;
WaitInput(&InBuffer);
ch = READ_CURRENT(InBuffer);
}
ptr = cmd = XtMalloc(len+1);
i = len;
while ( 0 < i )
{
WaitInput(&InBuffer);
while ( HAS_INPUT(InBuffer) && 0 < i )
{
*ptr++ = READ_CURRENT(InBuffer);
i--;
}
}
*ptr++ = 0;
GapWindowCmd( cmd, len );
XtFree(cmd);
}
/* ignore 'n' for the moment */
else if ( ch ==
'n' )
ch =
'n';
/* ignore 'f' for the moment */
else if ( ch ==
'f' )
ch =
'f';
/* ignore 's', see 'SimulateInput' */
else if ( ch ==
's' ) {
WaitInput(&InBuffer);
(
void)READ_CURRENT(InBuffer);
continue;
}
}
/* collect normal characters and display them */
else if (
' ' <= ch && ch < 127 && GapState == GAP_RUNNING )
{
TBuf[0] = ch;
for ( len = 1; len<SIZE_BUFFER && HAS_BUFFERED(InBuffer); )
if ( CURRENT(InBuffer) ==
'@' )
{
if ( LOOK_AHEAD(InBuffer) ==
'n' )
{
(
void)READ_CURRENT(InBuffer);
/* WaitInput(&InBuffer); */
(
void)READ_CURRENT(InBuffer);
}
else if ( LOOK_AHEAD(InBuffer) ==
'f' )
{
(
void)READ_CURRENT(InBuffer);
/* WaitInput(&InBuffer); */
(
void)READ_CURRENT(InBuffer);
}
else if ( LOOK_AHEAD(InBuffer) ==
'J' )
{
(
void)READ_CURRENT(InBuffer);
/* WaitInput(&InBuffer); */
(
void)READ_CURRENT(InBuffer);
TBuf[len++] =
'\n';
}
else
break;
}
else if (
' '<=CURRENT(InBuffer) && CURRENT(InBuffer)<127 )
TBuf[len++] = READ_CURRENT(InBuffer);
else
break;
GTReplaceText( GapTalk, TBuf, len );
}
/* collect normal characters and display them */
else if (
' ' <= ch && ch < 127 && GapState != GAP_RUNNING )
{
TBuf[0] = ch;
for ( len = 1; len<SIZE_BUFFER && HAS_INPUT(InBuffer); len++ )
if ( CURRENT(InBuffer) ==
'@' )
break;
else if (
' '<=CURRENT(InBuffer) && CURRENT(InBuffer)<127 )
TBuf[len] = READ_CURRENT(InBuffer);
else
break;
GTReplaceText( GapTalk, TBuf, len );
}
/* carriage return */
else if ( ch ==
'\n' )
{
if ( GapState != GAP_INPUT && GapState != GAP_ERROR )
GTReplaceText( GapTalk, &ch, 1 );
}
/* <CTR-G> rings a bell */
else if ( ch == CTR(
'G') )
GTBell(GapTalk);
/* <CTR-H> moves to the left */
else if ( ch == CTR(
'H') )
GTMoveCaret( GapTalk, -1 );
/* ignore anything else */
else
ch = ch;
}
}
/****************************************************************************
**
*F * * * * * * * * * * * * starting/stopping gap + * * * * * * * * * * * * *
*/
/****************************************************************************
**
*F KillGap() . . . . . . . . . . . . . . . . . . . . . kill the running gap
*/
void KillGap ()
{
if ( GapPID != -1 )
{
close(ToGap);
kill( GapPID, SIGKILL );
}
}
/****************************************************************************
**
*F InterruptGap() . . . . . . . . . . . . . . . . interrupt the running gap
*/
void InterruptGap ()
{
if ( GapPID != -1 )
kill( GapPID, SIGINT );
}
/****************************************************************************
**
*F OpenPty( <master>, <slave> ) . . . . . . open a pty master/slave pair
*/
#ifdef HAVE_OPENPTY
static UInt OpenPty(
int * master,
int * slave)
{
/* openpty is available on OpenBSD, NetBSD and FreeBSD, Mac OS X,
Cygwin, Interix, OSF/1 4 and 5, and glibc (since 1998), and hence
on most modern Linux systems. See also:
https://www.gnu.org/software/gnulib/manual/html_node/openpty.html */
return (openpty(master, slave, NULL, NULL, NULL) < 0);
}
#elif defined(HAVE_POSIX_OPENPT)
static UInt OpenPty(
int * master,
int * slave)
{
/* Attempt to use POSIX 98 pseudo ttys. Opening a master tty is done
via posix_openpt, which is available on virtually every current
UNIX system; indeed, according to gnulib, it is available on at
least the following systems:
- glibc >= 2.2.1 (released January 2001; but is a stub on GNU/Hurd),
- Mac OS X >= 10.4 (released April 2005),
- FreeBSD >= 5.1 (released June 2003),
- NetBSD >= 3.0 (released December 2005),
- AIX >= 5.2 (released October 2002),
- HP-UX >= 11.31 (released February 2007),
- Solaris >= 10 (released January 2005),
- Cygwin >= 1.7 (released December 2009).
Systems lacking posix_openpt (in addition to older versions of
the systems listed above) include:
- OpenBSD
- Minix 3.1.8
- IRIX 6.5
- OSF/1 5.1
- mingw
- MSVC 9
- Interix 3.5
- BeOS
*/
*master = posix_openpt(O_RDWR | O_NOCTTY);
if (*master < 0) {
fputs(
"OpenPty: posix_openpt failed\n");
return 1;
}
if (grantpt(*master)) {
fputs(
"OpenPty: grantpt failed\n");
goto error;
}
if (unlockpt(*master)) {
close(*master);
fputs(
"OpenPty: unlockpt failed\n");
goto error;
}
*slave = open(ptsname(*master), O_RDWR, 0);
if (*slave < 0) {
fputs(
"OpenPty: opening slave tty failed\n");
goto error;
}
return 0;
error:
close(*master);
return 1;
}
#else
static UInt OpenPty(
int * master,
int * slave)
{
fputs(
"no pseudo tty support available\n");
return 1;
}
#endif
/****************************************************************************
**
*F StartGapProcess( <name>, <argv> ) . . . start a gap subprocess using ptys
*/
static void GapStatusHasChanged (
int signo)
{
int w;
/* if the child was stopped return */
if ( waitpid( -1, &w, WNOHANG | WUNTRACED ) != GapPID || WIFSTOPPED(w) )
return;
# ifdef DEBUG_ON
fputs(
"gap status has changed, leaving xgap\n", stderr );
fprintf( stderr,
"Signal: %d\n",WTERMSIG(w));
# endif
exit(1);
}
int StartGapProcess (
String name,
String argv[])
{
Int j;
/* loop variables */
char c[10];
/* buffer for communication */
int master;
/* pipe to GAP */
int n;
/* return value of 'select' */
int slave;
/* pipe from GAP */
/* struct */ fd_set fds; /* for 'select' */
struct timeval timeout;
/* time to wait for aknowledgement */
# if HAVE_TERMIOS_H
struct termios tst;
/* old and new terminal state */
# else
# if HAVE_TERMIO_H
struct termio tst;
/* old and new terminal state */
# else
struct sgttyb tst;
/* old and new terminal state */
# endif
# endif
/* open pseudo terminal for communication with gap */
if (OpenPty(&master, &slave))
{
fprintf( stderr,
"open pty failed (errno %d)\n", errno );
exit(1);
}
# if HAVE_TERMIOS_H
if ( tcgetattr( slave, &tst ) == -1 )
{
fputs(
"tcgetattr on slave pty failed\n", stderr );
exit(1);
}
tst.c_cc[VINTR] = 0377;
tst.c_cc[VQUIT] = 0377;
tst.c_iflag &= ~(INLCR|ICRNL);
tst.c_cc[VMIN] = 1;
tst.c_cc[VTIME] = 0;
tst.c_lflag &= ~(ECHO|ICANON);
if ( tcsetattr( slave, TCSANOW, &tst ) == -1 )
{
fputs(
"tcsetattr on slave pty failed\n", stderr );
exit(1);
}
# else
# if HAVE_TERMIO_H
if ( ioctl( slave, TCGETA, &tst ) == -1 )
{
fputs(
"ioctl TCGETA on slave pty failed\n", stderr );
exit(1);
}
tst.c_cc[VINTR] = 0377;
tst.c_cc[VQUIT] = 0377;
tst.c_iflag &= ~(INLCR|ICRNL);
tst.c_cc[VMIN] = 1;
tst.c_cc[VTIME] = 0;
/* Note that this is at least on Linux dangerous!
Therefore, we now have the HAVE_TERMIOS_H section for POSIX
Terminal control. */
tst.c_lflag &= ~(ECHO|ICANON);
if ( ioctl( slave, TCSETAW, &tst ) == -1 )
{
fputs(
"ioctl TCSETAW on slave pty failed\n", stderr );
exit(1);
}
# else
if ( ioctl( slave, TIOCGETP, (
char*)&tst ) == -1 )
{
if ( ttydev )
fprintf( stderr,
"ioctl TIOCGETP on slave pty failed (%s)\n",
ttydev );
else
fputs(
"ioctl TIOCGETP on slave pty failed\n", stderr );
exit(1);
}
tst.sg_flags |= RAW;
tst.sg_flags &= ~ECHO;
if ( ioctl( slave, TIOCSETN, (
char*)&tst ) == -1 )
{
fputs(
"ioctl on TIOCSETN slave pty failed\n", stderr );
exit(1);
}
#endif
#endif
/* set input to non blocking operation */
if ( fcntl( master, F_SETFL, O_NDELAY ) < 0 )
{
fputs(
"Panic: cannot set non blocking operation.\n", stderr );
exit(1);
}
/* fork to gap, dup pipe to stdin and stdout */
GapPID = fork();
if ( GapPID == 0 )
{
dup2( slave, 0 );
dup2( slave, 1 );
/* The following is necessary because otherwise the GAP process
will ignore the SIGINT signal: */
signal( SIGINT, SIG_DFL );
# ifdef SYS_HAS_EXECV_CCHARPP
execv( name, (
const char**) argv );
# else
execv( name, (
void*) argv );
# endif
if (write( 1,
"@-", 2) < 2) fputs(
"Child: Cannot write @-", stderr);
close(slave);
_
exit(1);
}
ToGap = master;
FromGap = master;
/* check if the fork was successful */
if ( GapPID == -1 )
{
fputs(
"Panic: cannot fork to subprocess.\n", stderr );
exit(1);
}
/* wait at least 60 sec before giving up */
timeout.tv_sec = 60;
timeout.tv_usec = 0;
/* wait for an aknowledgement (@p) from the gap subprocess */
j = 0;
while ( j < 10 && c[j-1] !=
'.' )
{
/* set <FromGap> port for listen */
# ifdef FD_SET
FD_ZERO(&fds);
FD_SET( FromGap, &fds );
# else
{
Int i;
for ( i = FromGap/
sizeof(fds.fds_bits); 0 <= i; i-- )
fds.fds_bits[i] = 0;
fds.fds_bits[FromGap/
sizeof(fds.fds_bits)] =
( 1 << (FromGap %
sizeof(fds.fds_bits)) );
}
# endif
/* use 'select' to check port */
if ( (n = select(FromGap+1, &fds, 0, 0, &timeout)) == -1 )
{
kill( GapPID, SIGKILL );
perror(
"select failed");
exit(1);
}
else if ( n == 0 )
{
kill( GapPID, SIGKILL );
fputs(
"Panic: cannot establish communication with gap.", stderr);
exit(1);
}
else
ReadGap( &(c[j++]), 1 );
}
/* check if we got "@p" */
if ( c[j-1] !=
'.' || strncmp( c,
"@p", 2 ) )
{
if ( ! strncmp( c,
"@-", 2 ) )
{
fputs(
"Panic: cannot start subprocess ", stderr );
fputs( name, stderr );
fputs(
".\n", stderr );
}
else
{
strcpy( c+3,
"'\n" );
fputs(
"Panic: cannot talk with gap, got '", stderr );
fputs( c, stderr );
kill( GapPID, SIGKILL );
}
exit(1);
}
/* if the gap dies, stop program */
signal( SIGCHLD, GapStatusHasChanged );
InBuffer.pos = InBuffer.len = 0;
return FromGap;
}
/****************************************************************************
**
*E pty.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ends here
*/