/****************************************************************************
**
*A  main.c                      XGAP Source                      Frank Celler
**
*H  @(#)$Id: main.c,v 1.8 1994/06/11 12:41:43 fceller Exp $
**
*Y  Copyright 1993-1995,   Frank Celler,   Lehrstuhl D fuer Mathematik,  RWTH
**
*H  $Log: main.c,v $
*H  Revision 1.8  1994/06/11  12:41:43  fceller
*H  added 'SYS_HAS_TIME_PROTO'
*H
*H  Revision 1.7  1994/06/06  08:56:07  fceller
*H  updated database
*H
*H  Revision 1.6  1994/06/03  10:50:09  fceller
*H  fixed exec problem (again)
*H
*H  Revision 1.5  1993/12/23  08:45:16  fceller
*H  added "gap drop prompt"
*H  renamed 'SignalHandler' to 'MySignalHandler' to avoid linux conflict
*H
*H  Revision 1.4  1993/10/18  11:04:47  fceller
*H  added fast updated,  fixed timing problem
*H
*H  Revision 1.3  1993/10/06  16:17:09  fceller
*H  new gap package mode
*H
*H  Revision 1.2  1993/08/12  13:48:06  fceller
*H  added help menu
*H
*H  Revision 1.1  1993/07/29  07:02:30  fceller
*H  added 'Exec' hack
*H
*H  Revision 1.0  1993/04/05  11:42:18  fceller
*H  Initial revision
*/
#include    <stdio.h>
#include    <signal.h>

#include    <X11/Intrinsic.h>
#include    <X11/StringDefs.h>

#include    <X11/Xaw/Label.h>
#include    <X11/Xaw/Viewport.h>
#include    <X11/Xaw/Box.h>
#include    <X11/Xaw/MenuButton.h>
#include    <X11/Xaw/SimpleMenu.h>
#include    <X11/Xaw/SmeLine.h>
#include    <X11/Xaw/SmeBSB.h>
#include    <X11/Xaw/Paned.h>
#include    <X11/Xaw/Text.h>
#include    <X11/Xaw/TextSink.h>

#include    "utils.h"
#include    "gaptext.h"
#include    "pty.h"
#include    "xcmds.h"
#include    "main.h"


#ifdef __STDC__
static void KeyboardInput( char*, int );
static void UpdateMemoryInfo( int, long );
static void UpdateMenus( int );
static void GapOutput( XtPointer, int *,  XtInputId );
#else
static void KeyboardInput();
static void UpdateMemoryInfo();
static void UpdateMenus();
static void GapOutput();
#endif


/****************************************************************************
**
*F  AppContext	. . . . . . . . . . . . . . . . . . . . .  aplication context
*/
XtAppContext AppContext;


/****************************************************************************
**
*V  xGap  . . . . . . . . . . . . . . . . . . . . . . . . . .  toplevel shell
*/
Widget xGap;


/****************************************************************************
**
*V  GapTalk . . . . . . . . . . . . . . . . . . . . . . . . . gap text window
*/
Widget GapTalk;


/****************************************************************************
**

*V  FallbackResources . . . . . . . . . . . . . . . . . . . default resources
*/
static char *FallbackResources[] =
{
    "*menu.line.height:                       10",
    "*xgapMenu*shapeStyle:                    ShapeOval",
    "*xgapDialog*shapeStyle:                  ShapeOval",

    /* gap talk window */
    "*xgapTalk.height:                        600",
    "*xgapTalk.width:                         600",
    "*xgapMenu.showGrip:                      False",
    "*xgapTalk.showGrip:                      False",
    "*xgapTalk.quitGapCtrD:                   True",
    "*xgapTalk.pasteGapPrompt:                True",

    /* gap menu */
    "*xgapMenu.gapButton.label:               Gap",
    "*xgapMenu.gapButton*pastePrompt.label:   Paste 'gap>'",
    "*xgapMenu.gapButton*quitGapCTRD.label:   Quit on CTR-D",
    "*xgapMenu.gapButton*editFile.label:      Edit File ...",
    "*xgapMenu.gapButton*readFile.label:      Read File ...",
    "*xgapMenu.gapButton*reloadLib.label:     Reload Library",
    "*xgapMenu.gapButton*changeLib.label:     Change Library ...",
#ifdef DEBUG_ON
    "*xgapMenu.gapButton*resyncGap.label:     Resync with GAP",
#endif
    "*xgapMenu.gapButton*quit.label:          Quit Gap",
    "*xgapMenu.gapButton*kill.label:          Kill Gap",

    /* run menu */
    "*xgapMenu.runButton.label:               Run",
    "*xgapMenu.runButton*quitBreak.label:     Leave Breakloop",
    "*xgapMenu.runButton*contBreak.label:     Continue Execution",
    "*xgapMenu.runButton*interrupt.label:     Interrupt",
    "*xgapMenu.runButton*garbColl.label:      Collect Garbage",
    "*xgapMenu.runButton*garbMesg.label:      Toggle GC Messages",
    "*xgapMenu.runButton*infoRead.label:      Toggle Library Read Mesg",

    /* help menu */
    "*xgapMenu.helpButton.label:              Help",
    "*xgapMenu.helpButton*chpsHelp.label:     Chapters",
    "*xgapMenu.helpButton*secsHelp.label:     Sections",
    "*xgapMenu.helpButton*nchpHelp.label:     Next Chapter",
    "*xgapMenu.helpButton*pchpHelp.label:     Previous Chapter",
    "*xgapMenu.helpButton*nextHelp.label:     Next Help Section",
    "*xgapMenu.helpButton*prevHelp.label:     Previous Help Section",
    "*xgapMenu.helpButton*contHelp.label:     Continue Help",
    "*xgapMenu.helpButton*quitHelp.label:     Quit Help",

    /* gap graphic window */
    "*xgapWindowViewport.width:               400",
    "*xgapWindowViewport.height:              600",

    /* query a input file name */
    "*queryFileName.xgapDialog.icon:          Term",
    0
};


/****************************************************************************
**
*V  CommandOptions  . . . . . . . . . . . . . . . . . .  command line options
*/
static XrmOptionDescRec CommandOptions[] =
{
    { "-tiny",		"*tinyFont",	    XrmoptionSepArg,  0 },
    { "-tinyFont",      "*tinyFont",        XrmoptionSepArg,  0 },
    { "-small",         "*smallFont",       XrmoptionSepArg,  0 },
    { "-smallFont",     "*smallFont",       XrmoptionSepArg,  0 },
    { "-normal", 	"*normalFont",      XrmoptionSepArg,  0 },
    { "-normalFont", 	"*normalFont",      XrmoptionSepArg,  0 },
    { "-large",         "*largeFont",       XrmoptionSepArg,  0 },
    { "-largeFont",     "*largeFont",       XrmoptionSepArg,  0 },
    { "-huge",          "*hugeFont",        XrmoptionSepArg,  0 },
    { "-hugeFont",      "*hugeFont",        XrmoptionSepArg,  0 },
    { "-titlePosition", "*titlePosition",   XrmoptionSepArg,  0 },
    { "-positionTitle", "*titlePosition",   XrmoptionSepArg,  0 },
    { "-tp",            "*titlePosition",   XrmoptionSepArg,  0 }
};


/****************************************************************************
**

*V  GapState  . . . . . . . . . . . . . . . . . . . . . . . . . status of gap
*/
#define	GAP_RUNNING	1
#define GAP_INPUT       2
#define GAP_ERROR       3
#define GAP_HELP        4

int GapState = GAP_RUNNING;


/****************************************************************************
**

*V  InBuffer  . . . . . . . . . . . . . . . . . . . . .  buffer of gap output
*/
#define SIZE_BUFFER	5120

struct _in_buf
{
    char    buffer[SIZE_BUFFER];
    int     pos;
    int     len;
}
InBuffer;


/****************************************************************************
**
*V  GapBuffer . . . . . . . . . . . . . . . . temporary buffer for 'ReadLine'
*/
char GapBuffer[16000];


/****************************************************************************
**
*F  CURRENT(<buf>)  . . . . . . . . . . . . . . . . . . . . .  current symbol
*/
#define	CURRENT(buf)	    ((buf).buffer[(buf).pos])


/****************************************************************************
**
*F  READ_CURRENT( <buf> ) . . . . . . . . . . . . . .  consume current symbol
*/
#define	READ_CURRENT(buf)   ((buf).buffer[(buf).pos++])


/****************************************************************************
**
*F  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 )


/****************************************************************************
**
*F  HAS_BUFFERED( <buf> ) . . . . . . . . . . . . .  check for buffered input
*/
#define HAS_BUFFERED(buf)   ( (buf).pos < (buf).len )


/****************************************************************************
**
*F  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 input
*/
void WaitInput ( buf )
    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;
    }
}

void WaitInput2 ( buf )
    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 into <GapBuffer>
*/
void ReadLine ( buf )
    struct _in_buf    * buf;
{
    char              * 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;
	    }
	    *ptr++ = READ_CURRENT(*buf);
	}
	else
	    *ptr++ = READ_CURRENT(*buf);
    } while ( 1 );
}


/****************************************************************************
**

*V  LastLine  . . . . . . . . . . . . . . . . . . . .  beginning of last line
*/
long LastLine;


/****************************************************************************
**
*F  StoreInput( <str>, <len> )	. . . . . . . . . store input for later usage
*/
struct _storage
{
    char      * buffer;
    int         size;
    int         len;
}
Storage = { 0, 0, 0 };

void StoreInput ( str, len )
    char      * 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 ScreenSizeBuffer[128] = { 0 };
Boolean QuitGapCtrlD = FALSE;

void ProcessStoredInput ( state )
    int             state;
{
    char          * ptr;
    char          * free;
    int             len;
    static int      inProgress = 0;

    /* 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) || HAS_INPUT(InBuffer) )
	GapOutput( 0, 0, 0 );
    if ( GapState!=GAP_INPUT && GapState!=GAP_ERROR )
	return;

    /* send '@y' and wait for ACK '@s' */
    WriteGap( "@y", 2 );
    WaitInput(&InBuffer);
    if ( CURRENT(InBuffer) != '@' )
	goto again;
    WaitInput2(&InBuffer);
    if ( LOOK_AHEAD(InBuffer) != 's' )
	goto again;
    (void)READ_CURRENT(InBuffer);
    (void)READ_CURRENT(InBuffer);

    /* 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 = 1;
    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 = 0;
    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 ( str )
    char  * 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
*/
static void KeyboardInput ( str, len )
    char      * str;
    int     	len;
{
#ifndef	DO_EXIT_ON_DOUBLE_CTRL_C
#ifndef SYS_HAS_TIME_PROTO
    extern int  time();
#endif
    static int	ltime = 0;
    int         ntime;  
#endif

    /* handle help mode directly */
    if ( GapState == GAP_HELP )
    {
	if ( HAS_INPUT(InBuffer) || HAS_INPUT(InBuffer) )
	    GapOutput( 0, 0, 0 );
	if ( GapState != GAP_HELP )
	{
	    KeyboardInput( str, len );
	    return;
	}

	/* send '@y' and wait for ACK '@s' */
	WriteGap( "@y", 2 );
	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);

	/* 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 */
    while ( 0 < len )
    {

	/* handle <CTRL-C> */
	if ( 2 <= len && *str == '@' && str[1] == 'C' )
	{
#           ifndef DO_EXIT_ON_DOUBLE_CTRL_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
*/
static int CheckCaretPos ( new, old )
    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  ParseLong( <buf>, <val> )	. . . . . . . . . . . . . .  get a long value
*/
#ifdef __STDC__
static int ParseLong ( struct _in_buf*, long* );
#endif

static int ParseLong ( buf, val )
    struct _in_buf    * buf;
    long              * val;
{
    long                mult;

    *val = 0;
    mult = 1;
    do
    {
	WaitInput(buf);
	if ( CURRENT(*buf) == '+' )
	{
	    (void) READ_CURRENT(*buf);
	    return 1;
	}
	else if ( CURRENT(*buf) == '-' )
	{
	    (void) READ_CURRENT(*buf);
	    *val = -*val;
	    return 1;
	}
	else if ( '0' <= CURRENT(*buf) && CURRENT(*buf) <= '9' )
	    *val += mult * (READ_CURRENT(*buf)-'0');
	else
	    return 0;
	mult = mult * 10;
    } while (1);
}


/****************************************************************************
**
*F  GapOutput( <cld>, <fid>, <id> ) . . . . . . . . . . . . handle gap output
**
**  The following '@' sequence are recoginzed:
**
**    '@@'		a single '@'
**    '@A'..'@Z'	a control character
**    '@1'..'@4'        garbage information
**    '@c'              completion started
**    '@e'              gap is waiting for error input
**    '@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
**    '@s'              ACK for '@y' (ignore it here,  see 'SimulateInput')
**    '@w'              a window command follows
**    '@x'              the current input line is empty
**    '@z'              start of 'Exec'
*/
#define CTRL(a)	( a & 0x1f )
static char TBuf[SIZE_BUFFER];
int ExecRunning = 0;

static void GapOutput ( cld, fid, id )
    XtPointer       cld;
    int           * fid;
    XtInputId       id;
{
    char            ch;
    int             special;
    long            len;

    /* wait a while for input */
    HAS_INPUT(InBuffer);
    HAS_INPUT(InBuffer);
    HAS_INPUT(InBuffer);

    /* special code for 'Exec' */
    if ( ExecRunning )
    {
#       ifdef DEBUG_ON
            if ( Debug )
		fprintf( stderr, "%04d:%s:GapOutput:*** exec still active\n",
			 __LINE__, __FILE__ );
#       endif
	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 );
		    }

		    /* collect ouptut '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 = 0;
#                   ifdef DEBUG_ON
		       if ( Debug )
	                 fprintf( stderr, "%04d:%s:GapOutput:*** %s: '%s'\n",
			     __LINE__, __FILE__,
			     "leaving exec loop, input remaining",
			     InBuffer.buffer+InBuffer.pos );
#                   endif
		    goto end_exec_loop;

		}
		else
		{
		    TBuf[len++] = '@';
		    continue;
		}
	    }
	    
	    /* store input */
	    else
		TBuf[len++] = READ_CURRENT(InBuffer);
	}
	TBuf[len] = 0;
	GTReplaceText( GapTalk, TBuf, len );
	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 = CTRL(ch);
	    }
	    else if ( CURRENT(InBuffer) == '@' )
	    {
		special = 0;
		ch = READ_CURRENT(InBuffer);
	    }
	    else if ( CURRENT(InBuffer) == 'z' )
	    {
		(void)READ_CURRENT(InBuffer);
		ExecRunning = 1;
#               if DEBUG_ON
		    if ( Debug )
			fprintf( stderr, "%04d:%s:GapOutput:*** %s\n",
			    __LINE__, __FILE__, "entering exec loop" );
#               endif
		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 '4' are garbage */
	    if ( 1 <= ch && ch <= '4' )
	    {
		long	size;

		ParseLong( &InBuffer, &size );
		UpdateMemoryInfo( (int) (ch-'0'), size );
	    }

	    /* 'i' means gap is waiting for input */
	    else if ( ch == 'i' )
	    {
		LastLine = GTPosition(GapTalk);
		GapState = GAP_INPUT;
		UpdateMenus(GapState);;
		ProcessStoredInput(0);
	    }

	    /* 'e' means gap is waiting for error input */
	    else if ( ch == 'e' )
	    {
		LastLine = GTPosition(GapTalk);
		GapState = GAP_ERROR;
		UpdateMenus(GapState);;
		ProcessStoredInput(0);
	    }

	    /* 'r' is the current input line */
	    else if ( ch == 'r' )
	    {
		ReadLine(&InBuffer);
		GTSetPosition( GapTalk, LastLine );
		GTReplaceText( GapTalk, GapBuffer, strlen(GapBuffer) );
		GapState = GAP_RUNNING;
		UpdateMenus(GapState);;
		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);;
		LastLine = -1;
	    }
	    
	    /* 'c' completion output started */
	    else if ( ch == 'c' )
	    {
		GapState = GAP_RUNNING;
		UpdateMenus(GapState);;
		LastLine = -1;
	    }
	    
	    /* 'h' help output started */
	    else if ( ch == 'h' )
	    {
		GapState = GAP_HELP;
		UpdateMenus(GapState);;
		LastLine = -1;
	    }
	    
	    /* 'w' is a window command */
	    else if ( ch == 'w' )
	    {
		long	i;
		long    m;
		char  * ptr;
		char  * cmd;

		len = 0;
		for ( len = 0, i = 9, m = 1;  1 < i;  i--, m*= 10 )
		{
		    WaitInput(&InBuffer);
		    len += m * (READ_CURRENT(InBuffer)-'0');
		}
		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' )
		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 == CTRL('G') )
	    GTBell(GapTalk);

	/* <CTR-H> moves to the left */
	else if ( ch == CTRL('H') )
	    GTMoveCaret( GapTalk, -1 );

	/* ignore anything else */
	else 
	    ch = ch;
    }
}


/****************************************************************************
**

*V  GapMenu . . . . . . . . . . . . . . . . . . . . . . . . xgap's "Gap" menu
**
*/
void MenuQuitGap()   { KeyboardInput( "@C@A@Kquit;\nquit;\n", 18 );	}
void MenuKillGap()   { KillGap();                                       }
void MenuReloadLib() { SimulateInput( "ReadLib( \"init\" );\n" );       }

#ifdef DEBUG_ON
void MenuResyncGap()
{
    ExecRunning = 0;
    GapState = GAP_INPUT;
    UpdateMenus(GapState);;
    ProcessStoredInput(0);
}
#endif

void MenuPastePrompt( item )
    TypeMenuItem *      item;
{
    static Boolean	paste = False;

    paste = !paste;
    GTDropGapPrompt( GapTalk, !paste );
    if ( paste )
	XtVaSetValues( item->entry, XtNrightBitmap, CheckMarkSymbol, 0 );
    else
	XtVaSetValues( item->entry, XtNrightBitmap, EmptyMarkSymbol, 0 );
}

void MenuQuitGapCTRD( item )
    TypeMenuItem *      item;
{
    QuitGapCtrlD = !QuitGapCtrlD;
    if ( QuitGapCtrlD )
	XtVaSetValues( item->entry, XtNrightBitmap, CheckMarkSymbol, 0 );
    else
	XtVaSetValues( item->entry, XtNrightBitmap, EmptyMarkSymbol, 0 );
}

TypeMenuItem GapMenu[] =
{
    { "pastePrompt",    MenuPastePrompt,        S_ALWAYS,	0 },
    { "quitGapCTRD",    MenuQuitGapCTRD,        S_ALWAYS,       0 },
    { "-----------",    0,                      0,              0 },
    { "reloadLib",  	MenuReloadLib, 		S_NORMAL_ONLY,	0 },
    { "-----------",    0,                      0,              0 },
#ifdef DEBUG_ON
    { "resyncGap",      MenuResyncGap,          S_ALWAYS,       0 },
#endif
    { "quit",           MenuQuitGap,            S_ALWAYS,       0 },
    { "kill",           MenuKillGap,            S_ALWAYS,       0 },
    { 0,                0,             	        0,              0 }
};


/****************************************************************************
**
*V  RunMenu . . . . . . . . . . . . . . . . . . . . . . . . xgap's "Run" menu
**
*/
void MenuInterrupt () { InterruptGap();                            }
void MenuQuitBreak () { SimulateInput( "quit;\n" );                }
void MenuContBreak () { SimulateInput( "return;\n" );              }
void MenuGarbColl ()  { SimulateInput( "GASMAN(\"collect\");\n" ); }
void MenuGarbMesg ()  { SimulateInput( "GASMAN(\"message\");\n" ); }
void MenuInfoRead ()  { SimulateInput(
"if InfoRead1=Print then InfoRead1:=Ignore; else InfoRead1:=Print; fi;\n"); }

TypeMenuItem RunMenu[] =
{
    { "interrupt",      MenuInterrupt,          S_RUNNING_ONLY, 0 },
    { "---------",      0,                      0,              0 },
    { "quitBreak",    	MenuQuitBreak,    	S_ERROR_ONLY,	0 },
    { "contBreak",      MenuContBreak,          S_ERROR_ONLY,   0 },
    { "---------",      0,                      0,              0 },
    { "garbColl",       MenuGarbColl,           S_INPUT_ONLY,   0 }, 
    { "garbMesg",       MenuGarbMesg,           S_INPUT_ONLY,   0 },
    { "infoRead",       MenuInfoRead,           S_INPUT_ONLY,   0 },
    { 0,                0,             	        0,              0 }
};


/****************************************************************************
**
*V  HelpMenu  . . . . . . . . . . . . . . . . . . . . . .  xgap's "Help" menu
**
*/
void MenuChapters ()     { SimulateInput( "?Chapters\n" );}
void MenuSections ()     { SimulateInput( "?Sections\n" );}
void MenuNextHelp ()     { SimulateInput( "?>\n" );       }
void MenuNextChapter ()  { SimulateInput( "?>>\n" );      }
void MenuPrevChapter ()  { SimulateInput( "?<<\n" );      }
void MenuPrevHelp ()     { SimulateInput( "?<\n" );       }
void MenuContHelp ()     { KeyboardInput( " ", 1 );       }
void MenuQuitHelp ()     { KeyboardInput( "q", 1 );       }


TypeMenuItem HelpMenu[] =
{
    { "chpsHelp",       MenuChapters,           S_INPUT_ONLY,   0 },
    { "secsHelp",       MenuSections,           S_INPUT_ONLY,   0 },
    { "---------",      0,                      0,              0 },
    { "nchpHelp",       MenuNextChapter,        S_INPUT_ONLY,   0 },
    { "pchpHelp",       MenuPrevChapter,        S_INPUT_ONLY,   0 },
    { "nextHelp",       MenuNextHelp,           S_INPUT_ONLY,   0 },
    { "prevHelp",       MenuPrevHelp,           S_INPUT_ONLY,   0 },
    { "contHelp",       MenuContHelp,           S_HELP_ONLY,    0 },
    { "quitHelp",       MenuQuitHelp,           S_HELP_ONLY,    0 },
    { 0,              	0,               	0,              0 }
};


/****************************************************************************
**

*F  UpdateMemoryInfo( <type>, <val> )	. . . . . . . . . update memory usage
*/
static Widget LabelBytesFree;
static Widget LabelBytesTotal;
static Widget LabelHandleUsed;
static Widget LabelHandleDead;

static void UpdateMemoryInfo ( type, val )
    int		type;
    long        val;
{    char        tmp[30];

    
    switch ( type )
    {
	case 1:
	    sprintf( tmp, "Handle used: %-5ld ", val );
  	    XtVaSetValues( LabelHandleUsed, XtNlabel, tmp, NULL );
	    break;
	case 2:
            sprintf( tmp, "Handle dead: %-5ld ", val );
            XtVaSetValues( LabelHandleDead, XtNlabel, tmp, NULL );
	    break;
	case 3:
            sprintf( tmp, "KBytes free: %-4ld ", val/1024L );
            XtVaSetValues( LabelBytesFree, XtNlabel, tmp, NULL );
	    break;
	case 4:
            sprintf( tmp, "KBytes total: %-4ld ", val/1024L );
            XtVaSetValues( LabelBytesTotal, XtNlabel, tmp, NULL );
	    break;
    }
}


/****************************************************************************
**
*F  UpdateMenus( <state> )  . . . . . .  update menus in case of state change
*/
TypeList ListInputOnly   = 0;
TypeList ListErrorOnly   = 0;
TypeList ListNormalOnly  = 0;
TypeList ListRunningOnly = 0;
TypeList ListHelpOnly    = 0;

static void UpdateMenus ( state )
    int         state;
{
    TypeList	l;
    int         i;

    /* menu entry active only in break loop */
    l = ListErrorOnly;
    for ( i = 0;  i < l->len;  i++ )
    {
	if ( state == GAP_ERROR )
	    XtVaSetValues( (Widget)l->ptr[i], XtNsensitive, True, 0 );
	else
	    XtVaSetValues( (Widget)l->ptr[i], XtNsensitive, False, 0 );
    }

    /* menu entry active only during input */
    l = ListInputOnly;
    for ( i = 0;  i < l->len;  i++ )
    {
	if ( state == GAP_ERROR || state == GAP_INPUT )
	    XtVaSetValues( (Widget)l->ptr[i], XtNsensitive, True, 0 );
	else
	    XtVaSetValues( (Widget)l->ptr[i], XtNsensitive, False, 0 );
    }

    /* menu entry active only during normal input */
    l = ListNormalOnly;
    for ( i = 0;  i < l->len;  i++ )
    {
	if ( state == GAP_INPUT )
	    XtVaSetValues( (Widget)l->ptr[i], XtNsensitive, True, 0 );
	else
	    XtVaSetValues( (Widget)l->ptr[i], XtNsensitive, False, 0 );
    }

    /* menu entry active only while gap is running */
    l = ListRunningOnly;
    for ( i = 0;  i < l->len;  i++ )
    {
	if ( state == GAP_RUNNING )
	    XtVaSetValues( (Widget)l->ptr[i], XtNsensitive, True, 0 );
	else
	    XtVaSetValues( (Widget)l->ptr[i], XtNsensitive, False, 0 );
    }

    /* menu entry active only while gap is helping */
    l = ListHelpOnly;
    for ( i = 0;  i < l->len;  i++ )
    {
	if ( state == GAP_HELP )
	    XtVaSetValues( (Widget)l->ptr[i], XtNsensitive, True, 0 );
	else
	    XtVaSetValues( (Widget)l->ptr[i], XtNsensitive, False, 0 );
    }
}


/****************************************************************************
**
*F  CreateMenu( <button>, <items> ) . . . . . . . . . . . . create a pop menu
**
**  RESOURCES
**    *menu.line.height
**        height of menu line separator, default 10
*/
#ifdef __STDC__
static void CreateMenu( Widget, TypeMenuItem* );
static void MenuSelected( Widget, TypeMenuItem*, caddr_t );
#else
static void MenuSelected();
#endif

static void CreateMenu ( button, items )
    Widget	    button;
    TypeMenuItem  * items;
{
    Widget          menu;

    /* if this is the first call,  create lists */
    if ( ListInputOnly == 0 )
    {
	ListErrorOnly   = List(0);
	ListHelpOnly    = List(0);
	ListInputOnly   = List(0);
	ListNormalOnly  = List(0);
	ListRunningOnly = List(0);
    }

    /* create new simple menu */
    menu = XtCreatePopupShell( "menu", simpleMenuWidgetClass, button, 0, 0 );

    /* and add menu buttons */
    for ( ;  items->label != 0;  items++ )
    {
	if ( *(items->label) == '-' )
	    (void) XtVaCreateManagedWidget( "line",
		       smeLineObjectClass, menu, NULL );
	else
	{
	    items->entry = XtVaCreateManagedWidget(
			       items->label, smeBSBObjectClass, menu,
			       XtNrightMargin, 14,
                               XtNrightBitmap, EmptyMarkSymbol,
			       0 );
	    XtAddCallback( items->entry, XtNcallback,
			   (XtCallbackProc)MenuSelected, items );
	    switch ( items->sensitive )
	    {
		case S_INPUT_ONLY:
		    AddList( ListInputOnly, items->entry );
		    XtVaSetValues( items->entry, XtNsensitive, False, 0 );
		    break;
		case S_ERROR_ONLY:
		    AddList( ListErrorOnly, items->entry );
		    XtVaSetValues( items->entry, XtNsensitive, False, 0 );
		    break;
		case S_NORMAL_ONLY:
		    AddList( ListNormalOnly, items->entry );
		    XtVaSetValues( items->entry, XtNsensitive, False, 0 );
		    break;
		case S_RUNNING_ONLY:
		    AddList( ListRunningOnly, items->entry );
		    XtVaSetValues( items->entry, XtNsensitive, False, 0 );
		    break;
		case S_HELP_ONLY:
		    AddList( ListHelpOnly, items->entry );
		    XtVaSetValues( items->entry, XtNsensitive, False, 0 );
		    break;
		case S_ALWAYS:
		    break;
	    }
	}
    }
}

static void MenuSelected ( w, item, dummy )
    Widget 	    w;
    TypeMenuItem *  item;
    caddr_t         dummy;
{
    if ( item->click != 0 )
	(*(item->click))(item);
    else
    {
	fputs( "Warning: menu item ", stderr   );
	fputs( XtName(w), stderr               );
	fputs( " has been selected.\n", stderr );
    }
}


/****************************************************************************
**
*F  CreateGapWindow() . . . . . . . . . . . . . . create communication window
**
**  RESOURCES
**    *xgapMenu*shapeStyle
**        style of the menu buttons, default "ShapeOval"
**    *xgap.height
**    *xgap.width
**        start size of the communication text window
*/
#ifdef __STDC__
static void CreateGapWindow( void );
static void GapTalkResized( Widget, XtPointer, XEvent*, Boolean* );
#else
static void GapTalkResized();
#endif

static void CreateGapWindow ()
{
    Widget	paned;
    Widget      box;
    Widget      button;
    Pixmap      symbol;
    Display   * display;

    /* create a "paned" for the menu and text window */
    paned = XtVaCreateManagedWidget( "paned", panedWidgetClass,
				      xGap, NULL );

    /* create a menu box for the menu buttons */
    box = XtVaCreateManagedWidget( "xgapMenu", boxWidgetClass,
	      paned,
	      XtNx,	    	        0,
	      XtNy,                     0,
 	      XtNresizeToPreferred,	True,
	      NULL );

    /* create a menu button drop down symbol */
    display = XtDisplay(box);
    symbol = XCreateBitmapFromData( display,
	     DefaultRootWindow(display),
             "\376\3\2\2\2\6\162\6\2\6\162\6\2\6\162\6\2\6\2\6\376\7\370\7",
	     12, 12 );

    /* create file menu button and file menu */
    button = XtVaCreateManagedWidget( "gapButton", menuButtonWidgetClass,
	         box,
	         XtNleftBitmap,     symbol,
		 XtNx,              0,
		 NULL );
    CreateMenu( button, GapMenu );

    /* create run menu button and run menu */
    button = XtVaCreateManagedWidget( "runButton", menuButtonWidgetClass,
	         box,
		 XtNleftBitmap,     symbol,
		 XtNx,              10,
		 0 );
    CreateMenu( button, RunMenu );

    /* create help menu button and help menu */
    button = XtVaCreateManagedWidget( "helpButton", menuButtonWidgetClass,
	         box,
		 XtNleftBitmap,     symbol,
		 XtNx,              10,
		 0 );
    CreateMenu( button, HelpMenu );

    /* create the communication window */
    GapTalk = XtVaCreateManagedWidget( "xgapTalk", gapTextWidgetClass,
                  paned,
		  XtNinputCallback,    KeyboardInput,
		  XtNcheckCaretPos,    CheckCaretPos,
                  XtNscrollHorizontal, XawtextScrollWhenNeeded,
                  XtNscrollVertical,   XawtextScrollAlways,
                  XtNeditType,         XawtextEdit,
                  XtNbottomMargin,     15,
                  XtNx,                0,
                  XtNy,                10,
                  XtNdisplayCaret,     True,
                  NULL );
    XtAddEventHandler(GapTalk,StructureNotifyMask,False,GapTalkResized,0);
    GTDropGapPrompt( GapTalk, True );

    /* create a box and labels for garbage info */
    box = XtVaCreateManagedWidget( "xgapInfo", boxWidgetClass,
	      paned,
	      XtNx, 	                0,
	      XtNy,                     20,
	      XtNskipAdjust,            True,
	      XtNresizeToPreferred, 	True,
	      NULL );
    LabelBytesFree = XtVaCreateManagedWidget( "bytesFree",
			 labelWidgetClass, box,
			 XtNborderWidth, 0,
			 NULL );
    LabelBytesTotal = XtVaCreateManagedWidget( "bytesTotal",
		          labelWidgetClass, box,
 			  XtNborderWidth, 0,
			  NULL );
    LabelHandleUsed = XtVaCreateManagedWidget( "handleUsed",
		          labelWidgetClass, box,
			  XtNborderWidth, 0,
			  NULL );
    LabelHandleDead = XtVaCreateManagedWidget( "handleDead",
			  labelWidgetClass, box,
			  XtNborderWidth, 0,
			  NULL );
    UpdateMemoryInfo( 1, 0 );
    UpdateMemoryInfo( 2, 0 );
    UpdateMemoryInfo( 3, 0 );
    UpdateMemoryInfo( 4, 0 );
}

static void GapTalkResized ( talk, cd, evt, ctd )
    Widget	            talk;
    XtPointer               cd;
    XEvent                * evt;
    Boolean               * ctd;
{
    Widget                  snk;
    XFontStruct           * font;
    static unsigned long    w,  w1 = 0;
    static unsigned long    h,  h1 = 0;
    char                    buf[128];
    char                  * ptr;
    int                     i;

    /* is this a resize event */
    if ( evt->type == ConfigureNotify )
    {
	
	/* compute a sensible size */
	XtVaGetValues( talk, XtNtextSink, &snk,  0 );
	XtVaGetValues( snk,  XtNfont,     &font, 0 );
	w = evt->xconfigure.width / font->max_bounds.width - 3;
	h = evt->xconfigure.height / ( font->max_bounds.ascent
	    + font->max_bounds.descent ) - 2;
	if ( w < 2 )  w = 2;
	if ( h < 2 )  h = 2;
	if ( w == w1 && h == h1 )
	    return;
	w1 = w;
	h1 = h;

	/* construct gap command */
	strcpy( buf, "SizeScreen([ " );
	ptr = buf + strlen(buf);
	for ( i = 3;  0 <= i;  i--, w = w / 10 )
	    ptr[i] = w%10 + '0';
	ptr += 4;
	*ptr++ = ',';
	*ptr++ = ' ';
	for ( i = 3;  0 <= i;  i--, h = h / 10 )
	    ptr[i] = h%10 + '0';
        ptr += 4;
        strcpy( ptr, " ]);;\n" );

	/* if gap is waiting for input, do it */
	if ( GapState == GAP_INPUT || GapState == GAP_ERROR )
	    SimulateInput( buf );
	else
	    strcpy( ScreenSizeBuffer, buf );
    }
}


/****************************************************************************
**

*F  IOErrorHandler(<dis>) . . . . . . . . . . . . kill gap in case of X error
*/
int (*OldIOErrorHandler)();

int IOErrorHandler ( dis )
    Display   * dis;
{
    KillGap();
    return OldIOErrorHandler(dis);
}


/****************************************************************************
**
*F  ErrorHandler(<dis>) . . . . . . . . . . . . . kill gap in case of X error
*/
int (*OldErrorHandler)();

int ErrorHandler ( dis, evt )
    Display       * dis;
    XErrorEvent	    evt;
{
    KillGap();
    return OldErrorHandler( dis, evt );
}


/****************************************************************************
**
*F  MySignalHandler() . . . . . . . . . . . . . .  kill gap in case of signal
*/
void MySignalHandler ()
{
    KillGap();
    exit(1);
}


/****************************************************************************
**
*F  ParseArgs( <argc>, <argv> ) . . . . . . . . . . initialize gap subprocess
*/
char * nargv[1024];

void ParseArgs ( argc, argv )
    int         argc;
    char     ** argv;
{
    int		nargc;
    int         i,  j;
    char      * p;

    /* at first assume that "gap" is started with "gap", append "-p" */
    nargc = 0;
    nargv[nargc++] = "gap";
    nargv[nargc++] = "-p";

    /* parse arguments, collect arguments for gap in <nargv> */
    for ( argv++, argc--;  0 < argc;  argv++, argc-- )
    {
	if ( *argv[0] == '-' )
	{
	    if ( strlen(*argv) != 2 )
	    {
		fputs("Gap: sorry options must not be grouped '", stderr);
		fputs(*argv, stderr);
		fputs("'.\n", stderr);
		exit(1);
	    }
	    switch( argv[0][1] )
	    {
#               ifdef DEBUG_ON
		case 'D':
		    Debug = 1;
		    break;
#               endif

#               if defined(MALLOC_DEBUG_ON) || defined(X11_MALLOC_DEBUG_ON)
	        case 'M':
		    atexit(FCmalloc_check_free);
		    break;
#               endif

		/* get name of gap subprocess */
		case 'G':
		    if ( argc-- < 2 )
		    {
			fputs( "Gap: option '-G' must have an argument.\n",
			       stderr );
			exit(1);
		    }
		    p = *++argv;
		    nargv[0] = p;
		    j = 0;
		    while ( *++p )
		    {
			if ( *p == ' ' )
			{
			    *p = '\0';
			    j  = j + 1;
			}
		    }
		    if ( 0 < j )
		    {
			for ( i = nargc-1;  0 < i;  i-- )
			    nargv[i+j] = nargv[i];
			nargc = nargc + j;
			while ( 0 < j )
			{
			    while ( *--p ) ;
			    nargv[j--] = p+1;
			}
		    }
		    break;

		default:
		    nargv[nargc++] = *argv;
		    break;
	    }
	}
	else
	    break;
    }
    for ( ; 0 < argc;  argv++, argc-- )
	nargv[nargc++] = *argv;
    nargv[nargc] = 0;
    return;
}


/****************************************************************************
**
*F  main( <argc>, <argv> )  . . . . . . . . . . . . . . . .   main event loop
*/
int main ( argc,  argv )
    int         argc;
    char     ** argv;
{
    extern int  FromGap;
    Boolean     flag;
    int         i;

    /* create a new top level shell and an applictation context */
    xGap = XtVaAppInitialize( &AppContext, "XGap",
			      CommandOptions, XtNumber(CommandOptions),
			      &argc, argv, FallbackResources, 0 );

    /* install your error handler, we have to kill gap in this case */
    OldIOErrorHandler = XSetIOErrorHandler( IOErrorHandler );
    OldErrorHandler   = XSetErrorHandler  ( ErrorHandler   );

    /* install your signal handler, we have to kill gap in this case */
    signal( SIGHUP,  MySignalHandler );
    signal( SIGINT,  MySignalHandler );
    signal( SIGQUIT, MySignalHandler );
    signal( SIGILL,  MySignalHandler );
    signal( SIGIOT,  MySignalHandler );
    signal( SIGBUS,  MySignalHandler );
    signal( SIGSEGV, MySignalHandler );

    /* create the gap talk window */
    CreateGapWindow();
    XtRealizeWidget(xGap);

    /* initialize window commands */
    InitXCMDS();

    /* parse remaining arguments */
    ParseArgs( argc, argv );
    StartGapProcess( nargv[0], nargv );
    InBuffer.pos = InBuffer.len = 0;

    /* to quit or not do quit on CTR-D */
    XtVaGetValues( GapTalk, XtNquitGapCtrD, &flag,  0 );
    if ( flag )
    {
	for ( i = 0;  GapMenu[i].label;  i++ )
	    if ( !strcmp( GapMenu[i].label, "quitGapCTRD" ) )
		break;
	if ( GapMenu[i].label && GapMenu[i].click )
	    GapMenu[i].click(&(GapMenu[i]));
    }

    /* paste GAP prompt into talk window? */
    XtVaGetValues( GapTalk, XtNpasteGapPrompt, &flag,  0 );
    if ( flag )
    {
	for ( i = 0;  GapMenu[i].label;  i++ )
	    if ( !strcmp( GapMenu[i].label, "pastePrompt" ) )
		break;
	if ( GapMenu[i].label && GapMenu[i].click )
	    GapMenu[i].click(&(GapMenu[i]));
    }

    /* add callback for output from gap*/
    XtAppAddInput( AppContext,  FromGap,  (XtPointer) XtInputReadMask,
                   (XtInputCallbackProc) GapOutput, (XtPointer) 0 );

    /* force a garbage collection in the beginning */
    StoreInput( "GASMAN(\"collect\");\n", 19 );

    /* enter main read-eval loop */
    XtAppMainLoop(AppContext);
    return 0;
}
