633 lines
16 KiB
C
633 lines
16 KiB
C
/*
|
||
* generic/dpIdentity.c --
|
||
*
|
||
* This file contains the implementation of the identity filter channels.
|
||
* These are channels that are created by evaluating "dp_connect identity".
|
||
*
|
||
*/
|
||
|
||
|
||
#include <string.h>
|
||
#include <errno.h>
|
||
#include <tcl.h>
|
||
#include <generic/dpInt.h>
|
||
|
||
|
||
/*
|
||
* Prototypes for functions referenced only in this file.
|
||
*/
|
||
|
||
static int CloseIdChannel _ANSI_ARGS_((ClientData instanceData,
|
||
Tcl_Interp *interp));
|
||
|
||
static int InputIdChannel _ANSI_ARGS_((ClientData instanceData,
|
||
char *buf, int bufsize,
|
||
int *errorCodePtr));
|
||
|
||
static int OutputIdChannel _ANSI_ARGS_((ClientData instanceData,
|
||
char *buf, int toWrite,
|
||
int *errorCodePtr));
|
||
|
||
static int SOPIdChannel _ANSI_ARGS_((ClientData instanceData,
|
||
Tcl_Interp *interp,
|
||
char *optionName,
|
||
char *optionValue));
|
||
|
||
#ifdef _TCL76
|
||
static int GOPIdChannel _ANSI_ARGS_((ClientData instanceData,
|
||
char *optionName,
|
||
Tcl_DString *dsPtr));
|
||
#else
|
||
static int GOPIdChannel _ANSI_ARGS_((ClientData instanceData,
|
||
Tcl_Interp *interp,
|
||
char *optionName,
|
||
Tcl_DString *dsPtr));
|
||
#endif
|
||
|
||
#ifndef _TCL76
|
||
static int GFPIdChannel _ANSI_ARGS_((ClientData instanceData,
|
||
int direction,
|
||
FileHandle *handlePtr));
|
||
#else
|
||
static Tcl_File GFPIdChannel _ANSI_ARGS_((ClientData instanceData,
|
||
int direction));
|
||
#endif
|
||
|
||
static int CRPIdChannel _ANSI_ARGS_((ClientData instanceData,
|
||
int mask));
|
||
|
||
static void WCPIdChannel _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 an identity channel. To understand this entry,
|
||
* please refer to the documentation of the Tcl_CreateChannel and its associated
|
||
* functions in the Tcl 7.6 documentation.
|
||
*
|
||
* An identity channel will always be non-blocking.
|
||
* Seek on an identity channel is not allowed.
|
||
*/
|
||
|
||
|
||
Tcl_ChannelType idChannelType = {
|
||
"idfilter",
|
||
NULL, /* blockModeProc */
|
||
CloseIdChannel, /* closeProc */
|
||
InputIdChannel, /* inputProc */
|
||
OutputIdChannel, /* outputProc */
|
||
NULL, /* seekProc */
|
||
SOPIdChannel, /* setOptionProc */
|
||
GOPIdChannel, /* getOptionProc */
|
||
WCPIdChannel, /* watchChannelProc */
|
||
#ifdef _TCL76
|
||
CRPIdChannel, /* channelReadyProc */
|
||
#endif
|
||
GFPIdChannel /* getFileProc */
|
||
};
|
||
|
||
|
||
/*
|
||
* Structure that stores the data needed to manage an identity filter.
|
||
*/
|
||
|
||
typedef struct {
|
||
|
||
/* Pointer to the subordinated channel. */
|
||
Tcl_Channel channelPtr;
|
||
|
||
/* If peek = 0 consume input, otherwise not. */
|
||
int peek;
|
||
}
|
||
IdentityInfo;
|
||
|
||
|
||
|
||
/*
|
||
*-----------------------------------------------------------------------------
|
||
*
|
||
* DpCreateIdChannel --
|
||
*
|
||
* Creates an identity 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
|
||
DpCreateIdChannel (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;
|
||
IdentityInfo *instanceData;
|
||
Tcl_Channel newChannel;
|
||
char chanName [20];
|
||
|
||
|
||
instanceData = (IdentityInfo *)ckalloc(sizeof(IdentityInfo));
|
||
|
||
if(instanceData == NULL) {
|
||
Tcl_AppendResult(interp, "unable to allocate memory for identity ",
|
||
"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 an identity ",
|
||
"channel", NULL);
|
||
goto error1;
|
||
}
|
||
|
||
/* No peek by default. */
|
||
instanceData->peek = 0;
|
||
|
||
|
||
/* Identity filters are both readable and writable. */
|
||
sprintf(chanName, "idfilter%d", openedChannels++);
|
||
newChannel = Tcl_CreateChannel(&idChannelType, chanName,
|
||
(ClientData)instanceData, TCL_READABLE | TCL_WRITABLE);
|
||
if(newChannel == NULL) {
|
||
Tcl_AppendResult(interp, "tcl unable to create identity 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;
|
||
}
|
||
|
||
|
||
|
||
/*
|
||
*-----------------------------------------------------------------------------
|
||
*
|
||
* CloseIdChannel --
|
||
*
|
||
* Closes the given identity 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
|
||
CloseIdChannel (instanceData, interp)
|
||
ClientData instanceData; /* (in) Pointer to IdentityInfo struct. */
|
||
Tcl_Interp *interp; /* Pointer to the tcl interpreter. */
|
||
{
|
||
ckfree((char *)instanceData);
|
||
return 0;
|
||
}
|
||
|
||
|
||
|
||
/*
|
||
*-----------------------------------------------------------------------------
|
||
*
|
||
* InputIdChannel --
|
||
*
|
||
* Reads min(requested data, available data) from the subordinated channel.
|
||
*
|
||
* Results:
|
||
*
|
||
* Number of bytes of data read from the subordinated filter. If an error
|
||
* happened, it returns -1.
|
||
*
|
||
* Side effects:
|
||
*
|
||
* 1. Calls the read procedure of the subordinated channel.
|
||
* 2. Stores a POSIX code at errorBuffer if an error occurs.
|
||
*
|
||
* ----------------------------------------------------------------------------
|
||
*/
|
||
|
||
static int
|
||
InputIdChannel (instanceData, buf, bufsize, errorCodePtr)
|
||
ClientData instanceData; /* (in) Pointer to IdentityInfo struct. */
|
||
char *buf; /* (in/out) Buffer to fill. */
|
||
int bufsize; /* (in) Size of buffer. */
|
||
int *errorCodePtr; /* (out) POSIX error code (if any). */
|
||
{
|
||
int tmp;
|
||
|
||
Tcl_Channel channelPtr = ((IdentityInfo *)instanceData)->channelPtr;
|
||
|
||
tmp = Tcl_Read(channelPtr, buf, bufsize);
|
||
|
||
if(tmp == -1) {
|
||
*errorCodePtr = Tcl_GetErrno();
|
||
}
|
||
|
||
return tmp;
|
||
|
||
}
|
||
|
||
|
||
|
||
/*
|
||
*-----------------------------------------------------------------------------
|
||
*
|
||
* OutputIdChannel --
|
||
*
|
||
* Writes the data to the subordinated channel.
|
||
*
|
||
* Results:
|
||
*
|
||
* Number of writes written, or -1 if an error is signalled from the
|
||
* subordinated filter.
|
||
*
|
||
* Side effects:
|
||
*
|
||
* 1. Calls the write procedure of the subordinated channel.
|
||
* 2. Stores a POSIX code at errorBuffer if an error occurs.
|
||
*
|
||
* ----------------------------------------------------------------------------
|
||
*/
|
||
|
||
static int
|
||
OutputIdChannel (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 */
|
||
{
|
||
int tmp;
|
||
|
||
Tcl_Channel channelPtr = ((IdentityInfo *)instanceData)->channelPtr;
|
||
|
||
tmp = Tcl_Write(channelPtr, buf, toWrite);
|
||
|
||
if(tmp == -1) {
|
||
*errorCodePtr = Tcl_GetErrno();
|
||
}
|
||
|
||
return tmp;
|
||
|
||
}
|
||
|
||
|
||
|
||
/*
|
||
*-----------------------------------------------------------------------------
|
||
*
|
||
* GFPIdChannel --
|
||
*
|
||
* "Get file" function for identity channels. Since there are no files
|
||
* associated with filters, it always returns NULL.
|
||
*
|
||
* Results:
|
||
*
|
||
* Always NULL.
|
||
*
|
||
* Side effects:
|
||
*
|
||
* None.
|
||
*
|
||
* ----------------------------------------------------------------------------
|
||
*/
|
||
|
||
/* ARGSUSED */
|
||
#ifndef _TCL76
|
||
static int
|
||
GFPIdChannel (instanceData, direction, handlePtr)
|
||
ClientData instanceData;
|
||
int direction;
|
||
FileHandle *handlePtr;
|
||
{
|
||
*handlePtr = NULL;
|
||
return TCL_OK;
|
||
}
|
||
#else
|
||
static Tcl_File
|
||
GFPIdChannel (instanceData, direction)
|
||
ClientData instanceData;
|
||
int direction;
|
||
{
|
||
return NULL;
|
||
}
|
||
#endif
|
||
|
||
|
||
|
||
/*
|
||
*-----------------------------------------------------------------------------
|
||
*
|
||
* SOPIdChannel --
|
||
*
|
||
* "Set option" procedure for identity channels.
|
||
*
|
||
* Results:
|
||
*
|
||
* Standard Tcl result.
|
||
*
|
||
* Side effects:
|
||
*
|
||
* Sets the value of the specified option.
|
||
*
|
||
* ----------------------------------------------------------------------------
|
||
*/
|
||
|
||
static int
|
||
SOPIdChannel (instanceData, interp, optionName, optionValue)
|
||
ClientData instanceData; /* (in) Pointer to IdentityInfo struct. */
|
||
Tcl_Interp *interp; /* (in) Pointer to tcl interpreter. */
|
||
char *optionName;
|
||
char *optionValue;
|
||
{
|
||
int option;
|
||
int value;
|
||
|
||
IdentityInfo data;
|
||
memcpy((void *)&data, (void *)instanceData, sizeof(IdentityInfo));
|
||
|
||
/*
|
||
* 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 identity channel ",
|
||
"is opened", NULL);
|
||
return TCL_ERROR;
|
||
|
||
default:
|
||
Tcl_AppendResult (interp, "illegal option \"", optionName, "\" -- ",
|
||
"must be peek, or a standard fconfigure option", NULL);
|
||
return TCL_ERROR;
|
||
}
|
||
return TCL_OK;
|
||
}
|
||
|
||
|
||
|
||
/*
|
||
*--------------------------------------------------------------------------
|
||
*
|
||
* GOPIdChannel --
|
||
*
|
||
* "Get option" function for identity filters.
|
||
*
|
||
* 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
|
||
GOPIdChannel (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;
|
||
int size;
|
||
|
||
/*
|
||
* 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) GOPIdChannel(a, interp, b, c)
|
||
#else
|
||
#define IGO(a, b, c) GOPIdChannel(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);
|
||
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 (((IdentityInfo *)instanceData)->peek) {
|
||
Tcl_DStringAppend (dsPtr, "1", -1);
|
||
} else {
|
||
Tcl_DStringAppend (dsPtr, "0", -1);
|
||
}
|
||
break;
|
||
|
||
case DP_CHANNEL:
|
||
|
||
Tcl_DStringAppend (dsPtr,
|
||
Tcl_GetChannelName(((IdentityInfo *)instanceData)->channelPtr), -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;
|
||
}
|
||
|
||
|
||
|
||
/*
|
||
*-----------------------------------------------------------------------------
|
||
*
|
||
* WCPIdChannel --
|
||
*
|
||
* This is the "watch channel" procedure for identity 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
|
||
WCPIdChannel (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 = ((IdentityInfo *)instanceData)->channelPtr;
|
||
|
||
#ifdef _TCL76
|
||
(Tcl_GetChannelType(channelPtr)->watchChannelProc)
|
||
(Tcl_GetChannelInstanceData(channelPtr), mask);
|
||
#endif
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
|
||
/*
|
||
*-----------------------------------------------------------------------------
|
||
*
|
||
* CRPIdChannel --
|
||
*
|
||
* This is the "channel ready" procedure for identity 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
|
||
CRPIdChannel (instanceData, mask)
|
||
ClientData instanceData; /* (in) Pointer to IdentityInfo 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 = ((IdentityInfo *)instanceData)->channelPtr;
|
||
|
||
#ifdef _TCL76
|
||
return (Tcl_GetChannelType(channelPtr)->channelReadyProc)
|
||
(Tcl_GetChannelInstanceData(channelPtr), mask);
|
||
#else
|
||
return TCL_OK;
|
||
#endif
|
||
}
|
||
|
||
|