Intial commit

This commit is contained in:
Mario Fetka
2024-05-27 16:13:40 +02:00
parent f8dc12b10a
commit d71d446104
2495 changed files with 539746 additions and 0 deletions

101
tcl-dp/generic/dp.h Normal file
View File

@@ -0,0 +1,101 @@
/*
* dp.h --
*
* Declarations for Dp-related things that are visible
* outside of the Dp module itself.
*
* Copyright (c) 1995-1996 Cornell University.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
*/
#ifndef _DP
#define _DP
#define DP_VERSION "4.0"
#define DP_MAJOR_VERSION 4
#define DP_MINOR_VERSION 0
#ifndef _TCL
#include <tcl.h>
#endif
/*
* Procedure types defined by DP:
*/
typedef Tcl_Channel (Dp_ChanCreateProc) _ANSI_ARGS_((Tcl_Interp *interp,
int argc, char ** argv));
/*
* The following structure is used to register channel types which become
* available to various DP commands.
*/
typedef struct Dp_ChannelType {
struct Dp_ChannelType * nextPtr; /* Links to the next channel type
* record in the list of types.
* Must be set to NULL by the caller
* of Dp_RegisterChannelType() */
char * name; /* Name of this type of channel */
Dp_ChanCreateProc *createProc; /* Procedure to create a channel
* of this type */
} Dp_ChannelType;
/*
* Type for plug-in function.
*/
typedef int (Dp_PlugInFilterProc) _ANSI_ARGS_((char *inBuf, int inLength,
char **outBuf, int *outLength,
void **data, Tcl_Interp *interp,
int mode));
/*
* Any new filter that is registered should provide a (ckallock-ed) pointer to
* such a structure, whose "name" and "plugProc" fields must be set.
*/
typedef struct _Dp_PlugInFilter {
struct _Dp_PlugInFilter * nextPtr;
char * name;
Dp_PlugInFilterProc * plugProc;
} Dp_PlugInFilter;
/*
* Modes for the plug-in filter functions.
*/
#define DP_FILTER_NORMAL 0
#define DP_FILTER_FLUSH 1
#define DP_FILTER_CLOSE 3
#define DP_FILTER_SET 4
#define DP_FILTER_GET 5
#define DP_FILTER_EOF 6
/*
* Exported DP functions.
*/
EXTERN char * Dp_ListChannelTypes _ANSI_ARGS_((void));
EXTERN Dp_ChannelType * Dp_GetChannelType _ANSI_ARGS_((
Tcl_Interp * interp, char * name));
EXTERN int Dp_RegisterChannelType _ANSI_ARGS_((
Tcl_Interp * interp, Dp_ChannelType *newTypePtr));
EXTERN int Dp_RegisterPlugInFilter _ANSI_ARGS_((
Tcl_Interp * interp, Dp_PlugInFilter *plugInPtr));
EXTERN Dp_PlugInFilterProc *Dp_GetFilterPtr _ANSI_ARGS_((
Tcl_Interp * interp, char * name));
EXTERN char *Dp_GetFilterName _ANSI_ARGS_((
Dp_PlugInFilterProc *filter));
#endif /* _DP */

515
tcl-dp/generic/dpChan.c Normal file
View File

@@ -0,0 +1,515 @@
/*
* dpChan.c --
*
* Routines for managing install-able channel types.
*
* Copyright (c) 1995-1996 Cornell University.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
*/
#include <tcl.h>
#include <stdlib.h>
#include "generic/dpPort.h"
#include "generic/dpInt.h"
/*
* This variable holds the list of channel types.
*/
static Dp_ChannelType * chanTypeList = (Dp_ChannelType *) NULL;
/*
* All the DP built-in channel types.
*/
static Dp_ChannelType builtInTypes[] = {
{NULL, "ipm", DpOpenIpmChannel},
{NULL, "tcp", DpOpenTcpChannel},
#ifndef _WIN32
{NULL, "email", DpCreateEmailChannel},
#endif
{NULL, "identity", DpCreateIdChannel},
{NULL, "plugfilter", DpCreatePlugFChannel},
{NULL, "udp", DpOpenUdpChannel},
{NULL, "serial", DpOpenSerialChannel},
{NULL, "packoff", DpCreatePOChannel},
/*
* This array must end with the following element.
*/
{NULL, NULL, NULL}
};
/*
*--------------------------------------------------------------
*
* Dp_RegisterChannelType --
*
* Registers a new type of channel that can be used in the DP
* user-level commands. newTypePtr must points to a Dp_ChannelType
* structure in *static memory*, the contents of which must not
* be modified after calling this function.
*
* Results:
* Standard TCL return value. Fails if a channel type with the
* same name has already been registered.
*
* Side effects:
* On success, newTypePtr is inserted to the head of the list
* of channel types. Also, the next pointer of newTypePtr is
* modified.
*
*--------------------------------------------------------------
*/
int
Dp_RegisterChannelType(interp, newTypePtr)
Tcl_Interp * interp; /* Interpreter to report errors. */
Dp_ChannelType * newTypePtr;/* The DP channel type record */
{
Dp_ChannelType * chanTypePtr;
for (chanTypePtr = chanTypeList; chanTypePtr;
chanTypePtr=chanTypePtr->nextPtr) {
if (strcmp(chanTypePtr->name, newTypePtr->name)==0) {
Tcl_AppendResult(interp, "Channel type \"", newTypePtr->name,
"\" already exists", NULL);
return TCL_ERROR;
}
}
newTypePtr->nextPtr = chanTypeList;
chanTypeList = newTypePtr;
return TCL_OK;
}
/*
*--------------------------------------------------------------
*
* Dp_GetChannelType --
*
* Returns a registered channel type.
*
* Results:
* Standard TCL return value.
*
* Side effects:
* None.
*--------------------------------------------------------------
*/
Dp_ChannelType *
Dp_GetChannelType(interp, name)
Tcl_Interp * interp; /* Interpreter to report errors. */
char * name; /* String name of the channel type */
{
Dp_ChannelType * chanTypePtr;
for (chanTypePtr = chanTypeList; chanTypePtr;
chanTypePtr=chanTypePtr->nextPtr) {
if (strcmp(chanTypePtr->name, name)==0) {
return chanTypePtr;
}
}
return NULL;
}
/*
*--------------------------------------------------------------
*
* Dp_ListChannelTypes --
*
* Returns a list of valid channel types. The string is
* dynamically allocated, and must be freed by the caller.
*
* Results:
* Dynamically allocated string with valid channel types.
*
* Side effects:
* Return value must be free'd by caller.
*
*--------------------------------------------------------------
*/
char *
Dp_ListChannelTypes()
{
char *str;
int maxLen, currLen, len;
Dp_ChannelType *chanTypePtr;
maxLen = 1024;
currLen = 0;
str = ckalloc (maxLen);
for (chanTypePtr = chanTypeList; chanTypePtr != NULL;
chanTypePtr = chanTypePtr->nextPtr) {
len = strlen(chanTypePtr->name);
if ((len + currLen + 2) > maxLen) {
char *newStr;
maxLen += max (1024,len+512);
newStr = ckalloc (maxLen);
memcpy (newStr, str, currLen);
ckfree (str);
str = newStr;
}
sprintf (str + currLen, "%s ", chanTypePtr->name);
currLen += len + 1;
}
return str;
}
/*
*--------------------------------------------------------------
*
* DpClose --
*
* Closes a channel. We just eval "close" in case the
* close command has been overloaded a la dp_atclose.
*
* Results:
*
* TCL_OK or TCL_ERROR.
*
* Side effects:
*
* Closes a channel. It is no longer available in this
* interpreter.
*.
*--------------------------------------------------------------
*/
int
DpClose(interp, chan)
Tcl_Interp *interp;
Tcl_Channel chan;
{
char cmd[30];
sprintf(cmd, "close %s", Tcl_GetChannelName(chan));
return Tcl_GlobalEval(interp, cmd);
}
/*
*--------------------------------------------------------------
*
* DpInitChannels --
*
* Registers all the built-in channels supported by DP.
*
* Results:
* Standard TCL return value.
*
* Side effects:
* Built-in channels are registered in the channel type list.
*--------------------------------------------------------------
*/
int
DpInitChannels(interp)
Tcl_Interp * interp; /* Interpreter to report errors. */
{
int i;
for (i=0; ; i++) {
if (builtInTypes[i].name == NULL) {
break;
} else {
if (Dp_RegisterChannelType(interp, &builtInTypes[i]) != TCL_OK) {
return TCL_ERROR;
}
}
}
return TCL_OK;
}
/*
*--------------------------------------------------------------
*
* DpTranslateOption --
*
* This function translates a standard option name (e.g.,
* "sendBuffer") into a key (e.g., DP_SEND_BUFFER_SIZE)
*
* Results:
* value of option (see dpInt.h for valid keys -- search on
* DP_SEND_BUFFER_SIZE) if the option was found, -1 if not.
*
* Side effects:
* None
*
*--------------------------------------------------------------
*/
int
DpTranslateOption (name)
char *name; /* The name to translate */
{
char c;
int len;
c = name[0];
len = strlen(name);
if ((c == 'b') && (strncmp(name, "baudrate", len) == 0)) {
return DP_BAUDRATE;
} else if ((c == 'c') && (strncmp(name, "charsize", len) == 0)) {
return DP_CHARSIZE;
} else if ((c == 'g') && (strncmp(name, "group", len) == 0)) {
return DP_GROUP;
} else if ((c == 'h') && (strncmp(name, "host", len) == 0)) {
return DP_HOST;
} else if ((c == 'k') && (strncmp(name, "keepAlive", len) == 0)) {
return DP_KEEP_ALIVE;
} else if ((c == 'l') && (strncmp(name, "linger", len) == 0)) {
return DP_LINGER;
} else if ((c == 'l') && (strncmp(name, "loopback", len) == 0)) {
return DP_MULTICAST_LOOP;
} else if ((c == 'm') && (strncmp(name, "myport", len) == 0)) {
return DP_MYPORT;
} else if ((c == 'p') && (strncmp(name, "parity", len) == 0)) {
return DP_PARITY;
} else if ((c == 'p') && (strncmp(name, "peek", len) == 0)) {
return DP_PEEK;
} else if ((c == 'p') && (strncmp(name, "port", len) == 0)) {
return DP_PORT;
} else if ((c == 'r') && (strncmp(name, "recvBuffer", len) == 0)) {
return DP_RECV_BUFFER_SIZE;
} else if ((c == 'r') && (strncmp(name, "reuseAddr", len) == 0)) {
return DP_REUSEADDR;
} else if ((c == 's') && (strncmp(name, "sendBuffer", len) == 0)) {
return DP_SEND_BUFFER_SIZE;
} else if ((c == 's') && (strncmp(name, "stopbits", len) == 0)) {
return DP_STOPBITS;
} else if ((c == 'm') && (strncmp(name, "myIpAddr", len) == 0)) {
return DP_MYIPADDR;
} else if ((c == 'd') && (strncmp(name, "destIpAddr", len) == 0)) {
return DP_REMOTEIPADDR;
} else if ((c == 'd') && (strncmp(name, "destport", len) == 0)) {
return DP_REMOTEPORT;
} else if ((c == 'a') && (strncmp(name, "address", len) == 0)) {
return DP_ADDRESS;
} else if ((c == 'i') && (strncmp(name, "identifier", len) == 0)) {
return DP_IDENTIFIER;
} else if ((c == 's') && (strncmp(name, "sequence", len) == 0)) {
return DP_SEQUENCE;
} else if ((c == 'c') && (strncmp(name, "channel", len) == 0)) {
return DP_CHANNEL;
} else if ((c == 'i') && (strncmp(name, "infilter", len) == 0)) {
return DP_INFILTER;
} else if ((c == 'o') && (strncmp(name, "outfilter", len) == 0)) {
return DP_OUTFILTER;
} else if ((c == 'i') && (strncmp(name, "inset", len) == 0)) {
return DP_INSET;
} else if ((c == 'o') && (strncmp(name, "outset", len) == 0))
return DP_OUTSET;
return -1;
}
/*
* This variable holds the list of registered plug-in filters.
*/
static Dp_PlugInFilter *plugInList = (Dp_PlugInFilter *) NULL;
/*
* All the built-in plug-in functions.
*/
static Dp_PlugInFilter builtInPlugs[] = {
{NULL, "identity", Identity},
{NULL, "plug1to2", Plug1to2},
{NULL, "plug2to1", Plug2to1},
{NULL, "xor", Xor},
{NULL, "packon", PackOn},
{NULL, "uuencode", Uuencode},
{NULL, "uudecode", Uudecode},
{NULL, "tclfilter", TclFilter},
{NULL, "hexout", HexOut},
{NULL, "hexin", HexIn},
/*
* This array must end with the following element.
*/
{NULL, NULL, NULL}
};
/*
*-----------------------------------------------------------------------------
*
* Dp_RegisterPlugInFilter --
*
* Registers a new type of filter that can be used in the DP
* user-level commands. newPlugInPtr must point to a Dp_PlugInFilter
* structure in *static memory*, the contents of which must not
* be modified after calling this function.
*
* Results:
*
* Standard TCL return value. Fails if a filter with the
* same name has already been registered.
*
* Side effects:
* On success, newPlugInPtr is inserted to the head of the list
* of filter. Also, the next pointer of newPlugInPtr is
* modified.
*
*--------------------------------------------------------------
*/
int
Dp_RegisterPlugInFilter (interp, newPlugInPtr)
Tcl_Interp * interp; /* (in) Interpreter to report errors to. */
Dp_PlugInFilter * newPlugInPtr; /* (in) Pointer to the filter function. */
{
Dp_PlugInFilter *plugInPtr;
for (plugInPtr = plugInList; plugInPtr;
plugInPtr = plugInPtr->nextPtr) {
if (strcmp(plugInPtr->name, newPlugInPtr->name)==0) {
Tcl_AppendResult(interp, "Plug-in filter \"", newPlugInPtr->name,
"\" already exists", NULL);
return TCL_ERROR;
}
}
newPlugInPtr->nextPtr = plugInList;
plugInList = newPlugInPtr;
return TCL_OK;
}
/*
*-----------------------------------------------------------------------------
*
* Dp_GetFilterPtr --
*
* Returns a pointer to the filter function whose name was given.
*
* Results:
*
* Pointer to the filter function or NULL if the name is not the name of
* a registered filter function.
*
* Side effects:
*
* None.
*
*-----------------------------------------------------------------------------
*/
Dp_PlugInFilterProc *
Dp_GetFilterPtr (interp, name)
Tcl_Interp *interp; /* (in) Interpreter to report errors to. */
char *name; /* (in) Name of the filter function. */
{
Dp_PlugInFilter *plugInPtr;
for (plugInPtr = plugInList; plugInPtr;
plugInPtr = plugInPtr->nextPtr) {
if (strcmp(plugInPtr->name, name) == 0) {
return plugInPtr->plugProc;
}
}
Tcl_AppendResult(interp, "unknown plug-in function \"", name, "\"", NULL);
return NULL;
}
/*
*-----------------------------------------------------------------------------
*
* Dp_GetFilterName --
*
* Returns the name of the given filter function.
*
* Results:
*
* A pointer to the name of the filter function or NULL if the function
* pointer does not appear in the list of registered filter functions.
*
* Side effects:
*
* None.
*
*-----------------------------------------------------------------------------
*/
char *
Dp_GetFilterName (filter)
Dp_PlugInFilterProc *filter; /* (in) Pointer to the filter function. */
{
Dp_PlugInFilter *plugInPtr;
for (plugInPtr = plugInList; plugInPtr;
plugInPtr = plugInPtr->nextPtr) {
if (filter, plugInPtr->plugProc) {
return plugInPtr->name;
}
}
return NULL;
}
/*
*-----------------------------------------------------------------------------
*
* Dp_DpInitPlugIn --
*
* Registers all the built-in channels supported by DP.
*
* Results:
*
* Standard TCL return value.
*
* Side effects:
*
* Built-in filter functions are registered in the plug-in filter list.
*
*-----------------------------------------------------------------------------
*/
int
DpInitPlugIn(interp)
Tcl_Interp * interp; /* (in) Interpreter to report errors to. */
{
int i;
for (i=0; ; i++) {
if (builtInPlugs[i].name == NULL) {
break;
} else {
if (Dp_RegisterPlugInFilter(interp, &builtInPlugs[i]) != TCL_OK) {
return TCL_ERROR;
}
}
}
return TCL_OK;
}

527
tcl-dp/generic/dpCmds.c Normal file
View File

@@ -0,0 +1,527 @@
/*
* dpCmd.c --
*
* This file contains the command routines for most of
* the DP built-in commands.
*
* Copyright (c) 1995-1996 The Regents of Cornell University.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
*/
#include "generic/dpInt.h"
#include <tclInt.h>
/*
* The default number of bytes for "dp_copy" to read in each call
* to Tcl_Read().
*/
#define TCL_READ_CHUNK_SIZE 4096
/*
*----------------------------------------------------------------------
*
* Dp_AcceptCmd --
*
* This procedure is invoked to process the "dp_accept" Tcl command.
* See the user documentation for details on what it does.
*
* Results:
* A standard Tcl result.
*
* Side effects:
* See the user documentation.
*
*----------------------------------------------------------------------
*/
/* ARGSUSED */
int
Dp_AcceptCmd(dummy, interp, argc, argv)
ClientData dummy; /* Not used. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
char **argv; /* Argument strings. */
{
Tcl_Channel chan;
if (argc != 2) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " channelId\"", NULL);
return TCL_ERROR;
}
if ((chan = Dp_TcpAccept(interp, argv[1])) == NULL) {
return TCL_ERROR;
}
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* Dp_ConnectCmd --
*
* This procedure is invoked to process the "dp_connect" Tcl command.
* See the user documentation for details on what it does.
*
* Results:
* A standard Tcl result.
*
* Side effects:
* See the user documentation.
*
*----------------------------------------------------------------------
*/
/* ARGSUSED */
int
Dp_ConnectCmd(dummy, interp, argc, argv)
ClientData dummy; /* Not used. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
char **argv; /* Argument strings. */
{
Dp_ChannelType * chanTypePtr;
Tcl_Channel chan;
char *validTypes;
if (argc < 2) {
validTypes = Dp_ListChannelTypes();
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" channelType ?args ...?\"\nValid channel types are: ", validTypes, NULL);
ckfree (validTypes);
return TCL_ERROR;
}
chanTypePtr = Dp_GetChannelType(interp, argv[1]);
if (chanTypePtr == NULL) {
validTypes = Dp_ListChannelTypes();
Tcl_AppendResult(interp, "Unknown channel type \"", argv[1],
"\"\nValid channel types are: ", validTypes, NULL);
ckfree (validTypes);
return TCL_ERROR;
}
chan = chanTypePtr->createProc(interp, argc-2, argv+2);
if (chan == NULL) {
return TCL_ERROR;
} else {
Tcl_AppendResult(interp, Tcl_GetChannelName(chan), (char *) NULL);
return TCL_OK;
}
}
/*
*----------------------------------------------------------------------
*
* Dp_CopyCmd --
*
* This procedure is invoked to process the "dp_copy" Tcl command.
* See the user documentation for details on what it does.
*
* Results:
* A standard Tcl result.
*
* Side effects:
* See the user documentation.
*
*----------------------------------------------------------------------
*/
/* ARGSUSED */
int
Dp_CopyCmd(dummy, interp, argc, argv)
ClientData dummy; /* Not used. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
char **argv; /* Argument strings. */
{
Tcl_Channel inChan;
Tcl_Channel *outChans = NULL;
int numOutChans;
int i, m;
int requested = INT_MAX;
char *bufPtr = NULL;
int actuallyRead, actuallyWritten, totalRead, toReadNow, mode;
/*
* 1. Get the optional -size argument.
*/
m = 1;
if (argc > 2) {
if (argv[1][0] == '-' && strcmp(argv[1], "-size")==0) {
if (argc == 2) {
Tcl_AppendResult(interp, "value missing for \"-size\"", NULL);
goto error;
}
if (Tcl_GetInt(interp, argv[2], &requested) != TCL_OK) {
goto error;
}
if (requested < 0) {
requested = INT_MAX;
}
/*
* argv[m] is the in channel and argv[m+1, ...] are the out
* channels.
*/
m = 3;
}
}
if (argc-m < 2) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " ?-size size? inChanId outChanId ?outChanId ...?\"",
(char *) NULL);
goto error;
}
/*
* 2. Get the in channel.
*/
inChan = Tcl_GetChannel(interp, argv[m], &mode);
if (inChan == (Tcl_Channel) NULL) {
goto error;
}
if ((mode & TCL_READABLE) == 0) {
Tcl_AppendResult(interp, "channel \"", argv[m],
"\" wasn't opened for reading", (char *) NULL);
goto error;
}
/*
* 3. Get the out channel(s).
*/
numOutChans = argc - m - 1;
outChans = (Tcl_Channel*)ckalloc(sizeof(Tcl_Channel) * numOutChans);
for (i=0; i<numOutChans; i++) {
outChans[i] = Tcl_GetChannel(interp, argv[i + m+1], &mode);
if (outChans[i] == (Tcl_Channel) NULL) {
goto error;
}
if ((mode & TCL_WRITABLE) == 0) {
Tcl_AppendResult(interp, "channel \"", argv[i + m+1],
"\" wasn't opened for writing", (char *) NULL);
goto error;
}
}
/*
* 4. Copy the data.
*/
bufPtr = ckalloc((unsigned) TCL_READ_CHUNK_SIZE);
for (totalRead = 0;
requested > 0;
totalRead += actuallyRead, requested -= actuallyRead) {
toReadNow = requested;
if (toReadNow > TCL_READ_CHUNK_SIZE) {
toReadNow = TCL_READ_CHUNK_SIZE;
}
actuallyRead = Tcl_Read(inChan, bufPtr, toReadNow);
if (actuallyRead < 0) {
Tcl_AppendResult(interp, argv[0], ": ", Tcl_GetChannelName(inChan),
" ", Tcl_PosixError(interp), (char *) NULL);
goto error;
}
if (actuallyRead == 0) {
sprintf(interp->result, "%d", totalRead);
goto done;
}
for (i=0; i<numOutChans; i++) {
actuallyWritten = Tcl_Write(outChans[i], bufPtr, actuallyRead);
if (actuallyWritten < 0) {
Tcl_AppendResult(interp, argv[0], ": ",
Tcl_GetChannelName(outChans[i]), " ",
Tcl_PosixError(interp), (char *) NULL);
goto error;
}
}
}
done:
if (bufPtr != NULL) {
ckfree(bufPtr);
}
if (outChans != NULL) {
ckfree((char*)outChans);
}
sprintf(interp->result, "%d", totalRead);
return TCL_OK;
error:
if (bufPtr != NULL) {
ckfree(bufPtr);
}
if (outChans != NULL) {
ckfree((char*)outChans);
}
return TCL_ERROR;
}
/*
*----------------------------------------------------------------------
*
* Dp_NetInfoCmd --
*
* This procedure is invoked to process the "dp_netinfo" Tcl command.
* See the user documentation for details on what it does.
*
* Results:
* A standard Tcl result.
*
* Side effects:
* See the user documentation.
*
*----------------------------------------------------------------------
*/
/* ARGSUSED */
int
Dp_NetInfoCmd(dummy, interp, argc, argv)
ClientData dummy; /* Not used. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
char **argv; /* Argument strings. */
{
if (argc != 3) {
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" option arg\"", NULL);
return TCL_ERROR;
}
if (strlen(argv[1]) > 1) {
char c = argv[1][1];
switch (c) {
case 's': {
/*
* Get the service entry for service name or port number
*/
if (strcmp(argv[1], "-service") == 0) {
struct servent *serviceEntry = NULL;
char port[10];
int portNum;
/*
* First try argv[2] as a name
*/
serviceEntry = getservbyname(argv[2], (char *) NULL);
if (serviceEntry == NULL) {
/*
* Now try argv[2] as a port number
*/
portNum = atoi(argv[2]);
serviceEntry = getservbyport(htons((unsigned short)portNum),
(char *) NULL);
if (serviceEntry == NULL) {
Tcl_AppendResult(interp, argv[0],
" -service unknown service/port# \"",
argv[2], "\"", (char *) NULL);
return TCL_ERROR;
}
}
sprintf(port, "%4d", ntohs(serviceEntry->s_port));
Tcl_AppendResult(interp, serviceEntry->s_name, " ", port, " ",
(char *) NULL);
return TCL_OK;
}
}
break;
case 'a': {
/*
* Translate between host name and IP address.
*/
if (strcmp(argv[1], "-address") == 0) {
char hostName[120];
char addrStr[16];
int addr;
/*
* try argv[2] as an IP address first
*/
if ((addr = inet_addr(argv[2])) != -1) {
if (DpIpAddrToHost(addr, hostName)) {
Tcl_AppendResult(interp, hostName, (char *) NULL);
return TCL_OK;
} else {
Tcl_AppendResult(interp, argv[0],
" -address unknown host \"",
argv[2], "\"", (char *) NULL);
return TCL_ERROR;
}
} else {
if (DpHostToIpAddr(argv[2], &addr)) {
sprintf(addrStr, "%d.%d.%d.%d", (addr>>24)&0xFF,
(addr>>16)&0xFF, (addr>>8)&0xFF, addr&0xFF);
Tcl_AppendResult(interp, addrStr, (char *) NULL);
return TCL_OK;
} else {
Tcl_AppendResult(interp, argv[0],
" -address unknown host \"",
argv[2], "\"", (char *) NULL);
return TCL_ERROR;
}
}
}
}
break;
}
}
Tcl_AppendResult(interp, argv[0], ": unknown option \"",
argv[1], "\"", (char *) NULL);
return TCL_ERROR;
}
/* ----------------------------------------------------
*
* Dp_SendCmd --
*
* Implements a send-like command for channels.
* The Tcl I/O system has serious problems because
* it does internal buffering. One can use this
* command to bypass the Tcl I/O subsystem.
*
* Returns
*
* TCL_OK with the amount sent or TCL_ERROR.
*
* Side Effects
*
* The channel is written to.
*
* -----------------------------------------------------
*/
int
Dp_SendCmd(dummy, interp, argc, argv)
ClientData dummy; /* Not used. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
char **argv; /* Argument strings. */
{
Tcl_Channel chan;
char writ[10];
int errorCode = 0, toWrite, written = 0, mode;
if (argc != 3) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " channelId string\"", NULL);
return TCL_ERROR;
}
if ((chan = Tcl_GetChannel(interp, argv[1], &mode)) == NULL) {
return TCL_ERROR;
}
toWrite = strlen(argv[2]);
while (toWrite > written) {
written += (Tcl_GetChannelType(chan)->outputProc)
(Tcl_GetChannelInstanceData(chan), argv[2] + written,
toWrite - written, &errorCode);
if (errorCode > 0) {
break;
}
}
if (errorCode > 0) {
/*
* Translate error code to POSIX
*/
DppGetErrno();
Tcl_AppendResult(interp, "Error sending on channel \"", argv[1], "\":",
Tcl_PosixError(interp), (char *)NULL);
return TCL_ERROR;
}
sprintf(writ, "%d", written);
Tcl_AppendResult(interp, writ, (char *)NULL);
return TCL_OK;
}
/* ----------------------------------------------------
*
* Dp_RecvCmd --
*
* Implements a recv-like command for channels.
* The Tcl I/O system has serious problems because
* it does internal buffering. One can use this
* command to bypass the Tcl I/O subsystem.
*
* Returns
*
* TCL_OK with the message recv'd or TCL_ERROR.
*
* Side Effects
*
* The channel is read.
*
* -----------------------------------------------------
*/
int
Dp_RecvCmd(dummy, interp, argc, argv)
ClientData dummy; /* Not used. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
char **argv; /* Argument strings. */
{
Tcl_Channel chan;
Tcl_DString dstr;
int errorCode = 0, nread = 0, mode;
char buff[4096];
int blocking;
if (argc != 2) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " channelId\"", NULL);
return TCL_ERROR;
}
if ((chan = Tcl_GetChannel(interp, argv[1], &mode)) == NULL) {
return TCL_ERROR;
}
memset(buff, 0, sizeof(buff));
nread = (Tcl_GetChannelType(chan)->inputProc) (Tcl_GetChannelInstanceData(chan),
(char *)buff, sizeof(buff)-1, &errorCode);
if (nread == DP_SOCKET_ERROR) {
Tcl_DStringInit(&dstr);
Tcl_GetChannelOption(
#ifdef _TCL80
interp,
#endif
chan, "-blocking", &dstr);
Tcl_GetBoolean(interp, Tcl_DStringValue(&dstr), &blocking);
Tcl_DStringFree(&dstr);
if ((blocking == 0) && (DppGetErrno() == EWOULDBLOCK)) {
/*
* If the channel is non-blocking and we returned because
* there was nothing to read, then there is no error.
*/
} else {
/*
* The error code has already been translated by the above call
* to DppGetErrno().
*/
Tcl_AppendResult(interp, "Error receiving on channel \"", argv[1],
"\":", Tcl_PosixError(interp), (char *)NULL);
return TCL_ERROR;
}
}
Tcl_AppendResult(interp, buff, (char *)NULL);
return TCL_OK;
}

1620
tcl-dp/generic/dpFilters.c Normal file

File diff suppressed because it is too large Load Diff

1043
tcl-dp/generic/dpIPM.c Normal file

File diff suppressed because it is too large Load Diff

632
tcl-dp/generic/dpIdentity.c Normal file
View File

@@ -0,0 +1,632 @@
/*
* 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
}

99
tcl-dp/generic/dpInit.c Normal file
View File

@@ -0,0 +1,99 @@
/*
* dpInit.c --
*
* Initialize the Tcl-DP extension.
*
* Copyright (c) 1995-1996 Cornell University.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
*/
#include "generic/dpPort.h"
#include "generic/dpPatch.h"
#include "generic/dpInt.h"
/*
* The following structure defines all of the commands supported by
* Dp, and the C procedures that execute them.
*/
typedef struct {
char *name; /* Name of command. */
Tcl_CmdProc *cmdProc; /* Command procedure. */
} DpCmd;
static DpCmd commands[] = {
{"dp_accept", Dp_AcceptCmd},
{"dp_connect", Dp_ConnectCmd},
{"dp_copy", Dp_CopyCmd},
{"dp_netinfo", Dp_NetInfoCmd},
{"dp_RDO", Dp_RDOCmd},
{"dp_RPC", Dp_RPCCmd},
{"dp_admin", Dp_AdminCmd},
{"dp_CancelRPC", Dp_CancelRPCCmd},
{"dp_send", Dp_SendCmd},
{"dp_recv", Dp_RecvCmd},
{(char *) NULL, (Tcl_CmdProc *) NULL}
};
/*
*----------------------------------------------------------------------
*
* Dp_Init --
*
* This procedure is invoked to add DP to an interpreter. It
* incorporates all of DP's commands into the interpreter.
*
* Results:
* Returns a standard Tcl completion code and sets interp->result
* if there is an error.
*
* Side effects:
* Depends on various initialization scripts that get invoked.
*
*----------------------------------------------------------------------
*/
EXPORT(int,Dp_Init)(interp)
Tcl_Interp *interp; /* (in) Interpreter to initialize. */
{
DpCmd *cmdPtr;
if (Tcl_PkgRequire(interp, "Tcl", TCL_VERSION, 1) == NULL) {
return TCL_ERROR;
}
if (TclHasSockets(interp) != TCL_OK) {
return TCL_ERROR;
}
Tcl_SetVar(interp, "dp_patchLevel", DP_PATCH_LEVEL, TCL_GLOBAL_ONLY);
Tcl_SetVar(interp, "dp_version", DP_VERSION, TCL_GLOBAL_ONLY);
if (Tcl_PkgProvide(interp, "dp", DP_VERSION) != TCL_OK) {
return TCL_ERROR;
}
for (cmdPtr = commands; cmdPtr->name != NULL; cmdPtr++) {
Tcl_CreateCommand(interp, cmdPtr->name, cmdPtr->cmdProc,
(ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
}
if (DpInitChannels(interp) != TCL_OK) {
return TCL_ERROR;
}
if (DppInit(interp) != TCL_OK) {
return TCL_ERROR;
}
if (DpRPCInit(interp) != TCL_OK) {
return TCL_ERROR;
}
if (DpInitPlugIn(interp) != TCL_OK) {
return TCL_ERROR;
}
return TCL_OK;
}

248
tcl-dp/generic/dpInt.h Normal file
View File

@@ -0,0 +1,248 @@
/*
* generic/dpInt.h --
*
* Declarations for things used internally by Dp
* procedures but not exported outside the module.
*
* Copyright (c) 1995-1996 Cornell University.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
*/
#ifndef _DPINT
#define _DPINT
#ifdef SOCKDEBUG
#define SDBG(a) printf("0x%X - ", getpid()); printf a
#else
#define SDBG(a)
#endif
#ifndef _DP
#include "dp.h"
#endif
#ifndef _TCL
#include "tcl.h"
#endif
#ifndef _DPPORT
#include "generic/dpPort.h"
#endif
#ifndef max
# define max(a, b) (((a) > (b)) ? (a) : (b))
#endif
/*
*----------------------------------------------------------------------
* Constant definitions
*----------------------------------------------------------------------
*/
#define SOCKET_IPM (1<<31)
/*
* The following are used by the various SetSocketOption and
* GetSocketOption functions
*/
/*
* UDP/TCP/Generic
*/
#define DP_SEND_BUFFER_SIZE 1
#define DP_RECV_BUFFER_SIZE 2
#define DP_BLOCK 3
#define DP_REUSEADDR 4
#define DP_PEEK 6
#define DP_HOST 7
#define DP_PORT 8
#define DP_MYPORT 9
#define DP_KEEP_ALIVE 10
#define DP_LINGER 11
#define DP_REMOTEPORT 12
#define DP_MYIPADDR 13
#define DP_REMOTEIPADDR 14
#define DP_GROUP 20
#define DP_MULTICAST_TTL 21
#define DP_MULTICAST_LOOP 22
#define DP_ADD_MEMBERSHIP 23
#define DP_DROP_MEMBERSHIP 24
#define DP_BROADCAST 25
/*
* Serial port options
*/
#define DP_STOPBITS 101
#define DP_CHARSIZE 102
#define DP_BAUDRATE 103
#define DP_PARITY 104
#define DP_DEVICENAME 105
/*
* Email channel
*/
#define DP_ADDRESS 205
#define DP_IDENTIFIER 206
#define DP_SEQUENCE 207
/*
* Identity filter and other filters
*/
#define DP_CHANNEL 210
#define DP_INFILTER 211
#define DP_OUTFILTER 212
#define DP_INSET 213
#define DP_OUTSET 214
#ifndef _TCL80
typedef Tcl_File FileHandle;
#endif
#if !defined(_TCL80) || !defined(_WIN32)
typedef struct SocketInfo {int dummy;} SocketInfo;
#endif
/*
* A collection of all the data necessary for
* all the different types of sockets. We buy
* reusibility at the cost of size.
*/
typedef struct SocketState {
Tcl_Interp * interp;
DpSocket sock;
FileHandle sockFile;
Tcl_Channel channel;
Tcl_DString groupList; /* IPM */
DpSocketAddressIP sockaddr; /* UDP/IPM */
SocketInfo * sockInfo;
int flags;
int myIpAddr;
int myPort;
int recvBufSize; /* UDP/IPM */
int groupAddr; /* IPM */
int groupPort; /* IPM */
int destIpAddr; /* TCP */
int destPort; /* TCP */
} SocketState;
typedef struct SerialState {
SerialHandle fd;
FileHandle theFile;
char deviceName[20];
Tcl_Channel channel;
} SerialState;
/*
*----------------------------------------------------------------------
* Internal procedures shared among DP modules but not exported
* to the outside world:
*----------------------------------------------------------------------
*/
/*
* Library routines that aren't part of any particular protocol (or support
* multiple protocols)
*/
EXTERN int DpTranslateOption _ANSI_ARGS_((char *optionName));
EXTERN int DpHostToIpAddr _ANSI_ARGS_((char *hostname,
int *ipAddrPtr));
EXTERN int DpIpAddrToHost _ANSI_ARGS_((int ipAddr,
char *hostPtr));
EXTERN int DppCloseSocket _ANSI_ARGS_((DpSocket sock));
EXTERN int DppSetBlock _ANSI_ARGS_((DpSocket sock, int block));
EXTERN int DppGetErrno _ANSI_ARGS_(());
EXTERN int DppInit _ANSI_ARGS_((Tcl_Interp *interp));
/*
*----------------------------------------------------------------------
* Externally visible procedures that form the C API:
*----------------------------------------------------------------------
*/
EXTERN int DpInitChannels _ANSI_ARGS_((Tcl_Interp * interp));
EXTERN int DpInitPlugIn _ANSI_ARGS_((Tcl_Interp * interp));
EXTERN int DpRPCInit _ANSI_ARGS_((Tcl_Interp * interp));
EXTERN Tcl_Channel DpCreateEmailChannel _ANSI_ARGS_((Tcl_Interp *interp,
int argc, char **argv));
EXTERN Tcl_Channel DpCreateIdChannel _ANSI_ARGS_((Tcl_Interp *interp,
int argc, char **argv));
EXTERN Tcl_Channel DpCreatePlugFChannel _ANSI_ARGS_((Tcl_Interp *interp,
int argc, char **argv));
EXTERN Tcl_Channel DpCreatePOChannel _ANSI_ARGS_((Tcl_Interp *interp,
int argc, char **argv));
EXTERN Tcl_Channel DpOpenUdpChannel _ANSI_ARGS_((Tcl_Interp *interp,
int argc, char **argv));
EXTERN Tcl_Channel DpOpenSerialChannel _ANSI_ARGS_((Tcl_Interp *interp,
int argc, char **argv));
EXTERN Tcl_Channel DpOpenIpmChannel _ANSI_ARGS_((Tcl_Interp *interp,
int argc, char **argv));
EXTERN Tcl_Channel DpOpenTcpChannel _ANSI_ARGS_((Tcl_Interp *interp,
int argc, char **argv));
EXTERN Tcl_Channel Dp_TcpAccept _ANSI_ARGS_((Tcl_Interp *interp,
char *channelId));
/*
*----------------------------------------------------------------------
* Command procedures in the generic core:
*----------------------------------------------------------------------
*/
EXTERN int Dp_AcceptCmd _ANSI_ARGS_((ClientData clientData,
Tcl_Interp *interp, int argc, char **argv));
EXTERN int Dp_ConnectCmd _ANSI_ARGS_((ClientData clientData,
Tcl_Interp *interp, int argc, char **argv));
EXTERN int Dp_CopyCmd _ANSI_ARGS_((ClientData clientData,
Tcl_Interp *interp, int argc, char **argv));
EXTERN int Dp_FromCmd _ANSI_ARGS_((ClientData clientData,
Tcl_Interp *interp, int argc, char **argv));
EXTERN int Dp_NetInfoCmd _ANSI_ARGS_((ClientData clientData,
Tcl_Interp *interp, int argc, char **argv));
EXTERN int Dp_RDOCmd _ANSI_ARGS_((ClientData clientData,
Tcl_Interp *interp, int argc, char **argv));
EXTERN int Dp_RPCCmd _ANSI_ARGS_((ClientData clientData,
Tcl_Interp *interp, int argc, char **argv));
EXTERN int Dp_AdminCmd _ANSI_ARGS_((ClientData clientData,
Tcl_Interp *interp, int argc, char **argv));
EXTERN int Dp_CancelRPCCmd _ANSI_ARGS_((ClientData clientData,
Tcl_Interp *interp, int argc, char **argv));
EXTERN int Dp_SendCmd _ANSI_ARGS_((ClientData clientData,
Tcl_Interp *interp, int argc, char **argv));
EXTERN int Dp_RecvCmd _ANSI_ARGS_((ClientData clientData,
Tcl_Interp *interp, int argc, char **argv));
/*
* Plug-in filters.
*/
extern Dp_PlugInFilterProc Identity;
extern Dp_PlugInFilterProc Plug1to2;
extern Dp_PlugInFilterProc Plug2to1;
extern Dp_PlugInFilterProc Xor;
extern Dp_PlugInFilterProc PackOn;
extern Dp_PlugInFilterProc Uuencode;
extern Dp_PlugInFilterProc Uudecode;
extern Dp_PlugInFilterProc TclFilter;
extern Dp_PlugInFilterProc HexOut;
extern Dp_PlugInFilterProc HexIn;
/*
* Locking functions used in implementing email channels.
*/
extern int PutLock _ANSI_ARGS_((char *lockFilePath));
extern int RemoveLock _ANSI_ARGS_((char *lockFilePath));
#endif /* _DPINT */

686
tcl-dp/generic/dpPackOff.c Normal file
View File

@@ -0,0 +1,686 @@
/*
* 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++);
}

31
tcl-dp/generic/dpPatch.h Normal file
View File

@@ -0,0 +1,31 @@
/*
* dpPatch.h --
*
* This file does nothing except define a "patch level" for DP.
* The patch level has the form "X.YpZ" where X.Y is the base
* release, and Z is a serial number that is used to sequence
* patches for a given release. Thus 4.0p1 is the first patch to
* release 4.0, 4.0p2 is the patch that follows 4.0p1, and so on.
* The "pZ" is omitted in an original new release, and it is
* replaced with "bZ" for beta releases or "aZ" for alpha
* releases (e.g. 4.0b1 is the first beta release of Tk 4.0).
* The patch level ensures that patches are applied in the
* correct order and only to appropriate sources.
*
* Copyright (c) 1995-1996 Cornell University.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
*/
#ifndef _DPPATCH
#define DP_PATCH_LEVEL "4.0"
#endif

987
tcl-dp/generic/dpPlugF.c Normal file
View File

@@ -0,0 +1,987 @@
/*
* 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
}

56
tcl-dp/generic/dpPort.h Normal file
View File

@@ -0,0 +1,56 @@
/*
* dpPort.h --
*
* This header file handles porting issues that occur because of
* differences between systems. It reads in platform specific
* portability files.
*
* Copyright (c) 1995-1996 Cornell University.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
*/
#ifndef _DPPORT
#define _DPPORT
#ifndef _TCL
# include "tcl.h"
#endif
#ifndef _TCLPORT
# ifdef __SUNOS__
# define NO_FLOAT_H
# endif
#include "tclPort.h"
#endif
#ifndef _DP
# include "dp.h"
#endif
#if defined(__WIN32__) || defined(_WIN32)
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
# undef WIN32_LEAN_AND_MEAN
# include "win/dpPort.h"
#else
# define EXPORT(a,b) a b
# if defined(MAC_TCL)
/*
* (ToDo): there is currently no Mac Port of DP.
*/
# include "mac/dpPort.h"
# else
# include "unix/dpPort.h"
# endif
#endif
#endif /* _DPPORT */

1588
tcl-dp/generic/dpRPC.c Normal file

File diff suppressed because it is too large Load Diff

578
tcl-dp/generic/dpSerial.c Normal file
View File

@@ -0,0 +1,578 @@
/*
* This file holds the functions necessary for the generic layer
* of the DP serial channel driver. Platform-specific functions
* in %OS%/dpSerial.c do most of the work since there really is no
* standard serial port API.
*
* DP knows a serial port by two names: the OS specific name (i.e
* COM1 or /dev/ttya) and DP's own naming scheme that can be used
* on every platform for portable Tcl code: serialX where X is a
* number. Please see %OS%/dpSerial.c for more details.
*
* On Win32, serial2 cooresponds to COM2. On Unix, serial2
* cooresponds to the second serial port.
*
* YOU CAN ONLY USE ONE NAME TO OPEN A CONNECTION WITH DP_CONNECT:
* dp_connect serial -device serialX
*
*/
#include "generic/dpPort.h"
#include "generic/dpInt.h"
static unsigned int serialCount = 0;
static int SerialBlock _ANSI_ARGS_((ClientData instanceData,
int mode));
static int SerialInput _ANSI_ARGS_((ClientData instanceData,
char *bufPtr, int bufSize, int *errorPtr));
static int SerialOutput _ANSI_ARGS_((ClientData instanceData,
char *bufPtr, int toWrite, int *errorPtr));
static int SerialClose _ANSI_ARGS_((ClientData instanceData,
Tcl_Interp *interp));
static int SerialSetOption _ANSI_ARGS_((ClientData instanceData,
Tcl_Interp *interp, char *nameStr, char *val));
#ifndef _TCL76
static int SerialGetOption _ANSI_ARGS_((ClientData instanceData,
Tcl_Interp *interp, char *optionName,
Tcl_DString *dsPtr));
#else
static int SerialGetOption _ANSI_ARGS_((ClientData instanceData,
char *optionName, Tcl_DString *dsPtr));
#endif
#ifndef _TCL76
static int SerialGetFile _ANSI_ARGS_((ClientData instanceData,
int direction, FileHandle *handlePtr));
#else
static Tcl_File SerialGetFile _ANSI_ARGS_((ClientData instanceData,
int direction));
#endif
#ifdef _TCL76
static int SerialReady _ANSI_ARGS_((ClientData instanceData,
int direction));
#endif
static void SerialWatch _ANSI_ARGS_((ClientData instanceData,
int mask));
static Tcl_ChannelType serialChannelType = {
"serial",
SerialBlock,
SerialClose,
SerialInput,
SerialOutput,
NULL, /* Can't seek! */
SerialSetOption,
SerialGetOption,
SerialWatch,
#ifdef _TCL76
SerialReady,
#endif
SerialGetFile
};
/* --------------------------------------------------
*
* DpOpenSerialChannel --
*
* Generic routine to open a serial channel
*
* Returns
*
* A Tcl_Channel.
*
* Side Effects
*
* Opens the serial port and allocates memory.
*
* ---------------------------------------------------
*/
Tcl_Channel
DpOpenSerialChannel(interp, argc, argv)
Tcl_Interp *interp;
int argc;
char **argv;
{
Tcl_Channel chan;
SerialState *ssPtr;
char devStr[15];
char channelNameStr[10];
int block, ro;
int i, flags, mode;
block = 0;
ro = 0;
flags = 0;
strcpy(devStr, "");
for (i=0; i<argc; i+=2) {
int len = strlen(argv[i]);
if (strncmp(argv[i], "-device", len) == 0) {
if (i+1 == argc) goto arg_missing;
strcpy(devStr, argv[i+1]);
} else if (strncmp(argv[i], "-block", len) == 0) {
if (i+1 == argc) goto arg_missing;
if (Tcl_GetBoolean(interp, argv[i+1], &block) == TCL_ERROR) {
return NULL;
}
} else if (strncmp(argv[i], "-readonly", len) == 0) {
if (i+1 == argc) goto arg_missing;
if (Tcl_GetBoolean(interp, argv[i+1], &ro) == TCL_ERROR) {
return NULL;
}
} else {
Tcl_AppendResult(interp, "Unknown option \"", argv[i],
"\", must be -device, -block, or -readonly", NULL);
return NULL;
}
}
/*
* Combine the two flags into a bitmask
*/
if (block) {
flags = 1;
}
mode = TCL_READABLE | TCL_WRITABLE;
if (ro) {
flags |= 2;
mode &= ~(TCL_WRITABLE);
}
ssPtr = (SerialState *) ckalloc(sizeof(SerialState));
if (DppOpenSerialChannel(interp, (ClientData)ssPtr, devStr, flags)
!= TCL_OK) {
ckfree((char *)ssPtr);
return NULL;
}
#ifdef _TCL76
ssPtr->theFile = Tcl_GetFile((ClientData)ssPtr->fd, SERIAL_HANDLE);
#else
ssPtr->theFile = (FileHandle)ssPtr->fd;
#endif
/*
* Setup the serial channel to flush every line
*/
sprintf(channelNameStr, "serial%d", serialCount++);
chan = Tcl_CreateChannel(&serialChannelType, channelNameStr,
(ClientData) ssPtr, mode);
Tcl_RegisterChannel(interp, chan);
if (Tcl_SetChannelOption(interp, chan, "-buffering", "line")
!= TCL_OK) {
DpClose(interp, chan);
return NULL;
}
ssPtr->channel = chan;
return chan;
arg_missing:
Tcl_AppendResult(interp, "Value for \"", argv[i],
"\" missing", NULL);
return NULL;
}
/* --------------------------------------------------
*
* SerialBlock --
*
* Generic routine to set I/O to blocking or
* non-blocking.
*
* Returns
*
* TCL_OK or TCL_ERROR.
*
* Side Effects
*
* None.
*
* ---------------------------------------------------
*/
static int
SerialBlock(instanceData, mode)
ClientData instanceData;
int mode; /* (in) Block or not */
{
return DppSerialBlock(instanceData, mode);
}
/* --------------------------------------------------
*
* SerialInput --
*
* Generic read routine for serial ports
*
* Returns
*
* Amount read or -1 with errorcode in errorPtr.
*
* Side Effects
*
* Buffer is updated.
*
* ---------------------------------------------------
*/
static int
SerialInput(instanceData, bufPtr, bufSize, errorPtr)
ClientData instanceData;
char *bufPtr; /* (in) Ptr to buffer */
int bufSize; /* (in) sizeof buffer */
int *errorPtr; /* (out) error code */
{
return DppSerialInput(instanceData, bufPtr, bufSize, errorPtr);
}
/* --------------------------------------------------
*
* SerialOutput --
*
* Generic write routine for serial ports
*
* Returns
*
* Amount written or -1 with errorcode in errorPtr
*
* Side Effects
*
* None.
*
* ---------------------------------------------------
*/
static int
SerialOutput(instanceData, bufPtr, toWrite, errorPtr)
ClientData instanceData;
char *bufPtr; /* (in) Ptr to buffer */
int toWrite; /* (in) amount to write */
int *errorPtr; /* (out) error code */
{
return DppSerialOutput(instanceData, bufPtr, toWrite, errorPtr);
}
/* --------------------------------------------------
*
* SerialClose --
*
* Generic routine to close the serial port
*
* Returns
*
* 0 if successful or a POSIX errorcode with
* interp updated.
*
* Side Effects
*
* Channel is deleted.
*
* ---------------------------------------------------
*/
static int
SerialClose(instanceData, interp)
ClientData instanceData;
Tcl_Interp *interp;
{
SerialState *ssPtr = (SerialState *) instanceData;
int rc = TCL_OK;
rc = DppSerialClose(instanceData);
if ((rc != 0) && (interp != NULL)) {
Tcl_SetErrno(rc);
Tcl_SetResult(interp, Tcl_PosixError(interp), TCL_VOLATILE);
}
ckfree((char *)ssPtr);
return rc;
}
/* --------------------------------------------------
*
* SerialSetOptions --
*
* Sets "name" to "val". Possible
* options with valid arguments are:
*
* -parity [odd|even|none]
* -charsize [7|8]
* -stopbits [1|2]
* -baudrate [rate]
* -sendBuffer [size]
* -recvBuffer [size]
*
* Returns
*
* TCL_OK or TCL_ERROR with interp->result updated
*
* Side Effects
*
* Changes parameters for this channel
*
* ---------------------------------------------------
*/
static int
SerialSetOption(instanceData, interp, nameStr, valStr)
ClientData instanceData;
Tcl_Interp *interp;
char *nameStr; /* (in) Name of option */
char *valStr; /* (in) New value of option */
{
SerialState *ssPtr = (SerialState *) instanceData;
int optVal, option;
char errorStr[80];
int optBool;
if (nameStr[0] != '-') {
option = -1;
} else {
option = DpTranslateOption(nameStr+1);
}
switch (option) {
case DP_PARITY:
if (!strcmp(valStr, "none")) {
optVal = PARITY_NONE;
} else if (!strcmp(valStr, "even")) {
optVal = PARITY_EVEN;
} else if (!strcmp(valStr, "odd")) {
optVal = PARITY_ODD;
} else {
sprintf(errorStr, "Parity must be \"even\", \"odd\" or \"none\"");
goto argError;
}
return DppSerialSetOption(ssPtr, DP_PARITY, optVal);
case DP_CHARSIZE:
if (Tcl_GetInt(interp, valStr, &optVal) == TCL_ERROR) {
return TCL_ERROR;
}
if (optVal != 7 && optVal != 8) {
sprintf(errorStr, "Charsize must be 7 or 8");
goto argError;
}
return DppSerialSetOption(ssPtr, DP_CHARSIZE, optVal);
case DP_STOPBITS:
if (Tcl_GetInt(interp, valStr, &optVal) == TCL_ERROR) {
return TCL_ERROR;
}
if (optVal != 1 && optVal != 2) {
sprintf(errorStr, "Stopbits must be 1 or 2");
goto argError;
}
return DppSerialSetOption(ssPtr, DP_STOPBITS, optVal);
case DP_BAUDRATE:
if (Tcl_GetInt(interp, valStr, &optVal) == TCL_ERROR) {
return TCL_ERROR;
}
return DppSerialSetOption(ssPtr, DP_BAUDRATE, optVal);
case DP_BLOCK:
if (Tcl_GetBoolean(interp, valStr, &optBool) == TCL_ERROR) {
return TCL_ERROR;
}
return DppSerialSetOption(ssPtr, DP_BLOCK, optBool);
default:
Tcl_AppendResult (interp, "Illegal option \"", nameStr,
"\" -- must be charsize, stopbits, parity, baudrate, \
or a standard channel option", NULL);
return TCL_ERROR;
}
argError:
Tcl_AppendResult(interp, errorStr, (char *) NULL);
return TCL_ERROR;
}
/* ----------------------------------------------------
*
* SerialGetOption --
*
* Queries serial channel for the current value of
* the given option.
*
* Returns
*
* TCL_OK and dsPtr updated with the value or
* TCL_ERROR.
*
* Side Effects
*
* None.
*
* -----------------------------------------------------
*/
static int
SerialGetOption(instanceData,
#ifdef _TCL80
interp,
#endif
optionName, dsPtr)
ClientData instanceData;
#ifdef _TCL80
Tcl_Interp *interp;
#endif
char *optionName; /* (in) Name of option to retrieve */
Tcl_DString *dsPtr; /* (in) String to place value */
{
SerialState *ssPtr = (SerialState *) instanceData;
int option;
if (optionName != NULL) {
if (optionName[0] != '-') {
option = -1;
} else {
option = DpTranslateOption(optionName+1);
}
} else {
Tcl_DStringAppend(dsPtr, " -charsize ", -1);
DppSerialGetOption(ssPtr, DP_CHARSIZE, dsPtr);
Tcl_DStringAppend(dsPtr, " -stopbits ", -1);
DppSerialGetOption(ssPtr, DP_STOPBITS, dsPtr);
Tcl_DStringAppend(dsPtr, " -baudrate ", -1);
DppSerialGetOption(ssPtr, DP_BAUDRATE, dsPtr);
Tcl_DStringAppend(dsPtr, " -parity ", -1);
DppSerialGetOption(ssPtr, DP_PARITY, dsPtr);
Tcl_DStringAppend(dsPtr, " -device ", -1);
DppSerialGetOption(ssPtr, DP_DEVICENAME, dsPtr);
return TCL_OK;
}
if (option == -1)
{
#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 DppSerialGetOption(ssPtr, option, optionName, dsPtr);
}
/* ----------------------------------------------------
*
* SerialGetFile --
*
* See below
*
* Returns
*
* TCL_OK
*
* Side Effects
*
* None.
*
* -----------------------------------------------------
*/
#ifndef _TCL76
static int
SerialGetFile(instanceData, direction, handlePtr)
ClientData instanceData;
int direction;
FileHandle *handlePtr;
{
SerialState *statePtr = (SerialState *)instanceData;
*handlePtr = statePtr->theFile;
return TCL_OK;
}
#else
static Tcl_File
SerialGetFile(instanceData, direction)
ClientData instanceData;
int direction;
{
SerialState *statePtr = (SerialState *)instanceData;
return statePtr->theFile;
}
#endif
/* ----------------------------------------------------
*
* SerialReady --
*
* Determines whether serial port has data to be
* read or is OK for writing.
*
* Returns
*
* A bitmask of the events that were found (i.e.
* TCL_FILE_READABLE | TCL_FILE_WRITABLE).
*
* Side Effects
*
* None.
*
* -----------------------------------------------------
*/
#ifdef _TCL76
static int
SerialReady(instanceData, direction)
ClientData instanceData;
int direction;
{
return DppSerialFileReady(instanceData, direction);
}
#endif
/* ----------------------------------------------------
*
* SerialWatch --
*
* Sets up event handling on a serial port Tcl_Channel
*
* Returns
*
* Nothing
*
* Side Effects
*
* None.
*
* -----------------------------------------------------
*/
static void
SerialWatch(instanceData, mask)
ClientData instanceData;
int mask;
{
DppSerialWatchFile(instanceData, mask);
}

124
tcl-dp/generic/dpSock.c Normal file
View File

@@ -0,0 +1,124 @@
/*
* generic/dpSock.c --
*
* This file implements the socket code for various channel drivers.
* In other words, platforms that support Berkeley sockets can use routines
* in this module as part of their platform specific driver.
* This code is supported by the routines in the file dpSock.c in the
* platform directories (i.e., win/dpSock.c, unix/dpSock.c, mac/dpSock.c)
* which handle platform-specific error translation and other non-portable
* functions.
*
* Copyright (c) 1995-1996 Cornell University.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
*/
#include "generic/dpInt.h"
#include "generic/dpPort.h"
/*
*--------------------------------------------------------------
*
* DpHostToIpAddr --
*
* Find the IP address corresponding to a hostname.
*
* Results:
* 1 on success, 0 if host is unknown
*
* Side effects:
* None
*
*--------------------------------------------------------------
*/
int
DpHostToIpAddr (host, ipAddrPtr)
char *host; /* (in) Hostname (human readable) */
int *ipAddrPtr; /* (out) IP address of host */
{
struct hostent *hostent;
if (strcmp (host, "localhost") == 0) {
*ipAddrPtr = 0x7F000001;
return 1;
}
/*
* Gotta watch this one -- on NT, gethostbyname on "" goes out
* to lunch
*/
if ((host == NULL) || (host[0] == 0)) {
return 0;
}
/*
* Try looking host up by address (i.e., host is something
* like "128.84.253.1"). Since the value is returned in
* network byte order, we change it to host byte order.
* We do this first because it's much faster (doesn't require
* a trip to the DNS).
*/
*ipAddrPtr = inet_addr(host);
if (*ipAddrPtr != DP_INADDR_NONE) {
*ipAddrPtr = ntohl(*ipAddrPtr);
return 1;
}
/*
* Looking up the host by address failed. Try looking it up by
* name. If successful, the IP address is in network byte order
* in hostent->h_addr_list[0]
*/
hostent = gethostbyname(host);
if (hostent != NULL) {
memcpy ((char *)ipAddrPtr,
(char *) hostent->h_addr_list[0],
(size_t) hostent->h_length);
*ipAddrPtr = ntohl(*ipAddrPtr);
return 1;
}
/*
* Total failure
*/
return 0;
}
/*
*--------------------------------------------------------------
*
* DpIpAddrToHost --
*
* Find the hostname corresponding to an IP address
*
* Results:
* 1 on success, 0 for failure
*
* Side effects:
* None
*
*--------------------------------------------------------------
*/
int
DpIpAddrToHost (ipAddr, hostPtr)
int ipAddr; /* (in) IP addr */
char *hostPtr; /* (out) Corresponding hostname */
{
struct hostent *hEnt;
if (ipAddr == 0x7F000001) {
strcpy(hostPtr, "localhost");
} else {
hEnt = gethostbyaddr((char *)&ipAddr, sizeof(int), AF_INET);
if (hEnt == NULL) {
return 0;
}
strcpy(hostPtr, hEnt->h_name);
}
return 1;
}

1349
tcl-dp/generic/dpTcp.c Normal file

File diff suppressed because it is too large Load Diff

885
tcl-dp/generic/dpUdp.c Normal file
View File

@@ -0,0 +1,885 @@
/*
* generic/dpUdp.c --
*
* This file implements the generic code for a udp channel driver. These
* are channels that are created by evaluating "dp_connect udp".
*
* The architecture consists of a generic layer and a platform specific
* layer. The rational is that platform specific code goes in its layer,
* while platform independent code goes in its layer. However, most
* socket implementations use the Berkeley sockets interface, which is
* similar across platforms with a few annoying differences. These are
* separated into two files, dpSockUdp.c contains the generic socket code,
* which makes calls on routines in win/dpSock.c, which contains the
* platform specific code. We retain the two level architecture, though,
* so non-Berkeley socket interfaces can be built (if any still exist).
*
* Copyright (c) 1995-1996 Cornell University.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
*/
#include "generic/dpInt.h"
#include "generic/dpPort.h"
/*
* Below are all the channel driver procedures that must be supplied for
* a channel. Replace Udp with the name of this channel type.
* In many cases, the DpGeneric driver procedures can be used (e.g.,
* "DpGenericBlockMode)"
*/
static int UdpBlockMode _ANSI_ARGS_((ClientData instanceData,
int mode));
static int UdpInput _ANSI_ARGS_((ClientData instanceData,
char *buf, int bufSize,
int *errorCodePtr));
static int UdpOutput _ANSI_ARGS_((ClientData instanceData,
char *buf, int toWrite,
int *errorCodePtr));
static int UdpClose _ANSI_ARGS_((ClientData instanceData,
Tcl_Interp *interp));
static int UdpSetOption _ANSI_ARGS_((ClientData instanceData,
Tcl_Interp *interp, char *optionName,
char *optionValue));
static int UdpGetOption _ANSI_ARGS_((ClientData instanceData,
char *optionName, Tcl_DString *dsPtr));
static void UdpWatch _ANSI_ARGS_((ClientData instanceData,
int mask));
static int UdpReady _ANSI_ARGS_((ClientData instanceData,
int mask));
static Tcl_File UdpGetFile _ANSI_ARGS_((ClientData instanceData,
int direction));
typedef SocketState UdpState;
static Tcl_ChannelType udpChannelType = {
"udp", /* Name of channel */
UdpBlockMode, /* Proc to set blocking mode on socket */
UdpClose, /* Proc to close a socket */
UdpInput, /* Proc to get input from a socket */
UdpOutput, /* Proc to send output to a socket */
NULL, /* Can't seek on a socket! */
UdpSetOption, /* Proc to set a socket option */
UdpGetOption, /* Proc to set a socket option */
UdpWatch, /* Proc called to set event loop wait params */
UdpReady, /* Proc called to check if socket has input */
UdpGetFile /* Proc to return a handle assoc with socket */
};
#define PEEK_MODE (1<<1) /* Read without consuming? */
static int udpCount = 0; /* Number of udp files opened -- used to
* generate unique ids for channels */
/*
*--------------------------------------------------------------
*
* UdpBlockMode --
*
* Sets the udp socket to blocking or non-blocking. Just
* a wrapper around the platform specific function.
*
* Results:
* Zero if the operation was successful, or a nonzero POSIX
* error code if the operation failed.
*
* Side effects:
* None
*
*--------------------------------------------------------------
*/
static int
UdpBlockMode (instanceData, mode)
ClientData instanceData; /* Pointer to udpState struct */
int mode; /* TCL_MODE_BLOCKING or TCL_MODE_NONBLOCKING */
{
if (mode == TCL_MODE_BLOCKING) {
return DpUdpSetSocketOption(instanceData, DP_BLOCK, 1);
} else {
return DpUdpSetSocketOption(instanceData, DP_BLOCK, 0);
}
}
/*
*--------------------------------------------------------------
*
* UdpClose --
*
* This function is called by the Tcl channel driver when
* the caller want to close the socket.
* It releases the instanceData and closes the scoket
* All queued output will have been flushed to the device
* before this function is called.
*
* Results:
* Zero for success, otherwise a nonzero POSIX error code and,
* if interp is not NULL, an error message in interp->result
*
* Side effects:
* None
*
*--------------------------------------------------------------
*/
static int
UdpClose (instanceData, interp)
ClientData instanceData; /* (in) Pointer to udpState struct */
Tcl_Interp *interp; /* (in) For error reporting */
{
UdpState *statePtr = (UdpState *)instanceData;
int result;
result = DppCloseSocket(statePtr->sock);
if ((result != 0) && (interp != NULL)) {
DppGetErrno();
Tcl_SetResult(interp, Tcl_PosixError(interp), TCL_STATIC);
}
ckfree((char *)statePtr);
return result;
}
/*
*--------------------------------------------------------------
*
* UdpInput --
*
* This function is called by the Tcl channel driver whenever
* the user wants to get input from the UDP socket.
* If the socket has some data available but
* less than requested by the bufSize argument, we only read
* as much data as is available and return without blocking.
* If the socket has no data available whatsoever and is
* blocking, we block until at least one byte of data can be
* read from the socket.
*
* Results:
* A nonnegative integer indicating how many bytes were read,
* or -1 in case of error (with errorCodePtr set to the POSIX
* error code).
*
* Side effects:
* None
*
*--------------------------------------------------------------
*/
static int
UdpInput (instanceData, buf, bufSize, errorCodePtr)
ClientData instanceData; /* (in) Pointer to udpState struct */
char *buf; /* (in/out) Buffer to fill */
int bufSize; /* (in) Size of buffer */
int *errorCodePtr; /* (out) POSIX error code (if any) */
{
UdpState *statePtr = (UdpState *)instanceData;
int result, peek;
int fromHost, fromPort;
char str[256];
DpSocketAddressIP fromAddr;
int bytesRead, flags = 0, fromLen;
peek = (statePtr->flags & PEEK_MODE);
if (peek) {
flags = MSG_PEEK;
} else {
flags = 0;
}
fromLen = sizeof(fromAddr);
bytesRead = recvfrom(statePtr->sock, buf, bufSize, flags,
(DpSocketAddress *)&fromAddr, &fromLen);
if (bytesRead == DP_SOCKET_ERROR) {
*errorCodePtr = DppGetErrno();
return -1;
}
if (statePtr->interp != NULL) {
fromHost = ntohl(fromAddr.sin_addr.s_addr);
fromPort = ntohs(fromAddr.sin_port);
sprintf (str, "{%d.%d.%d.%d %d}", (fromHost>>24),
(fromHost>>16) & 0xFF, (fromHost>>8) & 0xFF, fromHost & 0xFF,
fromPort);
Tcl_SetVar(statePtr->interp, "dp_from", str, TCL_GLOBAL_ONLY);
}
return bytesRead;
}
/*
*--------------------------------------------------------------
*
* UdpOutput --
*
* This function is called by the Tcl channel driver whenever
* the user wants to send output to the UDP socket.
* The function writes toWrite bytes from buf to the socket.
*
* Results:
* A nonnegative integer indicating how many bytes were written
* to the socket. The return value is normally the same as toWrite,
* but may be less in some cases such as if the output operation
* is interrupted by a signal.
*
* Side effects:
* None
*
*--------------------------------------------------------------
*/
static int
UdpOutput (instanceData, buf, toWrite, errorCodePtr)
ClientData instanceData; /* (in) Pointer to udpState struct */
char *buf; /* (in) Buffer to write */
int toWrite; /* (in) Number of bytes to write */
int *errorCodePtr; /* (out) POSIX error code (if any) */
{
UdpState *statePtr = (UdpState *) instanceData;
DpSocketAddressIP dsa;
int result;
dsa.sin_family = AF_INET;
dsa.sin_addr.s_addr = htonl(statePtr->destIpAddr);
dsa.sin_port = htons((unsigned short)statePtr->destPort);
result = sendto(statePtr->sock, buf, toWrite, 0,
(DpSocketAddress *)&dsa, sizeof(dsa));
if (result == DP_SOCKET_ERROR) {
*errorCodePtr = DppGetErrno();
}
return result;
}
/*
*--------------------------------------------------------------
*
* UdpSetOption --
*
* This function is called by the Tcl channel driver
* whenever Tcl evaluates and fconfigure call to set
* some property of the udp socket (e.g., the buffer
* size). The valid options are "sendBuffer" and
* "recvBuffer"
*
* Results:
* Standard Tcl return value.
*
* Side effects:
* Depends on the option. Generally changes the maximum
* message size that can be sent/received.
*
*--------------------------------------------------------------
*/
static int
UdpSetOption (instanceData, interp, optionName, optionValue)
ClientData instanceData;
Tcl_Interp *interp;
char *optionName;
char *optionValue;
{
int option;
int value;
UdpState *statePtr = (UdpState *)instanceData;
/*
* Set the option specified by optionName
*/
if (optionName[0] == '-') {
option = DpTranslateOption(optionName+1);
} else {
option = -1;
}
switch (option) {
case DP_SEND_BUFFER_SIZE:
case DP_RECV_BUFFER_SIZE:
if (Tcl_GetInt(interp, optionValue, &value) != TCL_OK) {
return TCL_ERROR;
}
if (value <=0) {
Tcl_AppendResult (interp, "Buffer size must be > 0", NULL);
return TCL_ERROR;
}
return DpUdpSetSocketOption (statePtr, option, value);
case DP_PEEK:
if (Tcl_GetBoolean(interp, optionValue, &value) != TCL_OK) {
return TCL_ERROR;
}
if (value == 0) {
statePtr->flags &= ~PEEK_MODE;
} else {
statePtr->flags |= PEEK_MODE;
}
break;
case DP_HOST:
if (DpHostToIpAddr (optionValue, &value) == 0) {
Tcl_AppendResult (interp,
"Expected IP address or hostname but got \"",
optionValue, "\"", NULL);
return TCL_ERROR;
}
statePtr->destIpAddr = value;
break;
case DP_PORT:
if (Tcl_GetInt(interp, optionValue, &value) != TCL_OK) {
return TCL_ERROR;
}
if (value <= 0) {
Tcl_AppendResult (interp, "Port number must be > 0", NULL);
return TCL_ERROR;
}
statePtr->destPort = (unsigned short) value;
break;
case DP_MYPORT:
Tcl_AppendResult (interp, "Can't set port after socket is opened",
NULL);
return TCL_ERROR;
default:
Tcl_AppendResult (interp, "Illegal option \"", optionName,
"\" -- must be sendBuffer, recvBuffer, peek, ",
"host, port, or a standard fconfigure option", NULL);
return TCL_ERROR;
}
return TCL_OK;
}
/*
*--------------------------------------------------------------
*
* UdpGetOption --
*
* This function is called by the Tcl channel code to
* retrieve a parameter of the socket (e.g., a buffer size).
* The valid options are "sendBuffer" and "recvBuffer"
*
* Results:
* A standard Tcl result
*
* Side effects:
* None
*
*--------------------------------------------------------------
*/
static int
UdpGetOption (instanceData, optionName, dsPtr)
ClientData instanceData;
char *optionName;
Tcl_DString *dsPtr;
{
int option;
int size;
unsigned int addr;
char str[256];
UdpState *statePtr = (UdpState *)instanceData;
/*
* If optionName is NULL, then store an alternating list of
* all supported options and their current values in dsPtr
*/
if (optionName == NULL) {
Tcl_DStringAppend (dsPtr, " -sendBuffer ", -1);
UdpGetOption(instanceData, "-sendBuffer", dsPtr);
Tcl_DStringAppend (dsPtr, " -recvBuffer ", -1);
UdpGetOption(instanceData, "-recvBuffer", dsPtr);
Tcl_DStringAppend (dsPtr, " -peek ", -1);
UdpGetOption(instanceData, "-peek", dsPtr);
Tcl_DStringAppend (dsPtr, " -host ", -1);
UdpGetOption(instanceData, "-host", dsPtr);
Tcl_DStringAppend (dsPtr, " -port ", -1);
UdpGetOption(instanceData, "-port", dsPtr);
Tcl_DStringAppend (dsPtr, " -myport ", -1);
UdpGetOption(instanceData, "-myport", dsPtr);
return TCL_OK;
}
/*
* Retrive the value of the option specified by optionName
*/
if (optionName[0] == '-') {
option = DpTranslateOption(optionName+1);
} else {
option = -1;
}
switch (option) {
case DP_SEND_BUFFER_SIZE:
case DP_RECV_BUFFER_SIZE:
DpUdpGetSocketOption (statePtr, option, &size);
sprintf (str, "%d", size);
Tcl_DStringAppend (dsPtr, str, -1);
break;
case DP_PEEK:
if (statePtr->flags & PEEK_MODE) {
Tcl_DStringAppend (dsPtr, "1", -1);
} else {
Tcl_DStringAppend (dsPtr, "0", -1);
}
break;
case DP_HOST:
addr = statePtr->destIpAddr;
sprintf (str, "%d.%d.%d.%d",
(addr >>24), (addr >>16) & 0xff,
(addr >> 8) & 0xff, (addr) & 0xff);
Tcl_DStringAppend (dsPtr, str, -1);
break;
case DP_PORT:
sprintf (str, "%d", (unsigned short) statePtr->destPort);
Tcl_DStringAppend (dsPtr, str, -1);
break;
case DP_MYPORT:
sprintf (str, "%d", (unsigned short) statePtr->myPort);
Tcl_DStringAppend (dsPtr, str, -1);
break;
default:
{
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);
}
Tcl_SetErrno (EINVAL);
return TCL_ERROR;
}
return TCL_OK;
}
/*
*--------------------------------------------------------------
*
* DpOpenUdpChannel --
*
* Opens a new channel that uses the UDP protocol.
*
* Results:
* Returns a pointer to the newly created Tcl_Channel. This
* is the structure with all the function pointers Tcl needs
* to communicate with (read, write, close, etc) the channel.
*
* Side effects:
* A socket is created with the specified port. No other
* socket can use that port until this channel is closed.
*
*--------------------------------------------------------------
*/
Tcl_Channel
DpOpenUdpChannel(interp, argc, argv)
Tcl_Interp *interp; /* For error reporting; can be NULL. */
int argc; /* Number of arguments. */
char **argv; /* Argument strings. */
{
Tcl_Channel chan;
UdpState *statePtr;
char channelName[20];
int i, result;
/*
* The default values for the value-option pairs
*/
int hostIp = 0;
int port = 0;
int myIpAddr = DP_INADDR_ANY;
int myport = 0;
for (i=0; i<argc; i+=2) {
int v = i+1;
size_t len = strlen(argv[i]);
if (strncmp(argv[i], "-host", len)==0) {
if (v==argc) {goto arg_missing;}
if (!DpHostToIpAddr (argv[v], &hostIp)) {
Tcl_AppendResult (interp, "Unknown host \"", argv[v],
"\"", NULL);
return NULL;
}
} else if (strncmp(argv[i], "-port", len)==0) {
if (v==argc) {goto arg_missing;}
if (Tcl_GetInt(interp, argv[v], &port) != TCL_OK) {
return NULL;
}
if (port <= 0) {
Tcl_AppendResult (interp, "Port number must be > 0", NULL);
return NULL;
}
} else if (strncmp(argv[i], "-myaddr", len)==0) {
if (v==argc) {goto arg_missing;}
if (strcmp (argv[v], "any") == 0) {
myIpAddr = DP_INADDR_ANY;
} else if (!DpHostToIpAddr (argv[v], &myIpAddr)) {
Tcl_AppendResult (interp, "Illegal value for -myaddr \"",
argv[v], "\"", NULL);
return NULL;
}
} else if (strncmp(argv[i], "-myport", len)==0) {
if (v==argc) {goto arg_missing;}
if (Tcl_GetInt(interp, argv[v], &myport) != TCL_OK) {
return NULL;
}
if (myport <= 0) {
Tcl_AppendResult (interp,
"Port number for -myport must be > 0", NULL);
return NULL;
}
} else {
Tcl_AppendResult(interp, "unknown option \"",
argv[i], "\", must be -host, -myaddr, -myport ",
"or -port", NULL);
return NULL;
}
}
/*
* Create a new socket and wrap it in a channel.
*/
statePtr = (UdpState *)ckalloc(sizeof(UdpState));
statePtr->flags = 0;
statePtr->interp = interp;
statePtr->myPort = myport;
statePtr->destIpAddr = hostIp;
statePtr->destPort = port;
result = DpCreateUdpSocket(interp, myIpAddr, statePtr);
if (result != TCL_OK) {
ckfree((char *)statePtr);
return NULL;
}
sprintf(channelName, "udp%d", udpCount++);
chan = Tcl_CreateChannel(&udpChannelType, channelName,
(ClientData)statePtr, TCL_READABLE|TCL_WRITABLE);
Tcl_RegisterChannel(interp, chan);
/*
* Set the initial state of the channel.
* Make sure the socket's blocking, set the default buffer sizes,
* set the destination address as specified, disable Tcl buffering
* and translation.
*/
DpUdpSetSocketOption(statePtr, DP_SEND_BUFFER_SIZE, 8192);
DpUdpSetSocketOption(statePtr, DP_RECV_BUFFER_SIZE, 8192);
DpUdpGetSocketOption(statePtr, DP_RECV_BUFFER_SIZE,
&statePtr->recvBufSize);
if (Tcl_SetChannelOption(interp, chan, "-translation", "binary") !=
TCL_OK) {
DpClose(interp, chan);
ckfree((char *)statePtr);
return NULL;
}
if (Tcl_SetChannelOption(interp, chan, "-blocking", "1") !=
TCL_OK) {
DpClose(interp, chan);
ckfree((char *)statePtr);
return NULL;
}
if (Tcl_SetChannelOption(interp, chan, "-buffering", "none") !=
TCL_OK) {
DpClose(interp, chan);
ckfree((char *)statePtr);
return NULL;
}
return chan;
arg_missing:
Tcl_AppendResult(interp, "value for \"", argv[argc-1], "\" missing", NULL);
return NULL;
}
/*
*--------------------------------------------------------------
*
* UdpWatch --
*
* Gives a short overview (a few sentences), what other
* functions are related to this one.
*
* All changes to the module made that decrease resource usage,
* but make the function harder to understand, modify, and debug.
*
* Results:
* Description of return values.
*
* Side effects:
* Global variables touched.
* I/O operations performed.
* Delayed effects.
*
*--------------------------------------------------------------
*/
static void
UdpWatch (instanceData, mask)
ClientData instanceData;
int mask;
{
UdpState *infoPtr = (UdpState *) instanceData;
Tcl_WatchFile(infoPtr->sockFile, mask);
}
/*
*--------------------------------------------------------------
*
* UdpReady --
*
* Gives a short overview (a few sentences), what other
* functions are related to this one.
*
* All changes to the module made that decrease resource usage,
* but make the function harder to understand, modify, and debug.
*
* Results:
* Description of return values.
*
* Side effects:
* Global variables touched.
* I/O operations performed.
* Delayed effects.
*
*--------------------------------------------------------------
*/
static int
UdpReady (instanceData, mask)
ClientData instanceData;
int mask;
{
UdpState *statePtr = (UdpState *) instanceData;
return Tcl_FileReady(statePtr->sockFile, mask);
}
/*
*--------------------------------------------------------------
*
* UdpGetFile --
*
* Gives a short overview (a few sentences), what other
* functions are related to this one.
*
* All changes to the module made that decrease resource usage,
* but make the function harder to understand, modify, and debug.
*
* Results:
* Description of return values.
*
* Side effects:
* Global variables touched.
* I/O operations performed.
* Delayed effects.
*
*--------------------------------------------------------------
*/
static Tcl_File
UdpGetFile(instanceData, direction)
ClientData instanceData;
int direction;
{
UdpState *statePtr = (UdpState *)instanceData;
return statePtr->sockFile;
}
/*
*--------------------------------------------------------------
*
* DpUdpSetSocketOption --
*
* Sets a socket option. The allowable options for UDP
* sockets are
* DP_SEND_BUFFER_SIZE (int)
* DP_RECV_BUFFER_SIZE (int)
* DP_BLOCK (T/F)
*
* Results:
* Zero if the operation was successful, or a nonzero POSIX
* error code if the operation failed.
*
* Side effects:
* None
*
*--------------------------------------------------------------
*/
int
DpUdpSetSocketOption (clientData, option, value)
ClientData clientData; /* (in) UdpState structure */
int option; /* (in) Option to set */
int value; /* (in) new value for option */
{
UdpState *statePtr = (UdpState *)clientData;
int sock, result;
sock = statePtr->sock;
switch (option) {
case DP_SEND_BUFFER_SIZE:
result = setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *)&value,
sizeof(value));
break;
case DP_RECV_BUFFER_SIZE:
result = setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)&value,
sizeof(value));
break;
case DP_BLOCK:
result = DppSetBlock (sock, value);
break;
default:
return EINVAL;
}
if (result != 0) {
return Tcl_GetErrno();
}
return 0;
}
/*
*--------------------------------------------------------------
*
* DpUdpGetSocketOption --
*
* Sets a socket option. The allowable options for UDP
* sockets are
* DP_SEND_BUFFER_SIZE (int)
* DP_RECV_BUFFER_SIZE (int)
* Note that we can't determine whether a socket is blocking,
* so DP_BLOCK is not allowed.
*
* Results:
* Zero if the operation was successful, or a nonzero POSIX
* error code if the operation failed.
*
* Side effects:
* None
*
*--------------------------------------------------------------
*/
int
DpUdpGetSocketOption (clientData, option, valuePtr)
ClientData clientData; /* (in) UdpState structure */
int option; /* (in) Option to set */
int *valuePtr; /* (out) current value of option */
{
UdpState *statePtr = (UdpState *)clientData;
int sock, result, len;
sock = statePtr->sock;
len = sizeof(int);
switch (option) {
case DP_SEND_BUFFER_SIZE:
result = getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *)valuePtr,
&len);
break;
case DP_RECV_BUFFER_SIZE:
result = getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)valuePtr,
&len);
break;
default:
return EINVAL;
}
if (result != 0) {
return Tcl_GetErrno();
}
return 0;
}
/*
*--------------------------------------------------------------
*
* DpCreateUdpSocket --
*
* Create a udp socket.
*
* Results:
* A standard Tcl result.
*
* Side effects:
* None
*
*--------------------------------------------------------------
*/
int
DpCreateUdpSocket(interp, myIpAddr, statePtr)
Tcl_Interp *interp; /* (in) For error reporting. */
int myIpAddr; /* (in) IP addr of interface to use.
* DP_INADDR_ANY = default port */
UdpState *statePtr; /* (out) Pointer to local structure */
{
DpSocketAddressIP sockAddr;
DpSocket sock;
int len;
sock = socket(PF_INET, SOCK_DGRAM, 0);
if (sock == DP_SOCKET_ERROR) {
goto socketError;
}
statePtr->sock = sock;
/*
* Bind the socket.
* This is a bit of a mess, but it's Berkeley sockets. The sin_family
* is set to AF_INET, indicating IP addressing. The sin_addr.s_addr
* field says what interface to use. It can be INADDR_ANY to let
* the system choose a default interface. The port number can be
* zero (which tells the system to choose a port number) or > 1024,
* which is then used as the port number
*/
memset((char *)&sockAddr, 0, sizeof(sockAddr));
sockAddr.sin_family = AF_INET;
if (myIpAddr == DP_INADDR_ANY) {
sockAddr.sin_addr.s_addr = INADDR_ANY;
} else {
sockAddr.sin_addr.s_addr = htonl(myIpAddr);
}
sockAddr.sin_port = htons((unsigned short) statePtr->myPort);
if (bind(sock, (DpSocketAddress *)&sockAddr, sizeof(sockAddr)) ==
DP_SOCKET_ERROR) {
goto bindError;
}
/*
* Figure out what port number we got if we let the system chose it.
*/
if (statePtr->myPort == 0) {
len = sizeof(sockAddr);
getsockname (sock, (DpSocketAddress *)&sockAddr, &len);
statePtr->myPort = ntohs(sockAddr.sin_port);
}
statePtr->sockFile = Tcl_GetFile((ClientData)statePtr->sock, DP_SOCKET);
return TCL_OK;
bindError:
DppGetErrno();
Tcl_AppendResult(interp, "Error binding UDP socket to port: ",
Tcl_PosixError(interp), NULL);
DppCloseSocket (sock);
return TCL_ERROR;
socketError:
DppGetErrno();
Tcl_AppendResult(interp, "Error creating UDP socket: ",
Tcl_PosixError(interp), NULL);
return TCL_ERROR;
}