archie/tcl-dp/generic/dpPlugF.c

988 lines
27 KiB
C
Raw Permalink Normal View History

2024-05-27 16:13:40 +02:00
/*
* 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
}