archie/tcl-dp/generic/dpPackOff.c
2024-05-27 16:13:40 +02:00

687 lines
17 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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 <string.h>
#include <errno.h>
#include <tcl.h>
#include <generic/dpInt.h>
/*
* 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++);
}