/*
|
/*
|
* tclIOCmd.c --
|
* tclIOCmd.c --
|
*
|
*
|
* Contains the definitions of most of the Tcl commands relating to IO.
|
* Contains the definitions of most of the Tcl commands relating to IO.
|
*
|
*
|
* Copyright (c) 1995-1996 Sun Microsystems, Inc.
|
* Copyright (c) 1995-1996 Sun Microsystems, Inc.
|
*
|
*
|
* See the file "license.terms" for information on usage and redistribution
|
* See the file "license.terms" for information on usage and redistribution
|
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
*
|
*
|
* RCS: @(#) $Id: tclIOCmd.c,v 1.1.1.1 2002-01-16 10:25:27 markom Exp $
|
* RCS: @(#) $Id: tclIOCmd.c,v 1.1.1.1 2002-01-16 10:25:27 markom Exp $
|
*/
|
*/
|
|
|
#include "tclInt.h"
|
#include "tclInt.h"
|
#include "tclPort.h"
|
#include "tclPort.h"
|
|
|
/*
|
/*
|
* Return at most this number of bytes in one call to Tcl_Read:
|
* Return at most this number of bytes in one call to Tcl_Read:
|
*/
|
*/
|
|
|
#define TCL_READ_CHUNK_SIZE 4096
|
#define TCL_READ_CHUNK_SIZE 4096
|
|
|
/*
|
/*
|
* Callback structure for accept callback in a TCP server.
|
* Callback structure for accept callback in a TCP server.
|
*/
|
*/
|
|
|
typedef struct AcceptCallback {
|
typedef struct AcceptCallback {
|
char *script; /* Script to invoke. */
|
char *script; /* Script to invoke. */
|
Tcl_Interp *interp; /* Interpreter in which to run it. */
|
Tcl_Interp *interp; /* Interpreter in which to run it. */
|
} AcceptCallback;
|
} AcceptCallback;
|
|
|
/*
|
/*
|
* Static functions for this file:
|
* Static functions for this file:
|
*/
|
*/
|
|
|
static void AcceptCallbackProc _ANSI_ARGS_((ClientData callbackData,
|
static void AcceptCallbackProc _ANSI_ARGS_((ClientData callbackData,
|
Tcl_Channel chan, char *address, int port));
|
Tcl_Channel chan, char *address, int port));
|
static void RegisterTcpServerInterpCleanup _ANSI_ARGS_((Tcl_Interp *interp,
|
static void RegisterTcpServerInterpCleanup _ANSI_ARGS_((Tcl_Interp *interp,
|
AcceptCallback *acceptCallbackPtr));
|
AcceptCallback *acceptCallbackPtr));
|
static void TcpAcceptCallbacksDeleteProc _ANSI_ARGS_((
|
static void TcpAcceptCallbacksDeleteProc _ANSI_ARGS_((
|
ClientData clientData, Tcl_Interp *interp));
|
ClientData clientData, Tcl_Interp *interp));
|
static void TcpServerCloseProc _ANSI_ARGS_((ClientData callbackData));
|
static void TcpServerCloseProc _ANSI_ARGS_((ClientData callbackData));
|
static void UnregisterTcpServerInterpCleanupProc _ANSI_ARGS_((
|
static void UnregisterTcpServerInterpCleanupProc _ANSI_ARGS_((
|
Tcl_Interp *interp, AcceptCallback *acceptCallbackPtr));
|
Tcl_Interp *interp, AcceptCallback *acceptCallbackPtr));
|
|
|
/*
|
/*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*
|
*
|
* Tcl_PutsObjCmd --
|
* Tcl_PutsObjCmd --
|
*
|
*
|
* This procedure is invoked to process the "puts" Tcl command.
|
* This procedure is invoked to process the "puts" Tcl command.
|
* See the user documentation for details on what it does.
|
* See the user documentation for details on what it does.
|
*
|
*
|
* Results:
|
* Results:
|
* A standard Tcl result.
|
* A standard Tcl result.
|
*
|
*
|
* Side effects:
|
* Side effects:
|
* Produces output on a channel.
|
* Produces output on a channel.
|
*
|
*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*/
|
*/
|
|
|
/* ARGSUSED */
|
/* ARGSUSED */
|
int
|
int
|
Tcl_PutsObjCmd(dummy, interp, objc, objv)
|
Tcl_PutsObjCmd(dummy, interp, objc, objv)
|
ClientData dummy; /* Not used. */
|
ClientData dummy; /* Not used. */
|
Tcl_Interp *interp; /* Current interpreter. */
|
Tcl_Interp *interp; /* Current interpreter. */
|
int objc; /* Number of arguments. */
|
int objc; /* Number of arguments. */
|
Tcl_Obj *CONST objv[]; /* Argument objects. */
|
Tcl_Obj *CONST objv[]; /* Argument objects. */
|
{
|
{
|
Tcl_Channel chan; /* The channel to puts on. */
|
Tcl_Channel chan; /* The channel to puts on. */
|
int i; /* Counter. */
|
int i; /* Counter. */
|
int newline; /* Add a newline at end? */
|
int newline; /* Add a newline at end? */
|
char *channelId; /* Name of channel for puts. */
|
char *channelId; /* Name of channel for puts. */
|
int result; /* Result of puts operation. */
|
int result; /* Result of puts operation. */
|
int mode; /* Mode in which channel is opened. */
|
int mode; /* Mode in which channel is opened. */
|
char *arg;
|
char *arg;
|
int length;
|
int length;
|
Tcl_Obj *resultPtr;
|
Tcl_Obj *resultPtr;
|
|
|
i = 1;
|
i = 1;
|
newline = 1;
|
newline = 1;
|
if ((objc >= 2) && (strcmp(Tcl_GetStringFromObj(objv[1], NULL),
|
if ((objc >= 2) && (strcmp(Tcl_GetStringFromObj(objv[1], NULL),
|
"-nonewline") == 0)) {
|
"-nonewline") == 0)) {
|
newline = 0;
|
newline = 0;
|
i++;
|
i++;
|
}
|
}
|
if ((i < (objc-3)) || (i >= objc)) {
|
if ((i < (objc-3)) || (i >= objc)) {
|
Tcl_WrongNumArgs(interp, 1, objv, "?-nonewline? ?channelId? string");
|
Tcl_WrongNumArgs(interp, 1, objv, "?-nonewline? ?channelId? string");
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
|
|
/*
|
/*
|
* The code below provides backwards compatibility with an old
|
* The code below provides backwards compatibility with an old
|
* form of the command that is no longer recommended or documented.
|
* form of the command that is no longer recommended or documented.
|
*/
|
*/
|
|
|
resultPtr = Tcl_NewObj();
|
resultPtr = Tcl_NewObj();
|
if (i == (objc-3)) {
|
if (i == (objc-3)) {
|
arg = Tcl_GetStringFromObj(objv[i+2], &length);
|
arg = Tcl_GetStringFromObj(objv[i+2], &length);
|
if (strncmp(arg, "nonewline", (size_t) length) != 0) {
|
if (strncmp(arg, "nonewline", (size_t) length) != 0) {
|
Tcl_AppendStringsToObj(resultPtr, "bad argument \"", arg,
|
Tcl_AppendStringsToObj(resultPtr, "bad argument \"", arg,
|
"\": should be \"nonewline\"", (char *) NULL);
|
"\": should be \"nonewline\"", (char *) NULL);
|
Tcl_SetObjResult(interp, resultPtr);
|
Tcl_SetObjResult(interp, resultPtr);
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
newline = 0;
|
newline = 0;
|
}
|
}
|
if (i == (objc-1)) {
|
if (i == (objc-1)) {
|
channelId = "stdout";
|
channelId = "stdout";
|
} else {
|
} else {
|
channelId = Tcl_GetStringFromObj(objv[i], NULL);
|
channelId = Tcl_GetStringFromObj(objv[i], NULL);
|
i++;
|
i++;
|
}
|
}
|
chan = Tcl_GetChannel(interp, channelId, &mode);
|
chan = Tcl_GetChannel(interp, channelId, &mode);
|
if (chan == (Tcl_Channel) NULL) {
|
if (chan == (Tcl_Channel) NULL) {
|
Tcl_DecrRefCount(resultPtr);
|
Tcl_DecrRefCount(resultPtr);
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
if ((mode & TCL_WRITABLE) == 0) {
|
if ((mode & TCL_WRITABLE) == 0) {
|
Tcl_AppendStringsToObj(resultPtr, "channel \"", channelId,
|
Tcl_AppendStringsToObj(resultPtr, "channel \"", channelId,
|
"\" wasn't opened for writing", (char *) NULL);
|
"\" wasn't opened for writing", (char *) NULL);
|
Tcl_SetObjResult(interp, resultPtr);
|
Tcl_SetObjResult(interp, resultPtr);
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
|
|
arg = Tcl_GetStringFromObj(objv[i], &length);
|
arg = Tcl_GetStringFromObj(objv[i], &length);
|
result = Tcl_Write(chan, arg, length);
|
result = Tcl_Write(chan, arg, length);
|
if (result < 0) {
|
if (result < 0) {
|
goto error;
|
goto error;
|
}
|
}
|
if (newline != 0) {
|
if (newline != 0) {
|
result = Tcl_Write(chan, "\n", 1);
|
result = Tcl_Write(chan, "\n", 1);
|
if (result < 0) {
|
if (result < 0) {
|
goto error;
|
goto error;
|
}
|
}
|
}
|
}
|
Tcl_SetObjResult(interp, resultPtr);
|
Tcl_SetObjResult(interp, resultPtr);
|
return TCL_OK;
|
return TCL_OK;
|
error:
|
error:
|
Tcl_AppendStringsToObj(resultPtr, "error writing \"",
|
Tcl_AppendStringsToObj(resultPtr, "error writing \"",
|
Tcl_GetChannelName(chan), "\": ", Tcl_PosixError(interp),
|
Tcl_GetChannelName(chan), "\": ", Tcl_PosixError(interp),
|
(char *) NULL);
|
(char *) NULL);
|
Tcl_SetObjResult(interp, resultPtr);
|
Tcl_SetObjResult(interp, resultPtr);
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
|
|
/*
|
/*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*
|
*
|
* Tcl_FlushObjCmd --
|
* Tcl_FlushObjCmd --
|
*
|
*
|
* This procedure is called to process the Tcl "flush" command.
|
* This procedure is called to process the Tcl "flush" command.
|
* See the user documentation for details on what it does.
|
* See the user documentation for details on what it does.
|
*
|
*
|
* Results:
|
* Results:
|
* A standard Tcl result.
|
* A standard Tcl result.
|
*
|
*
|
* Side effects:
|
* Side effects:
|
* May cause output to appear on the specified channel.
|
* May cause output to appear on the specified channel.
|
*
|
*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*/
|
*/
|
|
|
/* ARGSUSED */
|
/* ARGSUSED */
|
int
|
int
|
Tcl_FlushObjCmd(dummy, interp, objc, objv)
|
Tcl_FlushObjCmd(dummy, interp, objc, objv)
|
ClientData dummy; /* Not used. */
|
ClientData dummy; /* Not used. */
|
Tcl_Interp *interp; /* Current interpreter. */
|
Tcl_Interp *interp; /* Current interpreter. */
|
int objc; /* Number of arguments. */
|
int objc; /* Number of arguments. */
|
Tcl_Obj *CONST objv[]; /* Argument objects. */
|
Tcl_Obj *CONST objv[]; /* Argument objects. */
|
{
|
{
|
Tcl_Channel chan; /* The channel to flush on. */
|
Tcl_Channel chan; /* The channel to flush on. */
|
char *arg;
|
char *arg;
|
Tcl_Obj *resultPtr;
|
Tcl_Obj *resultPtr;
|
int mode;
|
int mode;
|
|
|
if (objc != 2) {
|
if (objc != 2) {
|
Tcl_WrongNumArgs(interp, 1, objv, "channelId");
|
Tcl_WrongNumArgs(interp, 1, objv, "channelId");
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
arg = Tcl_GetStringFromObj(objv[1], NULL);
|
arg = Tcl_GetStringFromObj(objv[1], NULL);
|
chan = Tcl_GetChannel(interp, arg, &mode);
|
chan = Tcl_GetChannel(interp, arg, &mode);
|
if (chan == (Tcl_Channel) NULL) {
|
if (chan == (Tcl_Channel) NULL) {
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
resultPtr = Tcl_GetObjResult(interp);
|
resultPtr = Tcl_GetObjResult(interp);
|
if ((mode & TCL_WRITABLE) == 0) {
|
if ((mode & TCL_WRITABLE) == 0) {
|
Tcl_AppendStringsToObj(resultPtr, "channel \"",
|
Tcl_AppendStringsToObj(resultPtr, "channel \"",
|
Tcl_GetStringFromObj(objv[1], NULL),
|
Tcl_GetStringFromObj(objv[1], NULL),
|
"\" wasn't opened for writing", (char *) NULL);
|
"\" wasn't opened for writing", (char *) NULL);
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
|
|
if (Tcl_Flush(chan) != TCL_OK) {
|
if (Tcl_Flush(chan) != TCL_OK) {
|
Tcl_AppendStringsToObj(resultPtr, "error flushing \"",
|
Tcl_AppendStringsToObj(resultPtr, "error flushing \"",
|
Tcl_GetChannelName(chan), "\": ", Tcl_PosixError(interp),
|
Tcl_GetChannelName(chan), "\": ", Tcl_PosixError(interp),
|
(char *) NULL);
|
(char *) NULL);
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
return TCL_OK;
|
return TCL_OK;
|
}
|
}
|
|
|
/*
|
/*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*
|
*
|
* Tcl_GetsObjCmd --
|
* Tcl_GetsObjCmd --
|
*
|
*
|
* This procedure is called to process the Tcl "gets" command.
|
* This procedure is called to process the Tcl "gets" command.
|
* See the user documentation for details on what it does.
|
* See the user documentation for details on what it does.
|
*
|
*
|
* Results:
|
* Results:
|
* A standard Tcl result.
|
* A standard Tcl result.
|
*
|
*
|
* Side effects:
|
* Side effects:
|
* May consume input from channel.
|
* May consume input from channel.
|
*
|
*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*/
|
*/
|
|
|
/* ARGSUSED */
|
/* ARGSUSED */
|
int
|
int
|
Tcl_GetsObjCmd(dummy, interp, objc, objv)
|
Tcl_GetsObjCmd(dummy, interp, objc, objv)
|
ClientData dummy; /* Not used. */
|
ClientData dummy; /* Not used. */
|
Tcl_Interp *interp; /* Current interpreter. */
|
Tcl_Interp *interp; /* Current interpreter. */
|
int objc; /* Number of arguments. */
|
int objc; /* Number of arguments. */
|
Tcl_Obj *CONST objv[]; /* Argument objects. */
|
Tcl_Obj *CONST objv[]; /* Argument objects. */
|
{
|
{
|
Tcl_Channel chan; /* The channel to read from. */
|
Tcl_Channel chan; /* The channel to read from. */
|
int lineLen; /* Length of line just read. */
|
int lineLen; /* Length of line just read. */
|
int mode; /* Mode in which channel is opened. */
|
int mode; /* Mode in which channel is opened. */
|
char *arg;
|
char *arg;
|
Tcl_Obj *resultPtr, *objPtr;
|
Tcl_Obj *resultPtr, *objPtr;
|
|
|
if ((objc != 2) && (objc != 3)) {
|
if ((objc != 2) && (objc != 3)) {
|
Tcl_WrongNumArgs(interp, 1, objv, "channelId ?varName?");
|
Tcl_WrongNumArgs(interp, 1, objv, "channelId ?varName?");
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
arg = Tcl_GetStringFromObj(objv[1], NULL);
|
arg = Tcl_GetStringFromObj(objv[1], NULL);
|
chan = Tcl_GetChannel(interp, arg, &mode);
|
chan = Tcl_GetChannel(interp, arg, &mode);
|
if (chan == (Tcl_Channel) NULL) {
|
if (chan == (Tcl_Channel) NULL) {
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
resultPtr = Tcl_NewObj();
|
resultPtr = Tcl_NewObj();
|
if ((mode & TCL_READABLE) == 0) {
|
if ((mode & TCL_READABLE) == 0) {
|
Tcl_AppendStringsToObj(resultPtr, "channel \"", arg,
|
Tcl_AppendStringsToObj(resultPtr, "channel \"", arg,
|
"\" wasn't opened for reading", (char *) NULL);
|
"\" wasn't opened for reading", (char *) NULL);
|
Tcl_SetObjResult(interp, resultPtr);
|
Tcl_SetObjResult(interp, resultPtr);
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
|
|
lineLen = Tcl_GetsObj(chan, resultPtr);
|
lineLen = Tcl_GetsObj(chan, resultPtr);
|
if (lineLen < 0) {
|
if (lineLen < 0) {
|
if (!Tcl_Eof(chan) && !Tcl_InputBlocked(chan)) {
|
if (!Tcl_Eof(chan) && !Tcl_InputBlocked(chan)) {
|
Tcl_SetObjLength(resultPtr, 0);
|
Tcl_SetObjLength(resultPtr, 0);
|
Tcl_AppendStringsToObj(resultPtr, "error reading \"",
|
Tcl_AppendStringsToObj(resultPtr, "error reading \"",
|
Tcl_GetChannelName(chan), "\": ", Tcl_PosixError(interp),
|
Tcl_GetChannelName(chan), "\": ", Tcl_PosixError(interp),
|
(char *) NULL);
|
(char *) NULL);
|
Tcl_SetObjResult(interp, resultPtr);
|
Tcl_SetObjResult(interp, resultPtr);
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
lineLen = -1;
|
lineLen = -1;
|
}
|
}
|
if (objc == 3) {
|
if (objc == 3) {
|
Tcl_ResetResult(interp);
|
Tcl_ResetResult(interp);
|
objPtr = Tcl_ObjSetVar2(interp, objv[2], NULL,
|
objPtr = Tcl_ObjSetVar2(interp, objv[2], NULL,
|
resultPtr, TCL_LEAVE_ERR_MSG | TCL_PARSE_PART1);
|
resultPtr, TCL_LEAVE_ERR_MSG | TCL_PARSE_PART1);
|
if (objPtr == NULL) {
|
if (objPtr == NULL) {
|
Tcl_DecrRefCount(resultPtr);
|
Tcl_DecrRefCount(resultPtr);
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
Tcl_ResetResult(interp);
|
Tcl_ResetResult(interp);
|
Tcl_SetIntObj(Tcl_GetObjResult(interp), lineLen);
|
Tcl_SetIntObj(Tcl_GetObjResult(interp), lineLen);
|
return TCL_OK;
|
return TCL_OK;
|
}
|
}
|
Tcl_SetObjResult(interp, resultPtr);
|
Tcl_SetObjResult(interp, resultPtr);
|
return TCL_OK;
|
return TCL_OK;
|
}
|
}
|
|
|
/*
|
/*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*
|
*
|
* Tcl_ReadObjCmd --
|
* Tcl_ReadObjCmd --
|
*
|
*
|
* This procedure is invoked to process the Tcl "read" command.
|
* This procedure is invoked to process the Tcl "read" command.
|
* See the user documentation for details on what it does.
|
* See the user documentation for details on what it does.
|
*
|
*
|
* Results:
|
* Results:
|
* A standard Tcl result.
|
* A standard Tcl result.
|
*
|
*
|
* Side effects:
|
* Side effects:
|
* May consume input from channel.
|
* May consume input from channel.
|
*
|
*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*/
|
*/
|
|
|
/* ARGSUSED */
|
/* ARGSUSED */
|
int
|
int
|
Tcl_ReadObjCmd(dummy, interp, objc, objv)
|
Tcl_ReadObjCmd(dummy, interp, objc, objv)
|
ClientData dummy; /* Not used. */
|
ClientData dummy; /* Not used. */
|
Tcl_Interp *interp; /* Current interpreter. */
|
Tcl_Interp *interp; /* Current interpreter. */
|
int objc; /* Number of arguments. */
|
int objc; /* Number of arguments. */
|
Tcl_Obj *CONST objv[]; /* Argument objects. */
|
Tcl_Obj *CONST objv[]; /* Argument objects. */
|
{
|
{
|
Tcl_Channel chan; /* The channel to read from. */
|
Tcl_Channel chan; /* The channel to read from. */
|
int newline, i; /* Discard newline at end? */
|
int newline, i; /* Discard newline at end? */
|
int toRead; /* How many bytes to read? */
|
int toRead; /* How many bytes to read? */
|
int toReadNow; /* How many bytes to attempt to
|
int toReadNow; /* How many bytes to attempt to
|
* read in the current iteration? */
|
* read in the current iteration? */
|
int charactersRead; /* How many characters were read? */
|
int charactersRead; /* How many characters were read? */
|
int charactersReadNow; /* How many characters were read
|
int charactersReadNow; /* How many characters were read
|
* in this iteration? */
|
* in this iteration? */
|
int mode; /* Mode in which channel is opened. */
|
int mode; /* Mode in which channel is opened. */
|
int bufSize; /* Channel buffer size; used to decide
|
int bufSize; /* Channel buffer size; used to decide
|
* in what chunk sizes to read from
|
* in what chunk sizes to read from
|
* the channel. */
|
* the channel. */
|
char *arg;
|
char *arg;
|
Tcl_Obj *resultPtr;
|
Tcl_Obj *resultPtr;
|
|
|
if ((objc != 2) && (objc != 3)) {
|
if ((objc != 2) && (objc != 3)) {
|
argerror:
|
argerror:
|
Tcl_WrongNumArgs(interp, 1, objv, "channelId ?numBytes?");
|
Tcl_WrongNumArgs(interp, 1, objv, "channelId ?numBytes?");
|
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), " or \"",
|
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), " or \"",
|
Tcl_GetStringFromObj(objv[0], NULL),
|
Tcl_GetStringFromObj(objv[0], NULL),
|
" ?-nonewline? channelId\"", (char *) NULL);
|
" ?-nonewline? channelId\"", (char *) NULL);
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
i = 1;
|
i = 1;
|
newline = 0;
|
newline = 0;
|
if (strcmp(Tcl_GetStringFromObj(objv[1], NULL), "-nonewline") == 0) {
|
if (strcmp(Tcl_GetStringFromObj(objv[1], NULL), "-nonewline") == 0) {
|
newline = 1;
|
newline = 1;
|
i++;
|
i++;
|
}
|
}
|
|
|
if (i == objc) {
|
if (i == objc) {
|
goto argerror;
|
goto argerror;
|
}
|
}
|
|
|
arg = Tcl_GetStringFromObj(objv[i], NULL);
|
arg = Tcl_GetStringFromObj(objv[i], NULL);
|
chan = Tcl_GetChannel(interp, arg, &mode);
|
chan = Tcl_GetChannel(interp, arg, &mode);
|
if (chan == (Tcl_Channel) NULL) {
|
if (chan == (Tcl_Channel) NULL) {
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
if ((mode & TCL_READABLE) == 0) {
|
if ((mode & TCL_READABLE) == 0) {
|
resultPtr = Tcl_GetObjResult(interp);
|
resultPtr = Tcl_GetObjResult(interp);
|
Tcl_AppendStringsToObj(resultPtr, "channel \"", arg,
|
Tcl_AppendStringsToObj(resultPtr, "channel \"", arg,
|
"\" wasn't opened for reading", (char *) NULL);
|
"\" wasn't opened for reading", (char *) NULL);
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
|
|
i++; /* Consumed channel name. */
|
i++; /* Consumed channel name. */
|
|
|
/*
|
/*
|
* Compute how many bytes to read, and see whether the final
|
* Compute how many bytes to read, and see whether the final
|
* newline should be dropped.
|
* newline should be dropped.
|
*/
|
*/
|
|
|
toRead = INT_MAX;
|
toRead = INT_MAX;
|
if (i < objc) {
|
if (i < objc) {
|
arg = Tcl_GetStringFromObj(objv[i], NULL);
|
arg = Tcl_GetStringFromObj(objv[i], NULL);
|
if (isdigit((unsigned char) (arg[0]))) {
|
if (isdigit((unsigned char) (arg[0]))) {
|
if (Tcl_GetIntFromObj(interp, objv[i], &toRead) != TCL_OK) {
|
if (Tcl_GetIntFromObj(interp, objv[i], &toRead) != TCL_OK) {
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
Tcl_ResetResult(interp);
|
Tcl_ResetResult(interp);
|
} else if (strcmp(arg, "nonewline") == 0) {
|
} else if (strcmp(arg, "nonewline") == 0) {
|
newline = 1;
|
newline = 1;
|
} else {
|
} else {
|
resultPtr = Tcl_GetObjResult(interp);
|
resultPtr = Tcl_GetObjResult(interp);
|
Tcl_AppendStringsToObj(resultPtr, "bad argument \"", arg,
|
Tcl_AppendStringsToObj(resultPtr, "bad argument \"", arg,
|
"\": should be \"nonewline\"", (char *) NULL);
|
"\": should be \"nonewline\"", (char *) NULL);
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
}
|
}
|
|
|
/*
|
/*
|
* Create a new object and use that instead of the interpreter
|
* Create a new object and use that instead of the interpreter
|
* result. We cannot use the interpreter's result object because
|
* result. We cannot use the interpreter's result object because
|
* it may get smashed at any time by recursive calls.
|
* it may get smashed at any time by recursive calls.
|
*/
|
*/
|
|
|
resultPtr = Tcl_NewObj();
|
resultPtr = Tcl_NewObj();
|
|
|
bufSize = Tcl_GetChannelBufferSize(chan);
|
bufSize = Tcl_GetChannelBufferSize(chan);
|
|
|
/*
|
/*
|
* If the caller specified a maximum length to read, then that is
|
* If the caller specified a maximum length to read, then that is
|
* a good size to preallocate.
|
* a good size to preallocate.
|
*/
|
*/
|
|
|
if ((toRead != INT_MAX) && (toRead > bufSize)) {
|
if ((toRead != INT_MAX) && (toRead > bufSize)) {
|
Tcl_SetObjLength(resultPtr, toRead);
|
Tcl_SetObjLength(resultPtr, toRead);
|
}
|
}
|
|
|
for (charactersRead = 0; charactersRead < toRead; ) {
|
for (charactersRead = 0; charactersRead < toRead; ) {
|
toReadNow = toRead - charactersRead;
|
toReadNow = toRead - charactersRead;
|
if (toReadNow > bufSize) {
|
if (toReadNow > bufSize) {
|
toReadNow = bufSize;
|
toReadNow = bufSize;
|
}
|
}
|
|
|
/*
|
/*
|
* NOTE: This is a NOOP if we set the size (above) to the
|
* NOTE: This is a NOOP if we set the size (above) to the
|
* number of bytes we expect to read. In the degenerate
|
* number of bytes we expect to read. In the degenerate
|
* case, however, it will grow the buffer by the channel
|
* case, however, it will grow the buffer by the channel
|
* buffersize, which is 4K in most cases. This will result
|
* buffersize, which is 4K in most cases. This will result
|
* in inefficient copying for large files. This will be
|
* in inefficient copying for large files. This will be
|
* fixed in a future release.
|
* fixed in a future release.
|
*/
|
*/
|
|
|
Tcl_SetObjLength(resultPtr, charactersRead + toReadNow);
|
Tcl_SetObjLength(resultPtr, charactersRead + toReadNow);
|
charactersReadNow =
|
charactersReadNow =
|
Tcl_Read(chan, Tcl_GetStringFromObj(resultPtr, NULL)
|
Tcl_Read(chan, Tcl_GetStringFromObj(resultPtr, NULL)
|
+ charactersRead, toReadNow);
|
+ charactersRead, toReadNow);
|
if (charactersReadNow < 0) {
|
if (charactersReadNow < 0) {
|
Tcl_SetObjLength(resultPtr, 0);
|
Tcl_SetObjLength(resultPtr, 0);
|
Tcl_AppendStringsToObj(resultPtr, "error reading \"",
|
Tcl_AppendStringsToObj(resultPtr, "error reading \"",
|
Tcl_GetChannelName(chan), "\": ",
|
Tcl_GetChannelName(chan), "\": ",
|
Tcl_PosixError(interp), (char *) NULL);
|
Tcl_PosixError(interp), (char *) NULL);
|
Tcl_SetObjResult(interp, resultPtr);
|
Tcl_SetObjResult(interp, resultPtr);
|
|
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
|
|
/*
|
/*
|
* If we had a short read it means that we have either EOF
|
* If we had a short read it means that we have either EOF
|
* or BLOCKED on the channel, so break out.
|
* or BLOCKED on the channel, so break out.
|
*/
|
*/
|
|
|
charactersRead += charactersReadNow;
|
charactersRead += charactersReadNow;
|
|
|
/*
|
/*
|
* Do not call the driver again if we got a short read
|
* Do not call the driver again if we got a short read
|
*/
|
*/
|
|
|
if (charactersReadNow < toReadNow) {
|
if (charactersReadNow < toReadNow) {
|
break; /* Out of "for" loop. */
|
break; /* Out of "for" loop. */
|
}
|
}
|
}
|
}
|
|
|
/*
|
/*
|
* If requested, remove the last newline in the channel if at EOF.
|
* If requested, remove the last newline in the channel if at EOF.
|
*/
|
*/
|
|
|
if ((charactersRead > 0) && (newline) &&
|
if ((charactersRead > 0) && (newline) &&
|
(Tcl_GetStringFromObj(resultPtr, NULL)[charactersRead-1] == '\n')) {
|
(Tcl_GetStringFromObj(resultPtr, NULL)[charactersRead-1] == '\n')) {
|
charactersRead--;
|
charactersRead--;
|
}
|
}
|
Tcl_SetObjLength(resultPtr, charactersRead);
|
Tcl_SetObjLength(resultPtr, charactersRead);
|
|
|
/*
|
/*
|
* Now set the object into the interpreter result and release our
|
* Now set the object into the interpreter result and release our
|
* hold on it by decrrefing it.
|
* hold on it by decrrefing it.
|
*/
|
*/
|
|
|
Tcl_SetObjResult(interp, resultPtr);
|
Tcl_SetObjResult(interp, resultPtr);
|
|
|
return TCL_OK;
|
return TCL_OK;
|
}
|
}
|
|
|
/*
|
/*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*
|
*
|
* Tcl_SeekCmd --
|
* Tcl_SeekCmd --
|
*
|
*
|
* This procedure is invoked to process the Tcl "seek" command. See
|
* This procedure is invoked to process the Tcl "seek" command. See
|
* the user documentation for details on what it does.
|
* the user documentation for details on what it does.
|
*
|
*
|
* Results:
|
* Results:
|
* A standard Tcl result.
|
* A standard Tcl result.
|
*
|
*
|
* Side effects:
|
* Side effects:
|
* Moves the position of the access point on the specified channel.
|
* Moves the position of the access point on the specified channel.
|
* May flush queued output.
|
* May flush queued output.
|
*
|
*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*/
|
*/
|
|
|
/* ARGSUSED */
|
/* ARGSUSED */
|
int
|
int
|
Tcl_SeekCmd(clientData, interp, argc, argv)
|
Tcl_SeekCmd(clientData, interp, argc, argv)
|
ClientData clientData; /* Not used. */
|
ClientData clientData; /* Not used. */
|
Tcl_Interp *interp; /* Current interpreter. */
|
Tcl_Interp *interp; /* Current interpreter. */
|
int argc; /* Number of arguments. */
|
int argc; /* Number of arguments. */
|
char **argv; /* Argument strings. */
|
char **argv; /* Argument strings. */
|
{
|
{
|
Tcl_Channel chan; /* The channel to tell on. */
|
Tcl_Channel chan; /* The channel to tell on. */
|
int offset, mode; /* Where to seek? */
|
int offset, mode; /* Where to seek? */
|
int result; /* Of calling Tcl_Seek. */
|
int result; /* Of calling Tcl_Seek. */
|
|
|
if ((argc != 3) && (argc != 4)) {
|
if ((argc != 3) && (argc != 4)) {
|
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
" channelId offset ?origin?\"", (char *) NULL);
|
" channelId offset ?origin?\"", (char *) NULL);
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
chan = Tcl_GetChannel(interp, argv[1], NULL);
|
chan = Tcl_GetChannel(interp, argv[1], NULL);
|
if (chan == (Tcl_Channel) NULL) {
|
if (chan == (Tcl_Channel) NULL) {
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
if (Tcl_GetInt(interp, argv[2], &offset) != TCL_OK) {
|
if (Tcl_GetInt(interp, argv[2], &offset) != TCL_OK) {
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
mode = SEEK_SET;
|
mode = SEEK_SET;
|
if (argc == 4) {
|
if (argc == 4) {
|
size_t length;
|
size_t length;
|
int c;
|
int c;
|
|
|
length = strlen(argv[3]);
|
length = strlen(argv[3]);
|
c = argv[3][0];
|
c = argv[3][0];
|
if ((c == 's') && (strncmp(argv[3], "start", length) == 0)) {
|
if ((c == 's') && (strncmp(argv[3], "start", length) == 0)) {
|
mode = SEEK_SET;
|
mode = SEEK_SET;
|
} else if ((c == 'c') && (strncmp(argv[3], "current", length) == 0)) {
|
} else if ((c == 'c') && (strncmp(argv[3], "current", length) == 0)) {
|
mode = SEEK_CUR;
|
mode = SEEK_CUR;
|
} else if ((c == 'e') && (strncmp(argv[3], "end", length) == 0)) {
|
} else if ((c == 'e') && (strncmp(argv[3], "end", length) == 0)) {
|
mode = SEEK_END;
|
mode = SEEK_END;
|
} else {
|
} else {
|
Tcl_AppendResult(interp, "bad origin \"", argv[3],
|
Tcl_AppendResult(interp, "bad origin \"", argv[3],
|
"\": should be start, current, or end", (char *) NULL);
|
"\": should be start, current, or end", (char *) NULL);
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
}
|
}
|
|
|
result = Tcl_Seek(chan, offset, mode);
|
result = Tcl_Seek(chan, offset, mode);
|
if (result == -1) {
|
if (result == -1) {
|
Tcl_AppendResult(interp, "error during seek on \"",
|
Tcl_AppendResult(interp, "error during seek on \"",
|
Tcl_GetChannelName(chan), "\": ",
|
Tcl_GetChannelName(chan), "\": ",
|
Tcl_PosixError(interp), (char *) NULL);
|
Tcl_PosixError(interp), (char *) NULL);
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
return TCL_OK;
|
return TCL_OK;
|
}
|
}
|
|
|
/*
|
/*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*
|
*
|
* Tcl_TellCmd --
|
* Tcl_TellCmd --
|
*
|
*
|
* This procedure is invoked to process the Tcl "tell" command.
|
* This procedure is invoked to process the Tcl "tell" command.
|
* See the user documentation for details on what it does.
|
* See the user documentation for details on what it does.
|
*
|
*
|
* Results:
|
* Results:
|
* A standard Tcl result.
|
* A standard Tcl result.
|
*
|
*
|
* Side effects:
|
* Side effects:
|
* None.
|
* None.
|
*
|
*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*/
|
*/
|
|
|
/* ARGSUSED */
|
/* ARGSUSED */
|
int
|
int
|
Tcl_TellCmd(clientData, interp, argc, argv)
|
Tcl_TellCmd(clientData, interp, argc, argv)
|
ClientData clientData; /* Not used. */
|
ClientData clientData; /* Not used. */
|
Tcl_Interp *interp; /* Current interpreter. */
|
Tcl_Interp *interp; /* Current interpreter. */
|
int argc; /* Number of arguments. */
|
int argc; /* Number of arguments. */
|
char **argv; /* Argument strings. */
|
char **argv; /* Argument strings. */
|
{
|
{
|
Tcl_Channel chan; /* The channel to tell on. */
|
Tcl_Channel chan; /* The channel to tell on. */
|
char buf[40];
|
char buf[40];
|
|
|
if (argc != 2) {
|
if (argc != 2) {
|
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
" channelId\"", (char *) NULL);
|
" channelId\"", (char *) NULL);
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
/*
|
/*
|
* Try to find a channel with the right name and permissions in
|
* Try to find a channel with the right name and permissions in
|
* the IO channel table of this interpreter.
|
* the IO channel table of this interpreter.
|
*/
|
*/
|
|
|
chan = Tcl_GetChannel(interp, argv[1], NULL);
|
chan = Tcl_GetChannel(interp, argv[1], NULL);
|
if (chan == (Tcl_Channel) NULL) {
|
if (chan == (Tcl_Channel) NULL) {
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
TclFormatInt(buf, Tcl_Tell(chan));
|
TclFormatInt(buf, Tcl_Tell(chan));
|
Tcl_SetResult(interp, buf, TCL_VOLATILE);
|
Tcl_SetResult(interp, buf, TCL_VOLATILE);
|
return TCL_OK;
|
return TCL_OK;
|
}
|
}
|
|
|
/*
|
/*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*
|
*
|
* Tcl_CloseObjCmd --
|
* Tcl_CloseObjCmd --
|
*
|
*
|
* This procedure is invoked to process the Tcl "close" command.
|
* This procedure is invoked to process the Tcl "close" command.
|
* See the user documentation for details on what it does.
|
* See the user documentation for details on what it does.
|
*
|
*
|
* Results:
|
* Results:
|
* A standard Tcl result.
|
* A standard Tcl result.
|
*
|
*
|
* Side effects:
|
* Side effects:
|
* May discard queued input; may flush queued output.
|
* May discard queued input; may flush queued output.
|
*
|
*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*/
|
*/
|
|
|
/* ARGSUSED */
|
/* ARGSUSED */
|
int
|
int
|
Tcl_CloseObjCmd(clientData, interp, objc, objv)
|
Tcl_CloseObjCmd(clientData, interp, objc, objv)
|
ClientData clientData; /* Not used. */
|
ClientData clientData; /* Not used. */
|
Tcl_Interp *interp; /* Current interpreter. */
|
Tcl_Interp *interp; /* Current interpreter. */
|
int objc; /* Number of arguments. */
|
int objc; /* Number of arguments. */
|
Tcl_Obj *CONST objv[]; /* Argument objects. */
|
Tcl_Obj *CONST objv[]; /* Argument objects. */
|
{
|
{
|
Tcl_Channel chan; /* The channel to close. */
|
Tcl_Channel chan; /* The channel to close. */
|
int len; /* Length of error output. */
|
int len; /* Length of error output. */
|
char *arg;
|
char *arg;
|
|
|
if (objc != 2) {
|
if (objc != 2) {
|
Tcl_WrongNumArgs(interp, 1, objv, "channelId");
|
Tcl_WrongNumArgs(interp, 1, objv, "channelId");
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
|
|
arg = Tcl_GetStringFromObj(objv[1], NULL);
|
arg = Tcl_GetStringFromObj(objv[1], NULL);
|
chan = Tcl_GetChannel(interp, arg, NULL);
|
chan = Tcl_GetChannel(interp, arg, NULL);
|
if (chan == (Tcl_Channel) NULL) {
|
if (chan == (Tcl_Channel) NULL) {
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
|
|
if (Tcl_UnregisterChannel(interp, chan) != TCL_OK) {
|
if (Tcl_UnregisterChannel(interp, chan) != TCL_OK) {
|
/*
|
/*
|
* If there is an error message and it ends with a newline, remove
|
* If there is an error message and it ends with a newline, remove
|
* the newline. This is done for command pipeline channels where the
|
* the newline. This is done for command pipeline channels where the
|
* error output from the subprocesses is stored in interp->result.
|
* error output from the subprocesses is stored in interp->result.
|
*
|
*
|
* NOTE: This is likely to not have any effect on regular error
|
* NOTE: This is likely to not have any effect on regular error
|
* messages produced by drivers during the closing of a channel,
|
* messages produced by drivers during the closing of a channel,
|
* because the Tcl convention is that such error messages do not
|
* because the Tcl convention is that such error messages do not
|
* have a terminating newline.
|
* have a terminating newline.
|
*/
|
*/
|
|
|
len = strlen(interp->result);
|
len = strlen(interp->result);
|
if ((len > 0) && (interp->result[len - 1] == '\n')) {
|
if ((len > 0) && (interp->result[len - 1] == '\n')) {
|
interp->result[len - 1] = '\0';
|
interp->result[len - 1] = '\0';
|
}
|
}
|
|
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
|
|
return TCL_OK;
|
return TCL_OK;
|
}
|
}
|
|
|
/*
|
/*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*
|
*
|
* Tcl_FconfigureCmd --
|
* Tcl_FconfigureCmd --
|
*
|
*
|
* This procedure is invoked to process the Tcl "fconfigure" command.
|
* This procedure is invoked to process the Tcl "fconfigure" command.
|
* See the user documentation for details on what it does.
|
* See the user documentation for details on what it does.
|
*
|
*
|
* Results:
|
* Results:
|
* A standard Tcl result.
|
* A standard Tcl result.
|
*
|
*
|
* Side effects:
|
* Side effects:
|
* May modify the behavior of an IO channel.
|
* May modify the behavior of an IO channel.
|
*
|
*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*/
|
*/
|
|
|
/* ARGSUSED */
|
/* ARGSUSED */
|
int
|
int
|
Tcl_FconfigureCmd(clientData, interp, argc, argv)
|
Tcl_FconfigureCmd(clientData, interp, argc, argv)
|
ClientData clientData; /* Not used. */
|
ClientData clientData; /* Not used. */
|
Tcl_Interp *interp; /* Current interpreter. */
|
Tcl_Interp *interp; /* Current interpreter. */
|
int argc; /* Number of arguments. */
|
int argc; /* Number of arguments. */
|
char **argv; /* Argument strings. */
|
char **argv; /* Argument strings. */
|
{
|
{
|
Tcl_Channel chan; /* The channel to set a mode on. */
|
Tcl_Channel chan; /* The channel to set a mode on. */
|
int i; /* Iterate over arg-value pairs. */
|
int i; /* Iterate over arg-value pairs. */
|
Tcl_DString ds; /* DString to hold result of
|
Tcl_DString ds; /* DString to hold result of
|
* calling Tcl_GetChannelOption. */
|
* calling Tcl_GetChannelOption. */
|
|
|
if ((argc < 2) || (((argc % 2) == 1) && (argc != 3))) {
|
if ((argc < 2) || (((argc % 2) == 1) && (argc != 3))) {
|
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
" channelId ?optionName? ?value? ?optionName value?...\"",
|
" channelId ?optionName? ?value? ?optionName value?...\"",
|
(char *) NULL);
|
(char *) NULL);
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
chan = Tcl_GetChannel(interp, argv[1], NULL);
|
chan = Tcl_GetChannel(interp, argv[1], NULL);
|
if (chan == (Tcl_Channel) NULL) {
|
if (chan == (Tcl_Channel) NULL) {
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
if (argc == 2) {
|
if (argc == 2) {
|
Tcl_DStringInit(&ds);
|
Tcl_DStringInit(&ds);
|
if (Tcl_GetChannelOption(interp, chan, (char *) NULL, &ds) != TCL_OK) {
|
if (Tcl_GetChannelOption(interp, chan, (char *) NULL, &ds) != TCL_OK) {
|
Tcl_DStringFree(&ds);
|
Tcl_DStringFree(&ds);
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
Tcl_DStringResult(interp, &ds);
|
Tcl_DStringResult(interp, &ds);
|
return TCL_OK;
|
return TCL_OK;
|
}
|
}
|
if (argc == 3) {
|
if (argc == 3) {
|
Tcl_DStringInit(&ds);
|
Tcl_DStringInit(&ds);
|
if (Tcl_GetChannelOption(interp, chan, argv[2], &ds) != TCL_OK) {
|
if (Tcl_GetChannelOption(interp, chan, argv[2], &ds) != TCL_OK) {
|
Tcl_DStringFree(&ds);
|
Tcl_DStringFree(&ds);
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
Tcl_DStringResult(interp, &ds);
|
Tcl_DStringResult(interp, &ds);
|
return TCL_OK;
|
return TCL_OK;
|
}
|
}
|
for (i = 3; i < argc; i += 2) {
|
for (i = 3; i < argc; i += 2) {
|
if (Tcl_SetChannelOption(interp, chan, argv[i-1], argv[i]) != TCL_OK) {
|
if (Tcl_SetChannelOption(interp, chan, argv[i-1], argv[i]) != TCL_OK) {
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
}
|
}
|
return TCL_OK;
|
return TCL_OK;
|
}
|
}
|
|
|
/*
|
/*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*
|
*
|
* Tcl_EofObjCmd --
|
* Tcl_EofObjCmd --
|
*
|
*
|
* This procedure is invoked to process the Tcl "eof" command.
|
* This procedure is invoked to process the Tcl "eof" command.
|
* See the user documentation for details on what it does.
|
* See the user documentation for details on what it does.
|
*
|
*
|
* Results:
|
* Results:
|
* A standard Tcl result.
|
* A standard Tcl result.
|
*
|
*
|
* Side effects:
|
* Side effects:
|
* Sets interp->result to "0" or "1" depending on whether the
|
* Sets interp->result to "0" or "1" depending on whether the
|
* specified channel has an EOF condition.
|
* specified channel has an EOF condition.
|
*
|
*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*/
|
*/
|
|
|
/* ARGSUSED */
|
/* ARGSUSED */
|
int
|
int
|
Tcl_EofObjCmd(unused, interp, objc, objv)
|
Tcl_EofObjCmd(unused, interp, objc, objv)
|
ClientData unused; /* Not used. */
|
ClientData unused; /* Not used. */
|
Tcl_Interp *interp; /* Current interpreter. */
|
Tcl_Interp *interp; /* Current interpreter. */
|
int objc; /* Number of arguments. */
|
int objc; /* Number of arguments. */
|
Tcl_Obj *CONST objv[]; /* Argument objects. */
|
Tcl_Obj *CONST objv[]; /* Argument objects. */
|
{
|
{
|
Tcl_Channel chan; /* The channel to query for EOF. */
|
Tcl_Channel chan; /* The channel to query for EOF. */
|
int mode; /* Mode in which channel is opened. */
|
int mode; /* Mode in which channel is opened. */
|
char buf[40];
|
char buf[40];
|
char *arg;
|
char *arg;
|
|
|
if (objc != 2) {
|
if (objc != 2) {
|
Tcl_WrongNumArgs(interp, 1, objv, "channelId");
|
Tcl_WrongNumArgs(interp, 1, objv, "channelId");
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
|
|
arg = Tcl_GetStringFromObj(objv[1], NULL);
|
arg = Tcl_GetStringFromObj(objv[1], NULL);
|
chan = Tcl_GetChannel(interp, arg, &mode);
|
chan = Tcl_GetChannel(interp, arg, &mode);
|
if (chan == (Tcl_Channel) NULL) {
|
if (chan == (Tcl_Channel) NULL) {
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
|
|
TclFormatInt(buf, Tcl_Eof(chan) ? 1 : 0);
|
TclFormatInt(buf, Tcl_Eof(chan) ? 1 : 0);
|
Tcl_SetResult(interp, buf, TCL_VOLATILE);
|
Tcl_SetResult(interp, buf, TCL_VOLATILE);
|
return TCL_OK;
|
return TCL_OK;
|
}
|
}
|
|
|
/*
|
/*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*
|
*
|
* Tcl_ExecCmd --
|
* Tcl_ExecCmd --
|
*
|
*
|
* This procedure is invoked to process the "exec" Tcl command.
|
* This procedure is invoked to process the "exec" Tcl command.
|
* See the user documentation for details on what it does.
|
* See the user documentation for details on what it does.
|
*
|
*
|
* Results:
|
* Results:
|
* A standard Tcl result.
|
* A standard Tcl result.
|
*
|
*
|
* Side effects:
|
* Side effects:
|
* See the user documentation.
|
* See the user documentation.
|
*
|
*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*/
|
*/
|
|
|
/* ARGSUSED */
|
/* ARGSUSED */
|
int
|
int
|
Tcl_ExecCmd(dummy, interp, argc, argv)
|
Tcl_ExecCmd(dummy, interp, argc, argv)
|
ClientData dummy; /* Not used. */
|
ClientData dummy; /* Not used. */
|
Tcl_Interp *interp; /* Current interpreter. */
|
Tcl_Interp *interp; /* Current interpreter. */
|
int argc; /* Number of arguments. */
|
int argc; /* Number of arguments. */
|
char **argv; /* Argument strings. */
|
char **argv; /* Argument strings. */
|
{
|
{
|
#ifdef MAC_TCL
|
#ifdef MAC_TCL
|
Tcl_AppendResult(interp, "exec not implemented under Mac OS",
|
Tcl_AppendResult(interp, "exec not implemented under Mac OS",
|
(char *)NULL);
|
(char *)NULL);
|
return TCL_ERROR;
|
return TCL_ERROR;
|
#else /* !MAC_TCL */
|
#else /* !MAC_TCL */
|
int keepNewline, firstWord, background, length, result;
|
int keepNewline, firstWord, background, length, result;
|
Tcl_Channel chan;
|
Tcl_Channel chan;
|
Tcl_DString ds;
|
Tcl_DString ds;
|
int readSoFar, readNow, bufSize;
|
int readSoFar, readNow, bufSize;
|
|
|
/*
|
/*
|
* Check for a leading "-keepnewline" argument.
|
* Check for a leading "-keepnewline" argument.
|
*/
|
*/
|
|
|
keepNewline = 0;
|
keepNewline = 0;
|
for (firstWord = 1; (firstWord < argc) && (argv[firstWord][0] == '-');
|
for (firstWord = 1; (firstWord < argc) && (argv[firstWord][0] == '-');
|
firstWord++) {
|
firstWord++) {
|
if (strcmp(argv[firstWord], "-keepnewline") == 0) {
|
if (strcmp(argv[firstWord], "-keepnewline") == 0) {
|
keepNewline = 1;
|
keepNewline = 1;
|
} else if (strcmp(argv[firstWord], "--") == 0) {
|
} else if (strcmp(argv[firstWord], "--") == 0) {
|
firstWord++;
|
firstWord++;
|
break;
|
break;
|
} else {
|
} else {
|
Tcl_AppendResult(interp, "bad switch \"", argv[firstWord],
|
Tcl_AppendResult(interp, "bad switch \"", argv[firstWord],
|
"\": must be -keepnewline or --", (char *) NULL);
|
"\": must be -keepnewline or --", (char *) NULL);
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
}
|
}
|
|
|
if (argc <= firstWord) {
|
if (argc <= firstWord) {
|
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
" ?switches? arg ?arg ...?\"", (char *) NULL);
|
" ?switches? arg ?arg ...?\"", (char *) NULL);
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
|
|
/*
|
/*
|
* See if the command is to be run in background.
|
* See if the command is to be run in background.
|
*/
|
*/
|
|
|
background = 0;
|
background = 0;
|
if ((argv[argc-1][0] == '&') && (argv[argc-1][1] == 0)) {
|
if ((argv[argc-1][0] == '&') && (argv[argc-1][1] == 0)) {
|
argc--;
|
argc--;
|
argv[argc] = NULL;
|
argv[argc] = NULL;
|
background = 1;
|
background = 1;
|
}
|
}
|
|
|
chan = Tcl_OpenCommandChannel(interp, argc-firstWord,
|
chan = Tcl_OpenCommandChannel(interp, argc-firstWord,
|
argv+firstWord,
|
argv+firstWord,
|
(background ? 0 : TCL_STDOUT | TCL_STDERR));
|
(background ? 0 : TCL_STDOUT | TCL_STDERR));
|
|
|
if (chan == (Tcl_Channel) NULL) {
|
if (chan == (Tcl_Channel) NULL) {
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
|
|
if (background) {
|
if (background) {
|
|
|
/*
|
/*
|
* Get the list of PIDs from the pipeline into interp->result and
|
* Get the list of PIDs from the pipeline into interp->result and
|
* detach the PIDs (instead of waiting for them).
|
* detach the PIDs (instead of waiting for them).
|
*/
|
*/
|
|
|
TclGetAndDetachPids(interp, chan);
|
TclGetAndDetachPids(interp, chan);
|
|
|
if (Tcl_Close(interp, chan) != TCL_OK) {
|
if (Tcl_Close(interp, chan) != TCL_OK) {
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
return TCL_OK;
|
return TCL_OK;
|
}
|
}
|
|
|
if (Tcl_GetChannelHandle(chan, TCL_READABLE, NULL) == TCL_OK) {
|
if (Tcl_GetChannelHandle(chan, TCL_READABLE, NULL) == TCL_OK) {
|
#define EXEC_BUFFER_SIZE 4096
|
#define EXEC_BUFFER_SIZE 4096
|
|
|
Tcl_DStringInit(&ds);
|
Tcl_DStringInit(&ds);
|
readSoFar = 0; bufSize = 0;
|
readSoFar = 0; bufSize = 0;
|
while (1) {
|
while (1) {
|
bufSize += EXEC_BUFFER_SIZE;
|
bufSize += EXEC_BUFFER_SIZE;
|
Tcl_DStringSetLength(&ds, bufSize);
|
Tcl_DStringSetLength(&ds, bufSize);
|
readNow = Tcl_Read(chan, Tcl_DStringValue(&ds) + readSoFar,
|
readNow = Tcl_Read(chan, Tcl_DStringValue(&ds) + readSoFar,
|
EXEC_BUFFER_SIZE);
|
EXEC_BUFFER_SIZE);
|
if (readNow < 0) {
|
if (readNow < 0) {
|
Tcl_DStringFree(&ds);
|
Tcl_DStringFree(&ds);
|
Tcl_AppendResult(interp,
|
Tcl_AppendResult(interp,
|
"error reading output from command: ",
|
"error reading output from command: ",
|
Tcl_PosixError(interp), (char *) NULL);
|
Tcl_PosixError(interp), (char *) NULL);
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
readSoFar += readNow;
|
readSoFar += readNow;
|
if (readNow < EXEC_BUFFER_SIZE) {
|
if (readNow < EXEC_BUFFER_SIZE) {
|
break; /* Out of "while (1)" loop. */
|
break; /* Out of "while (1)" loop. */
|
}
|
}
|
}
|
}
|
Tcl_DStringSetLength(&ds, readSoFar);
|
Tcl_DStringSetLength(&ds, readSoFar);
|
Tcl_DStringResult(interp, &ds);
|
Tcl_DStringResult(interp, &ds);
|
}
|
}
|
|
|
result = Tcl_Close(interp, chan);
|
result = Tcl_Close(interp, chan);
|
|
|
/*
|
/*
|
* If the last character of interp->result is a newline, then remove
|
* If the last character of interp->result is a newline, then remove
|
* the newline character (the newline would just confuse things).
|
* the newline character (the newline would just confuse things).
|
* Special hack: must replace the old terminating null character
|
* Special hack: must replace the old terminating null character
|
* as a signal to Tcl_AppendResult et al. that we've mucked with
|
* as a signal to Tcl_AppendResult et al. that we've mucked with
|
* the string.
|
* the string.
|
*/
|
*/
|
|
|
length = strlen(interp->result);
|
length = strlen(interp->result);
|
if (!keepNewline && (length > 0) &&
|
if (!keepNewline && (length > 0) &&
|
(interp->result[length-1] == '\n')) {
|
(interp->result[length-1] == '\n')) {
|
interp->result[length-1] = '\0';
|
interp->result[length-1] = '\0';
|
interp->result[length] = 'x';
|
interp->result[length] = 'x';
|
}
|
}
|
|
|
return result;
|
return result;
|
#endif /* !MAC_TCL */
|
#endif /* !MAC_TCL */
|
}
|
}
|
|
|
/*
|
/*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*
|
*
|
* Tcl_FblockedObjCmd --
|
* Tcl_FblockedObjCmd --
|
*
|
*
|
* This procedure is invoked to process the Tcl "fblocked" command.
|
* This procedure is invoked to process the Tcl "fblocked" command.
|
* See the user documentation for details on what it does.
|
* See the user documentation for details on what it does.
|
*
|
*
|
* Results:
|
* Results:
|
* A standard Tcl result.
|
* A standard Tcl result.
|
*
|
*
|
* Side effects:
|
* Side effects:
|
* Sets interp->result to "0" or "1" depending on whether the
|
* Sets interp->result to "0" or "1" depending on whether the
|
* a preceding input operation on the channel would have blocked.
|
* a preceding input operation on the channel would have blocked.
|
*
|
*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*/
|
*/
|
|
|
/* ARGSUSED */
|
/* ARGSUSED */
|
int
|
int
|
Tcl_FblockedObjCmd(unused, interp, objc, objv)
|
Tcl_FblockedObjCmd(unused, interp, objc, objv)
|
ClientData unused; /* Not used. */
|
ClientData unused; /* Not used. */
|
Tcl_Interp *interp; /* Current interpreter. */
|
Tcl_Interp *interp; /* Current interpreter. */
|
int objc; /* Number of arguments. */
|
int objc; /* Number of arguments. */
|
Tcl_Obj *CONST objv[]; /* Argument objects. */
|
Tcl_Obj *CONST objv[]; /* Argument objects. */
|
{
|
{
|
Tcl_Channel chan; /* The channel to query for blocked. */
|
Tcl_Channel chan; /* The channel to query for blocked. */
|
int mode; /* Mode in which channel was opened. */
|
int mode; /* Mode in which channel was opened. */
|
char buf[40];
|
char buf[40];
|
char *arg;
|
char *arg;
|
|
|
if (objc != 2) {
|
if (objc != 2) {
|
Tcl_WrongNumArgs(interp, 1, objv, "channelId");
|
Tcl_WrongNumArgs(interp, 1, objv, "channelId");
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
|
|
arg = Tcl_GetStringFromObj(objv[1], NULL);
|
arg = Tcl_GetStringFromObj(objv[1], NULL);
|
chan = Tcl_GetChannel(interp, arg, &mode);
|
chan = Tcl_GetChannel(interp, arg, &mode);
|
if (chan == (Tcl_Channel) NULL) {
|
if (chan == (Tcl_Channel) NULL) {
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
if ((mode & TCL_READABLE) == 0) {
|
if ((mode & TCL_READABLE) == 0) {
|
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "channel \"",
|
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "channel \"",
|
Tcl_GetStringFromObj(objv[1], NULL),
|
Tcl_GetStringFromObj(objv[1], NULL),
|
"\" wasn't opened for reading", (char *) NULL);
|
"\" wasn't opened for reading", (char *) NULL);
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
|
|
TclFormatInt(buf, Tcl_InputBlocked(chan) ? 1 : 0);
|
TclFormatInt(buf, Tcl_InputBlocked(chan) ? 1 : 0);
|
Tcl_SetResult(interp, buf, TCL_VOLATILE);
|
Tcl_SetResult(interp, buf, TCL_VOLATILE);
|
return TCL_OK;
|
return TCL_OK;
|
}
|
}
|
|
|
/*
|
/*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*
|
*
|
* Tcl_OpenCmd --
|
* Tcl_OpenCmd --
|
*
|
*
|
* This procedure is invoked to process the "open" Tcl command.
|
* This procedure is invoked to process the "open" Tcl command.
|
* See the user documentation for details on what it does.
|
* See the user documentation for details on what it does.
|
*
|
*
|
* Results:
|
* Results:
|
* A standard Tcl result.
|
* A standard Tcl result.
|
*
|
*
|
* Side effects:
|
* Side effects:
|
* See the user documentation.
|
* See the user documentation.
|
*
|
*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*/
|
*/
|
|
|
/* ARGSUSED */
|
/* ARGSUSED */
|
int
|
int
|
Tcl_OpenCmd(notUsed, interp, argc, argv)
|
Tcl_OpenCmd(notUsed, interp, argc, argv)
|
ClientData notUsed; /* Not used. */
|
ClientData notUsed; /* Not used. */
|
Tcl_Interp *interp; /* Current interpreter. */
|
Tcl_Interp *interp; /* Current interpreter. */
|
int argc; /* Number of arguments. */
|
int argc; /* Number of arguments. */
|
char **argv; /* Argument strings. */
|
char **argv; /* Argument strings. */
|
{
|
{
|
int pipeline, prot;
|
int pipeline, prot;
|
char *modeString;
|
char *modeString;
|
Tcl_Channel chan;
|
Tcl_Channel chan;
|
|
|
if ((argc < 2) || (argc > 4)) {
|
if ((argc < 2) || (argc > 4)) {
|
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
" fileName ?access? ?permissions?\"", (char *) NULL);
|
" fileName ?access? ?permissions?\"", (char *) NULL);
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
prot = 0666;
|
prot = 0666;
|
if (argc == 2) {
|
if (argc == 2) {
|
modeString = "r";
|
modeString = "r";
|
} else {
|
} else {
|
modeString = argv[2];
|
modeString = argv[2];
|
if (argc == 4) {
|
if (argc == 4) {
|
if (Tcl_GetInt(interp, argv[3], &prot) != TCL_OK) {
|
if (Tcl_GetInt(interp, argv[3], &prot) != TCL_OK) {
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
pipeline = 0;
|
pipeline = 0;
|
if (argv[1][0] == '|') {
|
if (argv[1][0] == '|') {
|
pipeline = 1;
|
pipeline = 1;
|
}
|
}
|
|
|
/*
|
/*
|
* Open the file or create a process pipeline.
|
* Open the file or create a process pipeline.
|
*/
|
*/
|
|
|
if (!pipeline) {
|
if (!pipeline) {
|
chan = Tcl_OpenFileChannel(interp, argv[1], modeString, prot);
|
chan = Tcl_OpenFileChannel(interp, argv[1], modeString, prot);
|
} else {
|
} else {
|
#ifdef MAC_TCL
|
#ifdef MAC_TCL
|
Tcl_AppendResult(interp,
|
Tcl_AppendResult(interp,
|
"command pipelines not supported on Macintosh OS",
|
"command pipelines not supported on Macintosh OS",
|
(char *)NULL);
|
(char *)NULL);
|
return TCL_ERROR;
|
return TCL_ERROR;
|
#else
|
#else
|
int mode, seekFlag, cmdArgc;
|
int mode, seekFlag, cmdArgc;
|
char **cmdArgv;
|
char **cmdArgv;
|
|
|
if (Tcl_SplitList(interp, argv[1]+1, &cmdArgc, &cmdArgv) != TCL_OK) {
|
if (Tcl_SplitList(interp, argv[1]+1, &cmdArgc, &cmdArgv) != TCL_OK) {
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
|
|
mode = TclGetOpenMode(interp, modeString, &seekFlag);
|
mode = TclGetOpenMode(interp, modeString, &seekFlag);
|
if (mode == -1) {
|
if (mode == -1) {
|
chan = NULL;
|
chan = NULL;
|
} else {
|
} else {
|
int flags = TCL_STDERR | TCL_ENFORCE_MODE;
|
int flags = TCL_STDERR | TCL_ENFORCE_MODE;
|
switch (mode & (O_RDONLY | O_WRONLY | O_RDWR)) {
|
switch (mode & (O_RDONLY | O_WRONLY | O_RDWR)) {
|
case O_RDONLY:
|
case O_RDONLY:
|
flags |= TCL_STDOUT;
|
flags |= TCL_STDOUT;
|
break;
|
break;
|
case O_WRONLY:
|
case O_WRONLY:
|
flags |= TCL_STDIN;
|
flags |= TCL_STDIN;
|
break;
|
break;
|
case O_RDWR:
|
case O_RDWR:
|
flags |= (TCL_STDIN | TCL_STDOUT);
|
flags |= (TCL_STDIN | TCL_STDOUT);
|
break;
|
break;
|
default:
|
default:
|
panic("Tcl_OpenCmd: invalid mode value");
|
panic("Tcl_OpenCmd: invalid mode value");
|
break;
|
break;
|
}
|
}
|
chan = Tcl_OpenCommandChannel(interp, cmdArgc, cmdArgv, flags);
|
chan = Tcl_OpenCommandChannel(interp, cmdArgc, cmdArgv, flags);
|
}
|
}
|
ckfree((char *) cmdArgv);
|
ckfree((char *) cmdArgv);
|
#endif
|
#endif
|
}
|
}
|
if (chan == (Tcl_Channel) NULL) {
|
if (chan == (Tcl_Channel) NULL) {
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
Tcl_RegisterChannel(interp, chan);
|
Tcl_RegisterChannel(interp, chan);
|
Tcl_AppendResult(interp, Tcl_GetChannelName(chan), (char *) NULL);
|
Tcl_AppendResult(interp, Tcl_GetChannelName(chan), (char *) NULL);
|
return TCL_OK;
|
return TCL_OK;
|
}
|
}
|
|
|
/*
|
/*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*
|
*
|
* TcpAcceptCallbacksDeleteProc --
|
* TcpAcceptCallbacksDeleteProc --
|
*
|
*
|
* Assocdata cleanup routine called when an interpreter is being
|
* Assocdata cleanup routine called when an interpreter is being
|
* deleted to set the interp field of all the accept callback records
|
* deleted to set the interp field of all the accept callback records
|
* registered with the interpreter to NULL. This will prevent the
|
* registered with the interpreter to NULL. This will prevent the
|
* interpreter from being used in the future to eval accept scripts.
|
* interpreter from being used in the future to eval accept scripts.
|
*
|
*
|
* Results:
|
* Results:
|
* None.
|
* None.
|
*
|
*
|
* Side effects:
|
* Side effects:
|
* Deallocates memory and sets the interp field of all the accept
|
* Deallocates memory and sets the interp field of all the accept
|
* callback records to NULL to prevent this interpreter from being
|
* callback records to NULL to prevent this interpreter from being
|
* used subsequently to eval accept scripts.
|
* used subsequently to eval accept scripts.
|
*
|
*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*/
|
*/
|
|
|
/* ARGSUSED */
|
/* ARGSUSED */
|
static void
|
static void
|
TcpAcceptCallbacksDeleteProc(clientData, interp)
|
TcpAcceptCallbacksDeleteProc(clientData, interp)
|
ClientData clientData; /* Data which was passed when the assocdata
|
ClientData clientData; /* Data which was passed when the assocdata
|
* was registered. */
|
* was registered. */
|
Tcl_Interp *interp; /* Interpreter being deleted - not used. */
|
Tcl_Interp *interp; /* Interpreter being deleted - not used. */
|
{
|
{
|
Tcl_HashTable *hTblPtr;
|
Tcl_HashTable *hTblPtr;
|
Tcl_HashEntry *hPtr;
|
Tcl_HashEntry *hPtr;
|
Tcl_HashSearch hSearch;
|
Tcl_HashSearch hSearch;
|
AcceptCallback *acceptCallbackPtr;
|
AcceptCallback *acceptCallbackPtr;
|
|
|
hTblPtr = (Tcl_HashTable *) clientData;
|
hTblPtr = (Tcl_HashTable *) clientData;
|
for (hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch);
|
for (hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch);
|
hPtr != (Tcl_HashEntry *) NULL;
|
hPtr != (Tcl_HashEntry *) NULL;
|
hPtr = Tcl_NextHashEntry(&hSearch)) {
|
hPtr = Tcl_NextHashEntry(&hSearch)) {
|
acceptCallbackPtr = (AcceptCallback *) Tcl_GetHashValue(hPtr);
|
acceptCallbackPtr = (AcceptCallback *) Tcl_GetHashValue(hPtr);
|
acceptCallbackPtr->interp = (Tcl_Interp *) NULL;
|
acceptCallbackPtr->interp = (Tcl_Interp *) NULL;
|
}
|
}
|
Tcl_DeleteHashTable(hTblPtr);
|
Tcl_DeleteHashTable(hTblPtr);
|
ckfree((char *) hTblPtr);
|
ckfree((char *) hTblPtr);
|
}
|
}
|
|
|
/*
|
/*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*
|
*
|
* RegisterTcpServerInterpCleanup --
|
* RegisterTcpServerInterpCleanup --
|
*
|
*
|
* Registers an accept callback record to have its interp
|
* Registers an accept callback record to have its interp
|
* field set to NULL when the interpreter is deleted.
|
* field set to NULL when the interpreter is deleted.
|
*
|
*
|
* Results:
|
* Results:
|
* None.
|
* None.
|
*
|
*
|
* Side effects:
|
* Side effects:
|
* When, in the future, the interpreter is deleted, the interp
|
* When, in the future, the interpreter is deleted, the interp
|
* field of the accept callback data structure will be set to
|
* field of the accept callback data structure will be set to
|
* NULL. This will prevent attempts to eval the accept script
|
* NULL. This will prevent attempts to eval the accept script
|
* in a deleted interpreter.
|
* in a deleted interpreter.
|
*
|
*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*/
|
*/
|
|
|
static void
|
static void
|
RegisterTcpServerInterpCleanup(interp, acceptCallbackPtr)
|
RegisterTcpServerInterpCleanup(interp, acceptCallbackPtr)
|
Tcl_Interp *interp; /* Interpreter for which we want to be
|
Tcl_Interp *interp; /* Interpreter for which we want to be
|
* informed of deletion. */
|
* informed of deletion. */
|
AcceptCallback *acceptCallbackPtr;
|
AcceptCallback *acceptCallbackPtr;
|
/* The accept callback record whose
|
/* The accept callback record whose
|
* interp field we want set to NULL when
|
* interp field we want set to NULL when
|
* the interpreter is deleted. */
|
* the interpreter is deleted. */
|
{
|
{
|
Tcl_HashTable *hTblPtr; /* Hash table for accept callback
|
Tcl_HashTable *hTblPtr; /* Hash table for accept callback
|
* records to smash when the interpreter
|
* records to smash when the interpreter
|
* will be deleted. */
|
* will be deleted. */
|
Tcl_HashEntry *hPtr; /* Entry for this record. */
|
Tcl_HashEntry *hPtr; /* Entry for this record. */
|
int new; /* Is the entry new? */
|
int new; /* Is the entry new? */
|
|
|
hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp,
|
hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp,
|
"tclTCPAcceptCallbacks",
|
"tclTCPAcceptCallbacks",
|
NULL);
|
NULL);
|
if (hTblPtr == (Tcl_HashTable *) NULL) {
|
if (hTblPtr == (Tcl_HashTable *) NULL) {
|
hTblPtr = (Tcl_HashTable *) ckalloc((unsigned) sizeof(Tcl_HashTable));
|
hTblPtr = (Tcl_HashTable *) ckalloc((unsigned) sizeof(Tcl_HashTable));
|
Tcl_InitHashTable(hTblPtr, TCL_ONE_WORD_KEYS);
|
Tcl_InitHashTable(hTblPtr, TCL_ONE_WORD_KEYS);
|
(void) Tcl_SetAssocData(interp, "tclTCPAcceptCallbacks",
|
(void) Tcl_SetAssocData(interp, "tclTCPAcceptCallbacks",
|
TcpAcceptCallbacksDeleteProc, (ClientData) hTblPtr);
|
TcpAcceptCallbacksDeleteProc, (ClientData) hTblPtr);
|
}
|
}
|
hPtr = Tcl_CreateHashEntry(hTblPtr, (char *) acceptCallbackPtr, &new);
|
hPtr = Tcl_CreateHashEntry(hTblPtr, (char *) acceptCallbackPtr, &new);
|
if (!new) {
|
if (!new) {
|
panic("RegisterTcpServerCleanup: damaged accept record table");
|
panic("RegisterTcpServerCleanup: damaged accept record table");
|
}
|
}
|
Tcl_SetHashValue(hPtr, (ClientData) acceptCallbackPtr);
|
Tcl_SetHashValue(hPtr, (ClientData) acceptCallbackPtr);
|
}
|
}
|
|
|
/*
|
/*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*
|
*
|
* UnregisterTcpServerInterpCleanupProc --
|
* UnregisterTcpServerInterpCleanupProc --
|
*
|
*
|
* Unregister a previously registered accept callback record. The
|
* Unregister a previously registered accept callback record. The
|
* interp field of this record will no longer be set to NULL in
|
* interp field of this record will no longer be set to NULL in
|
* the future when the interpreter is deleted.
|
* the future when the interpreter is deleted.
|
*
|
*
|
* Results:
|
* Results:
|
* None.
|
* None.
|
*
|
*
|
* Side effects:
|
* Side effects:
|
* Prevents the interp field of the accept callback record from
|
* Prevents the interp field of the accept callback record from
|
* being set to NULL in the future when the interpreter is deleted.
|
* being set to NULL in the future when the interpreter is deleted.
|
*
|
*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*/
|
*/
|
|
|
static void
|
static void
|
UnregisterTcpServerInterpCleanupProc(interp, acceptCallbackPtr)
|
UnregisterTcpServerInterpCleanupProc(interp, acceptCallbackPtr)
|
Tcl_Interp *interp; /* Interpreter in which the accept callback
|
Tcl_Interp *interp; /* Interpreter in which the accept callback
|
* record was registered. */
|
* record was registered. */
|
AcceptCallback *acceptCallbackPtr;
|
AcceptCallback *acceptCallbackPtr;
|
/* The record for which to delete the
|
/* The record for which to delete the
|
* registration. */
|
* registration. */
|
{
|
{
|
Tcl_HashTable *hTblPtr;
|
Tcl_HashTable *hTblPtr;
|
Tcl_HashEntry *hPtr;
|
Tcl_HashEntry *hPtr;
|
|
|
hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp,
|
hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp,
|
"tclTCPAcceptCallbacks", NULL);
|
"tclTCPAcceptCallbacks", NULL);
|
if (hTblPtr == (Tcl_HashTable *) NULL) {
|
if (hTblPtr == (Tcl_HashTable *) NULL) {
|
return;
|
return;
|
}
|
}
|
hPtr = Tcl_FindHashEntry(hTblPtr, (char *) acceptCallbackPtr);
|
hPtr = Tcl_FindHashEntry(hTblPtr, (char *) acceptCallbackPtr);
|
if (hPtr == (Tcl_HashEntry *) NULL) {
|
if (hPtr == (Tcl_HashEntry *) NULL) {
|
return;
|
return;
|
}
|
}
|
Tcl_DeleteHashEntry(hPtr);
|
Tcl_DeleteHashEntry(hPtr);
|
}
|
}
|
|
|
/*
|
/*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*
|
*
|
* AcceptCallbackProc --
|
* AcceptCallbackProc --
|
*
|
*
|
* This callback is invoked by the TCP channel driver when it
|
* This callback is invoked by the TCP channel driver when it
|
* accepts a new connection from a client on a server socket.
|
* accepts a new connection from a client on a server socket.
|
*
|
*
|
* Results:
|
* Results:
|
* None.
|
* None.
|
*
|
*
|
* Side effects:
|
* Side effects:
|
* Whatever the script does.
|
* Whatever the script does.
|
*
|
*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*/
|
*/
|
|
|
static void
|
static void
|
AcceptCallbackProc(callbackData, chan, address, port)
|
AcceptCallbackProc(callbackData, chan, address, port)
|
ClientData callbackData; /* The data stored when the callback
|
ClientData callbackData; /* The data stored when the callback
|
* was created in the call to
|
* was created in the call to
|
* Tcl_OpenTcpServer. */
|
* Tcl_OpenTcpServer. */
|
Tcl_Channel chan; /* Channel for the newly accepted
|
Tcl_Channel chan; /* Channel for the newly accepted
|
* connection. */
|
* connection. */
|
char *address; /* Address of client that was
|
char *address; /* Address of client that was
|
* accepted. */
|
* accepted. */
|
int port; /* Port of client that was accepted. */
|
int port; /* Port of client that was accepted. */
|
{
|
{
|
AcceptCallback *acceptCallbackPtr;
|
AcceptCallback *acceptCallbackPtr;
|
Tcl_Interp *interp;
|
Tcl_Interp *interp;
|
char *script;
|
char *script;
|
char portBuf[10];
|
char portBuf[10];
|
int result;
|
int result;
|
|
|
acceptCallbackPtr = (AcceptCallback *) callbackData;
|
acceptCallbackPtr = (AcceptCallback *) callbackData;
|
|
|
/*
|
/*
|
* Check if the callback is still valid; the interpreter may have gone
|
* Check if the callback is still valid; the interpreter may have gone
|
* away, this is signalled by setting the interp field of the callback
|
* away, this is signalled by setting the interp field of the callback
|
* data to NULL.
|
* data to NULL.
|
*/
|
*/
|
|
|
if (acceptCallbackPtr->interp != (Tcl_Interp *) NULL) {
|
if (acceptCallbackPtr->interp != (Tcl_Interp *) NULL) {
|
|
|
script = acceptCallbackPtr->script;
|
script = acceptCallbackPtr->script;
|
interp = acceptCallbackPtr->interp;
|
interp = acceptCallbackPtr->interp;
|
|
|
Tcl_Preserve((ClientData) script);
|
Tcl_Preserve((ClientData) script);
|
Tcl_Preserve((ClientData) interp);
|
Tcl_Preserve((ClientData) interp);
|
|
|
TclFormatInt(portBuf, port);
|
TclFormatInt(portBuf, port);
|
Tcl_RegisterChannel(interp, chan);
|
Tcl_RegisterChannel(interp, chan);
|
|
|
/*
|
/*
|
* Artificially bump the refcount to protect the channel from
|
* Artificially bump the refcount to protect the channel from
|
* being deleted while the script is being evaluated.
|
* being deleted while the script is being evaluated.
|
*/
|
*/
|
|
|
Tcl_RegisterChannel((Tcl_Interp *) NULL, chan);
|
Tcl_RegisterChannel((Tcl_Interp *) NULL, chan);
|
|
|
result = Tcl_VarEval(interp, script, " ", Tcl_GetChannelName(chan),
|
result = Tcl_VarEval(interp, script, " ", Tcl_GetChannelName(chan),
|
" ", address, " ", portBuf, (char *) NULL);
|
" ", address, " ", portBuf, (char *) NULL);
|
if (result != TCL_OK) {
|
if (result != TCL_OK) {
|
Tcl_BackgroundError(interp);
|
Tcl_BackgroundError(interp);
|
Tcl_UnregisterChannel(interp, chan);
|
Tcl_UnregisterChannel(interp, chan);
|
}
|
}
|
|
|
/*
|
/*
|
* Decrement the artificially bumped refcount. After this it is
|
* Decrement the artificially bumped refcount. After this it is
|
* not safe anymore to use "chan", because it may now be deleted.
|
* not safe anymore to use "chan", because it may now be deleted.
|
*/
|
*/
|
|
|
Tcl_UnregisterChannel((Tcl_Interp *) NULL, chan);
|
Tcl_UnregisterChannel((Tcl_Interp *) NULL, chan);
|
|
|
Tcl_Release((ClientData) interp);
|
Tcl_Release((ClientData) interp);
|
Tcl_Release((ClientData) script);
|
Tcl_Release((ClientData) script);
|
} else {
|
} else {
|
|
|
/*
|
/*
|
* The interpreter has been deleted, so there is no useful
|
* The interpreter has been deleted, so there is no useful
|
* way to utilize the client socket - just close it.
|
* way to utilize the client socket - just close it.
|
*/
|
*/
|
|
|
Tcl_Close((Tcl_Interp *) NULL, chan);
|
Tcl_Close((Tcl_Interp *) NULL, chan);
|
}
|
}
|
}
|
}
|
|
|
/*
|
/*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*
|
*
|
* TcpServerCloseProc --
|
* TcpServerCloseProc --
|
*
|
*
|
* This callback is called when the TCP server channel for which it
|
* This callback is called when the TCP server channel for which it
|
* was registered is being closed. It informs the interpreter in
|
* was registered is being closed. It informs the interpreter in
|
* which the accept script is evaluated (if that interpreter still
|
* which the accept script is evaluated (if that interpreter still
|
* exists) that this channel no longer needs to be informed if the
|
* exists) that this channel no longer needs to be informed if the
|
* interpreter is deleted.
|
* interpreter is deleted.
|
*
|
*
|
* Results:
|
* Results:
|
* None.
|
* None.
|
*
|
*
|
* Side effects:
|
* Side effects:
|
* In the future, if the interpreter is deleted this channel will
|
* In the future, if the interpreter is deleted this channel will
|
* no longer be informed.
|
* no longer be informed.
|
*
|
*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*/
|
*/
|
|
|
static void
|
static void
|
TcpServerCloseProc(callbackData)
|
TcpServerCloseProc(callbackData)
|
ClientData callbackData; /* The data passed in the call to
|
ClientData callbackData; /* The data passed in the call to
|
* Tcl_CreateCloseHandler. */
|
* Tcl_CreateCloseHandler. */
|
{
|
{
|
AcceptCallback *acceptCallbackPtr;
|
AcceptCallback *acceptCallbackPtr;
|
/* The actual data. */
|
/* The actual data. */
|
|
|
acceptCallbackPtr = (AcceptCallback *) callbackData;
|
acceptCallbackPtr = (AcceptCallback *) callbackData;
|
if (acceptCallbackPtr->interp != (Tcl_Interp *) NULL) {
|
if (acceptCallbackPtr->interp != (Tcl_Interp *) NULL) {
|
UnregisterTcpServerInterpCleanupProc(acceptCallbackPtr->interp,
|
UnregisterTcpServerInterpCleanupProc(acceptCallbackPtr->interp,
|
acceptCallbackPtr);
|
acceptCallbackPtr);
|
}
|
}
|
Tcl_EventuallyFree((ClientData) acceptCallbackPtr->script, TCL_DYNAMIC);
|
Tcl_EventuallyFree((ClientData) acceptCallbackPtr->script, TCL_DYNAMIC);
|
ckfree((char *) acceptCallbackPtr);
|
ckfree((char *) acceptCallbackPtr);
|
}
|
}
|
|
|
/*
|
/*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*
|
*
|
* Tcl_SocketCmd --
|
* Tcl_SocketCmd --
|
*
|
*
|
* This procedure is invoked to process the "socket" Tcl command.
|
* This procedure is invoked to process the "socket" Tcl command.
|
* See the user documentation for details on what it does.
|
* See the user documentation for details on what it does.
|
*
|
*
|
* Results:
|
* Results:
|
* A standard Tcl result.
|
* A standard Tcl result.
|
*
|
*
|
* Side effects:
|
* Side effects:
|
* Creates a socket based channel.
|
* Creates a socket based channel.
|
*
|
*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*/
|
*/
|
|
|
int
|
int
|
Tcl_SocketCmd(notUsed, interp, argc, argv)
|
Tcl_SocketCmd(notUsed, interp, argc, argv)
|
ClientData notUsed; /* Not used. */
|
ClientData notUsed; /* Not used. */
|
Tcl_Interp *interp; /* Current interpreter. */
|
Tcl_Interp *interp; /* Current interpreter. */
|
int argc; /* Number of arguments. */
|
int argc; /* Number of arguments. */
|
char **argv; /* Argument strings. */
|
char **argv; /* Argument strings. */
|
{
|
{
|
int a, server, port;
|
int a, server, port;
|
char *arg, *copyScript, *host, *script;
|
char *arg, *copyScript, *host, *script;
|
char *myaddr = NULL;
|
char *myaddr = NULL;
|
int myport = 0;
|
int myport = 0;
|
int async = 0;
|
int async = 0;
|
Tcl_Channel chan;
|
Tcl_Channel chan;
|
AcceptCallback *acceptCallbackPtr;
|
AcceptCallback *acceptCallbackPtr;
|
|
|
server = 0;
|
server = 0;
|
script = NULL;
|
script = NULL;
|
|
|
if (TclHasSockets(interp) != TCL_OK) {
|
if (TclHasSockets(interp) != TCL_OK) {
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
|
|
for (a = 1; a < argc; a++) {
|
for (a = 1; a < argc; a++) {
|
arg = argv[a];
|
arg = argv[a];
|
if (arg[0] == '-') {
|
if (arg[0] == '-') {
|
if (strcmp(arg, "-server") == 0) {
|
if (strcmp(arg, "-server") == 0) {
|
if (async == 1) {
|
if (async == 1) {
|
Tcl_AppendResult(interp,
|
Tcl_AppendResult(interp,
|
"cannot set -async option for server sockets",
|
"cannot set -async option for server sockets",
|
(char *) NULL);
|
(char *) NULL);
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
server = 1;
|
server = 1;
|
a++;
|
a++;
|
if (a >= argc) {
|
if (a >= argc) {
|
Tcl_AppendResult(interp,
|
Tcl_AppendResult(interp,
|
"no argument given for -server option",
|
"no argument given for -server option",
|
(char *) NULL);
|
(char *) NULL);
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
script = argv[a];
|
script = argv[a];
|
} else if (strcmp(arg, "-myaddr") == 0) {
|
} else if (strcmp(arg, "-myaddr") == 0) {
|
a++;
|
a++;
|
if (a >= argc) {
|
if (a >= argc) {
|
Tcl_AppendResult(interp,
|
Tcl_AppendResult(interp,
|
"no argument given for -myaddr option",
|
"no argument given for -myaddr option",
|
(char *) NULL);
|
(char *) NULL);
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
myaddr = argv[a];
|
myaddr = argv[a];
|
} else if (strcmp(arg, "-myport") == 0) {
|
} else if (strcmp(arg, "-myport") == 0) {
|
a++;
|
a++;
|
if (a >= argc) {
|
if (a >= argc) {
|
Tcl_AppendResult(interp,
|
Tcl_AppendResult(interp,
|
"no argument given for -myport option",
|
"no argument given for -myport option",
|
(char *) NULL);
|
(char *) NULL);
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
if (TclSockGetPort(interp, argv[a], "tcp", &myport)
|
if (TclSockGetPort(interp, argv[a], "tcp", &myport)
|
!= TCL_OK) {
|
!= TCL_OK) {
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
} else if (strcmp(arg, "-async") == 0) {
|
} else if (strcmp(arg, "-async") == 0) {
|
if (server == 1) {
|
if (server == 1) {
|
Tcl_AppendResult(interp,
|
Tcl_AppendResult(interp,
|
"cannot set -async option for server sockets",
|
"cannot set -async option for server sockets",
|
(char *) NULL);
|
(char *) NULL);
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
async = 1;
|
async = 1;
|
} else {
|
} else {
|
Tcl_AppendResult(interp, "bad option \"", arg,
|
Tcl_AppendResult(interp, "bad option \"", arg,
|
"\", must be -async, -myaddr, -myport, or -server",
|
"\", must be -async, -myaddr, -myport, or -server",
|
(char *) NULL);
|
(char *) NULL);
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
} else {
|
} else {
|
break;
|
break;
|
}
|
}
|
}
|
}
|
if (server) {
|
if (server) {
|
host = myaddr; /* NULL implies INADDR_ANY */
|
host = myaddr; /* NULL implies INADDR_ANY */
|
if (myport != 0) {
|
if (myport != 0) {
|
Tcl_AppendResult(interp, "Option -myport is not valid for servers",
|
Tcl_AppendResult(interp, "Option -myport is not valid for servers",
|
NULL);
|
NULL);
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
} else if (a < argc) {
|
} else if (a < argc) {
|
host = argv[a];
|
host = argv[a];
|
a++;
|
a++;
|
} else {
|
} else {
|
wrongNumArgs:
|
wrongNumArgs:
|
Tcl_AppendResult(interp, "wrong # args: should be either:\n",
|
Tcl_AppendResult(interp, "wrong # args: should be either:\n",
|
argv[0],
|
argv[0],
|
" ?-myaddr addr? ?-myport myport? ?-async? host port\n",
|
" ?-myaddr addr? ?-myport myport? ?-async? host port\n",
|
argv[0],
|
argv[0],
|
" -server command ?-myaddr addr? port",
|
" -server command ?-myaddr addr? port",
|
(char *) NULL);
|
(char *) NULL);
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
|
|
if (a == argc-1) {
|
if (a == argc-1) {
|
if (TclSockGetPort(interp, argv[a], "tcp", &port) != TCL_OK) {
|
if (TclSockGetPort(interp, argv[a], "tcp", &port) != TCL_OK) {
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
} else {
|
} else {
|
goto wrongNumArgs;
|
goto wrongNumArgs;
|
}
|
}
|
|
|
if (server) {
|
if (server) {
|
acceptCallbackPtr = (AcceptCallback *) ckalloc((unsigned)
|
acceptCallbackPtr = (AcceptCallback *) ckalloc((unsigned)
|
sizeof(AcceptCallback));
|
sizeof(AcceptCallback));
|
copyScript = ckalloc((unsigned) strlen(script) + 1);
|
copyScript = ckalloc((unsigned) strlen(script) + 1);
|
strcpy(copyScript, script);
|
strcpy(copyScript, script);
|
acceptCallbackPtr->script = copyScript;
|
acceptCallbackPtr->script = copyScript;
|
acceptCallbackPtr->interp = interp;
|
acceptCallbackPtr->interp = interp;
|
chan = Tcl_OpenTcpServer(interp, port, host, AcceptCallbackProc,
|
chan = Tcl_OpenTcpServer(interp, port, host, AcceptCallbackProc,
|
(ClientData) acceptCallbackPtr);
|
(ClientData) acceptCallbackPtr);
|
if (chan == (Tcl_Channel) NULL) {
|
if (chan == (Tcl_Channel) NULL) {
|
ckfree(copyScript);
|
ckfree(copyScript);
|
ckfree((char *) acceptCallbackPtr);
|
ckfree((char *) acceptCallbackPtr);
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
|
|
/*
|
/*
|
* Register with the interpreter to let us know when the
|
* Register with the interpreter to let us know when the
|
* interpreter is deleted (by having the callback set the
|
* interpreter is deleted (by having the callback set the
|
* acceptCallbackPtr->interp field to NULL). This is to
|
* acceptCallbackPtr->interp field to NULL). This is to
|
* avoid trying to eval the script in a deleted interpreter.
|
* avoid trying to eval the script in a deleted interpreter.
|
*/
|
*/
|
|
|
RegisterTcpServerInterpCleanup(interp, acceptCallbackPtr);
|
RegisterTcpServerInterpCleanup(interp, acceptCallbackPtr);
|
|
|
/*
|
/*
|
* Register a close callback. This callback will inform the
|
* Register a close callback. This callback will inform the
|
* interpreter (if it still exists) that this channel does not
|
* interpreter (if it still exists) that this channel does not
|
* need to be informed when the interpreter is deleted.
|
* need to be informed when the interpreter is deleted.
|
*/
|
*/
|
|
|
Tcl_CreateCloseHandler(chan, TcpServerCloseProc,
|
Tcl_CreateCloseHandler(chan, TcpServerCloseProc,
|
(ClientData) acceptCallbackPtr);
|
(ClientData) acceptCallbackPtr);
|
} else {
|
} else {
|
chan = Tcl_OpenTcpClient(interp, port, host, myaddr, myport, async);
|
chan = Tcl_OpenTcpClient(interp, port, host, myaddr, myport, async);
|
if (chan == (Tcl_Channel) NULL) {
|
if (chan == (Tcl_Channel) NULL) {
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
}
|
}
|
Tcl_RegisterChannel(interp, chan);
|
Tcl_RegisterChannel(interp, chan);
|
Tcl_AppendResult(interp, Tcl_GetChannelName(chan), (char *) NULL);
|
Tcl_AppendResult(interp, Tcl_GetChannelName(chan), (char *) NULL);
|
|
|
return TCL_OK;
|
return TCL_OK;
|
}
|
}
|
|
|
/*
|
/*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*
|
*
|
* Tcl_FcopyObjCmd --
|
* Tcl_FcopyObjCmd --
|
*
|
*
|
* This procedure is invoked to process the "fcopy" Tcl command.
|
* This procedure is invoked to process the "fcopy" Tcl command.
|
* See the user documentation for details on what it does.
|
* See the user documentation for details on what it does.
|
*
|
*
|
* Results:
|
* Results:
|
* A standard Tcl result.
|
* A standard Tcl result.
|
*
|
*
|
* Side effects:
|
* Side effects:
|
* Moves data between two channels and possibly sets up a
|
* Moves data between two channels and possibly sets up a
|
* background copy handler.
|
* background copy handler.
|
*
|
*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*/
|
*/
|
|
|
int
|
int
|
Tcl_FcopyObjCmd(dummy, interp, objc, objv)
|
Tcl_FcopyObjCmd(dummy, interp, objc, objv)
|
ClientData dummy; /* Not used. */
|
ClientData dummy; /* Not used. */
|
Tcl_Interp *interp; /* Current interpreter. */
|
Tcl_Interp *interp; /* Current interpreter. */
|
int objc; /* Number of arguments. */
|
int objc; /* Number of arguments. */
|
Tcl_Obj *CONST objv[]; /* Argument objects. */
|
Tcl_Obj *CONST objv[]; /* Argument objects. */
|
{
|
{
|
Tcl_Channel inChan, outChan;
|
Tcl_Channel inChan, outChan;
|
char *arg;
|
char *arg;
|
int mode, i;
|
int mode, i;
|
int toRead;
|
int toRead;
|
Tcl_Obj *cmdPtr;
|
Tcl_Obj *cmdPtr;
|
static char* switches[] = { "-size", "-command", NULL };
|
static char* switches[] = { "-size", "-command", NULL };
|
enum { FcopySize, FcopyCommand } index;
|
enum { FcopySize, FcopyCommand } index;
|
|
|
if ((objc < 3) || (objc > 7) || (objc == 4) || (objc == 6)) {
|
if ((objc < 3) || (objc > 7) || (objc == 4) || (objc == 6)) {
|
Tcl_WrongNumArgs(interp, 1, objv,
|
Tcl_WrongNumArgs(interp, 1, objv,
|
"input output ?-size size? ?-command callback?");
|
"input output ?-size size? ?-command callback?");
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
|
|
/*
|
/*
|
* Parse the channel arguments and verify that they are readable
|
* Parse the channel arguments and verify that they are readable
|
* or writable, as appropriate.
|
* or writable, as appropriate.
|
*/
|
*/
|
|
|
arg = Tcl_GetStringFromObj(objv[1], NULL);
|
arg = Tcl_GetStringFromObj(objv[1], NULL);
|
inChan = Tcl_GetChannel(interp, arg, &mode);
|
inChan = Tcl_GetChannel(interp, arg, &mode);
|
if (inChan == (Tcl_Channel) NULL) {
|
if (inChan == (Tcl_Channel) NULL) {
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
if ((mode & TCL_READABLE) == 0) {
|
if ((mode & TCL_READABLE) == 0) {
|
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "channel \"",
|
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "channel \"",
|
Tcl_GetStringFromObj(objv[1], NULL),
|
Tcl_GetStringFromObj(objv[1], NULL),
|
"\" wasn't opened for reading", (char *) NULL);
|
"\" wasn't opened for reading", (char *) NULL);
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
arg = Tcl_GetStringFromObj(objv[2], NULL);
|
arg = Tcl_GetStringFromObj(objv[2], NULL);
|
outChan = Tcl_GetChannel(interp, arg, &mode);
|
outChan = Tcl_GetChannel(interp, arg, &mode);
|
if (outChan == (Tcl_Channel) NULL) {
|
if (outChan == (Tcl_Channel) NULL) {
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
if ((mode & TCL_WRITABLE) == 0) {
|
if ((mode & TCL_WRITABLE) == 0) {
|
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "channel \"",
|
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "channel \"",
|
Tcl_GetStringFromObj(objv[1], NULL),
|
Tcl_GetStringFromObj(objv[1], NULL),
|
"\" wasn't opened for writing", (char *) NULL);
|
"\" wasn't opened for writing", (char *) NULL);
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
|
|
toRead = -1;
|
toRead = -1;
|
cmdPtr = NULL;
|
cmdPtr = NULL;
|
for (i = 3; i < objc; i += 2) {
|
for (i = 3; i < objc; i += 2) {
|
if (Tcl_GetIndexFromObj(interp, objv[i], switches, "switch", 0,
|
if (Tcl_GetIndexFromObj(interp, objv[i], switches, "switch", 0,
|
(int *) &index) != TCL_OK) {
|
(int *) &index) != TCL_OK) {
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
switch (index) {
|
switch (index) {
|
case FcopySize:
|
case FcopySize:
|
if (Tcl_GetIntFromObj(interp, objv[i+1], &toRead) != TCL_OK) {
|
if (Tcl_GetIntFromObj(interp, objv[i+1], &toRead) != TCL_OK) {
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
break;
|
break;
|
case FcopyCommand:
|
case FcopyCommand:
|
cmdPtr = objv[i+1];
|
cmdPtr = objv[i+1];
|
break;
|
break;
|
}
|
}
|
}
|
}
|
|
|
return TclCopyChannel(interp, inChan, outChan, toRead, cmdPtr);
|
return TclCopyChannel(interp, inChan, outChan, toRead, cmdPtr);
|
}
|
}
|
|
|