#define HANDLERS_C
/*
#define TUNNELDEBUG
 */
#include "wafe.h"
#include "version.h"
#include <sys/wait.h>

#define EXTRA_HANDLERS 16

/* from AIX man page */
#define CMD_BUF_SIZE		32764
#define COM_BUF_SIZE		32764
#define STDIN_BUF_SIZE		10240
#define STDERR_BUF_SIZE		10240
#define EXTRA_BUF_SIZE		10240

/* global variables for signal handling */
char *wafeHandlers[HANDLERMAX];
static char *tclIntHandlers[SIGMAX];

static Boolean intPending[SIGMAX];
static Boolean duringEval    = False;
static int     anyIntPending = 0;

/* global variables for io handlers */
char   wafePromptChar  = '%';
static char *comBuffer = NULL;

static char  *communicationHandlerVarname = NULL;
static char  *communicationHandlerCommand = NULL;
static int    communicationHandlerLength = 0;
static int    communicationCount = 0;

static Boolean stdinHandlerRegistered  = False;
static Boolean stdoutHandlerRegistered = False;
static Boolean stderrHandlerRegistered = False;
static Boolean interactiveMode;

static char  inputBuffer[CMD_BUF_SIZE+2];
static int   leftBytes = 0;      

extern int Tcl_ListCmd(
#if NeedFunctionPrototypes
	ClientData, Tcl_Interp*, int, char**
#endif
);

static Tcl_DString tclCommand;	/* Used to buffer incomplete 
				 * commands being read from stdin. */
#define waitingForCompletion (Tcl_DStringLength(&tclCommand)>0)

/*
 * Declare the additional application resource:
 * initCom (Class InitCom) can be used to start an interpreter
 */

extern void wafeExecActionProc(
#if NeedFunctionPrototypes
     Widget, XEvent *, char **, Cardinal *
#endif
);

typedef struct {
    String  init_com;
  } AppData, *AppDataPtr;

static AppData    app_data;
static XtResource resources[] = {
	{
	"initCom",
	"InitCom",
	XtRString,
	sizeof(String),
	XtOffset(AppDataPtr, init_com),
	XtRImmediate,
	"",
	}
    };

static XtActionsRec    tclAction[] = {
    {"exec",  wafeExecActionProc},
    };



/*
 * The following functions simplify error handling - they all are
 * invoked under certain circumstances and print an error message
 *
 */


void 
wafeConvError (tclCommand, argc, tclArg, type)
char * tclCommand;
char * argc;
char * tclArg;
char * type;
    {
#ifdef RETURN_EMSG
    char errorBuffer[200];
    sprintf(errorBuffer, 
	    "%s: could not convert argument %s <%s> to type %s",
	    tclCommand, argc, tclArg, type);
    Tcl_SetResult(wafeInterpreter, errorBuffer, TCL_VOLATILE);
#else
    fprintf(stderr,
	    "Wafe(%s): could not convert argument %s <%s> to type %s\n",
	    tclCommand, argc, tclArg, type);
#endif
    }


void
wafeArgcError (tclCommand, quant, tclArgs, argc)
char *tclCommand;
char *quant;
int   tclArgs;
int   argc;
    {
#ifdef RETURN_EMSG
    char errorBuffer[200];
    sprintf(errorBuffer, 
	    "%s: %s%d arguments expected, %d arguments received",
            tclCommand, quant, tclArgs, argc - 1);
    Tcl_SetResult(wafeInterpreter, errorBuffer, TCL_VOLATILE);
#else
    fprintf(stderr,
	    "Wafe(%s): %s%d arguments expected, %d arguments received\n",
            tclCommand, quant, tclArgs, argc - 1);
#endif
    }

void
wafeNoVarCompError (tclCommand, name, comp)
char *tclCommand;
char *name;
char *comp;
    {
#ifdef RETURN_EMSG
    char errorBuffer[200];
    sprintf(errorBuffer, 
	    "%s: no component %s of structure %s",
            tclCommand, comp, name);
    Tcl_SetResult(wafeInterpreter, errorBuffer, TCL_VOLATILE);
#else
    fprintf(stderr,
	    "Wafe(%s): no component %s of structure %s\n",
            tclCommand, comp, name);
#endif
    }


int
wafeEval(interp, cmd, context)
    Tcl_Interp *interp;
    char *cmd;
    char *context;
    {
    int rc, sig;
    static void sigcldHandler();
    
    DBUG_PRINT(context, ("command: <%s>", cmd));

    duringEval = True;
    if ((rc = Tcl_GlobalEval(interp,cmd)) == TCL_ERROR) 
	{ 
	char *line = strtok(cmd,"\n");
	int   currLine = 1;

	fprintf(stderr, "Wafe(%s): %s\n", context, interp->result);
	do 
	    {
	    fprintf(stderr, "%2d: %s %s\n", 
		    currLine, currLine == interp->errorLine ? "->" : "  ",
		    line);
	    currLine ++;
	    }
	while ((line = strtok(NULL,"\n")));
	}
    else
	{
/*	if (interactiveMode && *interp->result != 0) */
	if (interactiveMode && !strcmp(context,"eval")) 
	    fprintf(stderr, "#R: {%s}\n",interp->result);
	}
    
    duringEval = False;

    if (anyIntPending) 
	{
	for (sig=0; sig < SIGMAX; sig++)
	    if (intPending[sig])
		{
		(void) wafeEval(interp, tclIntHandlers[sig], "sigPending");
		intPending[sig] = False;
		break;
		}
	anyIntPending --;
	}

    if (wafeClientPid)
	signal(SIGCLD,sigcldHandler);

    return rc;
    }

/*
 * SUBST_VARS can be used to activate variable substitution in 
 * lists of argument value pairs
 */
#define SUBST_VARS

Boolean
wafeMergeArguments(arguments,context,argc,argv)
char        *arguments;
char        *context;
int        * argc;
char      ***argv;
    {
    char *p = arguments;
#ifdef SUBST_VARS
    while (*p) 
	{
	if (*p == '\n') *p = ' ';
	p++;
	}

    if (Tcl_VarEval(wafeInterpreter,"list ", arguments, NULL) == TCL_ERROR)
	{
	fprintf(stderr,"Wafe(%s): %s\n",context,wafeInterpreter->result);
	return False;
	}
    
    p = wafeInterpreter->result;
#endif

    if (Tcl_SplitList(wafeInterpreter, p, argc, argv) == TCL_ERROR)
	{
	fprintf(stderr,"Wafe(%s): %s\n",context,wafeInterpreter->result);
	return False;
	}

#ifdef SUBST_VARS_VERBOSE
	{
	int i;
	char **arg = *argv;
	
	for (i=0; i < *argc; i++, arg++) 
	    {
	    fprintf(stderr,"arg %d: <%s>\n",i,*arg);
	    }
	}
#endif
    
    return True;
    }


void 
wafeExit (rc)
int rc;
    {
    static char *exitCmd = "if [string compare {} [info proc onExit]] onExit";
    (void) wafeEval(wafeInterpreter, exitCmd, "exit");
    exit(rc);
    }


void
wafeFatal(part, message, arg) 
String part;
String message;
String arg;
    {
    fprintf(stderr,"Wafe(%s): ",part);
    fprintf(stderr,message,arg);
    fprintf(stderr, "; aborting...\n");
    if (wafeClientPid) 
	kill(wafeClientPid, SIGTERM);
    wafeExit(-1);
    }


/*****************************************************************************
 *    FUNCTION:  signalHandler                                             
 *               If wafe receives a signal
 *               it is forwarded to the application                
 *                                                                         
 *    Arguments: signal number                                             
 *    Returns:   nothing                                                   
 *                                                                         
 *    Used by:   main                                                      
 *****************************************************************************/

static void
signalHandler(sig)
int   sig;
    {
    DBUG_ENTER ("signalHandler");
    DBUG_PRINT("signal", ("Signal %d received", sig));

    if (tclIntHandlers[sig]) 
      {
	if (duringEval) 
	  {
	    if (!intPending[sig]) 
	      {
		intPending[sig] = True;
		anyIntPending ++;
	      }
	  } 
	else 
	  (void) wafeEval(wafeInterpreter, tclIntHandlers[sig], "sig");
      }
    else 
      {
	fprintf(stderr, "Wafe(main): Signal %d received\n", sig);
	if (wafeClientPid)
	    {
	    fprintf(stderr, "Wafe(main): forwarding it to application!\n");
	    kill(wafeClientPid, sig);
	    }
	wafeExit(0); 
	}
    /* 
     * restore the signal handler 
     */
    signal(sig,signalHandler);
    DBUG_VOID_RETURN;
    }

static void
sigcldHandler(sig)
int   sig;
    {
    int status;
    if (waitpid (wafeClientPid, &status,WNOHANG) == wafeClientPid) 
	wafeExit(0);
    return;
    }

/*****************************************************************************
 *    FUNCTION:  XIOHandler                                             
 *               If wafe receives a XIOError
 *               SIGTERM is sent to the application
 *                                                                         
 *    Arguments: display, error event
 *    Returns:   nothing                                                   
 *                                                                         
 *    Used by:   main                                                      
 *****************************************************************************/

static int
IOErrorHandler(dpy)
Display *dpy;
    {
    char buffer[200];
    
    DBUG_ENTER ("IOErrorHandler");

    if (wafeHandlers[XIOERR])
	{
	sprintf(buffer,"%s %s",wafeHandlers[XIOERR],DisplayString(dpy));
	(void) wafeEval(wafeInterpreter, buffer, "XIOERR");
	}

    fprintf(stderr, "Wafe(main): XIOError on %s, terminating child!\n",
	    DisplayString(dpy));

    if (wafeClientPid) 
	kill(wafeClientPid, SIGTERM);
    else 
	wafeExit(0);
    
    DBUG_RETURN (-1);
    }





static void
outputBuffer(buffer) 
char * buffer;
    {
    if (wafeHandlers[STDOUT])
	{                    
	Tcl_SetVar(wafeInterpreter, "STDOUT", buffer, 
		   TCL_GLOBAL_ONLY);
	(void) wafeEval(wafeInterpreter, wafeHandlers[STDOUT], "STDOUT");
	}
    else
	fprintf(stdout,"%s",buffer);
    }


static void
getInput(client_data, fid, xid)
XtPointer   client_data;          /* PromptMode True oder False  */
int         *fid;
XtInputId   *xid;
    {

    char   *bufferPtr = inputBuffer + leftBytes;
    char   *firstPtr = bufferPtr;
    char   *secondPtr = inputBuffer;
    int     readBytes;
    Boolean overFlowFlag = True;
    Boolean evalCommand;

    DBUG_ENTER("getInput");

    DBUG_PRINT("io",
         ("reading inputBuffer=%p, bufptr=%p, left=%d, can read=%d", 
	  inputBuffer, bufferPtr, leftBytes, CMD_BUF_SIZE-leftBytes));
 
    if ((readBytes = read(*fid, bufferPtr, CMD_BUF_SIZE - leftBytes)) == -1)
	perror("Wafe: Communication error: ");

    DBUG_PRINT("io",
         ("read inputBuffer= %p, bufptr=%p, readBytes=%d, size=%d",
	    inputBuffer, bufferPtr, readBytes, leftBytes+readBytes));
    
    *(bufferPtr + readBytes) = '\0';
   
    DBUG_PRINT("io", ("BufferContents are:\n<%s>", bufferPtr));     

    if (!readBytes)
	{
	DBUG_PRINT("io",("Wafe: eof on primary input channel, bye.\n"));
	wafeExit(1);
	}    
 
    do
	{
	/* search for newline or end of string */
	while ((*firstPtr != '\n') && (*firstPtr != '\0'))
	    firstPtr++;

	if (*firstPtr == '\n') /* One buffer is ready */
	    {
	    char saveChar;
	    
	    leftBytes = 0;
	    overFlowFlag = False;
	    saveChar = *(firstPtr+1);
	    *(firstPtr+1) = '\0';
	    
	    DBUG_PRINT("io", ("Parsed Command <%s>", secondPtr));
	    
	    if (client_data)   /* Standard Mode, checking magic chars */
		{
		if (*secondPtr == wafePromptChar) 
		    {
		    ++secondPtr;
		    evalCommand = True;
		    }
		else
                    evalCommand = False;
	        }
            else
	        evalCommand = True;
	    
            if (evalCommand) 
                {
		if ((*secondPtr == '\\' || 
		     *secondPtr == '+'  || 
		     *secondPtr == '/'  || 
		     *secondPtr == '='
		     ) && !waitingForCompletion)
		    {
		    char* namePtr = secondPtr + 1;
		    char* valPtr = namePtr;
		    /*
		       fprintf(stderr,
		         "first=%p, second=%p\n",firstPtr,secondPtr);
		     */
		    for(; valPtr<firstPtr 
			&& *valPtr != ' ' 
			&& *valPtr != '\n'; valPtr++);

		    if (valPtr != namePtr)  /* we got a variable name */
			{
			
			*valPtr = '\0';     /* terminate varname */

			if (valPtr != firstPtr) 
			    valPtr++;
/*
		        fprintf(stderr,
                                "name=%p, value=%p\n",namePtr,valPtr);
 */
			switch (*secondPtr) 
			    {
			    case '\\':
			        if (valPtr == firstPtr)
				    valPtr = "\n";
				
				Tcl_SetVar(wafeInterpreter, namePtr, valPtr, 
					   TCL_APPEND_VALUE|TCL_GLOBAL_ONLY);

				DBUG_PRINT("assign", 
				     ("appending to variable <%s> value <%s>nl",
				      namePtr,valPtr));     
				break;

			    case '/':
				Tcl_SetVar(wafeInterpreter, namePtr, "\n", 
					   TCL_APPEND_VALUE|TCL_GLOBAL_ONLY);

			        if (valPtr != firstPtr)
				    {
				    *firstPtr = '\0';
				    Tcl_SetVar(wafeInterpreter, namePtr, valPtr, 
					TCL_APPEND_VALUE|TCL_GLOBAL_ONLY);
				    }

				DBUG_PRINT("assign", 
				     ("appending to variable <%s> value nl<%s>",
				      namePtr,valPtr));     
				break;
		    
			    case '+':
				if (valPtr != firstPtr) 
				    {
				    *firstPtr = '\0';
				    Tcl_SetVar(wafeInterpreter, namePtr, valPtr, 
					TCL_APPEND_VALUE|TCL_GLOBAL_ONLY);

				    DBUG_PRINT("assign", 
					       ("appending to <%s> value <%s>",
						namePtr,valPtr));     
				    }
				break;

			    case '=':
				*firstPtr = '\0';
				Tcl_SetVar(wafeInterpreter, namePtr, valPtr, 
					   TCL_GLOBAL_ONLY);
				DBUG_PRINT("assign", 
					   ("setting variable <%s> value <%s>",
					    namePtr,valPtr));     
				}
			}
		    else
			{  
			/* got no varname */
			outputBuffer(client_data ? secondPtr-1 : secondPtr);
			}
		    
		    }
		else  /* no assignment character, evaluate the command */
		    {
		    /* for inner Event loops or incomplete commands
		       the command buffer has to be copied
		     */
		    if (waitingForCompletion || wafeInnerEventLoop)
			{
			char *cmd = Tcl_DStringAppend(&tclCommand, 
						secondPtr, -1);
			DBUG_PRINT("eval", ("appending to command : <%s> ",
					    secondPtr));     
			if (Tcl_CommandComplete(cmd))
			    {
			    (void) wafeEval(wafeInterpreter,cmd,"eval");
			    Tcl_DStringFree(&tclCommand);    
			    }
			}
		    else 
			{
			if (Tcl_CommandComplete(secondPtr))
			    (void) wafeEval(wafeInterpreter,secondPtr,"eval");
			else 
			    {
			    DBUG_PRINT("eval", ("appending to command : <%s> ",
						secondPtr));     
			    (void) Tcl_DStringAppend(&tclCommand, 
						    secondPtr, -1);
			    }
			}
		    }
		}
	    else   /* no evaluation wanted */
		    outputBuffer(secondPtr);

	    /* we have processed something ending with \n */	    
	    firstPtr++;
	    *firstPtr = saveChar;
/*
	    fprintf(stderr,"one valid line processed %p-%p (%d)\n",
	               secondPtr, firstPtr, firstPtr-secondPtr);
*/
	    if (*firstPtr == '\0')      
		{
		DBUG_VOID_RETURN;
		}
	    else
		secondPtr = firstPtr;                  
	    }
	else
	    {

	    DBUG_PRINT("io", 
		 ("BUFFER without nl leftBytes(%d)+readBytes(%d)=%d < %d\n",
		  leftBytes,readBytes,leftBytes+readBytes,CMD_BUF_SIZE));

	    if (leftBytes + readBytes < CMD_BUF_SIZE) 
		{
		leftBytes = readBytes;
		overFlowFlag = False;
		}
	    }
	} while (*firstPtr != '\0');

    if (overFlowFlag)
	wafeFatal("io", 
		  "Incomplete command or communication buffer overflow, <%s>", 
		  secondPtr);

     /* if this one is the last command in the buffer and
      * it is not terminated with a \n => do buffering 
      */

    DBUG_PRINT("io", ("incomplete command: <%s>, length=%d", 
		      secondPtr,firstPtr-secondPtr)); 
/*
    strcpy(inputBuffer, secondPtr);
    leftBytes = strlen(inputBuffer);
 */

    leftBytes = firstPtr-secondPtr;
    if (inputBuffer != secondPtr) 
	memcpy(inputBuffer, secondPtr,leftBytes);

    DBUG_VOID_RETURN;
    }


static void
stdinHandlerProc(client_data, fid, xid)
XtPointer   client_data; /* Prompt-Mode: True oder False */
int         *fid;
XtInputId   *xid;
    {
    int     readBytes;
    char    buffer[STDIN_BUF_SIZE];

    DBUG_ENTER("getInputFromStdin");

    if ((readBytes = read(*fid, buffer, STDIN_BUF_SIZE)) == -1)
	perror("getInput");

    DBUG_PRINT("io", ("reading STDIN, got %d bytes", readBytes));
    
    if (!readBytes)
	{
	DBUG_PRINT("handler", ("EOF - condition on stdin"));
	DBUG_VOID_RETURN;
	}

/* fprintf(stderr,"HERE I AM readin STDIN, string=<%s>\n", buffer);*/

    if (wafeHandlers[STDIN])
	{
	*(buffer + readBytes) = '\0';  /* assure that the string is valid */
	Tcl_SetVar(wafeInterpreter, "STDIN", buffer, TCL_GLOBAL_ONLY);
	(void) wafeEval(wafeInterpreter, wafeHandlers[STDIN], "STDIN");
	}
    else
	write(wafeToClient, buffer, readBytes);

    DBUG_VOID_RETURN;
    }


static void
stderrHandlerProc(clientData, fid, xid)
XtPointer    clientData;
int         *fid;
XtInputId   *xid;
    {   
    int     readBytes;
    char    buffer[STDERR_BUF_SIZE];

    DBUG_ENTER("stderrHandlerProc");

    if ((readBytes = read(*fid, buffer, STDERR_BUF_SIZE-1)) == -1)
	perror("stderrHandlerProc");

    DBUG_PRINT("io", ("reading STDERR, got %d bytes", readBytes));

    if (!readBytes)
	{
	DBUG_PRINT("handler", ("EOF - condition on stderr"));
	DBUG_VOID_RETURN;
	}

/* fprintf(stderr,"***readin STDERR, string=<%s>\n", buffer); */

    if (wafeHandlers[STDERR])
	{
	*(buffer + readBytes) = '\0';  /* assure that it is a valid string */
	Tcl_SetVar(wafeInterpreter, "STDERR", buffer, TCL_GLOBAL_ONLY);
	(void) wafeEval(wafeInterpreter, wafeHandlers[STDERR], "STDERR");
	}
    else
	write(fileno(stderr), buffer, readBytes);

    DBUG_VOID_RETURN;
    }



static void
communicationHandlerProc(clientData, fid, xid)
XtPointer    clientData;
int         *fid;
XtInputId   *xid;
    {   
    int     readBytes;
    char    buffer[COM_BUF_SIZE];

    DBUG_ENTER("communicationHandlerProc");
    
    if ((readBytes = read(*fid, buffer, COM_BUF_SIZE-1)) == -1)
	perror("communicationHandlerProc");

#ifdef TUNNELDEBUG
    fprintf(stderr, "HandlerProc: I read %d Bytes\n", readBytes); 
#endif

    *(buffer + readBytes) = '\0';

    communicationCount += readBytes;

#ifdef TUNNELDEBUG
    fprintf(stderr, "HandlerProc: This makes now %d Bytes total\n", 
	    communicationCount); 
#endif

    if (!readBytes)
	{
	DBUG_PRINT("handler", ("EOF - condition on communication channel"));
	DBUG_VOID_RETURN;
	}

    if (!comBuffer)
	{
	comBuffer = XtNewString(buffer);
	}
    else
	{
	comBuffer = XtRealloc(comBuffer, strlen(comBuffer) + readBytes + 1);
	strcat(comBuffer, buffer);
	}
    
    if ((communicationHandlerCommand) && (communicationHandlerLength <= communicationCount))
	{
#ifdef TUNNELDEBUG
	fprintf(stderr, "HandlerProc: we got it %d <= %d, executing %s\n", 
		communicationHandlerLength, communicationCount,
		communicationHandlerCommand); 
#endif
	if (!comBuffer)
	    Tcl_SetVar(wafeInterpreter, communicationHandlerVarname, "", TCL_GLOBAL_ONLY);
	else
	    {
	    (void)Tcl_UnsetVar(wafeInterpreter, communicationHandlerVarname, 0);
	    Tcl_SetVar(wafeInterpreter, communicationHandlerVarname, comBuffer, 
		       TCL_GLOBAL_ONLY);
	    XtFree(comBuffer);
	    comBuffer = NULL;
	       }

          (void) wafeEval(wafeInterpreter, communicationHandlerCommand,
			  "setCommunicationVariable");
          
	XtFree(communicationHandlerVarname);
	XtFree(communicationHandlerCommand);
	communicationCount = 0;
	communicationHandlerLength = 0;
	communicationHandlerCommand = NULL;
	}
    else
	{
#ifdef TUNNELDEBUG
	fprintf(stderr, "HandlerProc: I do nothing, Data is not complete %d <= %d!\n",
		communicationHandlerLength,communicationCount);
#endif
	}
    DBUG_VOID_RETURN;
     }

void
wafeSetCommunicationVariableCmd(varName,length,command)
String   varName;
int      length;
String   command;
    {

    communicationHandlerVarname = varName;
    communicationHandlerLength = length;
    communicationHandlerCommand = command;

#ifdef TUNNELDEBUG
    fprintf(stderr, "Command: setting communicationHandlerLength = %d\n",
	    communicationHandlerLength);

    fprintf(stderr, "Command: communicationHandlerLength %d <= communicationCount(global): %d\n", 
	    communicationHandlerLength, communicationCount);
#endif

    if (communicationHandlerLength <= communicationCount)
	{ 
#ifdef TUNNELDEBUG
	fprintf(stderr, "Command: data available, executing <%s>\n", 
		communicationHandlerCommand); 
#endif
	if (!comBuffer)
	    Tcl_SetVar(wafeInterpreter, communicationHandlerVarname, 
		       "", TCL_GLOBAL_ONLY);
	else
	    {
	    (void)Tcl_UnsetVar(wafeInterpreter, communicationHandlerVarname, 0);
	    if (communicationHandlerLength < communicationCount) 
		{
		char saveChar;

		saveChar = comBuffer[communicationHandlerLength+1];
#ifdef TUNNELDEBUG
		fprintf(stderr, "Command: MORE THAN WE NEED! c=<%d>\n",
			(int)saveChar);
#endif
		comBuffer[communicationHandlerLength+1] = '\0';
		Tcl_SetVar(wafeInterpreter, communicationHandlerVarname, 
			   comBuffer, TCL_GLOBAL_ONLY);
		comBuffer[communicationHandlerLength+1] = saveChar;

		memcpy(comBuffer,&comBuffer[communicationHandlerLength],
		       communicationCount-communicationHandlerLength);
		communicationCount = communicationCount - 
		    communicationHandlerLength;
		} 
	    else 
		{
		Tcl_SetVar(wafeInterpreter, communicationHandlerVarname, 
			   comBuffer, TCL_GLOBAL_ONLY);
		communicationCount = 0;
		XtFree(comBuffer);
		comBuffer = NULL;
		}
	    }  

	(void) wafeEval(wafeInterpreter, communicationHandlerCommand, 
		 "setCommunicationVariable");
          
	XtFree(communicationHandlerVarname);
	XtFree(communicationHandlerCommand);
	communicationHandlerLength = 0;
	communicationHandlerCommand = NULL;
#ifdef TUNNELDEBUG
	fprintf(stderr, "Command: setting command and length to NULL\n"); 
#endif
	}
    else
	{
#ifdef TUNNELDEBUG
	fprintf(stderr, "Command: I do nothing, because Data is not yet complete!\n");
#endif
	}
    }


/*
@handlerType = (
	    "stdin", STDIN,
	    "stdout", STDOUT,
	    "stderr", STDERR,
	    "xioerr", XIOERR,
	    @signalType
		  );
*/

void 
wafeRegisterCmd(handlerType,string)
int handlerType;
String string;
    {
    char **table;

    switch(handlerType)
	{
    case STDIN: 
	if (!stdinHandlerRegistered)
	    {
	    fprintf(stderr, "Warning: No input-handler in this mode!\n");
	    return;
	    }
	break;
	
    case STDOUT:
	if (!stdoutHandlerRegistered)
	    {
	    fprintf(stderr, "Warning: No output-handler in this mode!\n");
	    return;
	    }
	break;

    case STDERR:
	if (!stderrHandlerRegistered)
	    {
	    fprintf(stderr, "Warning: No error-handler in this mode!\n");
	    return;
	    }
	break;

    default:
	break;
	}

    if (handlerType>HANDLERMAX)
	{
	table = tclIntHandlers;
	handlerType -= HANDLERMAX;
        if (!strcmp("IGNORE",string)) 
	    signal(handlerType,SIG_IGN);
	else
        if (!strcmp("DEFAULT",string)) 
	    signal(handlerType,SIG_DFL);
	else
	    signal(handlerType,signalHandler);
	}
    else
	table = wafeHandlers;

    if (table[handlerType]) 
	XtFree(table[handlerType]);

    table[handlerType] = XtNewString(string);
    } 


void 
wafeUnregisterCmd(handlerType)
int handlerType;
    {    
    char **table;

    if (handlerType>HANDLERMAX)
	{
	table = tclIntHandlers;
	handlerType -= HANDLERMAX;
	if (!strcmp("IGNORE",tclIntHandlers[handlerType])) 
	    signal(handlerType,signalHandler);
	else
	if (!strcmp("DEFAULT",tclIntHandlers[handlerType])) 
	    signal(handlerType,signalHandler);
	}
    else
	table = wafeHandlers;

    if (!table[handlerType]) 
	fprintf(stderr, "Wafe(unregister): Warning, no handler registered!\n");
    else 
        XtFree(table[handlerType]);

    table[handlerType] = NULL;
    }


/*
 * extra input handlers, which are user maintained
 */
typedef struct
    {
    String varName;
    String command;
    XtPointer mask;
    XtInputId id;
    } extraInput;

static extraInput extraInputs[EXTRA_HANDLERS];

    
static void
extraHandlerProc(clientData, fid, xid)
XtPointer    clientData;
int         *fid;
XtInputId   *xid;
    {   
    extraInput *slot = (extraInput *)clientData;

    DBUG_ENTER("extraHandlerProc");

    if (slot->mask == (XtPointer)XtInputReadMask) 
	{
	int readBytes;	
	char buffer[EXTRA_BUF_SIZE];

/*	fprintf(stderr,"***extra: expecting to read something on %d\n",*fid);*/
	if ((readBytes = read(*fid, buffer, EXTRA_BUF_SIZE-1)) == -1)
	    perror("extraHandlerProc");

/*	fprintf(stderr,"***extra, got %d bytes\n", readBytes);*/
	if (!readBytes)
	    {
	    DBUG_PRINT("handler", ("EOF - condition on extra"));
	    DBUG_VOID_RETURN;
	    }

	if (slot->varName) 
	    {
	    *(buffer + readBytes) = '\0'; /* make it a valid string */
	    /* fprintf(stderr,"***extra string=<%s>\n", buffer); */
	    Tcl_SetVar(wafeInterpreter,  slot->varName, buffer, 
		       TCL_GLOBAL_ONLY|TCL_APPEND_VALUE);
	    }
	}
    if (slot->command)
	(void) wafeEval(wafeInterpreter, slot->command, "extraInput");

    DBUG_VOID_RETURN;
    }


XtInputId
wafeAddInputCmd(source,condition,varName,command)
int source;
XtPointer condition;
String varName;
String command;
    {
    int i;
    extraInput *slot = NULL;

    if ((stdinHandlerRegistered  && (fileno(stdin) == source) ) 
	|| (!fileMode && wafeFromClient == source) 
	|| (stderrHandlerRegistered && wafeErrorClient == source) 
       )
	{
	fprintf(stderr, "Wafe(addInput): cannot add input handler!\n");
	return 0;
	}

    for (i=0; i<EXTRA_HANDLERS; i++) 
	{
	if (!(extraInputs[i].id))
	    {
	    slot = &extraInputs[i];
	    break;
	    }
	}
    if (slot) 
	{
	slot->varName = XtNewString(varName);
	slot->command = XtNewString(command);
	slot->mask = condition;
	slot->id = XtAppAddInput(wafeAppContext, source, condition,
				 extraHandlerProc, (XtPointer)slot);
	return slot->id;
	}
    else 
	{
	fprintf(stderr,
		"Wafe(addInput): cannot allocate slot for input handler\n");
	return 0;
	}
    }

void
wafeRemoveInputCmd(id)
XtInputId id;
    {
    int i;
    extraInput *slot = NULL;

    for (i=0; i<EXTRA_HANDLERS; i++) 
	{
	if (extraInputs[i].id == id)
	    {
	    slot = &extraInputs[i];
	    XtRemoveInput(id);
	    slot->id = 0;
	    XtFree(slot->varName);
	    XtFree(slot->command);
	    return;
	    }
	}
    fprintf(stderr,"Wafe(removeInput): no such input %lu\n",id);
    }




void
wafeInit(argc, argv, inputMode, promptMode) 
int    argc;
char **argv;
Boolean inputMode;
Boolean promptMode;
    {
    int i;
    char *p;
    char intBuffer[INT_AS_STRING];
    char charBuffer[200];
    Boolean canAddStdinHandler = True;
    
    extern void wafeCallbackQuarkInitialize();
    extern void wafeInitialize_MiscGen();
    extern void wafeInitialize_XmGen();
    extern void wafeInitialize_Xm12Gen();
    extern void wafeInitialize_XawGen();
    extern void wafeInitialize_Xaw5Gen();
    extern void wafeInitialize_XmGrGen();
    extern void wafeInitialize_AtGen();
    extern void wafeInitialize_htmlGen();
#ifdef XC
    extern void wafeInitialize_XcGen();
#endif
    extern void wafeInitialize_rddGen();
    extern void wafeInitialize_XtGen();
    static char *defaultFileSearchPath = wafeDefaultFileSearchPath;

    wafeInterpreter = Tcl_CreateInterp();

    /* if nobody has provided values for the channels,
     * use stdin for commands and stdout for echo
     */
    if (wafeFromClient == -1) wafeFromClient = fileno(stdin);
    if (wafeToClient   == -1) wafeToClient   = fileno(stdout);

    /* initialize handlers */
    for (i=0; i<HANDLERMAX; wafeHandlers[i]=NULL, i++); 
    for (i=0; i<SIGMAX; tclIntHandlers[i]=NULL, i++); 

    /* setup signal handlers:
     * first, set a different process group 
     */
    if (wafeClientPid) setpgrp();

    /* For the following signals we install the wafe signal handler
     * SIGCLD (death of child signal) is only handled in frontend mode
     */
    signal(SIGTERM, signalHandler);  
    signal(SIGQUIT, signalHandler);
    signal(SIGINT,  signalHandler);
    signal(SIGHUP,  signalHandler);
    signal(SIGPIPE, signalHandler);
    signal(SIGUSR1, signalHandler);
    signal(SIGUSR2, signalHandler);

    if (wafeClientPid) signal(SIGCLD, sigcldHandler);

    /* set handler for x io errors */
    XSetIOErrorHandler(IOErrorHandler);

    /* the default widget tree is under topLevel */
    wafeAddToEndOfWidgetList(&wafeWidgetTrees, wafeTopLevel, "topLevel");

    XtAppAddActions(wafeAppContext, tclAction, 1);
    XtVaGetApplicationResources(wafeTopLevel, &app_data, resources,
				XtNumber(resources), NULL);

    /* add various Xt inputs depending on mode */
    if (!fileMode)
	{
#ifdef _IBMR2
	if (wafeClientPid) 
	    {
            /* fprintf(stderr, "setting FNDELAY for %d\n",wafeFromClient); */
	    fcntl(wafeFromClient, F_SETFL, FNDELAY);
	    }
#endif
	XtAppAddInput(wafeAppContext, wafeFromClient, 
		      (XtPointer)XtInputReadMask,
		      getInput, (XtPointer)(int)promptMode);
	canAddStdinHandler = (wafeFromClient != fileno(stdin));
	stdoutHandlerRegistered = promptMode;
	}
    
    if (wafeClientPid)
	{ 
#ifdef _IBMR2
	/* fprintf(stderr, "setting FNDELAY for %d\n",wafeErrorClient); */
	fcntl(wafeErrorClient, F_SETFL, FNDELAY);
#endif
	XtAppAddInput(wafeAppContext, wafeErrorClient,
		      (XtPointer)XtInputReadMask, stderrHandlerProc, 
		      (XtPointer)NULL); 
	stderrHandlerRegistered = True;
	}

    if (wafeExtraCom)
	{
	XtAppAddInput(wafeAppContext, wafeExtraCom, 
		      (XtPointer)XtInputReadMask,
		      communicationHandlerProc, NULL); 
	}

    if (inputMode)
	if (canAddStdinHandler)
	    {
	    XtAppAddInput(wafeAppContext, fileno(stdin), 
			  (XtPointer)XtInputReadMask,
			  stdinHandlerProc, NULL); 
	    stdinHandlerRegistered = True;
	    }
	else
	    wafeFatal("io",
		      "Wafe: cannot add input handler in this mode combination",
		      NULL);
    

    if (strcmp(app_data.init_com, ""))
        {
	DBUG_PRINT("start", ("Wafe: Start mit %s\n", app_data.init_com));
	write(wafeToClient, app_data.init_com, strlen(app_data.init_com));
	write(wafeToClient, "\n", 1);
	}

    /*
     * since the athena list widget is created by the command list,
     * we use the name llist instead to list
    Tcl_CreateCommand(wafeInterpreter, "llist", Tcl_ListCmd, NULL, NULL);
     */


    /* intialize global quarks */
    wafeCallbackQuarkInitialize();

    /* initialize various packages */
    wafeInitialize_MiscGen();
    wafeInitialize_XmGen();
    wafeInitialize_Xm12Gen();
    wafeInitialize_XawGen();
    wafeInitialize_Xaw5Gen();
    wafeInitialize_XmGrGen();
    wafeInitialize_AtGen();
    wafeInitialize_htmlGen();
#ifdef XC
    wafeInitialize_XcGen();
#endif
    wafeInitialize_rddGen();
    wafeInitialize_XtGen();  /* should be after Xaw for StringToWidget conv.*/
#ifdef ITCL
    if (Itcl_Init(wafeInterpreter) == TCL_ERROR)
	{
	fprintf(stderr,"Wafe: could not initialize [incr Tcl]\n");
	}
#endif
    
    /* define global variables */
#ifdef PRER5
    Tcl_SetVar(wafeInterpreter, "XVERSION", "R4", TCL_GLOBAL_ONLY);
#else
    Tcl_SetVar(wafeInterpreter, "XVERSION", "R5", TCL_GLOBAL_ONLY);
#endif
    Tcl_SetVar(wafeInterpreter, "WAFEVERSION", VERSION, TCL_GLOBAL_ONLY);

    p = Tcl_Merge(argc, argv);
    Tcl_SetVar(wafeInterpreter, "ARGV", p, TCL_GLOBAL_ONLY);
    XtFree(p);

    sprintf(intBuffer, "%d", argc);
    Tcl_SetVar(wafeInterpreter, "ARGC",  intBuffer, TCL_GLOBAL_ONLY);

    sprintf(intBuffer, "%d", argc-1);
    Tcl_SetVar(wafeInterpreter, "argc",  intBuffer, TCL_GLOBAL_ONLY);

    p = Tcl_Merge(argc-1, argv+1);
    Tcl_SetVar(wafeInterpreter, "argv", p, TCL_GLOBAL_ONLY);
    XtFree(p);

    Tcl_SetVar(wafeInterpreter, "argv0", 
	       (fileMode) ? wafeScriptName : argv[0], TCL_GLOBAL_ONLY);

    Tcl_SetVar(wafeInterpreter, "tcl_interactive",
            (interactiveMode = ((!frontendMode) && (!fileMode) && isatty(0))) ?
	       "1" : "0", TCL_GLOBAL_ONLY);

    sprintf(intBuffer, "%d", getpid());
    Tcl_SetVar(wafeInterpreter, "PID", intBuffer, TCL_GLOBAL_ONLY);

    sprintf(intBuffer, "%d", wafeClientPid);
    Tcl_SetVar(wafeInterpreter, "CHILDPID", intBuffer, TCL_GLOBAL_ONLY);

    if ((p = (char *)getenv("XFILESEARCHPATH")))
	wafeFileSearchPath = XtNewString(p);
    else
	/* it is nesscessary to make a dynamically allocated copy for link var
	 */
	wafeFileSearchPath = XtNewString(defaultFileSearchPath);

    Tcl_LinkVar(wafeInterpreter, "FILESEARCHPATH", 
		(char *)&wafeFileSearchPath,  TCL_LINK_STRING);

    Tcl_DStringInit(&tclCommand);
    Tcl_Init(wafeInterpreter);

    if (!(p = (char *)getenv("WAFELIB")))
        p = WAFELIB;

    Tcl_SetVar(wafeInterpreter, "WAFELIB", p, TCL_GLOBAL_ONLY);
   
    sprintf(charBuffer,"%s/tcllib",p);
    Tcl_SetVar(wafeInterpreter, "auto_path", charBuffer, 
	       TCL_LIST_ELEMENT|TCL_APPEND_VALUE);
    sprintf(charBuffer,"%s/tcllib/bc",p);
    Tcl_SetVar(wafeInterpreter, "auto_path", charBuffer, 
	       TCL_LIST_ELEMENT|TCL_APPEND_VALUE);
    }


