/*=========================================================================

	tlayer.c	Main file for making target simulation with SDT 3.02/3.1 ...3.5
			Version: 1.0	July 12, 1996
			Author:  Knut Oedman, Telelogic, Sweden
					knut.odman@telelogic.se
			Version 1.1 May 15, 1997
			Author:  Knut Odman, Telelogic Princeton, USA
					kod@telelogic.com
			See tlayer.h for info about changes.
					
	Making target:
		gcc -c tlayer.c -DTARGET -Dprotocol -Dtargettype -Iincludes -o post.o
			Currently defined: protocol TCP
			targettype VXWORKS_SDT/ PSOS_SDT/ WINDOWS(default SUNOS5/SOLARIS)
			(includes tlayer.h, sp.h, spevent.h)
			(for TCP includes tcp.c)
		The new post.o is used with "make SDTlib" (see SDT manual)
	Making host:
		gcc -c tlayer.c -DHOST -Dprotocol -Iincludes -o tlayer.o
			Currently defined: protocol TCP
			(includes tlayer.h, sp.h, spevent.h, parser.c)
			(for TCP includes tcp.c)
		gcc -c postf.c -Iincludes 
			(includes sp.h, spevent.h)
		gcc -c sp.c -Iincludes
			(includes sp.h, spevent.h)
		gcc  tlayer.o $sdtdir/post.o -lsocket -lnsl -o host_smr.sct
			host_smr.sct is used to run in SDT Simulator Interface.
			host_smr.sct needs a configuration file in _the_same_ directory,
			configfile must be called "tarsim.cfg", for syntax see
			instructions following this software.
			
	WINDOWS: see manual
	    
	Other:		
		Other includes: all standard stdio, stdlib, time etc.
		for tcp needs files in /usr/include/sys and /usr/include/netinet
			and libraries libsocket.a and libnsl.a (or corr. statics),
			or on SunOS4 they are already included in libc.
		Windows use the winsock2.h and corresponding library.
==========================================================================*/
#include "tlayer.h"

INT16	sperrno;
extern int spPostPid;
SPPID	PostPid = 0;
SPPID	SIMUIPid = 0;
BOOL	HostReady = FALSE;
BOOL	TargetReady = FALSE;
BOOL	TargetSuspended = FALSE;
BOOL  ConfigFileOk = FALSE;	

SPPID	MSCEPid=0,SDLEPid=0;
BOOL	UseTimeout = TRUE;
BOOL  FORCEIP = FALSE;

static char GlobalBuffer[DEFAULTBUFSIZE];
static char MessageBuffer[DEFAULTBUFSIZE];
static char *globuf = GlobalBuffer;
static char *databuf = MessageBuffer;

#ifdef TARGET
BOOL	DefaultaddressSet = FALSE;
SPPID	Defaultaddress;
#endif


/*********************************************************
*
*	Target Part
*________________________________________________________*/

#ifdef TARGET

DLLEXPORT SPRead( int timeOut, int *sender, int *message, void **data, int * len)
{
	int re=-1,ev=0,si,fr,to,st,retStatus,blocker;
	blocker = (timeOut == SPWAITFOREVER)?1:0;
	outremote(7,blocker,NULL,0,0,0); /* inform host state == read */
	while(1){
		retStatus = inremote(&re,&ev,&si,&fr,&to,&st,(blocker)?timeOut:4000);

		if(retStatus==SPOK && re==6){
			outremote(6,0,NULL,0,blocker,0); 
			/* e.g. ev==0 but we cannot repeat last command now. */
			retStatus = inremote(&re,&ev,&si,&fr,&to,&st,(blocker)?timeOut:4000);
		}
		if(retStatus==SPOK && re==SPERROR && ev==7){
			/* nothing to read at HostPM, skip! */
			return SPERROR;
		}
		if(ev==HOSTEXIT || ev==SESTOPNOTIFY ||
			comerror == CE_WRITEERROR || comerror == CE_READERROR){
			/* host quit or broken pipe (== host killed) */
			SDL_Halt();
		}
		if(retStatus==SPOK) break;
	}
	if(fr == SIMUIPid && DefaultaddressSet){
		fr = (int) Defaultaddress;
	}
	*len = si; *message = ev; *sender = fr;
	if(si>0){
		*data = (char *)malloc(si + 1);
		memcpy(*data,databuf,si);
	}	
	return(re);
}

int SPBroadcast( int event, void *data, int size)
{
	int SPSendToPid(int pid, int event, void *data, int size);
	return SPSendToPid(0, event, data, size);
}

int SPSendToPid( int pid, int event, void *data, int size)
{
	int retStatus;
	int re,ev,si,fr,to,st;
	char *buf = (char*) data;
	int topid = pid;
	if(!DefaultaddressSet){
		DefaultaddressSet = TRUE;
		Defaultaddress = pid;
	}
   
		if(DefaultaddressSet && topid==Defaultaddress) topid = (int)SIMUIPid;
		(void) outremote(0,event,buf,size,-1,topid);
		if(	comerror == CE_WRITEERROR || comerror == CE_READERROR){
			/* broken pipe (== host killed) */
			SDL_Halt();
		}
/*********from*tcp.c************/
		retStatus = inremote(&re,&ev,&si,&fr,&to,&st,4000);
		if(retStatus == SPOK && re==6 && ev ==0){
			/* Host is waiting for message from target but got nothing */
			retStatus = inremote(&re,&ev,&si,&fr,&to,&st,400);
			if(retStatus == SPOK && re == 8)return SPOK;
			(void) outremote(6,1,NULL,0,0,0);
			retStatus = inremote(&re,&ev,&si,&fr,&to,&st,2000);
			if(retStatus == SPOK && re==6 && ev ==1){
				(void) outremote(0,event,buf,size,-1,topid);
				retStatus = inremote(&re,&ev,&si,&fr,&to,&st,2000);
			}
			else if(UseTimeout)SDL_Halt();
		}
		while(!(re == 8 && retStatus == SPOK) && topid){
			/* Host has not completed transmission to PostMaster */
			if(UseTimeout)SDL_Halt();
			else retStatus = inremote(&re,&ev,&si,&fr,&to,&st,2000);
		}
	return(retStatus);
}

int SPInit ( int toolType, char *hello,  SPMList * mlist)
{
	int retValue;
	int 	tr=0,ts=0;
	int re,ev,si,fr,to,st;
	if ((retValue = initremote()) == SPERROR )
     	SDL_Halt();
     while(!HostReady && !TargetReady && ts<2){
     	if(hello!=NULL)
     		retValue = outremote(5,TARGETINIT,hello,strlen(hello)+1,0,0);
     	else
      		retValue = outremote(5,TARGETINIT,NULL,0,0,0);  	
     	if(retValue==SPERROR)break;
     	while(!HostReady && tr<2){
     		retValue = inremote(&re,&ev,&si,&fr,&to,&st,2000);
     		if(retValue==SPOK && ev == HOSTINIT){
     			if(re !=SPERROR){
     				HostReady = TRUE;
     				break;
     			}
     		}
     		tr++;
     	}
     	ts++;  /* Host is tired, try again */
     }
     TargetReady = TRUE;
     if(!HostReady)SDL_Halt();
     if(fr > 0)SIMUIPid = fr;
     PostPid = to;
     UseTimeout = si;
     /* Just to stop BCC nagging about unused param */
     si = (int) (toolType + *(int*) &mlist);/* Not used to anything!*/
     return (int) PostPid;
}

int SPExit (void)
{
	if(comerror!=CE_READERROR || comerror!=CE_WRITEERROR)
		outremote(5,TARGETEXIT,NULL,0,0,0);
	(void) exitremote();
	return SPOK;
}

void SPFree(void *ptr)
{
 free(ptr);
}

#endif /*TARGET*/

/**************************************************
*
*  Host Part
*_________________________________________________*/
     
#ifdef HOST 

int LastEvent;

int HostReadTarget(int Resync)
{
	static int re = -1,ev= -1 , si,fr, to,st,retStatus = -1;

	retStatus = inremote(&re,&ev,&si,&fr,&to,&st,4000);

	if(Resync){
		if(!TargetReady && retStatus == SPOK){
			TargetReady = TRUE;
			TargetSuspended = FALSE;
			return SPOK;
		}
		else return SPERROR;
	}
	if(retStatus == SPOK && (ev==TARGETEXIT || ev==SESTOPNOTIFY))
   				{ LastEvent = ev; HostExitPM(); }
	if(retStatus !=SPOK && TargetReady) {
		/* target dead ? */
		(void)outremote(6,0,NULL,0,0,0);
		retStatus = inremote(&re,&ev,&si,&fr,&to,&st,1000);
		if(!(retStatus==SPOK && re==6)){
			TargetReady = FALSE;
			if(UseTimeout)HostExitPM();			
		}
		else if(ev==0) return HostReadPM(fr); /* target in read mode */
		else { /* target has something to send! */
REPEATSTATE:
			(void) outremote(6,1,NULL,0,0,0);
			retStatus = inremote(&re,&ev,&si,&fr,&to,&st,2000);
			if(!(retStatus==SPOK && re==0)){
				/* target has gone ballistic */
				TargetReady = FALSE;
				if(UseTimeout)HostExitPM();
			}
		}			
	}
	if(!UseTimeout && retStatus == SPOK && TargetReady){
		if ( re == 6 && ev == 1) goto REPEATSTATE;
		if ( re == 6 && ev == 0) {ev = fr; goto READPMSTATE;}
	}
	if(!TargetReady && !UseTimeout){
		if(comerror == CE_READERROR || comerror == CE_WRITEERROR){
			xErrorShell(TRG_DEAD);
			return SPOK;
		}
		else if(comerror == CE_TIMEOUT){
			TargetSuspended = TRUE;
			xErrorShell(TRG_SUSPEND);
			/* the target is now in a random state and we try
			   to analyze what's in the static variables, hopefully
			   set by the xErrorShell in parser.c */
			if ( re == 7 ) goto READPMSTATE;
			if ( re == 6 && ev == 1) goto REPEATSTATE;
			if ( re == 0 ) goto RECEIVESTATE;
			if ( re == 6 && ev == 0) {ev = fr; goto READPMSTATE;}
         if ( re == 5 && ev == TARGETEXIT ){
         		TargetReady = FALSE; HostExitPM(); }
			else {
				/* target killed itself or is in an undeterministic state */
				xPrintString("\n# cannot syncronize target, please restart it\n");
				TargetReady = FALSE;
				xErrorShell(TRG_DEAD);
			}
			return SPOK;
		}
	}
		
	if(re==7){
READPMSTATE:
		/* target informs that it's in infinite read-loop, goto readPM! */
		retStatus = HostReadPM(ev); /* ev: bool blocking_mode */
		return SPOK;
	}
RECEIVESTATE:
	LastEvent = ev;
    if(ev==SEGETTOOLPID || ev ==SESTART){	
		sscanf(databuf,"%d",&PIDREQUEST);
    }		
	
	if(SDLEPid){	/* no path change for MSCE */
	if((ev==SESHOWREF || ev==SEINSERTOBJECT) && CHANGEPATH && to==SDLEPid) {
	  PathChanger(MessageBuffer);
          if(si<(signed)strlen(databuf)) {/*Most likely an SDTREF L.R. 990128 */
            si = strlen(databuf); /*Can be longer because of PathChange. L.R. 990128 */
          }
        }
    }
	if(re != 0) return SPERROR; /* something has $%&ú@ up */
#ifndef WINDOWS
	if(DEBUG)
		logger(TRG,ev,fr,to,si,databuf);
#endif
	if(si>0)memcpy(indata,databuf,si);			
	if(to == 0){
	  retStatus = SPBroadcast(ev,indata,si);
	}	 
	else{
	  retStatus = SPSendToPid(to,ev,indata,si);
	 }
	outremote(8,0,NULL,0,0,0); /* ready signal to target */
	return SPOK;
}

int HostReadPM(int targetreadrequest)
{
int sender = -1, event = -1, size = -1, pmping;
void * data=NULL;
int timeOut = (targetreadrequest)?3000:3;
int retStatus , i, j;
int xShellInt=1;
	while(xShellInt){
	    retStatus = SPRead(timeOut,&sender,&event,&data,&size);
	    		
		while(targetreadrequest == 1 && 
	          comerror != CE_READERROR && retStatus != SPOK
	          && ((pmping=xPingPM()) == TRUE)){
	        if(!pmping)HostExitPM();
			retStatus = SPRead(timeOut,&sender,&event,&data,&size);			
		}
		if(retStatus != SPOK){
			(void)outremote(retStatus,7,NULL,0,0,0);
			return(SPERROR);
		}
#ifndef WINDOWS
		if(event==SESIMUICOMMAND && !strcmp((char*)data,":\n"))xShell();
		else
#endif
      xShellInt=0;
	}

	if(size>1)
		memcpy(databuf,data,size);
	else
		strcpy(databuf,"");
#ifndef WINDOWS
	if(DEBUG)
		logger(PM,event,sender,0,size,databuf);	
#endif
	if(event==SESIMUICOMMAND && (databuf[0]=='q' || databuf[0]=='Q')){
		for(i=0,j = strlen(databuf);i<j;
		databuf[i]= (char) toupper(databuf[i++]));
		if(!strcmp(databuf,"QUIT"))HostExitPM();
	}	

	LastEvent = event;
	if(PIDREQUEST){
		int dummy1,dummy2,thepid;
		if(event == SEGETTOOLPIDREPLY){
			sscanf(databuf,"%d %d %d",&dummy1,&dummy2,&thepid);
			if(dummy2 != 0){
				if(PIDREQUEST==SET_MSCE) MSCEPid=thepid;
				else if(PIDREQUEST==SET_ORGANIZER) SDLEPid=thepid;
				PIDREQUEST = 0;
			}
		}
		else if(event == SESTARTREPLY){
			sscanf(databuf,"%d %d",&dummy1,&thepid);
			if(dummy1 == 0){
				if(PIDREQUEST==SET_MSCE) MSCEPid=thepid;
				else if(PIDREQUEST==SET_ORGANIZER) SDLEPid=thepid;
				PIDREQUEST = 0;
			}
		}	
	}
   if(TargetReady)
		retStatus = outremote(retStatus,event,databuf,size,sender,0);
	return(retStatus);
}

int HostInitPM(int reinit)
{
	/* reinit 0 for startup, reinit 1 for reinit of targetcom */
	int retValue,tr=0,re,ev,si,to,fr,st;
	static int spinit;
	if(!reinit){
		indata=(char *)malloc(400);
		spinit=SPInit(SET_SIMULATOR,INITARG,NULL);
	/* in this version target's argv0 is never passed to PM. Doesn't matter */
	   	if(spinit==SPERROR){
     		  PostPid = 0;
     		  return SPERROR;
     	      }
		if(!LoadConfigFile()){
              ConfigFileOk = FALSE;
              return -6;
            }
            else ConfigFileOk = TRUE;
#ifdef WINDOWS
	DEBUG = FALSE;
	/* no use if you don't have a stderr! */
#endif
     	PostPid = spPostPid;
     	HostReady = TRUE;
	}
	comerror=initremote();
	if(comerror) return -7;
    	while(!TargetReady && tr<5){
     	retValue = inremote(&re,&ev,&si,&fr,&to,&st,2000);
     	if(retValue==SPOK && ev == TARGETINIT){    			
     		TargetReady = TRUE;
     		break;
     	}
     	tr++;
     } /* if tr==5 then target == dead */
     if(TargetReady)
     	outremote(spinit,HOSTINIT,NULL,
     		UseTimeout,SIMUIPid,spinit);
     else return -7;
     return SPOK;     	
}

int HostExitPM()
{  
   if((TargetReady || TargetSuspended) && LastEvent != TARGETEXIT)
		(void)outremote(0,HOSTEXIT,NULL,0,0,0);
   TargetReady = FALSE;
   (void) exitremote();
   xExitMessage();
  (void) SPBroadcast(SESTOPNOTIFY,NULL,0);
  (void) SPExit();
#ifndef WINDOWS
  if(strcmp(LOGFILESTRING,"stdout") &&
     strcmp(LOGFILESTRING,"stderr") &&
     strcmp(LOGFILESTRING,"NONE") )
           fclose(LOGFILE);
#endif /*!WINDOWS*/
   if(indata!=NULL){
		free(indata);
		indata=NULL;
	}
   	exit(0);
   	return 0; /* dummy for Borland */
}

int main(int argc, char* argv[])
{
	int retValue;
  
	if( argc<3){
		fprintf(stderr,"%s\n",
		"This file must be started from SDT v3.x Simulator UI");
		exit(1);
	}
	if(!strcmp(argv[1],"-post") && argv[2]!=NULL)
		SIMUIPid = (SPPID) atoi(argv[2]);
	else{
		fprintf(stderr,"%s\n",
		"This file must be started from SDT v3.x Simulator UI");
		exit(1);
	}
	retValue = HostInitPM(0);
	if(retValue == SPERROR){
		fprintf(stderr,"%s\n","Postmaster Init. failed");
		(void)HostExitPM();
		exit(1);
	}
	else if(retValue == -6){
		fprintf(stderr,"%s\n","Reading config. file failed");
		(void)HostExitPM();
		exit(1);		
	}
	else if(retValue == -7){
		fprintf(stderr,"%s\n","TCP Socket Init. failed");
		xErrorShell(TRG_DEAD);
	}

	sprintf(globuf,"Target contacted on IP %s PORT %d via %s\n",
		TARGETIPNR,TARGETPORT,TARGETCOMSTRING);
	xPrintString(globuf);
	xPrintString("================================================\n");
	while(1){
		(void) HostReadTarget(0);
	}
}	
#endif  /* HOST */	
/*******************************************************
*
*	Common part
*    This is the only part in tlayer.c to change
*    for a new protocol. (and in tlayer.h header)
*______________________________________________________*/

int outremote(int ret, int event, char *data, 
		int size, int fromPid, int toPid)
{
#ifdef TCP
	return tcpsend(ret,event,data,size,fromPid,toPid);
#endif
}

int inremote(int *reP,int *evP, int *siP, int *frP, 
			int *toP, int *stP, int timeOut)
{
#ifdef TCP
	return tcpread(reP,evP,siP,frP,toP,stP,timeOut);
#endif
}

int initremote(void)
{
#ifdef TCP
	return xInitSocket();
#endif
}

void exitremote(void)
{
#ifdef TCP
	xCloseSocket();
#endif
}

#ifdef TCP
#include "tcp.c"
#endif

#ifdef HOST
#include "parser.c"
#endif


