/*----------------------------------------------------------------------- The code in this file allows to override or define Drivable interfaces through tcl scripts. For more details, see tcldrivable.tex copyright: see file COPYRIGHT Mark Koennecke, September 2003 ----------------------------------------------------------------------*/ #include #include #include "sics.h" #include #include "fortify.h" #include "lld.h" #include "tcldrivable.h" /*---------------------------------------------------------------------- The index of the list used for keeping the dictionary for mapping drivable interface functions to scripts. -------------------------------------------------------------------------*/ static int tclDrivableDictionary = -1; /*======================================================================= The structure used for keeping data associated with this scheme --------------------------------------------------------------------------*/ typedef struct{ void *objectPointer; char *tclName; char *tclHalt; char *tclCheckLimits; char *tclSetValue; char *tclCheckStatus; char *tclGetValue; }TclDrivable, *pTclDrivable; /*========================================================================= book keeping and search functions for the dictionary -------------------------------------------------------------------------*/ static void clearTclDrivable(pTclDrivable pNew){ memset(pNew,0,sizeof(TclDrivable)); } /*-----------------------------------------------------------------------*/ static void deleteTclDrivable(pTclDrivable target){ if(target->tclName != NULL){ free(target->tclName); } if(target->tclHalt != NULL){ free(target->tclHalt); } if(target->tclCheckLimits != NULL){ free(target->tclCheckLimits); } if(target->tclSetValue != NULL){ free(target->tclSetValue); } if(target->tclCheckStatus != NULL){ free(target->tclCheckStatus); } if(target->tclGetValue != NULL){ free(target->tclGetValue); } } /*-------------- compare function for lld to find key in list Function must return 0 if found --------*/ static int tclDrivableCompare(const void *p1, const void *p2){ pTclDrivable pt1, pt2; pt1 = (pTclDrivable)p1; pt2 = (pTclDrivable)p2; if(pt1->objectPointer == pt2->objectPointer){ return 0; } return 1; } /*--------------- assignFunction --------------------------------------*/ static void assignFunction(pTclDrivable self, int functionIndex, char *script){ switch(functionIndex){ case TCLHALT: if(self->tclHalt != NULL){ free(self->tclHalt); } self->tclHalt = strdup(script); break; case TCLCHECK: if(self->tclCheckLimits != NULL){ free(self->tclCheckLimits); } self->tclCheckLimits = strdup(script); break; case TCLSET: if(self->tclSetValue != NULL){ free(self->tclSetValue); } self->tclSetValue = strdup(script); break; case TCLSTATUS: if(self->tclCheckStatus != NULL){ free(self->tclCheckStatus); } self->tclCheckStatus = strdup(script); break; case TCLGET: if(self->tclGetValue != NULL){ free(self->tclGetValue); } self->tclGetValue = strdup(script); break; default: assert(0); } } /*-----------------------------------------------------------------------*/ static int registerTclDrivableFunction(void *objectPointer, int functionIndex, char *script, char *tclName){ TclDrivable entry; /* if no list: create it */ if(tclDrivableDictionary < 0){ tclDrivableDictionary = LLDcreate(sizeof(TclDrivable)); } /* find entry, or create a new one if it does not exist */ LLDnodePtr2First(tclDrivableDictionary); entry.objectPointer = objectPointer; if(LLDnodeFind(tclDrivableDictionary,tclDrivableCompare, &entry) == 0){ LLDnodeDataTo(tclDrivableDictionary,&entry); if(strcmp(tclName,entry.tclName) != 0){ return 0; } assignFunction(&entry,functionIndex,script); LLDnodeDataFrom(tclDrivableDictionary,&entry); } else { clearTclDrivable(&entry); entry.objectPointer = objectPointer; entry.tclName = strdup(tclName); assignFunction(&entry,functionIndex,script); LLDnodeAppendFrom(tclDrivableDictionary,&entry); } return 1; } /*-----------------------------------------------------------------*/ static char *combine(char *t1, char *t2){ char *result = NULL; int len; len = strlen(t1) + strlen(t2) + 3; result = (char *)malloc(len*sizeof(char)); if(!result){ return NULL; } memset(result,0,len); strcpy(result,t1); strcat(result," "); strcat(result,t2); return result; } /*------------------------------------------------------------------*/ static char *getTclDrivableCommand(void *objectPointer, int functionIndex){ TclDrivable entry; assert(tclDrivableDictionary > 0); LLDnodePtr2First(tclDrivableDictionary); entry.objectPointer = objectPointer; if(LLDnodeFind(tclDrivableDictionary,tclDrivableCompare,&entry) == 0){ LLDnodeDataTo(tclDrivableDictionary,&entry); switch(functionIndex){ case TCLHALT: return combine(entry.tclHalt,entry.tclName); break; case TCLCHECK: return combine(entry.tclCheckLimits,entry.tclName); break; case TCLSET: return combine(entry.tclSetValue,entry.tclName); break; case TCLSTATUS: return combine(entry.tclCheckStatus,entry.tclName); break; case TCLGET: return combine(entry.tclGetValue,entry.tclName); break; default: assert(0); } } else { return NULL; } } /*---------------------------------------------------------------------------*/ void killTclDrivable(){ TclDrivable entry; int stat; if(tclDrivableDictionary > 0){ stat = LLDnodePtr2First(tclDrivableDictionary); while(stat != 0){ LLDnodeDataTo(tclDrivableDictionary,&entry); deleteTclDrivable(&entry); stat = LLDnodePtr2Next(tclDrivableDictionary); } LLDdelete(tclDrivableDictionary); tclDrivableDictionary = -1; } } /*--------------------------------------------------------------------------*/ int mapDrivableFunctionNames(char *name){ if(strcmp(name,"halt") == 0){ return TCLHALT; } if(strcmp(name,"checklimits") == 0){ return TCLCHECK; } if(strcmp(name,"setvalue") == 0){ return TCLSET; } if(strcmp(name,"checkstatus") == 0){ return TCLSTATUS; } if(strcmp(name,"getvalue") == 0){ return TCLGET; } return -1; } /*=========================================================================== The functions with which to overload the original functions ----------------------------------------------------------------------------*/ static int TclDrivableHalt(void *data){ char *command = NULL; Tcl_Interp *pTcl; int status; pTcl = InterpGetTcl(pServ->pSics); command = getTclDrivableCommand(data,TCLHALT); if(command != NULL){ status = Tcl_Eval(pTcl,command); if(status != TCL_OK){ fprintf(stderr,"Tcl ERROR: while invoking %s\n\tTcl reported %s\n", command,pTcl->result); status = 0 ; } else { status = 1; } free(command); } else { status = 0; fprintf(stderr,"Tcl ERROR: trying to invoke non existent Tcl %s\n", "drivable halt function"); } return status; } /*--------------------------------------------------------------------*/ static int TclDrivableCheckLimits(void *data, float fVal, char *error, int errlen){ char *commandPart1 = NULL; char *command = NULL; char pNum[30]; Tcl_Interp *pTcl; int status; pTcl = InterpGetTcl(pServ->pSics); commandPart1 = getTclDrivableCommand(data,TCLCHECK); if(commandPart1 != NULL){ sprintf(pNum,"%f",fVal); command = combine(commandPart1,pNum); if(command){ status = Tcl_Eval(pTcl,command); if(status != TCL_OK){ strncpy(error,pTcl->result,errlen); status = 0; } else { status = 1; } free(command); } else { fprintf(stderr,"Out of memory creating drivable command\n"); status = 0; } free(commandPart1); } else { status = 0; fprintf(stderr,"Tcl ERROR: trying to invoke non existent Tcl %s\n", "drivable CheckLimits function"); } return status; } /*--------------------------------------------------------------------------*/ static long TclDrivableSetValue(void *data, SConnection *pCon, float fVal){ Tcl_Interp *pTcl; int status; char *commandHead = NULL, *command = NULL; char pNum[30]; pTcl = InterpGetTcl(pServ->pSics); /* build and check command string */ sprintf(pNum,"%f", fVal); commandHead = getTclDrivableCommand(data,TCLSET); if(commandHead == NULL){ SCWrite(pCon, "ERROR: trying to invoke non existent Tcl drivable function SetValue", eError); return HWFault; } command = combine(commandHead,pNum); if(command == NULL){ SCWrite(pCon,"ERROR: out of memory creating tcl drivable SetValue command", eError); return HWFault; } /* invoke */ status = Tcl_Eval(pTcl,command); free(command); free(commandHead); if(status != TCL_OK){ SCWrite(pCon,"ERROR: Tcl drivable SetValue reported",eError); SCWrite(pCon,pTcl->result,eError); return HWFault; } status = atoi(pTcl->result); return status; } /*--------------------------------------------------------------------------*/ static int TclDrivableCheckStatus(void *data, SConnection *pCon){ Tcl_Interp *pTcl; int status; char *command = NULL; pTcl = InterpGetTcl(pServ->pSics); /* build and check command string */ command = getTclDrivableCommand(data,TCLSTATUS); if(command == NULL){ SCWrite(pCon, "ERROR: Tcl drivable CheckStatus not found!", eError); return HWFault; } /* invoke */ status = Tcl_Eval(pTcl,command); free(command); if(status != TCL_OK){ SCWrite(pCon,"ERROR: Tcl drivable CheckStatus reported",eError); SCWrite(pCon,pTcl->result,eError); return HWFault; } status = atoi(pTcl->result); return status; } /*--------------------------------------------------------------------------*/ static float TclDrivableGetValue(void *data, SConnection *pCon){ Tcl_Interp *pTcl; int status; char *command = NULL; pTcl = InterpGetTcl(pServ->pSics); /* build and check command string */ command = getTclDrivableCommand(data,TCLGET); if(command == NULL){ SCWrite(pCon, "ERROR: Tcl drivable GetValue not found!", eError); return -999999.99; } /* invoke */ status = Tcl_Eval(pTcl,command); free(command); if(status != TCL_OK){ SCWrite(pCon,"ERROR: Tcl drivable GetValue reported",eError); SCWrite(pCon,pTcl->result,eError); return -99999.99; } return atof(pTcl->result); } /*====================================================================== The function for doing it alltogether ------------------------------------------------------------------------*/ int replaceDrivableByTcl(void *commandData, int functionIndex, char *script, char *tclName){ pIDrivable pDriv = NULL; int status; pDriv = GetDrivableInterface(commandData); assert(pDriv != NULL); /* register the new script */ status = registerTclDrivableFunction(commandData,functionIndex, script,tclName); if(status <= 0){ return status; } /* replace the appropriate Drivable function */ switch(functionIndex){ case TCLHALT: pDriv->Halt = TclDrivableHalt; break; case TCLCHECK: pDriv->CheckLimits = TclDrivableCheckLimits; break; case TCLSET: pDriv->SetValue = TclDrivableSetValue; break; case TCLSTATUS: pDriv->CheckStatus = TclDrivableCheckStatus; break; case TCLGET: pDriv->GetValue = TclDrivableGetValue; break; default: assert(0); break; } return 1; } /*========================================================================= Interpreter Interface ------------------------------------------------------------------------*/ int TclReplaceDrivable(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]){ pIDrivable pDriv = NULL; void *commandData; CommandList *pCom = NULL; int functionIndex, status; char pBueffel[256]; /* only managers can do this! */ if(!SCMatchRights(pCon,usMugger)){ return 0; } /* we need tons of arguments! */ if(argc < 5){ SCWrite(pCon,"ERROR: not enough arguments to TclReplaceDrivable", eError); return 0; } /* get and check object */ strtolower(argv[1]); pDriv = FindDrivable(pSics,argv[1]); if(!pDriv){ snprintf(pBueffel,255,"ERROR: %s is not drivable!",argv[1]); SCWrite(pCon,pBueffel,eError); return 0; } /* This must work as FindDrivable could not work else */ pCom = FindCommand(pSics,argv[1]); commandData = pCom->pData; /* convert and check function name */ strtolower(argv[2]); functionIndex = mapDrivableFunctionNames(argv[2]); if(functionIndex <= 0){ snprintf(pBueffel,255, "ERROR: %s is not a known function name for drivable", argv[2]); SCWrite(pCon,pBueffel,eError); return 0; } status = replaceDrivableByTcl(commandData,functionIndex,argv[3], argv[4]); if(status <= 0){ SCWrite(pCon,"ERROR mismatch in parameter tclName",eError); return 0; } SCSendOK(pCon); return 1; } /*-----------------------------------------------------------------------*/ int TclDrivableInvoke(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]){ pIDrivable pDriv = NULL; void *commandData; CommandList *pCom = NULL; int functionIndex, status; char pBueffel[256], pError[132]; float fVal; /* we need tons of arguments! */ if(argc < 3){ SCWrite(pCon,"ERROR: not enough arguments to TclDrivableInvoke", eError); return 0; } /* get and check object */ strtolower(argv[1]); pDriv = FindDrivable(pSics,argv[1]); if(!pDriv){ snprintf(pBueffel,255,"ERROR: %s is not drivable!",argv[1]); SCWrite(pCon,pBueffel,eError); return 0; } /* This must work as FindDrivable could not work else */ pCom = FindCommand(pSics,argv[1]); commandData = pCom->pData; /* convert and check function name */ strtolower(argv[2]); functionIndex = mapDrivableFunctionNames(argv[2]); if(functionIndex <= 0){ snprintf(pBueffel,255, "ERROR: %s is not a known function name for drivable", argv[2]); SCWrite(pCon,pBueffel,eError); return 0; } /* invoke and report */ memset(pError,0,132*sizeof(char)); switch(functionIndex){ case TCLHALT: if(!pDriv->Halt){ SCWrite(pCon,"ERROR: no drivable Halt function!",eError); return 0; } status = pDriv->Halt(commandData); snprintf(pBueffel,255,"Drivable Halt returned: %d",status); SCWrite(pCon,pBueffel,eValue); break; case TCLCHECK: if(!pDriv->CheckLimits){ SCWrite(pCon,"ERROR: no drivable CheckLimits function!",eError); return 0; } if(argc < 4){ SCWrite(pCon,"ERROR: need drive target for CheckLimits",eError); return 0; } status = pDriv->CheckLimits(commandData,atof(argv[3]),pError,131); snprintf(pBueffel,255,"Drivable CheckLimits returned: %d, %s", status,pError); SCWrite(pCon,pBueffel,eValue); break; case TCLSET: if(!pDriv->SetValue){ SCWrite(pCon,"ERROR: no drivable SetValue function!",eError); return 0; } if(argc < 4){ SCWrite(pCon,"ERROR: need drive target for SetValue",eError); return 0; } status = (int)pDriv->SetValue(commandData,pCon,atof(argv[3])); snprintf(pBueffel,255,"Drivable SetValue returned %d", status); SCWrite(pCon,pBueffel,eValue); break; case TCLSTATUS: if(!pDriv->CheckStatus){ SCWrite(pCon,"ERROR: no drivable CheckStatus function!",eError); return 0; } status = pDriv->CheckStatus(commandData,pCon); snprintf(pBueffel,255,"Drivable CheckStatus returned %d", status); SCWrite(pCon,pBueffel,eValue); break; case TCLGET: if(!pDriv->GetValue){ SCWrite(pCon,"ERROR: no drivable GetValue function!",eError); return 0; } fVal = pDriv->GetValue(commandData,pCon); snprintf(pBueffel,255,"Drivable GetValue returned: %f", fVal); SCWrite(pCon,pBueffel,eValue); break; default: /* do not know how I could get here */ assert(0); } return 1; }