988 lines
27 KiB
C
988 lines
27 KiB
C
|
/*
|
|||
|
* generic/dpPlugF.c --
|
|||
|
*
|
|||
|
* This file contains the implementation of the plug-in filter (PIF)
|
|||
|
* channel. These are channels that are created by evaluating
|
|||
|
* "dp_connect filter".
|
|||
|
*/
|
|||
|
|
|||
|
/*
|
|||
|
* Major unsolved problems:
|
|||
|
*
|
|||
|
* 1. Should the PIF channel set the nonblocking option for the subordinated
|
|||
|
* channel?
|
|||
|
*/
|
|||
|
|
|||
|
#include <generic/dpInt.h>
|
|||
|
|
|||
|
#define DP_ARBITRARY_LIMIT 500
|
|||
|
|
|||
|
typedef struct {
|
|||
|
char *outBuf;
|
|||
|
int outLength;
|
|||
|
int outUsed;
|
|||
|
int eof;
|
|||
|
} FiltBuffer;
|
|||
|
|
|||
|
typedef struct {
|
|||
|
Tcl_Channel channelPtr;
|
|||
|
int peek;
|
|||
|
FiltBuffer i;
|
|||
|
Tcl_Interp *interp;
|
|||
|
Dp_PlugInFilterProc *inFilter;
|
|||
|
Dp_PlugInFilterProc *outFilter;
|
|||
|
void *inData;
|
|||
|
void *outData;
|
|||
|
} PlugFInfo;
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* Prototypes for functions referenced only in this file.
|
|||
|
*/
|
|||
|
|
|||
|
static int ClosePlugFChannel _ANSI_ARGS_((ClientData instanceData,
|
|||
|
Tcl_Interp *interp));
|
|||
|
|
|||
|
static int InputPlugFChannel _ANSI_ARGS_((ClientData instanceData,
|
|||
|
char *buf, int bufsize,
|
|||
|
int *errorCodePtr));
|
|||
|
|
|||
|
static int OutputPlugFChannel _ANSI_ARGS_((ClientData instanceData,
|
|||
|
char *buf, int toWrite,
|
|||
|
int *errorCodePtr));
|
|||
|
|
|||
|
static int SOPPlugFChannel _ANSI_ARGS_((ClientData instanceData,
|
|||
|
Tcl_Interp *interp,
|
|||
|
char *optionName,
|
|||
|
char *optionValue));
|
|||
|
|
|||
|
#ifdef _TCL76
|
|||
|
static int GOPPlugFChannel _ANSI_ARGS_((ClientData instanceData,
|
|||
|
char *optionName,
|
|||
|
Tcl_DString *dsPtr));
|
|||
|
#else
|
|||
|
static int GOPPlugFChannel _ANSI_ARGS_((ClientData instanceData,
|
|||
|
Tcl_Interp *interp,
|
|||
|
char *optionName,
|
|||
|
Tcl_DString *dsPtr));
|
|||
|
#endif
|
|||
|
|
|||
|
#ifndef _TCL76
|
|||
|
static int GFPPlugFChannel _ANSI_ARGS_((ClientData instanceData,
|
|||
|
int direction,
|
|||
|
FileHandle *handlePtr));
|
|||
|
#else
|
|||
|
static Tcl_File GFPPlugFChannel _ANSI_ARGS_((ClientData instanceData,
|
|||
|
int direction));
|
|||
|
#endif
|
|||
|
|
|||
|
static int CRPPlugFChannel _ANSI_ARGS_((ClientData instanceData,
|
|||
|
int mask));
|
|||
|
|
|||
|
static void WCPPlugFChannel _ANSI_ARGS_((ClientData instanceData,
|
|||
|
int mask));
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* This structure stores the names of the functions that Tcl calls when certain
|
|||
|
* actions have to be performed on a PIF 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 PIF channel will always be non-blocking.
|
|||
|
* Seek on a PIF channel is not allowed.
|
|||
|
*/
|
|||
|
|
|||
|
Tcl_ChannelType plugFChannelType = {
|
|||
|
"plugfilter",
|
|||
|
NULL, /* blockModeProc */
|
|||
|
ClosePlugFChannel, /* closeProc */
|
|||
|
InputPlugFChannel, /* inputProc */
|
|||
|
OutputPlugFChannel, /* outputProc */
|
|||
|
NULL, /* seekProc */
|
|||
|
SOPPlugFChannel, /* setOptionProc */
|
|||
|
GOPPlugFChannel, /* getOptionProc */
|
|||
|
WCPPlugFChannel, /* watchChannelProc */
|
|||
|
#ifdef _TCL76
|
|||
|
CRPPlugFChannel, /* channelReadyProc */
|
|||
|
#endif
|
|||
|
GFPPlugFChannel /* getFileProc */
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*
|
|||
|
* DpCreatePlugFChannel --
|
|||
|
*
|
|||
|
* Creates a PIF 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
|
|||
|
DpCreatePlugFChannel (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;
|
|||
|
PlugFInfo *instanceData;
|
|||
|
Tcl_Channel newChannel;
|
|||
|
char chanName [20];
|
|||
|
|
|||
|
|
|||
|
instanceData = (PlugFInfo *)ckalloc(sizeof(PlugFInfo));
|
|||
|
|
|||
|
if (instanceData == NULL) {
|
|||
|
Tcl_SetErrno(ENOMEM);
|
|||
|
Tcl_AppendResult(interp, "unable to allocate memory for plug-in filter ",
|
|||
|
"channel", NULL);
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
/* Install the default identity filters. */
|
|||
|
|
|||
|
instanceData->channelPtr = NULL;
|
|||
|
instanceData->inFilter = Dp_GetFilterPtr (interp, "identity");
|
|||
|
|
|||
|
if (instanceData->inFilter == NULL) {
|
|||
|
Tcl_AppendResult(interp, "unable to find identity plug-in filter",
|
|||
|
NULL);
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
instanceData->outFilter = Dp_GetFilterPtr (interp, "identity");
|
|||
|
|
|||
|
if (instanceData->outFilter == NULL) {
|
|||
|
Tcl_AppendResult(interp, "unable to find identity plug-in filter",
|
|||
|
NULL);
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
/* Identify the given options and take appropriate actions. */
|
|||
|
|
|||
|
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 if (strncmp(argv[i], "-infilter", len)==0) {
|
|||
|
if (v == argc) {goto error2;}
|
|||
|
|
|||
|
instanceData->inFilter = Dp_GetFilterPtr (interp, argv[v]);
|
|||
|
if (instanceData->inFilter == NULL) {
|
|||
|
Tcl_AppendResult(interp, "unable to find plug-in filter ",
|
|||
|
argv[v], NULL);
|
|||
|
goto error1;
|
|||
|
}
|
|||
|
} else if (strncmp(argv[i], "-outfilter", len)==0) {
|
|||
|
if (v == argc) {goto error2;}
|
|||
|
|
|||
|
instanceData->outFilter = Dp_GetFilterPtr (interp, argv[v]);
|
|||
|
if (instanceData->outFilter == NULL) {
|
|||
|
Tcl_AppendResult(interp, "unable to find plug-in filter ",
|
|||
|
argv[v], 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 plug-in",
|
|||
|
" channel", NULL);
|
|||
|
goto error1;
|
|||
|
}
|
|||
|
|
|||
|
/* No peek by default. */
|
|||
|
instanceData->peek = 0;
|
|||
|
|
|||
|
/*
|
|||
|
* A PIF channel is always both writable and readable. The real behavior
|
|||
|
* depends on the properties of the subordinated channel.
|
|||
|
*/
|
|||
|
|
|||
|
sprintf(chanName, "plugfilter%d", openedChannels++);
|
|||
|
newChannel = Tcl_CreateChannel(&plugFChannelType, chanName,
|
|||
|
(ClientData)instanceData, TCL_READABLE | TCL_WRITABLE);
|
|||
|
if (newChannel == NULL) {
|
|||
|
Tcl_AppendResult(interp, "Unable to create plug-in channel", NULL);
|
|||
|
goto error1;
|
|||
|
}
|
|||
|
Tcl_RegisterChannel(interp, newChannel);
|
|||
|
|
|||
|
/*
|
|||
|
* Initialize the data related to buffering. Notice the asymmetry
|
|||
|
* between the handling of input and output buffers.
|
|||
|
*/
|
|||
|
|
|||
|
instanceData->i.outBuf = NULL;
|
|||
|
instanceData->i.outLength = 0;
|
|||
|
instanceData->i.outUsed = 0;
|
|||
|
instanceData->i.eof = 0;
|
|||
|
|
|||
|
instanceData->interp = interp;
|
|||
|
|
|||
|
instanceData->inData = NULL;
|
|||
|
instanceData->outData = NULL;
|
|||
|
|
|||
|
return newChannel;
|
|||
|
|
|||
|
error2:
|
|||
|
Tcl_AppendResult(interp, "option value missing for -channel", NULL);
|
|||
|
/* continues with error1 */
|
|||
|
|
|||
|
error1:
|
|||
|
ckfree((char *)instanceData);
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*
|
|||
|
* ClosePlugFChannel --
|
|||
|
*
|
|||
|
* Closes the given PIF channel.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
*
|
|||
|
* If everything goes well, returns 0. If any error happens,
|
|||
|
* it returns a POSIX error code.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
*
|
|||
|
* 1. It calls the plug-in filters indicating that there
|
|||
|
* they should release all the memory they allocated and should
|
|||
|
* return the data they might still buffer internally.
|
|||
|
* 2. It writes the data that it returned by the output filter on the
|
|||
|
* subordinated channel.
|
|||
|
* 3. It frees all the internal channel buffers.
|
|||
|
* 4. It frees the instance data associated with the channel.
|
|||
|
*
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
static int
|
|||
|
ClosePlugFChannel (instanceData, interp)
|
|||
|
ClientData instanceData; /* (in) Pointer to PlugFInfo struct. */
|
|||
|
Tcl_Interp *interp; /* (in) Pointer to tcl interpreter. */
|
|||
|
{
|
|||
|
char *outBuf;
|
|||
|
int outLength, error, status, tmp;
|
|||
|
|
|||
|
PlugFInfo *data = (PlugFInfo *)instanceData;
|
|||
|
|
|||
|
status = 0;
|
|||
|
|
|||
|
if (data->i.outBuf != NULL) {
|
|||
|
ckfree(data->i.outBuf);
|
|||
|
/*
|
|||
|
* If close fails, and is repeated later, this will prevent
|
|||
|
* freeing the buffer again.
|
|||
|
*/
|
|||
|
data->i.outBuf = NULL;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* In case the data was incomplete, and the filter was waiting for
|
|||
|
* more data, this will signal that now it is the last chance to
|
|||
|
* write to the subordinated channel. Also, all memory allocated
|
|||
|
* by the filter should be released now.
|
|||
|
*/
|
|||
|
|
|||
|
error = (data->outFilter) (NULL, 0, &outBuf, &outLength,
|
|||
|
&(data->outData), data->interp, DP_FILTER_CLOSE);
|
|||
|
Tcl_SetErrno(error);
|
|||
|
|
|||
|
if (error != 0) {
|
|||
|
Tcl_SetErrno(error);
|
|||
|
|
|||
|
/*
|
|||
|
* Do not free instance data - the user might take some corrective
|
|||
|
* action based on the POSIX error code, could even write to the
|
|||
|
* channel, and then attept to close it again.
|
|||
|
*/
|
|||
|
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
if (outLength > 0) {
|
|||
|
tmp = Tcl_Write(data->channelPtr, outBuf, outLength);
|
|||
|
|
|||
|
if (tmp == -1) {
|
|||
|
status = -1;
|
|||
|
} else if (tmp != outLength) {
|
|||
|
/*
|
|||
|
* We could not write everything to the subordinated channel.
|
|||
|
* Try again, if it fails, report the error.
|
|||
|
*/
|
|||
|
int tmp1;
|
|||
|
|
|||
|
tmp1 = Tcl_Write(data->channelPtr, outBuf + tmp, outLength - tmp);
|
|||
|
if (tmp1 != (outLength - tmp)) {
|
|||
|
Tcl_SetErrno(ENOSPC);
|
|||
|
status = -1;
|
|||
|
}
|
|||
|
}
|
|||
|
ckfree(outBuf);
|
|||
|
}
|
|||
|
|
|||
|
/* If the channel is closed, nobody is interested in reading from
|
|||
|
* it anymore. Signall to the filter and ignore the output.
|
|||
|
*/
|
|||
|
|
|||
|
error = (data->inFilter) (NULL, 0, &outBuf, &outLength,
|
|||
|
&(data->inData), data->interp, DP_FILTER_CLOSE);
|
|||
|
Tcl_SetErrno(error);
|
|||
|
|
|||
|
if (error != 0) {
|
|||
|
Tcl_SetErrno(error);
|
|||
|
|
|||
|
/*
|
|||
|
* Do not free instance data - the user might take some corrective
|
|||
|
* action based on the POSIX error code, could even write to the
|
|||
|
* channel, and then attept to close it again.
|
|||
|
*/
|
|||
|
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
if (outLength > 0) {
|
|||
|
ckfree(outBuf);
|
|||
|
}
|
|||
|
|
|||
|
if (instanceData != NULL) {
|
|||
|
ckfree((char *)instanceData);
|
|||
|
}
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*
|
|||
|
* InputPlugFChannel --
|
|||
|
*
|
|||
|
* Reads data from the subordinated channel and feeds it into the input
|
|||
|
* filter. It continues until the filter outputs at least as much data
|
|||
|
* as it was requested, or until there is no more data to be read from
|
|||
|
* the subordinated channel.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
*
|
|||
|
* Number of bytes output by the filter, which is at most the amount
|
|||
|
* requested. If the filter returns more bytes that requested, the
|
|||
|
* difference is buffered internally. If an error happened, the return
|
|||
|
* is -1.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
*
|
|||
|
* 1. Calls the read procedure of the subordinated channel.
|
|||
|
* 2. Modifies the buffers associated with the input filter.
|
|||
|
* 3. Stores a POSIX code at errorBuffer if an error occurs.
|
|||
|
* 4. The data that is returned is stored in buf.
|
|||
|
*
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
static int
|
|||
|
InputPlugFChannel (instanceData, buf, bufsize, errorCodePtr)
|
|||
|
ClientData instanceData; /* (in) Pointer to PlugFInfo struct. */
|
|||
|
char *buf; /* (in/out) Buffer to fill. */
|
|||
|
int bufsize; /* (in) Size of buffer. */
|
|||
|
int *errorCodePtr; /* (out) POSIX error code (if any). */
|
|||
|
{
|
|||
|
int transferred, count, inUsed, inBufLength;
|
|||
|
char inBuf [20 * DP_ARBITRARY_LIMIT];
|
|||
|
FiltBuffer *x;
|
|||
|
|
|||
|
PlugFInfo *data = (PlugFInfo *)instanceData;
|
|||
|
|
|||
|
inBufLength = sizeof(inBuf);
|
|||
|
x = &(data->i);
|
|||
|
inUsed = 0;
|
|||
|
|
|||
|
transferred = 0;
|
|||
|
count = 0;
|
|||
|
|
|||
|
while (transferred < bufsize) {
|
|||
|
if (x->outLength > 0) {
|
|||
|
if (bufsize - transferred < x->outLength - x->outUsed) {
|
|||
|
memcpy(buf + transferred, x->outBuf + x->outUsed,
|
|||
|
bufsize - transferred);
|
|||
|
x->outUsed += (bufsize - transferred);
|
|||
|
transferred += (bufsize - transferred);
|
|||
|
} else {
|
|||
|
memcpy(buf + transferred, x->outBuf + x->outUsed,
|
|||
|
x->outLength - x->outUsed);
|
|||
|
transferred += (x->outLength - x->outUsed);
|
|||
|
x->outUsed = x->outLength;
|
|||
|
}
|
|||
|
|
|||
|
if (x->outUsed == x->outLength) {
|
|||
|
x->outLength = 0;
|
|||
|
x->outUsed = 0;
|
|||
|
if (x->outBuf != NULL) {
|
|||
|
ckfree(x->outBuf);
|
|||
|
}
|
|||
|
x->outBuf = NULL;
|
|||
|
}
|
|||
|
} else { /* outLength == 0 */
|
|||
|
/* Try to get some output from the filter. */
|
|||
|
|
|||
|
int error;
|
|||
|
|
|||
|
if(!(x->eof)) {
|
|||
|
error = (data->inFilter) (inBuf, inUsed, &(x->outBuf),
|
|||
|
&(x->outLength), &(data->inData),
|
|||
|
data->interp, DP_FILTER_NORMAL);
|
|||
|
} else {
|
|||
|
error = (data->inFilter) (inBuf, inUsed, &(x->outBuf),
|
|||
|
&(x->outLength), &(data->inData),
|
|||
|
data->interp, DP_FILTER_EOF);
|
|||
|
}
|
|||
|
|
|||
|
inUsed = 0;
|
|||
|
|
|||
|
if (error != 0) {
|
|||
|
*errorCodePtr = error;
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
if(x->outLength == 0) {
|
|||
|
/*
|
|||
|
* We got no data from the filter. Try to read something from
|
|||
|
* the subordinated channel, and pipe it later in the filter.
|
|||
|
*/
|
|||
|
|
|||
|
int newData;
|
|||
|
|
|||
|
if(!(x->eof)) {
|
|||
|
newData = Tcl_Read(data->channelPtr, inBuf, inBufLength);
|
|||
|
} else {
|
|||
|
newData = 0;
|
|||
|
}
|
|||
|
|
|||
|
if (newData == -1) {
|
|||
|
*errorCodePtr = Tcl_GetErrno();
|
|||
|
return -1;
|
|||
|
} else if (newData == 0) {
|
|||
|
|
|||
|
/* No data available in the subordinated channel. */
|
|||
|
|
|||
|
/* Did the underlying channel reach eof? */
|
|||
|
if(!(x->eof)) {
|
|||
|
if(Tcl_Eof(data->channelPtr)) {
|
|||
|
x->eof = 1;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
count++;
|
|||
|
if ((count == 2) || (x->eof == 1)) {
|
|||
|
return transferred;
|
|||
|
}
|
|||
|
} else {
|
|||
|
count = 0;
|
|||
|
}
|
|||
|
inUsed = newData;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return transferred;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*
|
|||
|
* OutputPlugFChannel --
|
|||
|
*
|
|||
|
* Feeds the data through the output filter. If the filter produces any
|
|||
|
* output it writes it to the subordinated channel. If the filter
|
|||
|
* can not process the data completely, the difference is buffered
|
|||
|
* internally.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
*
|
|||
|
* Number of bytes "written" (fed into the filter), or -1 if an error
|
|||
|
* is signalled. If there is no error, the returned value always coincides
|
|||
|
* with the request amount.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
*
|
|||
|
* 1. Calls the write procedure of the subordinated channel.
|
|||
|
* 2. Modifies the buffers associated with the output filter.
|
|||
|
* 3. Stores a POSIX code at errorBuffer if an error occurs.
|
|||
|
*
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
static int
|
|||
|
OutputPlugFChannel (instanceData, buf, toWrite, errorCodePtr)
|
|||
|
ClientData instanceData; /* (in) Pointer to PlugFInfo struct. */
|
|||
|
char *buf; /* (in) Buffer to write. */
|
|||
|
int toWrite; /* (in) Number of bytes to write. */
|
|||
|
int *errorCodePtr; /* (out) POSIX error code (if any). */
|
|||
|
{
|
|||
|
int tmp, error, outLength, mode;
|
|||
|
char *outBuf = NULL;
|
|||
|
Tcl_DString option;
|
|||
|
|
|||
|
char *cx;
|
|||
|
|
|||
|
PlugFInfo *data = (PlugFInfo *)instanceData;
|
|||
|
|
|||
|
Tcl_DStringInit(&option);
|
|||
|
Tcl_GetChannelOption(
|
|||
|
#ifdef _TCL80
|
|||
|
data->interp,
|
|||
|
#endif
|
|||
|
data->channelPtr, "-buffering", &option);
|
|||
|
cx = Tcl_DStringValue(&option);
|
|||
|
|
|||
|
if (strcmp(cx, "none")) {
|
|||
|
/* Buffering is "line" or "full". */
|
|||
|
mode = DP_FILTER_FLUSH;
|
|||
|
} else {
|
|||
|
/* Buffering is "none". */
|
|||
|
mode = DP_FILTER_NORMAL;
|
|||
|
}
|
|||
|
Tcl_DStringFree(&option);
|
|||
|
|
|||
|
error = (data->outFilter) (buf, toWrite, &outBuf, &outLength,
|
|||
|
&(data->outData), data->interp, mode);
|
|||
|
|
|||
|
if (error != 0) {
|
|||
|
*errorCodePtr = error;
|
|||
|
goto error1;
|
|||
|
}
|
|||
|
|
|||
|
if (outLength > 0) {
|
|||
|
tmp = Tcl_Write(data->channelPtr, outBuf, outLength);
|
|||
|
|
|||
|
if (tmp == -1) {
|
|||
|
*errorCodePtr = Tcl_GetErrno();
|
|||
|
goto error1;
|
|||
|
} else if (tmp != outLength) {
|
|||
|
/*
|
|||
|
* We could not write everything to the subordinated channel.
|
|||
|
* Try again, if it fails, report the error.
|
|||
|
*/
|
|||
|
int tmp1;
|
|||
|
|
|||
|
tmp1 = Tcl_Write(data->channelPtr, outBuf + tmp, outLength - tmp);
|
|||
|
|
|||
|
if (tmp1 != outLength - tmp) {
|
|||
|
*errorCodePtr = ENOSPC;
|
|||
|
goto error1;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (outBuf != NULL) {
|
|||
|
ckfree(outBuf);
|
|||
|
}
|
|||
|
return toWrite;
|
|||
|
|
|||
|
error1:
|
|||
|
if (outBuf != NULL) {
|
|||
|
ckfree(outBuf);
|
|||
|
}
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*
|
|||
|
* GFPPlugFChannel --
|
|||
|
*
|
|||
|
* "Get file" function for PIF channels. Since there are no files
|
|||
|
* associated with filters, it always returns NULL.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
*
|
|||
|
* Always NULL.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
*
|
|||
|
* None.
|
|||
|
*
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*/
|
|||
|
/* ARGSUSED */
|
|||
|
#ifndef _TCL76
|
|||
|
static int
|
|||
|
GFPPlugFChannel (instanceData, direction, handlePtr)
|
|||
|
ClientData instanceData;
|
|||
|
int direction;
|
|||
|
FileHandle *handlePtr;
|
|||
|
{
|
|||
|
*handlePtr = NULL;
|
|||
|
return TCL_OK;
|
|||
|
}
|
|||
|
#else
|
|||
|
static Tcl_File
|
|||
|
GFPPlugFChannel (instanceData, direction)
|
|||
|
ClientData instanceData;
|
|||
|
int direction;
|
|||
|
{
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*
|
|||
|
* SOPPlugFChannel --
|
|||
|
*
|
|||
|
* "Set option" procedure for PIF channels.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
*
|
|||
|
* Standard Tcl result.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
*
|
|||
|
* Sets the value of the specified option.
|
|||
|
*
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
static int
|
|||
|
SOPPlugFChannel (instanceData, interp, optionName, optionValue)
|
|||
|
ClientData instanceData; /* (in) Pointer to PlugFInfo struct. */
|
|||
|
Tcl_Interp *interp; /* (in) Pointer to tcl interpreter. */
|
|||
|
char *optionName;
|
|||
|
char *optionValue;
|
|||
|
{
|
|||
|
int option, value, error;
|
|||
|
|
|||
|
PlugFInfo *data = (PlugFInfo *)instanceData;
|
|||
|
|
|||
|
/*
|
|||
|
* Set the option specified by optionName.
|
|||
|
*/
|
|||
|
|
|||
|
if (optionName[0] == '-') {
|
|||
|
option = DpTranslateOption(optionName+1);
|
|||
|
} else {
|
|||
|
option = -1;
|
|||
|
}
|
|||
|
|
|||
|
switch(option) {
|
|||
|
case DP_PEEK:
|
|||
|
if (Tcl_GetBoolean(interp, optionValue, &value) != TCL_OK) {
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
if (value == 0) {
|
|||
|
data->peek = 0;
|
|||
|
if (Tcl_SetChannelOption(interp, data->channelPtr, "-peek",
|
|||
|
"no") == TCL_ERROR) {
|
|||
|
Tcl_AppendResult(interp,
|
|||
|
": subordinated channel error in ",
|
|||
|
Tcl_GetChannelName(data->channelPtr), NULL);
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
} else {
|
|||
|
data->peek = 1;
|
|||
|
if (Tcl_SetChannelOption(interp, data->channelPtr, "-peek",
|
|||
|
"yes") == TCL_ERROR) {
|
|||
|
Tcl_AppendResult(interp,
|
|||
|
": subordinated channel error in ",
|
|||
|
Tcl_GetChannelName(data->channelPtr), NULL);
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case DP_CHANNEL:
|
|||
|
Tcl_AppendResult(interp, "can't set channel after plug-in",
|
|||
|
" channel is opened", NULL);
|
|||
|
return TCL_ERROR;
|
|||
|
|
|||
|
case DP_INFILTER:
|
|||
|
Tcl_AppendResult(interp, "can't set infilter after plug-in",
|
|||
|
" channel is opened", NULL);
|
|||
|
return TCL_ERROR;
|
|||
|
|
|||
|
case DP_OUTFILTER:
|
|||
|
Tcl_AppendResult(interp, "can't set outfilter after plug-in",
|
|||
|
" channel is opened", NULL);
|
|||
|
return TCL_ERROR;
|
|||
|
|
|||
|
case DP_OUTSET:
|
|||
|
error = (data->outFilter) (optionValue, strlen(optionValue), NULL,
|
|||
|
NULL, &(data->outData), data->interp, DP_FILTER_SET);
|
|||
|
|
|||
|
if (error != 0) {
|
|||
|
Tcl_AppendResult(interp, "can't set option ", optionValue,
|
|||
|
" for output filter", NULL);
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case DP_INSET:
|
|||
|
error = (data->inFilter) (optionValue, strlen(optionValue), NULL,
|
|||
|
NULL, &(data->inData), data->interp, DP_FILTER_SET);
|
|||
|
|
|||
|
if (error != 0) {
|
|||
|
Tcl_AppendResult(interp, "can't set option ", optionValue,
|
|||
|
" for input filter", NULL);
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
Tcl_AppendResult (interp, "bad option \"", optionName,
|
|||
|
"\": must be peek, infilter, outfilter or a standard",
|
|||
|
"fconfigure option", NULL);
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
return TCL_OK;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*
|
|||
|
* GOPPlugFChannel --
|
|||
|
*
|
|||
|
* "Get option" function for PIF channels.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
*
|
|||
|
* Standard Tcl result.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
*
|
|||
|
* Returns the value of a non-standard option. If no option is specified,
|
|||
|
* a list of all options, together with their values, is returned.
|
|||
|
*
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
static int
|
|||
|
GOPPlugFChannel (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. */
|
|||
|
{
|
|||
|
int option;
|
|||
|
char *internal;
|
|||
|
|
|||
|
PlugFInfo *data = (PlugFInfo *)instanceData;
|
|||
|
|
|||
|
/*
|
|||
|
* If optionName is NULL, then store an alternating list of
|
|||
|
* all supported options and their current values in dsPtr.
|
|||
|
*/
|
|||
|
#ifdef _TCL80
|
|||
|
#define IGO(a, b, c) GOPPlugFChannel(a, interp, b, c)
|
|||
|
#else
|
|||
|
#define IGO(a, b, c) GOPPlugFChannel(a, b, c)
|
|||
|
#endif
|
|||
|
if (optionName == NULL) {
|
|||
|
Tcl_DStringAppend (dsPtr, " -channel ", -1);
|
|||
|
IGO (instanceData, "-channel", dsPtr);
|
|||
|
Tcl_DStringAppend (dsPtr, " -peek ", -1);
|
|||
|
IGO (instanceData, "-peek", dsPtr);
|
|||
|
Tcl_DStringAppend (dsPtr, " -inset ", -1);
|
|||
|
IGO (instanceData, "-inset", dsPtr);
|
|||
|
Tcl_DStringAppend (dsPtr, " -outset ", -1);
|
|||
|
IGO (instanceData, "-outset", dsPtr);
|
|||
|
return TCL_OK;
|
|||
|
}
|
|||
|
#undef IGO
|
|||
|
|
|||
|
/*
|
|||
|
* Retrieve the value of the option specified by optionName.
|
|||
|
*/
|
|||
|
|
|||
|
if (optionName[0] == '-') {
|
|||
|
option = DpTranslateOption(optionName+1);
|
|||
|
} else {
|
|||
|
option = -1;
|
|||
|
}
|
|||
|
|
|||
|
switch (option) {
|
|||
|
case DP_PEEK:
|
|||
|
if (data->peek) {
|
|||
|
Tcl_DStringAppend (dsPtr, "1", -1);
|
|||
|
} else {
|
|||
|
Tcl_DStringAppend (dsPtr, "0", -1);
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case DP_CHANNEL:
|
|||
|
Tcl_DStringAppend (dsPtr, Tcl_GetChannelName(data->channelPtr), -1);
|
|||
|
break;
|
|||
|
|
|||
|
case DP_INFILTER:
|
|||
|
Tcl_DStringAppend (dsPtr, Dp_GetFilterName(data->inFilter), -1);
|
|||
|
break;
|
|||
|
|
|||
|
case DP_OUTFILTER:
|
|||
|
Tcl_DStringAppend (dsPtr, Dp_GetFilterName(data->outFilter), -1);
|
|||
|
break;
|
|||
|
|
|||
|
case DP_INSET:
|
|||
|
(data->inFilter) (NULL, 0, &internal, NULL,
|
|||
|
&(data->inData), data->interp, DP_FILTER_GET);
|
|||
|
Tcl_DStringAppend (dsPtr, internal, -1);
|
|||
|
break;
|
|||
|
|
|||
|
case DP_OUTSET:
|
|||
|
(data->outFilter) (NULL, 0, &internal, NULL,
|
|||
|
&(data->outData), data->interp, DP_FILTER_GET);
|
|||
|
Tcl_DStringAppend (dsPtr, internal, -1);
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
#ifndef _TCL76
|
|||
|
Tcl_AppendResult(interp,
|
|||
|
"bad option \"", optionName,"\": must be -blocking,",
|
|||
|
" -buffering, -buffersize, -eofchar, -translation,",
|
|||
|
" or a channel type specific option", NULL);
|
|||
|
#else
|
|||
|
{
|
|||
|
char errStr[128];
|
|||
|
sprintf(errStr, "bad option \"%s\": must be -blocking,"
|
|||
|
"-buffering, -buffersize, -eofchar, -translation,"
|
|||
|
" or a channel type specific option", optionName);
|
|||
|
Tcl_DStringAppend(dsPtr, errStr, -1);
|
|||
|
}
|
|||
|
#endif
|
|||
|
Tcl_SetErrno (EINVAL);
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
return TCL_OK;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*
|
|||
|
* WCPPlugFChannel --
|
|||
|
*
|
|||
|
* This is the "watch channel" procedure for PIF channels. 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
|
|||
|
WCPPlugFChannel (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 = ((PlugFInfo *)instanceData)->channelPtr;
|
|||
|
|
|||
|
#ifdef _TCL76
|
|||
|
(Tcl_GetChannelType(channelPtr)->watchChannelProc)
|
|||
|
(Tcl_GetChannelInstanceData(channelPtr), mask);
|
|||
|
#endif
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*
|
|||
|
* CRPPlugFChannel --
|
|||
|
*
|
|||
|
* This is the "channel ready" procedure for PIF channels. 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
|
|||
|
CRPPlugFChannel (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 whose occurence has to
|
|||
|
* be signalled.
|
|||
|
*/
|
|||
|
{
|
|||
|
Tcl_Channel channelPtr = ((PlugFInfo *)instanceData)->channelPtr;
|
|||
|
|
|||
|
#ifdef _TCL76
|
|||
|
return (Tcl_GetChannelType(channelPtr)->channelReadyProc)
|
|||
|
(Tcl_GetChannelInstanceData(channelPtr), mask);
|
|||
|
#else
|
|||
|
return 1; /* to prevent compilation errors - Tcl 8.0 doesn't use
|
|||
|
// this function so this should never be executed */
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|