/* * generic/dpPackOff.c -- * * This file contains the implementation of the packoff channel. This type of * filter identifies packets generated by the packon plugin filter and separates * them from the input stream, returning them separately. Since this operation * makes sense only when reading data, this channel is not writable. * These are channels that are created by evaluating "dp_connect dpPackOff". * */ #include #include #include #include /* * Prototypes for functions referenced only in this file. */ static int ClosePOChannel _ANSI_ARGS_((ClientData instanceData, Tcl_Interp *interp)); static int InputPOChannel _ANSI_ARGS_((ClientData instanceData, char *buf, int bufsize, int *errorCodePtr)); static int OutputPOChannel _ANSI_ARGS_((ClientData instanceData, char *buf, int toWrite, int *errorCodePtr)); static int SOPPOChannel _ANSI_ARGS_((ClientData instanceData, Tcl_Interp *interp, char *optionName, char *optionValue)); #ifndef _TCL76 static int GOPPOChannel _ANSI_ARGS_((ClientData instanceData, Tcl_Interp *interp, char *optionName, Tcl_DString *dsPtr)); #else static int GOPPOChannel _ANSI_ARGS_((ClientData instanceData, char *optionName, Tcl_DString *dsPtr)); #endif #ifndef _TCL76 static int GFPPOChannel _ANSI_ARGS_((ClientData instanceData, int direction, FileHandle *handlePtr)); #else static Tcl_File GFPPOChannel _ANSI_ARGS_((ClientData instanceData, int direction)); #endif static int CRPPOChannel _ANSI_ARGS_((ClientData instanceData, int mask)); static void WCPPOChannel _ANSI_ARGS_((ClientData instanceData, int mask)); /* memmove does not seem to be available on all systems */ static void mymove _ANSI_ARGS_((char *to, char *from, int number)); /* * This structure stores the names of the functions that tcl calls when certain * actions have to be performed on a packoff channel. To understand this entry, * please refer to the documentation of the Tcl_CreateChannel and its associated * functions in the tcl 7.6 documentation. * * A packoff channel will always be non-blocking, and non-writable. * Seek on a packoff channel is not allowed. */ Tcl_ChannelType poChannelType = { "packoff", NULL, /* blockModeProc */ ClosePOChannel, /* closeProc */ InputPOChannel, /* inputProc */ OutputPOChannel, /* outputProc */ NULL, /* seekProc */ SOPPOChannel, /* setOptionProc */ GOPPOChannel, /* getOptionProc */ WCPPOChannel, /* watchChannelProc */ #ifdef _TCL76 CRPPOChannel, /* channelReadyProc */ #endif GFPPOChannel /* getFileProc */ }; /* * Structure that stores the data needed to manage a packoff filter. */ typedef struct { /* Pointer to the subordinated channel. */ Tcl_Channel channelPtr; /* If peek = 0 consume input, otherwise not. */ int peek; /* The variables below are used in managing the input. Please refer to the * input function to understand their meaning. */ char *buffer; int bufLength; int used; int dataLength; int precRead; int ignoreNextRead; } PackOffInfo; /* Arbitrary value, it should not be less then the tcl's buffer size. */ #define BUFFER_CHUNK 8 * 1024 /* *----------------------------------------------------------------------------- * * DpCreatePOChannel -- * * Creates a packoff filter channel. * * Results: * * Returns a channel data structure. If an error happens, NULL * is returned. * * Side effects: * * Alocates memory for the instance data that is associated * with the channel. * * ---------------------------------------------------------------------------- */ Tcl_Channel DpCreatePOChannel (interp, argc, argv) Tcl_Interp *interp; /* (in) Pointer to tcl interpreter. */ int argc; /* (in) Number of arguments. */ char **argv; /* (in) Argument strings. */ { static int openedChannels = 0; int i; PackOffInfo *instanceData; Tcl_Channel newChannel; char chanName [20]; instanceData = (PackOffInfo *)ckalloc(sizeof(PackOffInfo)); if(instanceData == NULL) { Tcl_AppendResult(interp, "unable to allocate memory for packoff ", "filter channel", NULL); return NULL; } instanceData->channelPtr = NULL; for (i = 0; i < argc; i += 2) { int v = i+1; size_t len = strlen(argv[i]); if (strncmp(argv[i], "-channel", len)==0) { if (v == argc) { goto error2; } instanceData->channelPtr = Tcl_GetChannel(interp, argv[v], NULL); if(instanceData->channelPtr == NULL) { goto error1; } } else { Tcl_AppendResult(interp, "unknown option \"", argv[i], "\", must be -channel", NULL); goto error1; } } if(instanceData->channelPtr == NULL) { Tcl_AppendResult(interp, "-channel must be defined for a packoff ", "channel", NULL); goto error1; } /* No peek by default. */ instanceData->peek = 0; /* Variables related to buffer management (see input function). */ instanceData->buffer = (char *)ckalloc(BUFFER_CHUNK); if(instanceData->buffer == NULL) { Tcl_AppendResult(interp, "unable to allocate memory for packoff ", "filter channel buffer", NULL); goto error1; } instanceData->used = 0; instanceData->bufLength = BUFFER_CHUNK; instanceData->dataLength = 0; instanceData->ignoreNextRead = 0; /* Packoff filters are only readable. */ sprintf(chanName, "pofilter%d", openedChannels++); newChannel = Tcl_CreateChannel(&poChannelType, chanName, (ClientData)instanceData, TCL_READABLE); if(newChannel == NULL) { Tcl_AppendResult(interp, "tcl unable to create packoff channel", NULL); goto error1; } Tcl_RegisterChannel(interp, newChannel); return newChannel; error2: Tcl_AppendResult(interp, "option value missing for -channel", NULL); /* continues with error1 */ error1: ckfree((char *)instanceData); return NULL; } /* *----------------------------------------------------------------------------- * * ClosePOChannel -- * * Closes the given packoff filter channel. * * Results: * * If everything goes well, returns 0. If any error happens, * it returns a POSIX error code. * * Side effects: * * It frees the instance data associated with the channel. * * ---------------------------------------------------------------------------- */ static int ClosePOChannel (instanceData, interp) ClientData instanceData; /* (in) Pointer to PackOffInfo struct. */ Tcl_Interp *interp; /* Pointer to the tcl interpreter. */ { PackOffInfo *pd = (PackOffInfo *) instanceData; ckfree((char *)pd->buffer); ckfree((char *)instanceData); return 0; } /* *----------------------------------------------------------------------------- * * InputPOChannel -- * * Reads in a stream of data that was generated using the packon plugin * filter (or a similar algorithm), and separates the packets, returning them * separately to the tcl level. * * Results: * * Number of bytes read if no error happened, -1 otherwise. * * Side effects: * * 1. Calls the read procedure of the subordinated channel. * 2. Stores a POSIX code at errorBuffer if an error occurs. * * ---------------------------------------------------------------------------- */ static int InputPOChannel (instanceData, buf, bufsize, errorCodePtr) ClientData instanceData; /* (in) Pointer to PackOffInfo struct. */ char *buf; /* (in/out) Buffer to fill. */ int bufsize; /* (in) Size of buffer. */ int *errorCodePtr; /* (out) POSIX error code (if any). */ { PackOffInfo *pD = (PackOffInfo *)instanceData; char temp [7], inBufX [BUFFER_CHUNK]; int messLength, inLength; char *inBuf = inBufX; inLength = Tcl_Read(pD->channelPtr, inBuf, BUFFER_CHUNK); if (inLength == -1) { *errorCodePtr = Tcl_GetErrno(); return -1; } /* Do we have at least a message header in the buffer? */ if (pD->dataLength - pD->used < 6) { /* No. Can we make up a header using the input? */ int available = pD->dataLength - pD->used; if (inLength >= 6 - available) { /* Yes. Transfer rest of header and adjust local structures. */ mymove(pD->buffer, pD->buffer + pD->used, 6 - available); mymove(pD->buffer + available, inBuf, 6 - available); pD->dataLength = 6; pD->used = 0; inBuf += 6 - available; inLength -= 6 - available; } else { /* No. Transfer input and return. */ mymove(pD->buffer, pD->buffer + pD->used, inLength); pD->dataLength += inLength; pD->used = 0; *errorCodePtr = EAGAIN; return 0; } } /* See how many bytes we need for the next message. */ memcpy(temp, pD->buffer + pD->used, 6); temp[6] = '\0'; if((messLength = atoi(temp)) <= 0) { return -1; } /* Do not allow packet sizes that are bigger than what the size of buf is. * This is because we want a packet to be returned as a unit. */ if(messLength > bufsize) { *errorCodePtr = EINVAL; return -1; } /* Do we have enough data in the buffer? */ if(pD->dataLength - pD->used >= 6 + messLength) { /* Yes. Output data and adjust local structures. */ memcpy(buf, pD->buffer + pD->used + 6, messLength); pD->used += 6 + messLength; pD->ignoreNextRead = 1; } else if(pD->dataLength - pD->used + inLength >= 6 + messLength) { /* Do we have enough data in buffer and inBuf together? */ int useful = pD->dataLength - pD->used - 6; memcpy(buf, pD->buffer + pD->used + 6, useful); memcpy(buf + useful, inBuf, messLength - useful); pD->used = pD->dataLength = 0; inBuf += messLength - useful; inLength -= messLength - useful; pD->ignoreNextRead = 1; } else { messLength = 0; } /* Is there enough space to hold the data? */ if(pD->bufLength - (pD->dataLength - pD->used) >= inLength) { /* If yes, just put everything in place. */ if(pD->dataLength - pD->used > 0) { mymove(pD->buffer, pD->buffer + pD->used, pD->dataLength - pD->used); } mymove(pD->buffer + pD->dataLength - pD->used, inBuf, inLength); pD->dataLength = pD->dataLength - pD->used + inLength; pD->used = 0; } else { /* If no, create a bigger buffer. */ int neededSpace = inLength - (pD->bufLength - (pD->dataLength - pD->used)); char *temp = ckalloc(pD->bufLength + neededSpace + BUFFER_CHUNK); if(temp == NULL) { return ENOMEM; } memcpy(temp, pD->buffer + pD->used, pD->dataLength - pD->used); memcpy(temp + pD->dataLength - pD->used, inBuf, inLength); pD->dataLength = pD->dataLength - pD->used + inLength; pD->bufLength = pD->bufLength + neededSpace + BUFFER_CHUNK; pD->used = 0; ckfree(pD->buffer); pD->buffer = temp; } return messLength; } /* *----------------------------------------------------------------------------- * * OutputPOChannel -- * * A packoff channel is not writable. * * Results: * * Error code EINVAL. * * Side effects: * * None. * * ---------------------------------------------------------------------------- */ /* ARGSUSED */ static int OutputPOChannel (instanceData, buf, toWrite, errorCodePtr) ClientData instanceData; /* channel to send the message to */ char *buf; /* output buffer */ int toWrite; /* number of characters to write */ int *errorCodePtr; /* place to store the POSIX error code */ { return -1; } /* *----------------------------------------------------------------------------- * * GFPPOChannel -- * * "Get file" function for packoff channels. Since there are no files * associated with filters, it always returns NULL. * * Results: * * TCL_OK * * Side effects: * * None. * * ---------------------------------------------------------------------------- */ /* ARGSUSED */ #ifndef _TCL76 static int GFPPOChannel (instanceData, direction, handlePtr) ClientData instanceData; int direction; FileHandle *handlePtr; { *handlePtr = NULL; return TCL_OK; } #else static Tcl_File GFPPOChannel (instanceData, direction) ClientData instanceData; int direction; { return NULL; } #endif /* *----------------------------------------------------------------------------- * * SOPPOChannel -- * * There is no non-standard option allowed for packoff filters. * * Results: * * Tcl error code. * * Side effects: * * None. * * ---------------------------------------------------------------------------- */ /* ARGSUSED */ static int SOPPOChannel (instanceData, interp, optionName, optionValue) ClientData instanceData; /* (in) Pointer to PackOffInfo struct. */ Tcl_Interp *interp; /* (in) Pointer to tcl interpreter. */ char *optionName; char *optionValue; { Tcl_AppendResult (interp, "illegal option \"", optionName, "\" -- ", "must be a standard fconfigure option", NULL); return TCL_ERROR; } /* *----------------------------------------------------------------------------- * * GOPPOChannel -- * * There are no non-standard options for a packoff channel. * * Results: * * Standard Tcl result. * * Side effects: * * None. * * ---------------------------------------------------------------------------- */ static int GOPPOChannel (instanceData, #ifdef _TCL80 interp, #endif optionName, dsPtr) ClientData instanceData; #ifdef _TCL80 Tcl_Interp *interp; #endif char *optionName; Tcl_DString *dsPtr; /* (out) String to store the result in. */ { if (optionName != NULL) { Tcl_SetErrno(EINVAL); return TCL_ERROR; } return TCL_OK; } /* *----------------------------------------------------------------------------- * * WCPPOChannel -- * * This is the "watch channel" procedure for packoff filters. It is * assumed that no events are generated internally in the filter channel, * so the procedure only calls the corresponding procedure of the * subordinated channel. * * Results: * * None. * * Side effects: * * Calls the "watch channel" procedure of the subordinated channel. * * ---------------------------------------------------------------------------- */ static void WCPPOChannel (instanceData, mask) ClientData instanceData; /* (in) Pointer to PlugFInfo struct. */ int mask; /* (in) ORed combination of TCL_READABLE, * TCL_WRITABLE and TCL_EXCEPTION. It designates * the event categories that have to be watched. */ { Tcl_Channel channelPtr = ((PackOffInfo *)instanceData)->channelPtr; #ifdef _TCL76 (Tcl_GetChannelType(channelPtr)->watchChannelProc) (Tcl_GetChannelInstanceData(channelPtr), mask); #endif return; } /* *----------------------------------------------------------------------------- * * CRPPOChannel -- * * This is the "channel ready" procedure for packoff filters. It is * assumed that no events are generated internally in the filter channel, * so the procedure only calls the corresponding procedure of the * subordinated channel. * * Results: * * The value returned by the "channel ready" procedure of the subordinated * channel. * * Side effects: * * Calls the "channel ready" procedure of the subordinated channel. * * ---------------------------------------------------------------------------- */ static int CRPPOChannel (instanceData, mask) ClientData instanceData; /* (in) Pointer to PackOffInfo struct. */ int mask; /* (in) ORed combination of TCL_READABLE, * TCL_WRITABLE and TCL_EXCEPTION. It designates * the event categories whose occurence has to * be signalled. */ { Tcl_Channel channelPtr = ((PackOffInfo *)instanceData)->channelPtr; #ifdef _TCL76 return (Tcl_GetChannelType(channelPtr)->channelReadyProc) (Tcl_GetChannelInstanceData(channelPtr), mask); #else return TCL_OK; #endif } /* memmove does not seem to be available on all systems */ static void mymove (to, from, number) char *to; char *from; int number; { for(/* empty */; number--; *to++ = *from++); }