#include "tclInt.h"
#include "tclPort.h"
#define TCL_READ_CHUNK_SIZE 4096
typedef struct AcceptCallback {
char *script;
Tcl_Interp *interp;
} AcceptCallback;
static void AcceptCallbackProc _ANSI_ARGS_((ClientData callbackData,
Tcl_Channel chan, char *address, int port));
static void RegisterTcpServerInterpCleanup _ANSI_ARGS_((Tcl_Interp *interp,
AcceptCallback *acceptCallbackPtr));
static void TcpAcceptCallbacksDeleteProc _ANSI_ARGS_((
ClientData clientData, Tcl_Interp *interp));
static void TcpServerCloseProc _ANSI_ARGS_((ClientData callbackData));
static void UnregisterTcpServerInterpCleanupProc _ANSI_ARGS_((
Tcl_Interp *interp, AcceptCallback *acceptCallbackPtr));
int
Tcl_PutsCmd(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char **argv;
{
Tcl_Channel chan;
int i;
int newline;
char *channelId;
int result;
int mode;
i = 1;
newline = 1;
if ((argc >= 2) && (strcmp(argv[1], "-nonewline") == 0)) {
newline = 0;
i++;
}
if ((i < (argc-3)) || (i >= argc)) {
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" ?-nonewline? ?channelId? string\"", (char *) NULL);
return TCL_ERROR;
}
if (i == (argc-3)) {
if (strncmp(argv[i+2], "nonewline", strlen(argv[i+2])) != 0) {
Tcl_AppendResult(interp, "bad argument \"", argv[i+2],
"\": should be \"nonewline\"", (char *) NULL);
return TCL_ERROR;
}
newline = 0;
}
if (i == (argc-1)) {
channelId = "stdout";
} else {
channelId = argv[i];
i++;
}
chan = Tcl_GetChannel(interp, channelId, &mode);
if (chan == (Tcl_Channel) NULL) {
return TCL_ERROR;
}
if ((mode & TCL_WRITABLE) == 0) {
Tcl_AppendResult(interp, "channel \"", channelId,
"\" wasn't opened for writing", (char *) NULL);
return TCL_ERROR;
}
result = Tcl_Write(chan, argv[i], -1);
if (result < 0) {
goto error;
}
if (newline != 0) {
result = Tcl_Write(chan, "\n", 1);
if (result < 0) {
goto error;
}
}
return TCL_OK;
error:
Tcl_AppendResult(interp, "error writing \"", Tcl_GetChannelName(chan),
"\": ", Tcl_PosixError(interp), (char *) NULL);
return TCL_ERROR;
}
int
Tcl_FlushCmd(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char **argv;
{
Tcl_Channel chan;
int result;
int mode;
if (argc != 2) {
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" channelId\"", (char *) NULL);
return TCL_ERROR;
}
chan = Tcl_GetChannel(interp, argv[1], &mode);
if (chan == (Tcl_Channel) NULL) {
return TCL_ERROR;
}
if ((mode & TCL_WRITABLE) == 0) {
Tcl_AppendResult(interp, "channel \"", argv[1],
"\" wasn't opened for writing", (char *) NULL);
return TCL_ERROR;
}
result = Tcl_Flush(chan);
if (result != TCL_OK) {
Tcl_AppendResult(interp, "error flushing \"", Tcl_GetChannelName(chan),
"\": ", Tcl_PosixError(interp), (char *) NULL);
}
return result;
}
int
Tcl_GetsCmd(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char **argv;
{
Tcl_Channel chan;
char *varName;
char buf[128];
Tcl_DString ds;
int lineLen;
int mode;
if ((argc != 2) && (argc != 3)) {
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" channelId ?varName?\"", (char *) NULL);
return TCL_ERROR;
}
chan = Tcl_GetChannel(interp, argv[1], &mode);
if (chan == (Tcl_Channel) NULL) {
return TCL_ERROR;
}
if ((mode & TCL_READABLE) == 0) {
Tcl_AppendResult(interp, "channel \"", argv[1],
"\" wasn't opened for reading", (char *) NULL);
return TCL_ERROR;
}
if (argc != 3) {
varName = (char *) NULL;
} else {
varName = argv[2];
}
Tcl_DStringInit(&ds);
lineLen = Tcl_Gets(chan, &ds);
if (lineLen < 0) {
if (!Tcl_Eof(chan) && !Tcl_InputBlocked(chan)) {
Tcl_DStringFree(&ds);
Tcl_AppendResult(interp, "error reading \"",
Tcl_GetChannelName(chan), "\": ", Tcl_PosixError(interp),
(char *) NULL);
return TCL_ERROR;
}
lineLen = -1;
}
if (varName == (char *) NULL) {
Tcl_DStringResult(interp, &ds);
} else {
if (Tcl_SetVar(interp, varName, Tcl_DStringValue(&ds),
TCL_LEAVE_ERR_MSG) == NULL) {
Tcl_DStringFree(&ds);
return TCL_ERROR;
}
Tcl_ResetResult(interp);
sprintf(buf, "%d", lineLen);
Tcl_AppendResult(interp, buf, (char *) NULL);
}
Tcl_DStringFree(&ds);
return TCL_OK;
}
int
Tcl_ReadCmd(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char **argv;
{
Tcl_Channel chan;
int newline, i;
int toRead;
int toReadNow;
int charactersRead;
int charactersReadNow;
int mode;
Tcl_DString ds;
int bufSize;
if ((argc != 2) && (argc != 3)) {
argerror:
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" channelId ?numBytes?\" or \"", argv[0],
" ?-nonewline? channelId\"", (char *) NULL);
return TCL_ERROR;
}
i = 1;
newline = 0;
if (strcmp(argv[i], "-nonewline") == 0) {
newline = 1;
i++;
}
if (i == argc) {
goto argerror;
}
chan = Tcl_GetChannel(interp, argv[i], &mode);
if (chan == (Tcl_Channel) NULL) {
return TCL_ERROR;
}
if ((mode & TCL_READABLE) == 0) {
Tcl_AppendResult(interp, "channel \"", argv[i],
"\" wasn't opened for reading", (char *) NULL);
return TCL_ERROR;
}
i++;
toRead = INT_MAX;
if (i < argc) {
if (isdigit((unsigned char) (argv[i][0]))) {
if (Tcl_GetInt(interp, argv[i], &toRead) != TCL_OK) {
return TCL_ERROR;
}
} else if (strcmp(argv[i], "nonewline") == 0) {
newline = 1;
} else {
Tcl_AppendResult(interp, "bad argument \"", argv[i],
"\": should be \"nonewline\"", (char *) NULL);
return TCL_ERROR;
}
}
bufSize = Tcl_GetChannelBufferSize(chan);
Tcl_DStringInit(&ds);
for (charactersRead = 0; charactersRead < toRead; ) {
toReadNow = toRead - charactersRead;
if (toReadNow > bufSize) {
toReadNow = bufSize;
}
Tcl_DStringSetLength(&ds, charactersRead + toReadNow);
charactersReadNow =
Tcl_Read(chan, Tcl_DStringValue(&ds) + charactersRead, toReadNow);
if (charactersReadNow < 0) {
Tcl_DStringFree(&ds);
Tcl_AppendResult(interp, "error reading \"",
Tcl_GetChannelName(chan), "\": ",
Tcl_PosixError(interp), (char *) NULL);
return TCL_ERROR;
}
charactersRead += charactersReadNow;
if (charactersReadNow < toReadNow) {
break;
}
}
Tcl_DStringSetLength(&ds, charactersRead);
Tcl_DStringResult(interp, &ds);
Tcl_DStringFree(&ds);
if ((charactersRead > 0) && (newline) &&
(interp->result[charactersRead-1] == '\n')) {
interp->result[charactersRead-1] = '\0';
}
return TCL_OK;
}
int
TclUnsupported0Cmd(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char **argv;
{
Tcl_Channel inChan, outChan;
int requested;
char *bufPtr;
int actuallyRead, actuallyWritten, totalRead, toReadNow, mode;
requested = INT_MAX;
if ((argc < 3) || (argc > 4)) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " inChanId outChanId ?chunkSize?\"", (char *) NULL);
return TCL_ERROR;
}
inChan = Tcl_GetChannel(interp, argv[1], &mode);
if (inChan == (Tcl_Channel) NULL) {
return TCL_ERROR;
}
if ((mode & TCL_READABLE) == 0) {
Tcl_AppendResult(interp, "channel \"", argv[1],
"\" wasn't opened for reading", (char *) NULL);
return TCL_ERROR;
}
outChan = Tcl_GetChannel(interp, argv[2], &mode);
if (outChan == (Tcl_Channel) NULL) {
return TCL_ERROR;
}
if ((mode & TCL_WRITABLE) == 0) {
Tcl_AppendResult(interp, "channel \"", argv[2],
"\" wasn't opened for writing", (char *) NULL);
return TCL_ERROR;
}
if (argc == 4) {
if (Tcl_GetInt(interp, argv[3], &requested) != TCL_OK) {
return TCL_ERROR;
}
if (requested < 0) {
requested = INT_MAX;
}
}
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) {
ckfree(bufPtr);
Tcl_AppendResult(interp, argv[0], ": ", Tcl_GetChannelName(inChan),
Tcl_PosixError(interp), (char *) NULL);
return TCL_ERROR;
}
if (actuallyRead == 0) {
ckfree(bufPtr);
sprintf(interp->result, "%d", totalRead);
return TCL_OK;
}
actuallyWritten = Tcl_Write(outChan, bufPtr, actuallyRead);
if (actuallyWritten < 0) {
ckfree(bufPtr);
Tcl_AppendResult(interp, argv[0], ": ", Tcl_GetChannelName(outChan),
Tcl_PosixError(interp), (char *) NULL);
return TCL_ERROR;
}
}
ckfree(bufPtr);
sprintf(interp->result, "%d", totalRead);
return TCL_OK;
}
int
Tcl_SeekCmd(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char **argv;
{
Tcl_Channel chan;
int offset, mode;
int result;
if ((argc != 3) && (argc != 4)) {
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" channelId offset ?origin?\"", (char *) NULL);
return TCL_ERROR;
}
chan = Tcl_GetChannel(interp, argv[1], NULL);
if (chan == (Tcl_Channel) NULL) {
return TCL_ERROR;
}
if (Tcl_GetInt(interp, argv[2], &offset) != TCL_OK) {
return TCL_ERROR;
}
mode = SEEK_SET;
if (argc == 4) {
size_t length;
int c;
length = strlen(argv[3]);
c = argv[3][0];
if ((c == 's') && (strncmp(argv[3], "start", length) == 0)) {
mode = SEEK_SET;
} else if ((c == 'c') && (strncmp(argv[3], "current", length) == 0)) {
mode = SEEK_CUR;
} else if ((c == 'e') && (strncmp(argv[3], "end", length) == 0)) {
mode = SEEK_END;
} else {
Tcl_AppendResult(interp, "bad origin \"", argv[3],
"\": should be start, current, or end", (char *) NULL);
return TCL_ERROR;
}
}
result = Tcl_Seek(chan, offset, mode);
if (result == -1) {
Tcl_AppendResult(interp, "error during seek on \"",
Tcl_GetChannelName(chan), "\": ",
Tcl_PosixError(interp), (char *) NULL);
return TCL_ERROR;
}
return TCL_OK;
}
int
Tcl_TellCmd(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char **argv;
{
Tcl_Channel chan;
if (argc != 2) {
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" channelId\"", (char *) NULL);
return TCL_ERROR;
}
chan = Tcl_GetChannel(interp, argv[1], NULL);
if (chan == (Tcl_Channel) NULL) {
return TCL_ERROR;
}
sprintf(interp->result, "%d", Tcl_Tell(chan));
return TCL_OK;
}
int
Tcl_CloseCmd(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char **argv;
{
Tcl_Channel chan;
int len;
if (argc != 2) {
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" channelId\"", (char *) NULL);
return TCL_ERROR;
}
chan = Tcl_GetChannel(interp, argv[1], NULL);
if (chan == (Tcl_Channel) NULL) {
return TCL_ERROR;
}
if (Tcl_UnregisterChannel(interp, chan) != TCL_OK) {
len = strlen(interp->result);
if ((len > 0) && (interp->result[len - 1] == '\n')) {
interp->result[len - 1] = '\0';
}
return TCL_ERROR;
}
return TCL_OK;
}
int
Tcl_FconfigureCmd(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char **argv;
{
Tcl_Channel chan;
int result;
int i;
Tcl_DString ds;
if ((argc < 2) || (((argc % 2) == 1) && (argc != 3))) {
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" channelId ?optionName? ?value? ?optionName value?...\"",
(char *) NULL);
return TCL_ERROR;
}
chan = Tcl_GetChannel(interp, argv[1], NULL);
if (chan == (Tcl_Channel) NULL) {
return TCL_ERROR;
}
if (argc == 2) {
Tcl_DStringInit(&ds);
if (Tcl_GetChannelOption(chan, (char *) NULL, &ds) != TCL_OK) {
Tcl_AppendResult(interp, "option retrieval failed",
(char *) NULL);
return TCL_ERROR;
}
Tcl_DStringResult(interp, &ds);
Tcl_DStringFree(&ds);
return TCL_OK;
}
if (argc == 3) {
Tcl_DStringInit(&ds);
if (Tcl_GetChannelOption(chan, argv[2], &ds) != TCL_OK) {
Tcl_DStringFree(&ds);
Tcl_AppendResult(interp, "bad option \"", argv[2],
"\": must be -blocking, -buffering, -buffersize, ",
"-eofchar, -translation, ",
"or a channel type specific option", (char *) NULL);
return TCL_ERROR;
}
Tcl_DStringResult(interp, &ds);
Tcl_DStringFree(&ds);
return TCL_OK;
}
for (i = 3; i < argc; i += 2) {
result = Tcl_SetChannelOption(interp, chan, argv[i-1], argv[i]);
if (result != TCL_OK) {
return result;
}
}
return TCL_OK;
}
int
Tcl_EofCmd(unused, interp, argc, argv)
ClientData unused;
Tcl_Interp *interp;
int argc;
char **argv;
{
Tcl_Channel chan;
int mode;
if (argc != 2) {
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" channelId\"", (char *) NULL);
return TCL_ERROR;
}
chan = Tcl_GetChannel(interp, argv[1], &mode);
if (chan == (Tcl_Channel) NULL) {
return TCL_ERROR;
}
sprintf(interp->result, "%d", Tcl_Eof(chan) ? 1 : 0);
return TCL_OK;
}
#if 0
int
Tcl_ExecCmd(dummy, interp, argc, argv)
ClientData dummy;
Tcl_Interp *interp;
int argc;
char **argv;
{
#ifdef MAC_TCL
Tcl_AppendResult(interp, "exec not implemented under Mac OS",
(char *)NULL);
return TCL_ERROR;
#else
int keepNewline, firstWord, background, length, result;
Tcl_Channel chan;
Tcl_DString ds;
int readSoFar, readNow, bufSize;
keepNewline = 0;
for (firstWord = 1; (firstWord < argc) && (argv[firstWord][0] == '-');
firstWord++) {
if (strcmp(argv[firstWord], "-keepnewline") == 0) {
keepNewline = 1;
} else if (strcmp(argv[firstWord], "--") == 0) {
firstWord++;
break;
} else {
Tcl_AppendResult(interp, "bad switch \"", argv[firstWord],
"\": must be -keepnewline or --", (char *) NULL);
return TCL_ERROR;
}
}
if (argc <= firstWord) {
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" ?switches? arg ?arg ...?\"", (char *) NULL);
return TCL_ERROR;
}
background = 0;
if ((argv[argc-1][0] == '&') && (argv[argc-1][1] == 0)) {
argc--;
argv[argc] = NULL;
background = 1;
}
chan = Tcl_OpenCommandChannel(interp, argc-firstWord,
argv+firstWord,
(background ? 0 : TCL_STDOUT | TCL_STDERR));
if (chan == (Tcl_Channel) NULL) {
return TCL_ERROR;
}
if (background) {
TclGetAndDetachPids(interp, chan);
if (Tcl_Close(interp, chan) != TCL_OK) {
return TCL_ERROR;
}
return TCL_OK;
}
if (Tcl_GetChannelFile(chan, TCL_READABLE) != NULL) {
#define EXEC_BUFFER_SIZE 4096
Tcl_DStringInit(&ds);
readSoFar = 0; bufSize = 0;
while (1) {
bufSize += EXEC_BUFFER_SIZE;
Tcl_DStringSetLength(&ds, bufSize);
readNow = Tcl_Read(chan, Tcl_DStringValue(&ds) + readSoFar,
EXEC_BUFFER_SIZE);
if (readNow < 0) {
Tcl_DStringFree(&ds);
Tcl_AppendResult(interp,
"error reading output from command: ",
Tcl_PosixError(interp), (char *) NULL);
return TCL_ERROR;
}
readSoFar += readNow;
if (readNow < EXEC_BUFFER_SIZE) {
break;
}
}
Tcl_DStringSetLength(&ds, readSoFar);
Tcl_DStringResult(interp, &ds);
Tcl_DStringFree(&ds);
}
result = Tcl_Close(interp, chan);
length = strlen(interp->result);
if (!keepNewline && (length > 0) &&
(interp->result[length-1] == '\n')) {
interp->result[length-1] = '\0';
interp->result[length] = 'x';
}
return result;
#endif
}
#endif
int
Tcl_FblockedCmd(unused, interp, argc, argv)
ClientData unused;
Tcl_Interp *interp;
int argc;
char **argv;
{
Tcl_Channel chan;
int mode;
if (argc != 2) {
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" channelId\"", (char *) NULL);
return TCL_ERROR;
}
chan = Tcl_GetChannel(interp, argv[1], &mode);
if (chan == (Tcl_Channel) NULL) {
return TCL_ERROR;
}
if ((mode & TCL_READABLE) == 0) {
Tcl_AppendResult(interp, "channel \"", argv[1],
"\" wasn't opened for reading", (char *) NULL);
return TCL_ERROR;
}
sprintf(interp->result, "%d", Tcl_InputBlocked(chan) ? 1 : 0);
return TCL_OK;
}
int
Tcl_OpenCmd(notUsed, interp, argc, argv)
ClientData notUsed;
Tcl_Interp *interp;
int argc;
char **argv;
{
int pipeline, prot;
char *modeString;
Tcl_Channel chan;
if ((argc < 2) || (argc > 4)) {
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" fileName ?access? ?permissions?\"", (char *) NULL);
return TCL_ERROR;
}
prot = 0666;
if (argc == 2) {
modeString = "r";
} else {
modeString = argv[2];
if (argc == 4) {
if (Tcl_GetInt(interp, argv[3], &prot) != TCL_OK) {
return TCL_ERROR;
}
}
}
pipeline = 0;
if (argv[1][0] == '|') {
pipeline = 1;
}
if (!pipeline) {
chan = Tcl_OpenFileChannel(interp, argv[1], modeString, prot);
} else {
int mode, seekFlag, cmdArgc;
char **cmdArgv;
if (Tcl_TclSplitList(interp, argv[1]+1, &cmdArgc, &cmdArgv) != TCL_OK) {
return TCL_ERROR;
}
mode = TclGetOpenMode(interp, modeString, &seekFlag);
if (mode == -1) {
chan = NULL;
} else {
int flags = TCL_STDERR | TCL_ENFORCE_MODE;
switch (mode & (O_RDONLY | O_WRONLY | O_RDWR)) {
case O_RDONLY:
flags |= TCL_STDOUT;
break;
case O_WRONLY:
flags |= TCL_STDIN;
break;
case O_RDWR:
flags |= (TCL_STDIN | TCL_STDOUT);
break;
default:
panic("Tcl_OpenCmd: invalid mode value");
break;
}
chan = Tcl_OpenCommandChannel(interp, cmdArgc, cmdArgv, flags);
}
ckfree((char *) cmdArgv);
}
if (chan == (Tcl_Channel) NULL) {
return TCL_ERROR;
}
Tcl_RegisterChannel(interp, chan);
Tcl_AppendResult(interp, Tcl_GetChannelName(chan), (char *) NULL);
return TCL_OK;
}
#if 0
static void
TcpAcceptCallbacksDeleteProc(clientData, interp)
ClientData clientData;
Tcl_Interp *interp;
{
Tcl_HashTable *hTblPtr;
Tcl_HashEntry *hPtr;
Tcl_HashSearch hSearch;
AcceptCallback *acceptCallbackPtr;
hTblPtr = (Tcl_HashTable *) clientData;
for (hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch);
hPtr != (Tcl_HashEntry *) NULL;
hPtr = Tcl_NextHashEntry(&hSearch)) {
acceptCallbackPtr = (AcceptCallback *) Tcl_GetHashValue(hPtr);
acceptCallbackPtr->interp = (Tcl_Interp *) NULL;
}
Tcl_DeleteHashTable(hTblPtr);
ckfree((char *) hTblPtr);
}
static void
RegisterTcpServerInterpCleanup(interp, acceptCallbackPtr)
Tcl_Interp *interp;
AcceptCallback *acceptCallbackPtr;
{
Tcl_HashTable *hTblPtr;
Tcl_HashEntry *hPtr;
int new;
hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp,
"tclTCPAcceptCallbacks",
NULL);
if (hTblPtr == (Tcl_HashTable *) NULL) {
hTblPtr = (Tcl_HashTable *) ckalloc((unsigned) sizeof(Tcl_HashTable));
Tcl_InitHashTable(hTblPtr, TCL_ONE_WORD_KEYS);
(void) Tcl_SetAssocData(interp, "tclTCPAcceptCallbacks",
TcpAcceptCallbacksDeleteProc, (ClientData) hTblPtr);
}
hPtr = Tcl_CreateHashEntry(hTblPtr, (char *) acceptCallbackPtr, &new);
if (!new) {
panic("RegisterTcpServerCleanup: damaged accept record table");
}
Tcl_SetHashValue(hPtr, (ClientData) acceptCallbackPtr);
}
static void
UnregisterTcpServerInterpCleanupProc(interp, acceptCallbackPtr)
Tcl_Interp *interp;
AcceptCallback *acceptCallbackPtr;
{
Tcl_HashTable *hTblPtr;
Tcl_HashEntry *hPtr;
hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp,
"tclTCPAcceptCallbacks", NULL);
if (hTblPtr == (Tcl_HashTable *) NULL) {
return;
}
hPtr = Tcl_FindHashEntry(hTblPtr, (char *) acceptCallbackPtr);
if (hPtr == (Tcl_HashEntry *) NULL) {
return;
}
Tcl_DeleteHashEntry(hPtr);
}
static void
AcceptCallbackProc(callbackData, chan, address, port)
ClientData callbackData;
Tcl_Channel chan;
char *address;
int port;
{
AcceptCallback *acceptCallbackPtr;
Tcl_Interp *interp;
char *script;
char portBuf[10];
int result;
acceptCallbackPtr = (AcceptCallback *) callbackData;
if (acceptCallbackPtr->interp != (Tcl_Interp *) NULL) {
script = acceptCallbackPtr->script;
interp = acceptCallbackPtr->interp;
Tcl_Preserve((ClientData) script);
Tcl_Preserve((ClientData) interp);
sprintf(portBuf, "%d", port);
Tcl_RegisterChannel(interp, chan);
result = Tcl_VarEval(interp, script, " ", Tcl_GetChannelName(chan),
" ", address, " ", portBuf, (char *) NULL);
if (result != TCL_OK) {
Tcl_BackgroundError(interp);
Tcl_UnregisterChannel(interp, chan);
}
Tcl_Release((ClientData) interp);
Tcl_Release((ClientData) script);
} else {
Tcl_Close((Tcl_Interp *) NULL, chan);
}
}
static void
TcpServerCloseProc(callbackData)
ClientData callbackData;
{
AcceptCallback *acceptCallbackPtr;
acceptCallbackPtr = (AcceptCallback *) callbackData;
if (acceptCallbackPtr->interp != (Tcl_Interp *) NULL) {
UnregisterTcpServerInterpCleanupProc(acceptCallbackPtr->interp,
acceptCallbackPtr);
}
Tcl_EventuallyFree((ClientData) acceptCallbackPtr->script, TCL_DYNAMIC);
ckfree((char *) acceptCallbackPtr);
}
int
Tcl_SocketCmd(notUsed, interp, argc, argv)
ClientData notUsed;
Tcl_Interp *interp;
int argc;
char **argv;
{
int a, server, port;
char *arg, *copyScript, *host, *script;
char *myaddr = NULL;
int myport = 0;
int async = 0;
Tcl_Channel chan;
AcceptCallback *acceptCallbackPtr;
server = 0;
script = NULL;
if (TclHasSockets(interp) != TCL_OK) {
return TCL_ERROR;
}
for (a = 1; a < argc; a++) {
arg = argv[a];
if (arg[0] == '-') {
if (strcmp(arg, "-server") == 0) {
if (async == 1) {
Tcl_AppendResult(interp,
"cannot set -async option for server sockets",
(char *) NULL);
return TCL_ERROR;
}
server = 1;
a++;
if (a >= argc) {
Tcl_AppendResult(interp,
"no argument given for -server option",
(char *) NULL);
return TCL_ERROR;
}
script = argv[a];
} else if (strcmp(arg, "-myaddr") == 0) {
a++;
if (a >= argc) {
Tcl_AppendResult(interp,
"no argument given for -myaddr option",
(char *) NULL);
return TCL_ERROR;
}
myaddr = argv[a];
} else if (strcmp(arg, "-myport") == 0) {
a++;
if (a >= argc) {
Tcl_AppendResult(interp,
"no argument given for -myport option",
(char *) NULL);
return TCL_ERROR;
}
if (TclSockGetPort(interp, argv[a], "tcp", &myport)
!= TCL_OK) {
return TCL_ERROR;
}
} else if (strcmp(arg, "-async") == 0) {
if (server == 1) {
Tcl_AppendResult(interp,
"cannot set -async option for server sockets",
(char *) NULL);
return TCL_ERROR;
}
async = 1;
} else {
Tcl_AppendResult(interp, "bad option \"", arg,
"\", must be -async, -myaddr, -myport, or -server",
(char *) NULL);
return TCL_ERROR;
}
} else {
break;
}
}
if (server) {
host = myaddr;
if (myport != 0) {
Tcl_AppendResult(interp, "Option -myport is not valid for servers",
NULL);
return TCL_ERROR;
}
} else if (a < argc) {
host = argv[a];
a++;
} else {
wrongNumArgs:
Tcl_AppendResult(interp, "wrong # args: should be either:\n",
argv[0],
" ?-myaddr addr? ?-myport myport? ?-async? host port\n",
argv[0],
" -server command ?-myaddr addr? port",
(char *) NULL);
return TCL_ERROR;
}
if (a == argc-1) {
if (TclSockGetPort(interp, argv[a], "tcp", &port) != TCL_OK) {
return TCL_ERROR;
}
} else {
goto wrongNumArgs;
}
if (server) {
acceptCallbackPtr = (AcceptCallback *) ckalloc((unsigned)
sizeof(AcceptCallback));
copyScript = ckalloc((unsigned) strlen(script) + 1);
strcpy(copyScript, script);
acceptCallbackPtr->script = copyScript;
acceptCallbackPtr->interp = interp;
chan = Tcl_OpenTcpServer(interp, port, host, AcceptCallbackProc,
(ClientData) acceptCallbackPtr);
if (chan == (Tcl_Channel) NULL) {
ckfree(copyScript);
ckfree((char *) acceptCallbackPtr);
return TCL_ERROR;
}
RegisterTcpServerInterpCleanup(interp, acceptCallbackPtr);
Tcl_CreateCloseHandler(chan, TcpServerCloseProc,
(ClientData) acceptCallbackPtr);
} else {
chan = Tcl_OpenTcpClient(interp, port, host, myaddr, myport, async);
if (chan == (Tcl_Channel) NULL) {
return TCL_ERROR;
}
}
Tcl_RegisterChannel(interp, chan);
Tcl_AppendResult(interp, Tcl_GetChannelName(chan), (char *) NULL);
return TCL_OK;
}
#endif