/* * Copyright (c) 1999-2003 Smithsonian Astrophysical Observatory */ #include #if HAVE_TCL #include /* *---------------------------------------------------------------------------- * * * Private Routines and Data * * *---------------------------------------------------------------------------- */ /* with Tcl 8.4, some function prototypes have added CONST qualifiers, which we try to deal with in a backward-compatible way */ #define XCONST84 #if TCL_MAJOR_VERSION >= 8 #if TCL_MINOR_VERSION >= 4 #undef XCONST84 #define XCONST84 CONST #endif #endif #ifdef NULLSTRING #undef NULLSTRING #endif #define NULLSTRING "" #define TCL_NULLSTR(s) (!s || !*s || !strcmp(s, "{}")) #define TY_CLIENT 1 #define TY_SERVER 2 #ifndef MAX_XPAS #define MAX_XPAS 10000 #endif /* * * record struct for client_data for XPATcl[Send,Receive,Info] routines * */ typedef struct xpatclclientdatarec{ Tcl_Interp *interp; char *callback; char *client_data; } *XPATclClientData, XPATclClientDataRec; /* *---------------------------------------------------------------------------- * * Routine: Tcl_GetXPAFromObj * * Purpose: convert string to XPA handle * * Returns: Tcl error code * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC static int Tcl_GetXPAFromObj(Tcl_Interp *interp, Tcl_Obj *obj, int flag, XPA *xpa) #else static int Tcl_GetXPAFromObj(interp, obj, flag, xpa) Tcl_Interp *interp; Tcl_Obj *obj; int flag; XPA *xpa; #endif { char *s; void *lval; Tcl_Obj *resultPtr; /* get result pointer */ resultPtr = Tcl_GetObjResult(interp); if( (s = Tcl_GetStringFromObj(obj, NULL)) == NULL ){ return(TCL_ERROR); } if( strncmp(s, "xpa_", 4) || (sscanf(&(s[4]), "%p", &lval) != 1) ){ Tcl_SetStringObj(resultPtr, "XPA$ERROR: invalid xpa handle", -1); return(TCL_ERROR); } *xpa = (XPA)lval; /* make sure its a valid xpa */ switch(flag){ case TY_CLIENT: if( !XPAClientValid(*xpa) ){ Tcl_SetStringObj(resultPtr, "XPA$ERROR: invalid xpa client handle", -1); return(TCL_ERROR); } break; case TY_SERVER: if( !XPAValid(*xpa) ){ Tcl_SetStringObj(resultPtr, "XPA$ERROR: invalid xpa server handle", -1); return(TCL_ERROR); } break; } return(TCL_OK); } /* *---------------------------------------------------------------------------- * * Routine: XPATclHandler * * Purpose: common handler for access points written in tcl * execute the tcl receive command with xpa arguments * used instead of Tcl_Eval so we can avoid interpreting either * paramlist or buf * * * Returns: 0 on success, -1 on failure * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC static int XPATclHandler (void *client_data, void *call_data, char *paramlist, char *buf, int len, int nargs) #else static int XPATclHandler(client_data, call_data, paramlist, buf, len, nargs) void *client_data; void *call_data; char *paramlist; char *buf; int len; int nargs; #endif { XPA xpa = (XPA)call_data; XPATclClientData xptr = (XPATclClientData)client_data; Tcl_CmdInfo info; /* Info about command procedures */ Tcl_Obj *objv[10]; /* Object vector for arguments */ Tcl_Obj *resultPtr; /* The result object */ int result; /* TCL_OK or TCL_ERROR */ int object; char tbuf[SZ_LINE]; XCONST84 char *argv[10]; char *s=NULL; char *t=NULL; char *cmd; /* make sure we have a callback */ if( !xptr || !xptr->callback ){ XPAError(xpa, "Invalid tcl command for xpa callback"); return(-1); } /* set command name */ cmd = xptr->callback; /* Map from the command name to a C procedure */ if( !Tcl_GetCommandInfo(xptr->interp, cmd, &info) ){ XPAError(xpa, "Unknown tcl command for xpa callback"); return(-1); } /* string-ize some values */ snprintf(tbuf, SZ_LINE, "xpa_%p", xpa); s = xstrdup(tbuf); if( nargs > 4 ){ snprintf(tbuf, SZ_LINE, "%d", len); t = xstrdup(tbuf); } /* package up argument values */ object = info.isNativeObjectProc; if (object) { /* The object interface is preferred for this command */ objv[0] = Tcl_NewStringObj(cmd, strlen(cmd)); objv[1] = Tcl_NewStringObj(s, strlen(s)); if( (xptr->client_data == NULL) || (*xptr->client_data == '\0') ) objv[2] = Tcl_NewStringObj(NULLSTRING, strlen(NULLSTRING)); else objv[2] = Tcl_NewStringObj(xptr->client_data, strlen(xptr->client_data)); if( (paramlist == NULL) || (*paramlist == '\0') ) objv[3] = Tcl_NewStringObj(NULLSTRING, strlen(NULLSTRING)); else objv[3] = Tcl_NewStringObj(paramlist, strlen(paramlist)); if( nargs > 4 ){ if( (buf == NULL) || (*buf == '\0') || (len == 0) ) objv[4] = Tcl_NewStringObj(NULLSTRING, strlen(NULLSTRING)); else objv[4] = Tcl_NewStringObj(buf, len); objv[5] = Tcl_NewStringObj(t, strlen(t)); } } else { argv[0] = cmd; argv[1] = s; argv[2] = xptr->client_data; argv[3] = paramlist; if( nargs > 4 ){ argv[4] = buf; argv[5] = t; } } /* reset before we make C call */ Tcl_ResetResult(xptr->interp); /* * Invoke the C procedure. */ if (object) { result = (*info.objProc)(info.objClientData, xptr->interp, nargs, objv); /* Get the string value from the result object */ resultPtr = Tcl_GetObjResult(xptr->interp); Tcl_SetResult(xptr->interp, Tcl_GetStringFromObj(resultPtr, NULL), TCL_VOLATILE); } else { result = (*info.proc)(info.clientData, xptr->interp, nargs, argv); } /* clean up */ if( s ) xfree(s); if( t ) xfree(t); /* translate Tcl status into XPA status */ if( result == TCL_OK ){ return(0); } else{ s = (char *)Tcl_GetStringResult(xptr->interp); if( !strncmp(s, "XPA$ERROR: ", 11) ) s += 11; XPAError(xpa, s); return(-1); } } /* *---------------------------------------------------------------------------- * * Routine: XPATclReceive * * Purpose: receive handler for access points written in tcl * execute the tcl receive command with xpa arguments * used instead of Tcl_Eval so we can avoid interpreting either * paramlist or buf * * * Returns: 0 on success, -1 on failure * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC static int XPATclReceive (void *client_data, void *call_data, char *paramlist, char *buf, size_t len) #else static int XPATclReceive(client_data, call_data, paramlist, buf, len) void *client_data; void *call_data; char *paramlist; char *buf; size_t len; #endif { return(XPATclHandler(client_data, call_data, paramlist, buf, len, 6)); } /* *---------------------------------------------------------------------------- * * Routine: XPATclSend * * Purpose: send handler for access points written in tcl * * Returns: 0 on success, -1 on failure * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC static int XPATclSend (void *client_data, void *call_data, char *paramlist, char **buf, size_t *len) #else static int XPATclSend(client_data, call_data, paramlist, buf, len) void *client_data; void *call_data; char *paramlist; char **buf; size_t *len; #endif { return(XPATclHandler(client_data, call_data, paramlist, NULL, 0, 4)); } /* *---------------------------------------------------------------------------- * * Routine: XPATclInfo * * Purpose: info handler for access points written in tcl * * Returns: 0 on success, -1 on failure * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC static int XPATclInfo (void *client_data, void *call_data, char *paramlist) #else static int XPATclInfo(client_data, call_data, paramlist) void *client_data; void *call_data; char *paramlist; #endif { return(XPATclHandler(client_data, call_data, paramlist, NULL, 0, 4)); } /* *---------------------------------------------------------------------------- * * Routine: XPANew_Tcl * * Purpose: Tcl binding to XPANew procedure * * Tcl call: * * xpanew class name help sproc sdata smode rproc rdata rmode * * use the empty string to specify NULL arguments * * Returns: Tcl error code * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC static int XPANew_Tcl(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) #else static int XPANew_Tcl(clientData, interp, objc, objv) ClientData clientData; Tcl_Interp *interp; int objc; Tcl_Obj *CONST objv[]; #endif { char *xclass; char *name; char *help; char *send_cb; char *sdata; char *smode; char *rec_cb; char *rdata; char *rmode; char tbuf[SZ_LINE]; XPA xpa; SendCb sproc; ReceiveCb rproc; XPATclClientData sptr; XPATclClientData rptr; Tcl_Obj *resultPtr; /* make sure argument count is correct */ if( objc != 10 ){ Tcl_WrongNumArgs(interp, 1, objv, "class name help sproc sdata smode rproc rdata rmode"); return(TCL_ERROR); } /* get arguments as strings */ xclass = Tcl_GetStringFromObj(objv[1], NULL); name = Tcl_GetStringFromObj(objv[2], NULL); help = Tcl_GetStringFromObj(objv[3], NULL); send_cb = Tcl_GetStringFromObj(objv[4], NULL); sdata = Tcl_GetStringFromObj(objv[5], NULL); smode = Tcl_GetStringFromObj(objv[6], NULL); rec_cb = Tcl_GetStringFromObj(objv[7], NULL); rdata = Tcl_GetStringFromObj(objv[8], NULL); rmode = Tcl_GetStringFromObj(objv[9], NULL); /* this will hold the result */ resultPtr = Tcl_GetObjResult(interp); /* set up callback procedures */ if( ((send_cb == NULL) || (*send_cb == '\0') ) ){ sproc = NULL; sptr = NULL; } else{ sproc = XPATclSend; sptr = (XPATclClientData)xcalloc(1, sizeof(XPATclClientDataRec)); sptr->interp = interp; sptr->callback = xstrdup(send_cb); sptr->client_data = xstrdup(sdata); } if( ((rec_cb == NULL) || (*rec_cb == '\0') ) ){ rproc = NULL; rptr = NULL; } else{ rproc = XPATclReceive; rptr = (XPATclClientData)xcalloc(1, sizeof(XPATclClientDataRec)); rptr->interp = interp; rptr->callback = xstrdup(rec_cb); rptr->client_data = xstrdup(rdata); } /* make sure we have either a send or receive callback */ if( !sproc && !rproc ){ Tcl_SetStringObj(resultPtr, "XPA$ERROR: xpanew requires send_cb or rec_cb (or both)", -1); return(TCL_ERROR); } /* set up the tcl handler for the xpa access point */ xpa = XPANew(xclass, name, help, sproc, sptr, smode, rproc, rptr, rmode); if( xpa == NULL ){ Tcl_SetStringObj(resultPtr, "XPA$ERROR: could not create XPA access point", -1); return(TCL_ERROR); } else{ /* add this xpa to the Tcl event loop */ XPATclAddInput(xpa); /* return xpa address to tcl in a string */ snprintf(tbuf, SZ_LINE, "xpa_%p", xpa); Tcl_SetStringObj(resultPtr, tbuf, -1); return(TCL_OK); } } /* *---------------------------------------------------------------------------- * * Routine: XPAFree_Tcl * * Purpose: Tcl binding to XPAFree procedure * * Tcl call: * * xpafree xpa * * Returns: Tcl error code * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC static int XPAFree_Tcl(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) #else static int XPAFree_Tcl(clientData, interp, objc, objv) ClientData clientData; Tcl_Interp *interp; int objc; Tcl_Obj *CONST objv[]; #endif { XPA xpa; XPATclClientData ptr; /* make sure argument count is correct */ if( objc != 2 ){ Tcl_WrongNumArgs(interp, 1, objv, "xpa"); return(TCL_ERROR); } /* get xpa, which is always arg 1 */ if( Tcl_GetXPAFromObj(interp, objv[1], TY_SERVER, &xpa) != TCL_OK){ return(TCL_ERROR); } /* reset error/result condition */ Tcl_ResetResult(interp); /* free the associated tcl record, stored in the client data */ if( xpa->send_data ){ ptr = (XPATclClientData)xpa->send_data; if( ptr->callback ) xfree( ptr->callback); if( ptr->client_data ) xfree( ptr->client_data); xfree(xpa->send_data); } if( xpa->receive_data ){ ptr = (XPATclClientData)xpa->receive_data; if( ptr->callback ) xfree( ptr->callback); if( ptr->client_data ) xfree( ptr->client_data); xfree(xpa->receive_data); } if( xpa->info_data ){ ptr = (XPATclClientData)xpa->info_data; if( ptr->callback ) xfree( ptr->callback); if( ptr->client_data ) xfree( ptr->client_data); xfree(xpa->info_data); } /* call the XPAFree routine */ XPAFree(xpa); return(TCL_OK); } /* *---------------------------------------------------------------------------- * * Routine: XPAInfoNew_Tcl * * Purpose: Tcl binding to XPAInfoNew procedure * * Tcl call: * * xpanew class name help iproc idata imode * * use the empty string to specify NULL arguments * * Returns: Tcl error code * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC static int XPAInfoNew_Tcl(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) #else static int XPAInfoNew_Tcl(clientData, interp, objc, objv) ClientData clientData; Tcl_Interp *interp; int objc; Tcl_Obj *CONST objv[]; #endif { char *xclass; char *name; char *info_cb; char *idata; char *imode; char tbuf[SZ_LINE]; XPA xpa; InfoCb iproc; XPATclClientData iptr; Tcl_Obj *resultPtr; /* make sure argument count is correct */ if( objc != 6 ){ Tcl_WrongNumArgs(interp, 1, objv, "class name iproc idata imode"); return(TCL_ERROR); } /* get arguments as strings */ xclass = Tcl_GetStringFromObj(objv[1], NULL); name = Tcl_GetStringFromObj(objv[2], NULL); info_cb = Tcl_GetStringFromObj(objv[3], NULL); idata = Tcl_GetStringFromObj(objv[4], NULL); imode = Tcl_GetStringFromObj(objv[5], NULL); /* this will hold the result */ resultPtr = Tcl_GetObjResult(interp); /* set up callback procedures */ if( info_cb == NULL ){ Tcl_SetStringObj(resultPtr, "XPA$ERROR: xpainfonew requires info_cb", -1); return(TCL_ERROR); } else{ iproc = XPATclInfo; iptr = (XPATclClientData)xcalloc(1, sizeof(XPATclClientDataRec)); iptr->interp = interp; iptr->callback = xstrdup(info_cb); iptr->client_data = xstrdup(idata); } /* set up the tcl handler for the xpa access point */ xpa = XPAInfoNew(xclass, name, iproc, iptr, imode); if( xpa == NULL ){ Tcl_SetStringObj(resultPtr, "XPA$ERROR: could not create XPA info access point", -1); return(TCL_ERROR); } else{ /* add this xpa to the Tcl event loop */ XPATclAddInput(xpa); /* return xpa address to tcl in a string */ snprintf(tbuf, SZ_LINE, "xpa_%p", xpa); Tcl_SetStringObj(resultPtr, tbuf, -1); return(TCL_OK); } } /* *---------------------------------------------------------------------------- * * Routine: XPACmdNew_Tcl * * Purpose: Tcl binding to XPACmdNew procedure * * Tcl call: * * xpacmdnew class name * * Returns: Tcl error code * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC static int XPACmdNew_Tcl(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) #else static int XPACmdNew_Tcl(clientData, interp, objc, objv) ClientData clientData; Tcl_Interp *interp; int objc; Tcl_Obj *CONST objv[]; #endif { char *xclass; char *name; char tbuf[SZ_LINE]; XPA xpa; Tcl_Obj *resultPtr; /* make sure argument count is correct */ if( objc != 3 ){ Tcl_WrongNumArgs(interp, 1, objv, "class name"); return(TCL_ERROR); } /* get arguments as strings */ xclass = Tcl_GetStringFromObj(objv[1], NULL); name = Tcl_GetStringFromObj(objv[2], NULL); /* this will hold the result */ resultPtr = Tcl_GetObjResult(interp); /* set up the tcl handler for the xpa access point */ if( (xpa = XPACmdNew(xclass, name)) == NULL ){ Tcl_SetStringObj(resultPtr, "XPA$ERROR: could not create XPA command access point", -1); return(TCL_ERROR); } else{ /* add this xpa to the Tcl event loop */ XPATclAddInput(xpa); /* return xpa address to tcl in a string */ snprintf(tbuf, SZ_LINE, "xpa_%p", xpa); Tcl_SetStringObj(resultPtr, tbuf, -1); return(TCL_OK); } } /* *---------------------------------------------------------------------------- * * Routine: XPACmdAdd_Tcl * * Purpose: Tcl binding to XPACmdAdd procedure * * Tcl call: * * xpacmdadd xpa name help sproc sdata smode rproc rdata rmode * * use the empty string to specify NULL arguments * * Returns: Tcl error code * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC static int XPACmdAdd_Tcl(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) #else static int XPACmdAdd_Tcl(clientData, interp, objc, objv) ClientData clientData; Tcl_Interp *interp; int objc; Tcl_Obj *CONST objv[]; #endif { char *name; char *help; char *send_cb; char *sdata; char *smode; char *rec_cb; char *rdata; char *rmode; char tbuf[SZ_LINE]; XPA xpa; XPACmd xpacmd; SendCb sproc; ReceiveCb rproc; XPATclClientData sptr; XPATclClientData rptr; Tcl_Obj *resultPtr; /* make sure argument count is correct */ if( objc != 10 ){ Tcl_WrongNumArgs(interp, 1, objv, "class name help sproc sdata smode rproc rdata rmode"); return(TCL_ERROR); } /* get xpa, which is always arg 1 */ if( Tcl_GetXPAFromObj(interp, objv[1], TY_SERVER, &xpa) != TCL_OK){ return(TCL_ERROR); } name = Tcl_GetStringFromObj(objv[2], NULL); help = Tcl_GetStringFromObj(objv[3], NULL); send_cb = Tcl_GetStringFromObj(objv[4], NULL); sdata = Tcl_GetStringFromObj(objv[5], NULL); smode = Tcl_GetStringFromObj(objv[6], NULL); rec_cb = Tcl_GetStringFromObj(objv[7], NULL); rdata = Tcl_GetStringFromObj(objv[8], NULL); rmode = Tcl_GetStringFromObj(objv[9], NULL); /* this will hold the result */ resultPtr = Tcl_GetObjResult(interp); /* set up callback procedures */ if( ((send_cb == NULL) || (*send_cb == '\0') ) ){ sproc = NULL; sptr = NULL; } else{ sproc = XPATclSend; sptr = (XPATclClientData)xcalloc(1, sizeof(XPATclClientDataRec)); sptr->interp = interp; sptr->callback = xstrdup(send_cb); sptr->client_data = xstrdup(sdata); } if( ((rec_cb == NULL) || (*rec_cb == '\0') ) ){ rproc = NULL; rptr = NULL; } else{ rproc = XPATclReceive; rptr = (XPATclClientData)xcalloc(1, sizeof(XPATclClientDataRec)); rptr->interp = interp; rptr->callback = xstrdup(rec_cb); rptr->client_data = xstrdup(rdata); } /* make sure we have either a send or receive callback */ if( !sproc && !rproc ){ Tcl_SetStringObj(resultPtr, "XPA$ERROR: xpacmdadd requires send_cb or rec_cb (or both)", -1); return(TCL_ERROR); } /* set up the tcl handler for the xpa access point */ xpacmd = XPACmdAdd(xpa, name, help, sproc, sptr, smode, rproc, rptr, rmode); if( xpacmd == NULL ){ Tcl_SetStringObj(resultPtr, "XPA$ERROR: could not create XPA command", -1); return(TCL_ERROR); } else{ /* return xpa address to tcl in a string */ snprintf(tbuf, SZ_LINE, "xpacmd_%p", xpacmd); Tcl_SetStringObj(resultPtr, tbuf, -1); return(TCL_OK); } } /* *---------------------------------------------------------------------------- * * Routine: XPACmdDel_Tcl * * Purpose: Tcl binding to XPACmdDel procedure * * Tcl call: * * xpacmddel xpa cmd * * Returns: Tcl error code * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC static int XPACmdDel_Tcl(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) #else static int XPACmdDel_Tcl(clientData, interp, objc, objv) ClientData clientData; Tcl_Interp *interp; int objc; Tcl_Obj *CONST objv[]; #endif { void *lval; char *s; XPA xpa; XPACmd cmd; Tcl_Obj *resultPtr; /* make sure argument count is correct */ if( objc != 3 ){ Tcl_WrongNumArgs(interp, 1, objv, "xpa cmd"); return(TCL_ERROR); } /* get result pointer */ resultPtr = Tcl_GetObjResult(interp); /* get xpa, which is always arg 1 */ if( Tcl_GetXPAFromObj(interp, objv[1], TY_SERVER, &xpa) != TCL_OK){ return(TCL_ERROR); } /* get xpacmd, which is always arg 2 */ if( (s = Tcl_GetStringFromObj(objv[2], NULL)) == NULL ){ return(TCL_ERROR); } if( strncmp(s, "xpacmd_", 7) || (sscanf(&(s[7]), "%p", &lval) != 1) ){ Tcl_SetStringObj(resultPtr, "XPA$ERROR: invalid xpacmd handle", -1); return(TCL_ERROR); } cmd = (XPACmd)lval; /* reset error/result condition */ Tcl_ResetResult(interp); /* call the XPACmdDel routine */ if( XPACmdDel(xpa, cmd) == 0 ){ /* free the associated tcl record, stored in the client data */ if( cmd->send_data ) xfree(cmd->send_data); if( cmd->receive_data ) xfree(cmd->receive_data); return(TCL_OK); } else{ resultPtr = Tcl_GetObjResult(interp); Tcl_SetStringObj(resultPtr, "XPA$ERROR: could not delete xpa cmd", -1); return(TCL_ERROR); } } /* *---------------------------------------------------------------------------- * * Routine: XPARec_Tcl * * Purpose: Tcl binding to retrieve info from the xpa struct * * Tcl call: * * set val [xparec xpa