summaryrefslogtreecommitdiffstats
path: root/tcl8.6/generic/tclIOCmd.c
diff options
context:
space:
mode:
authorWilliam Joye <wjoye@cfa.harvard.edu>2016-12-21 22:13:18 (GMT)
committerWilliam Joye <wjoye@cfa.harvard.edu>2016-12-21 22:13:18 (GMT)
commit07e464099b99459d0a37757771791598ef3395d9 (patch)
tree4ba7d8aad13735e52f59bdce7ca5ba3151ebd7e3 /tcl8.6/generic/tclIOCmd.c
parentdeb3650e37f26f651f280e480c4df3d7dde87bae (diff)
downloadblt-07e464099b99459d0a37757771791598ef3395d9.zip
blt-07e464099b99459d0a37757771791598ef3395d9.tar.gz
blt-07e464099b99459d0a37757771791598ef3395d9.tar.bz2
new subtree for tcl/tk
Diffstat (limited to 'tcl8.6/generic/tclIOCmd.c')
-rw-r--r--tcl8.6/generic/tclIOCmd.c2027
1 files changed, 0 insertions, 2027 deletions
diff --git a/tcl8.6/generic/tclIOCmd.c b/tcl8.6/generic/tclIOCmd.c
deleted file mode 100644
index 834f225..0000000
--- a/tcl8.6/generic/tclIOCmd.c
+++ /dev/null
@@ -1,2027 +0,0 @@
-/*
- * tclIOCmd.c --
- *
- * Contains the definitions of most of the Tcl commands relating to IO.
- *
- * Copyright (c) 1995-1997 Sun Microsystems, Inc.
- *
- * See the file "license.terms" for information on usage and redistribution of
- * this file, and for a DISCLAIMER OF ALL WARRANTIES.
- */
-
-#include "tclInt.h"
-
-/*
- * Callback structure for accept callback in a TCP server.
- */
-
-typedef struct AcceptCallback {
- char *script; /* Script to invoke. */
- Tcl_Interp *interp; /* Interpreter in which to run it. */
-} AcceptCallback;
-
-/*
- * Thread local storage used to maintain a per-thread stdout channel obj.
- * It must be per-thread because of std channel limitations.
- */
-
-typedef struct ThreadSpecificData {
- int initialized; /* Set to 1 when the module is initialized. */
- Tcl_Obj *stdoutObjPtr; /* Cached stdout channel Tcl_Obj */
-} ThreadSpecificData;
-
-static Tcl_ThreadDataKey dataKey;
-
-/*
- * Static functions for this file:
- */
-
-static void FinalizeIOCmdTSD(ClientData clientData);
-static void AcceptCallbackProc(ClientData callbackData,
- Tcl_Channel chan, char *address, int port);
-static int ChanPendingObjCmd(ClientData unused,
- Tcl_Interp *interp, int objc,
- Tcl_Obj *const objv[]);
-static int ChanTruncateObjCmd(ClientData dummy,
- Tcl_Interp *interp, int objc,
- Tcl_Obj *const objv[]);
-static void RegisterTcpServerInterpCleanup(Tcl_Interp *interp,
- AcceptCallback *acceptCallbackPtr);
-static void TcpAcceptCallbacksDeleteProc(ClientData clientData,
- Tcl_Interp *interp);
-static void TcpServerCloseProc(ClientData callbackData);
-static void UnregisterTcpServerInterpCleanupProc(
- Tcl_Interp *interp,
- AcceptCallback *acceptCallbackPtr);
-
-/*
- *----------------------------------------------------------------------
- *
- * FinalizeIOCmdTSD --
- *
- * Release the storage associated with the per-thread cache.
- *
- * Results:
- * None.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
-
-static void
-FinalizeIOCmdTSD(
- ClientData clientData) /* Not used. */
-{
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
-
- if (tsdPtr->stdoutObjPtr != NULL) {
- Tcl_DecrRefCount(tsdPtr->stdoutObjPtr);
- tsdPtr->stdoutObjPtr = NULL;
- }
- tsdPtr->initialized = 0;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * Tcl_PutsObjCmd --
- *
- * This function is invoked to process the "puts" Tcl command. See the
- * user documentation for details on what it does.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * Produces output on a channel.
- *
- *----------------------------------------------------------------------
- */
-
- /* ARGSUSED */
-int
-Tcl_PutsObjCmd(
- ClientData dummy, /* Not used. */
- Tcl_Interp *interp, /* Current interpreter. */
- int objc, /* Number of arguments. */
- Tcl_Obj *const objv[]) /* Argument objects. */
-{
- Tcl_Channel chan; /* The channel to puts on. */
- Tcl_Obj *string; /* String to write. */
- Tcl_Obj *chanObjPtr = NULL; /* channel object. */
- int newline; /* Add a newline at end? */
- int result; /* Result of puts operation. */
- int mode; /* Mode in which channel is opened. */
- ThreadSpecificData *tsdPtr;
-
- switch (objc) {
- case 2: /* [puts $x] */
- string = objv[1];
- newline = 1;
- break;
-
- case 3: /* [puts -nonewline $x] or [puts $chan $x] */
- if (strcmp(TclGetString(objv[1]), "-nonewline") == 0) {
- newline = 0;
- } else {
- newline = 1;
- chanObjPtr = objv[1];
- }
- string = objv[2];
- break;
-
- case 4: /* [puts -nonewline $chan $x] or
- * [puts $chan $x nonewline] */
- newline = 0;
- if (strcmp(TclGetString(objv[1]), "-nonewline") == 0) {
- chanObjPtr = objv[2];
- string = objv[3];
- break;
-#if TCL_MAJOR_VERSION < 9
- } else if (strcmp(TclGetString(objv[3]), "nonewline") == 0) {
- /*
- * The code below provides backwards compatibility with an old
- * form of the command that is no longer recommended or
- * documented. See also [Bug #3151675]. Will be removed in Tcl 9,
- * maybe even earlier.
- */
-
- chanObjPtr = objv[1];
- string = objv[2];
- break;
-#endif
- }
- /* Fall through */
- default: /* [puts] or
- * [puts some bad number of arguments...] */
- Tcl_WrongNumArgs(interp, 1, objv, "?-nonewline? ?channelId? string");
- return TCL_ERROR;
- }
-
- if (chanObjPtr == NULL) {
- tsdPtr = TCL_TSD_INIT(&dataKey);
-
- if (!tsdPtr->initialized) {
- tsdPtr->initialized = 1;
- TclNewLiteralStringObj(tsdPtr->stdoutObjPtr, "stdout");
- Tcl_IncrRefCount(tsdPtr->stdoutObjPtr);
- Tcl_CreateThreadExitHandler(FinalizeIOCmdTSD, NULL);
- }
- chanObjPtr = tsdPtr->stdoutObjPtr;
- }
- if (TclGetChannelFromObj(interp, chanObjPtr, &chan, &mode, 0) != TCL_OK) {
- return TCL_ERROR;
- }
- if (!(mode & TCL_WRITABLE)) {
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "channel \"%s\" wasn't opened for writing",
- TclGetString(chanObjPtr)));
- return TCL_ERROR;
- }
-
- TclChannelPreserve(chan);
- result = Tcl_WriteObj(chan, string);
- if (result < 0) {
- goto error;
- }
- if (newline != 0) {
- result = Tcl_WriteChars(chan, "\n", 1);
- if (result < 0) {
- goto error;
- }
- }
- TclChannelRelease(chan);
- return TCL_OK;
-
- /*
- * TIP #219.
- * Capture error messages put by the driver into the bypass area and put
- * them into the regular interpreter result. Fall back to the regular
- * message if nothing was found in the bypass.
- */
-
- error:
- if (!TclChanCaughtErrorBypass(interp, chan)) {
- Tcl_SetObjResult(interp, Tcl_ObjPrintf("error writing \"%s\": %s",
- TclGetString(chanObjPtr), Tcl_PosixError(interp)));
- }
- TclChannelRelease(chan);
- return TCL_ERROR;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * Tcl_FlushObjCmd --
- *
- * This function is called to process the Tcl "flush" command. See the
- * user documentation for details on what it does.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * May cause output to appear on the specified channel.
- *
- *----------------------------------------------------------------------
- */
-
- /* ARGSUSED */
-int
-Tcl_FlushObjCmd(
- ClientData dummy, /* Not used. */
- Tcl_Interp *interp, /* Current interpreter. */
- int objc, /* Number of arguments. */
- Tcl_Obj *const objv[]) /* Argument objects. */
-{
- Tcl_Obj *chanObjPtr;
- Tcl_Channel chan; /* The channel to flush on. */
- int mode;
-
- if (objc != 2) {
- Tcl_WrongNumArgs(interp, 1, objv, "channelId");
- return TCL_ERROR;
- }
- chanObjPtr = objv[1];
- if (TclGetChannelFromObj(interp, chanObjPtr, &chan, &mode, 0) != TCL_OK) {
- return TCL_ERROR;
- }
- if (!(mode & TCL_WRITABLE)) {
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "channel \"%s\" wasn't opened for writing",
- TclGetString(chanObjPtr)));
- return TCL_ERROR;
- }
-
- TclChannelPreserve(chan);
- if (Tcl_Flush(chan) != TCL_OK) {
- /*
- * TIP #219.
- * Capture error messages put by the driver into the bypass area and
- * put them into the regular interpreter result. Fall back to the
- * regular message if nothing was found in the bypass.
- */
-
- if (!TclChanCaughtErrorBypass(interp, chan)) {
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "error flushing \"%s\": %s",
- TclGetString(chanObjPtr), Tcl_PosixError(interp)));
- }
- TclChannelRelease(chan);
- return TCL_ERROR;
- }
- TclChannelRelease(chan);
- return TCL_OK;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * Tcl_GetsObjCmd --
- *
- * This function is called to process the Tcl "gets" command. See the
- * user documentation for details on what it does.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * May consume input from channel.
- *
- *----------------------------------------------------------------------
- */
-
- /* ARGSUSED */
-int
-Tcl_GetsObjCmd(
- ClientData dummy, /* Not used. */
- Tcl_Interp *interp, /* Current interpreter. */
- int objc, /* Number of arguments. */
- Tcl_Obj *const objv[]) /* Argument objects. */
-{
- Tcl_Channel chan; /* The channel to read from. */
- int lineLen; /* Length of line just read. */
- int mode; /* Mode in which channel is opened. */
- Tcl_Obj *linePtr, *chanObjPtr;
- int code = TCL_OK;
-
- if ((objc != 2) && (objc != 3)) {
- Tcl_WrongNumArgs(interp, 1, objv, "channelId ?varName?");
- return TCL_ERROR;
- }
- chanObjPtr = objv[1];
- if (TclGetChannelFromObj(interp, chanObjPtr, &chan, &mode, 0) != TCL_OK) {
- return TCL_ERROR;
- }
- if (!(mode & TCL_READABLE)) {
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "channel \"%s\" wasn't opened for reading",
- TclGetString(chanObjPtr)));
- return TCL_ERROR;
- }
-
- TclChannelPreserve(chan);
- linePtr = Tcl_NewObj();
- lineLen = Tcl_GetsObj(chan, linePtr);
- if (lineLen < 0) {
- if (!Tcl_Eof(chan) && !Tcl_InputBlocked(chan)) {
- Tcl_DecrRefCount(linePtr);
-
- /*
- * TIP #219.
- * Capture error messages put by the driver into the bypass area
- * and put them into the regular interpreter result. Fall back to
- * the regular message if nothing was found in the bypass.
- */
-
- if (!TclChanCaughtErrorBypass(interp, chan)) {
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "error reading \"%s\": %s",
- TclGetString(chanObjPtr), Tcl_PosixError(interp)));
- }
- code = TCL_ERROR;
- goto done;
- }
- lineLen = -1;
- }
- if (objc == 3) {
- if (Tcl_ObjSetVar2(interp, objv[2], NULL, linePtr,
- TCL_LEAVE_ERR_MSG) == NULL) {
- code = TCL_ERROR;
- goto done;
- }
- Tcl_SetObjResult(interp, Tcl_NewIntObj(lineLen));
- } else {
- Tcl_SetObjResult(interp, linePtr);
- }
- done:
- TclChannelRelease(chan);
- return code;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * Tcl_ReadObjCmd --
- *
- * This function is invoked to process the Tcl "read" command. See the
- * user documentation for details on what it does.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * May consume input from channel.
- *
- *----------------------------------------------------------------------
- */
-
- /* ARGSUSED */
-int
-Tcl_ReadObjCmd(
- ClientData dummy, /* Not used. */
- Tcl_Interp *interp, /* Current interpreter. */
- int objc, /* Number of arguments. */
- Tcl_Obj *const objv[]) /* Argument objects. */
-{
- Tcl_Channel chan; /* The channel to read from. */
- int newline, i; /* Discard newline at end? */
- int toRead; /* How many bytes to read? */
- int charactersRead; /* How many characters were read? */
- int mode; /* Mode in which channel is opened. */
- Tcl_Obj *resultPtr, *chanObjPtr;
-
- if ((objc != 2) && (objc != 3)) {
- Interp *iPtr;
-
- argerror:
- iPtr = (Interp *) interp;
- Tcl_WrongNumArgs(interp, 1, objv, "channelId ?numChars?");
-
- /*
- * Do not append directly; that makes ensembles using this command as
- * a subcommand produce the wrong message.
- */
-
- iPtr->flags |= INTERP_ALTERNATE_WRONG_ARGS;
- Tcl_WrongNumArgs(interp, 1, objv, "?-nonewline? channelId");
- return TCL_ERROR;
- }
-
- i = 1;
- newline = 0;
- if (strcmp(TclGetString(objv[1]), "-nonewline") == 0) {
- newline = 1;
- i++;
- }
-
- if (i == objc) {
- goto argerror;
- }
-
- chanObjPtr = objv[i];
- if (TclGetChannelFromObj(interp, chanObjPtr, &chan, &mode, 0) != TCL_OK) {
- return TCL_ERROR;
- }
- if (!(mode & TCL_READABLE)) {
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "channel \"%s\" wasn't opened for reading",
- TclGetString(chanObjPtr)));
- return TCL_ERROR;
- }
- i++; /* Consumed channel name. */
-
- /*
- * Compute how many bytes to read.
- */
-
- toRead = -1;
- if (i < objc) {
- if ((TclGetIntFromObj(interp, objv[i], &toRead) != TCL_OK)
- || (toRead < 0)) {
-#if TCL_MAJOR_VERSION < 9
- /*
- * The code below provides backwards compatibility with an old
- * form of the command that is no longer recommended or
- * documented. See also [Bug #3151675]. Will be removed in Tcl 9,
- * maybe even earlier.
- */
-
- if (strcmp(TclGetString(objv[i]), "nonewline") != 0) {
-#endif
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "expected non-negative integer but got \"%s\"",
- TclGetString(objv[i])));
- Tcl_SetErrorCode(interp, "TCL", "VALUE", "NUMBER", NULL);
- return TCL_ERROR;
-#if TCL_MAJOR_VERSION < 9
- }
- newline = 1;
-#endif
- }
- }
-
- resultPtr = Tcl_NewObj();
- Tcl_IncrRefCount(resultPtr);
- TclChannelPreserve(chan);
- charactersRead = Tcl_ReadChars(chan, resultPtr, toRead, 0);
- if (charactersRead < 0) {
- /*
- * TIP #219.
- * Capture error messages put by the driver into the bypass area and
- * put them into the regular interpreter result. Fall back to the
- * regular message if nothing was found in the bypass.
- */
-
- if (!TclChanCaughtErrorBypass(interp, chan)) {
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "error reading \"%s\": %s",
- TclGetString(chanObjPtr), Tcl_PosixError(interp)));
- }
- TclChannelRelease(chan);
- Tcl_DecrRefCount(resultPtr);
- return TCL_ERROR;
- }
-
- /*
- * If requested, remove the last newline in the channel if at EOF.
- */
-
- if ((charactersRead > 0) && (newline != 0)) {
- const char *result;
- int length;
-
- result = TclGetStringFromObj(resultPtr, &length);
- if (result[length - 1] == '\n') {
- Tcl_SetObjLength(resultPtr, length - 1);
- }
- }
- Tcl_SetObjResult(interp, resultPtr);
- TclChannelRelease(chan);
- Tcl_DecrRefCount(resultPtr);
- return TCL_OK;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * Tcl_SeekObjCmd --
- *
- * This function is invoked to process the Tcl "seek" command. See the
- * user documentation for details on what it does.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * Moves the position of the access point on the specified channel. May
- * flush queued output.
- *
- *----------------------------------------------------------------------
- */
-
- /* ARGSUSED */
-int
-Tcl_SeekObjCmd(
- ClientData clientData, /* Not used. */
- Tcl_Interp *interp, /* Current interpreter. */
- int objc, /* Number of arguments. */
- Tcl_Obj *const objv[]) /* Argument objects. */
-{
- Tcl_Channel chan; /* The channel to tell on. */
- Tcl_WideInt offset; /* Where to seek? */
- int mode; /* How to seek? */
- Tcl_WideInt result; /* Of calling Tcl_Seek. */
- int optionIndex;
- static const char *const originOptions[] = {
- "start", "current", "end", NULL
- };
- static const int modeArray[] = {SEEK_SET, SEEK_CUR, SEEK_END};
-
- if ((objc != 3) && (objc != 4)) {
- Tcl_WrongNumArgs(interp, 1, objv, "channelId offset ?origin?");
- return TCL_ERROR;
- }
- if (TclGetChannelFromObj(interp, objv[1], &chan, NULL, 0) != TCL_OK) {
- return TCL_ERROR;
- }
- if (Tcl_GetWideIntFromObj(interp, objv[2], &offset) != TCL_OK) {
- return TCL_ERROR;
- }
- mode = SEEK_SET;
- if (objc == 4) {
- if (Tcl_GetIndexFromObj(interp, objv[3], originOptions, "origin", 0,
- &optionIndex) != TCL_OK) {
- return TCL_ERROR;
- }
- mode = modeArray[optionIndex];
- }
-
- TclChannelPreserve(chan);
- result = Tcl_Seek(chan, offset, mode);
- if (result == Tcl_LongAsWide(-1)) {
- /*
- * TIP #219.
- * Capture error messages put by the driver into the bypass area and
- * put them into the regular interpreter result. Fall back to the
- * regular message if nothing was found in the bypass.
- */
-
- if (!TclChanCaughtErrorBypass(interp, chan)) {
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "error during seek on \"%s\": %s",
- TclGetString(objv[1]), Tcl_PosixError(interp)));
- }
- TclChannelRelease(chan);
- return TCL_ERROR;
- }
- TclChannelRelease(chan);
- return TCL_OK;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * Tcl_TellObjCmd --
- *
- * This function is invoked to process the Tcl "tell" command. See the
- * user documentation for details on what it does.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
-
- /* ARGSUSED */
-int
-Tcl_TellObjCmd(
- ClientData clientData, /* Not used. */
- Tcl_Interp *interp, /* Current interpreter. */
- int objc, /* Number of arguments. */
- Tcl_Obj *const objv[]) /* Argument objects. */
-{
- Tcl_Channel chan; /* The channel to tell on. */
- Tcl_WideInt newLoc;
- int code;
-
- if (objc != 2) {
- Tcl_WrongNumArgs(interp, 1, objv, "channelId");
- return TCL_ERROR;
- }
-
- /*
- * Try to find a channel with the right name and permissions in the IO
- * channel table of this interpreter.
- */
-
- if (TclGetChannelFromObj(interp, objv[1], &chan, NULL, 0) != TCL_OK) {
- return TCL_ERROR;
- }
-
- TclChannelPreserve(chan);
- newLoc = Tcl_Tell(chan);
-
- /*
- * TIP #219.
- * Capture error messages put by the driver into the bypass area and put
- * them into the regular interpreter result.
- */
-
-
- code = TclChanCaughtErrorBypass(interp, chan);
- TclChannelRelease(chan);
- if (code) {
- return TCL_ERROR;
- }
-
- Tcl_SetObjResult(interp, Tcl_NewWideIntObj(newLoc));
- return TCL_OK;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * Tcl_CloseObjCmd --
- *
- * This function is invoked to process the Tcl "close" command. See the
- * user documentation for details on what it does.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * May discard queued input; may flush queued output.
- *
- *----------------------------------------------------------------------
- */
-
- /* ARGSUSED */
-int
-Tcl_CloseObjCmd(
- ClientData clientData, /* Not used. */
- Tcl_Interp *interp, /* Current interpreter. */
- int objc, /* Number of arguments. */
- Tcl_Obj *const objv[]) /* Argument objects. */
-{
- Tcl_Channel chan; /* The channel to close. */
- static const char *const dirOptions[] = {
- "read", "write", NULL
- };
- static const int dirArray[] = {TCL_CLOSE_READ, TCL_CLOSE_WRITE};
-
- if ((objc != 2) && (objc != 3)) {
- Tcl_WrongNumArgs(interp, 1, objv, "channelId ?direction?");
- return TCL_ERROR;
- }
-
- if (TclGetChannelFromObj(interp, objv[1], &chan, NULL, 0) != TCL_OK) {
- return TCL_ERROR;
- }
-
- if (objc == 3) {
- int index, dir;
-
- /*
- * Get direction requested to close, and check syntax.
- */
-
- if (Tcl_GetIndexFromObj(interp, objv[2], dirOptions, "direction", 0,
- &index) != TCL_OK) {
- return TCL_ERROR;
- }
- dir = dirArray[index];
-
- /*
- * Check direction against channel mode. It is an error if we try to
- * close a direction not supported by the channel (already closed, or
- * never opened for that direction).
- */
-
- if (!(dir & Tcl_GetChannelMode(chan))) {
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "Half-close of %s-side not possible, side not opened"
- " or already closed", dirOptions[index]));
- return TCL_ERROR;
- }
-
- /*
- * Special handling is needed if and only if the channel mode supports
- * more than the direction to close. Because if the close the last
- * direction suppported we can and will go through the regular
- * process.
- */
-
- if ((Tcl_GetChannelMode(chan) &
- (TCL_CLOSE_READ|TCL_CLOSE_WRITE)) != dir) {
- return Tcl_CloseEx(interp, chan, dir);
- }
- }
-
- if (Tcl_UnregisterChannel(interp, chan) != TCL_OK) {
- /*
- * If there is an error message and it ends with a newline, remove the
- * newline. This is done for command pipeline channels where the error
- * output from the subprocesses is stored in interp's result.
- *
- * NOTE: This is likely to not have any effect on regular error
- * messages produced by drivers during the closing of a channel,
- * because the Tcl convention is that such error messages do not have
- * a terminating newline.
- */
-
- Tcl_Obj *resultPtr = Tcl_GetObjResult(interp);
- const char *string;
- int len;
-
- if (Tcl_IsShared(resultPtr)) {
- resultPtr = Tcl_DuplicateObj(resultPtr);
- Tcl_SetObjResult(interp, resultPtr);
- }
- string = TclGetStringFromObj(resultPtr, &len);
- if ((len > 0) && (string[len - 1] == '\n')) {
- Tcl_SetObjLength(resultPtr, len - 1);
- }
- return TCL_ERROR;
- }
-
- return TCL_OK;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * Tcl_FconfigureObjCmd --
- *
- * This function is invoked to process the Tcl "fconfigure" command. See
- * the user documentation for details on what it does.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * May modify the behavior of an IO channel.
- *
- *----------------------------------------------------------------------
- */
-
- /* ARGSUSED */
-int
-Tcl_FconfigureObjCmd(
- ClientData clientData, /* Not used. */
- Tcl_Interp *interp, /* Current interpreter. */
- int objc, /* Number of arguments. */
- Tcl_Obj *const objv[]) /* Argument objects. */
-{
- const char *optionName, *valueName;
- Tcl_Channel chan; /* The channel to set a mode on. */
- int i; /* Iterate over arg-value pairs. */
-
- if ((objc < 2) || (((objc % 2) == 1) && (objc != 3))) {
- Tcl_WrongNumArgs(interp, 1, objv, "channelId ?-option value ...?");
- return TCL_ERROR;
- }
-
- if (TclGetChannelFromObj(interp, objv[1], &chan, NULL, 0) != TCL_OK) {
- return TCL_ERROR;
- }
-
- if (objc == 2) {
- Tcl_DString ds; /* DString to hold result of calling
- * Tcl_GetChannelOption. */
-
- Tcl_DStringInit(&ds);
- if (Tcl_GetChannelOption(interp, chan, NULL, &ds) != TCL_OK) {
- Tcl_DStringFree(&ds);
- return TCL_ERROR;
- }
- Tcl_DStringResult(interp, &ds);
- return TCL_OK;
- } else if (objc == 3) {
- Tcl_DString ds; /* DString to hold result of calling
- * Tcl_GetChannelOption. */
-
- Tcl_DStringInit(&ds);
- optionName = TclGetString(objv[2]);
- if (Tcl_GetChannelOption(interp, chan, optionName, &ds) != TCL_OK) {
- Tcl_DStringFree(&ds);
- return TCL_ERROR;
- }
- Tcl_DStringResult(interp, &ds);
- return TCL_OK;
- }
-
- for (i = 3; i < objc; i += 2) {
- optionName = TclGetString(objv[i-1]);
- valueName = TclGetString(objv[i]);
- if (Tcl_SetChannelOption(interp, chan, optionName, valueName)
- != TCL_OK) {
- return TCL_ERROR;
- }
- }
-
- return TCL_OK;
-}
-
-/*
- *---------------------------------------------------------------------------
- *
- * Tcl_EofObjCmd --
- *
- * This function is invoked to process the Tcl "eof" command. See the
- * user documentation for details on what it does.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * Sets interp's result to boolean true or false depending on whether the
- * specified channel has an EOF condition.
- *
- *---------------------------------------------------------------------------
- */
-
- /* ARGSUSED */
-int
-Tcl_EofObjCmd(
- ClientData unused, /* Not used. */
- Tcl_Interp *interp, /* Current interpreter. */
- int objc, /* Number of arguments. */
- Tcl_Obj *const objv[]) /* Argument objects. */
-{
- Tcl_Channel chan;
-
- if (objc != 2) {
- Tcl_WrongNumArgs(interp, 1, objv, "channelId");
- return TCL_ERROR;
- }
-
- if (TclGetChannelFromObj(interp, objv[1], &chan, NULL, 0) != TCL_OK) {
- return TCL_ERROR;
- }
-
- Tcl_SetObjResult(interp, Tcl_NewBooleanObj(Tcl_Eof(chan)));
- return TCL_OK;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * Tcl_ExecObjCmd --
- *
- * This function is invoked to process the "exec" Tcl command. See the
- * user documentation for details on what it does.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *----------------------------------------------------------------------
- */
-
- /* ARGSUSED */
-int
-Tcl_ExecObjCmd(
- ClientData dummy, /* Not used. */
- Tcl_Interp *interp, /* Current interpreter. */
- int objc, /* Number of arguments. */
- Tcl_Obj *const objv[]) /* Argument objects. */
-{
- Tcl_Obj *resultPtr;
- const char **argv; /* An array for the string arguments. Stored
- * on the _Tcl_ stack. */
- const char *string;
- Tcl_Channel chan;
- int argc, background, i, index, keepNewline, result, skip, length;
- int ignoreStderr;
- static const char *const options[] = {
- "-ignorestderr", "-keepnewline", "--", NULL
- };
- enum options {
- EXEC_IGNORESTDERR, EXEC_KEEPNEWLINE, EXEC_LAST
- };
-
- /*
- * Check for any leading option arguments.
- */
-
- keepNewline = 0;
- ignoreStderr = 0;
- for (skip = 1; skip < objc; skip++) {
- string = TclGetString(objv[skip]);
- if (string[0] != '-') {
- break;
- }
- if (Tcl_GetIndexFromObj(interp, objv[skip], options, "option",
- TCL_EXACT, &index) != TCL_OK) {
- return TCL_ERROR;
- }
- if (index == EXEC_KEEPNEWLINE) {
- keepNewline = 1;
- } else if (index == EXEC_IGNORESTDERR) {
- ignoreStderr = 1;
- } else {
- skip++;
- break;
- }
- }
- if (objc <= skip) {
- Tcl_WrongNumArgs(interp, 1, objv, "?-option ...? arg ?arg ...?");
- return TCL_ERROR;
- }
-
- /*
- * See if the command is to be run in background.
- */
-
- background = 0;
- string = TclGetString(objv[objc - 1]);
- if ((string[0] == '&') && (string[1] == '\0')) {
- objc--;
- background = 1;
- }
-
- /*
- * Create the string argument array "argv". Make sure argv is large enough
- * to hold the argc arguments plus 1 extra for the zero end-of-argv word.
- */
-
- argc = objc - skip;
- argv = TclStackAlloc(interp, (unsigned)(argc + 1) * sizeof(char *));
-
- /*
- * Copy the string conversions of each (post option) object into the
- * argument vector.
- */
-
- for (i = 0; i < argc; i++) {
- argv[i] = TclGetString(objv[i + skip]);
- }
- argv[argc] = NULL;
- chan = Tcl_OpenCommandChannel(interp, argc, argv, (background ? 0 :
- ignoreStderr ? TCL_STDOUT : TCL_STDOUT|TCL_STDERR));
-
- /*
- * Free the argv array.
- */
-
- TclStackFree(interp, (void *) argv);
-
- if (chan == NULL) {
- return TCL_ERROR;
- }
-
- if (background) {
- /*
- * Store the list of PIDs from the pipeline in interp's result and
- * detach the PIDs (instead of waiting for them).
- */
-
- TclGetAndDetachPids(interp, chan);
- if (Tcl_Close(interp, chan) != TCL_OK) {
- return TCL_ERROR;
- }
- return TCL_OK;
- }
-
- resultPtr = Tcl_NewObj();
- if (Tcl_GetChannelHandle(chan, TCL_READABLE, NULL) == TCL_OK) {
- if (Tcl_ReadChars(chan, resultPtr, -1, 0) < 0) {
- /*
- * TIP #219.
- * Capture error messages put by the driver into the bypass area
- * and put them into the regular interpreter result. Fall back to
- * the regular message if nothing was found in the bypass.
- */
-
- if (!TclChanCaughtErrorBypass(interp, chan)) {
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "error reading output from command: %s",
- Tcl_PosixError(interp)));
- Tcl_DecrRefCount(resultPtr);
- }
- return TCL_ERROR;
- }
- }
-
- /*
- * If the process produced anything on stderr, it will have been returned
- * in the interpreter result. It needs to be appended to the result
- * string.
- */
-
- result = Tcl_Close(interp, chan);
- Tcl_AppendObjToObj(resultPtr, Tcl_GetObjResult(interp));
-
- /*
- * If the last character of the result is a newline, then remove the
- * newline character.
- */
-
- if (keepNewline == 0) {
- string = TclGetStringFromObj(resultPtr, &length);
- if ((length > 0) && (string[length - 1] == '\n')) {
- Tcl_SetObjLength(resultPtr, length - 1);
- }
- }
- Tcl_SetObjResult(interp, resultPtr);
-
- return result;
-}
-
-/*
- *---------------------------------------------------------------------------
- *
- * Tcl_FblockedObjCmd --
- *
- * This function is invoked to process the Tcl "fblocked" command. See
- * the user documentation for details on what it does.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * Sets interp's result to boolean true or false depending on whether the
- * preceeding input operation on the channel would have blocked.
- *
- *---------------------------------------------------------------------------
- */
-
- /* ARGSUSED */
-int
-Tcl_FblockedObjCmd(
- ClientData unused, /* Not used. */
- Tcl_Interp *interp, /* Current interpreter. */
- int objc, /* Number of arguments. */
- Tcl_Obj *const objv[]) /* Argument objects. */
-{
- Tcl_Channel chan;
- int mode;
-
- if (objc != 2) {
- Tcl_WrongNumArgs(interp, 1, objv, "channelId");
- return TCL_ERROR;
- }
-
- if (TclGetChannelFromObj(interp, objv[1], &chan, &mode, 0) != TCL_OK) {
- return TCL_ERROR;
- }
- if (!(mode & TCL_READABLE)) {
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "channel \"%s\" wasn't opened for reading",
- TclGetString(objv[1])));
- return TCL_ERROR;
- }
-
- Tcl_SetObjResult(interp, Tcl_NewBooleanObj(Tcl_InputBlocked(chan)));
- return TCL_OK;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * Tcl_OpenObjCmd --
- *
- * This function is invoked to process the "open" Tcl command. See the
- * user documentation for details on what it does.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *----------------------------------------------------------------------
- */
-
- /* ARGSUSED */
-int
-Tcl_OpenObjCmd(
- ClientData notUsed, /* Not used. */
- Tcl_Interp *interp, /* Current interpreter. */
- int objc, /* Number of arguments. */
- Tcl_Obj *const objv[]) /* Argument objects. */
-{
- int pipeline, prot;
- const char *modeString, *what;
- Tcl_Channel chan;
-
- if ((objc < 2) || (objc > 4)) {
- Tcl_WrongNumArgs(interp, 1, objv, "fileName ?access? ?permissions?");
- return TCL_ERROR;
- }
- prot = 0666;
- if (objc == 2) {
- modeString = "r";
- } else {
- modeString = TclGetString(objv[2]);
- if (objc == 4) {
- const char *permString = TclGetString(objv[3]);
- int code = TCL_ERROR;
- int scanned = TclParseAllWhiteSpace(permString, -1);
-
- /*
- * Support legacy octal numbers.
- */
-
- if ((permString[scanned] == '0')
- && (permString[scanned+1] >= '0')
- && (permString[scanned+1] <= '7')) {
- Tcl_Obj *permObj;
-
- TclNewLiteralStringObj(permObj, "0o");
- Tcl_AppendToObj(permObj, permString+scanned+1, -1);
- code = TclGetIntFromObj(NULL, permObj, &prot);
- Tcl_DecrRefCount(permObj);
- }
-
- if ((code == TCL_ERROR)
- && TclGetIntFromObj(interp, objv[3], &prot) != TCL_OK) {
- return TCL_ERROR;
- }
- }
- }
-
- pipeline = 0;
- what = TclGetString(objv[1]);
- if (what[0] == '|') {
- pipeline = 1;
- }
-
- /*
- * Open the file or create a process pipeline.
- */
-
- if (!pipeline) {
- chan = Tcl_FSOpenFileChannel(interp, objv[1], modeString, prot);
- } else {
- int mode, seekFlag, cmdObjc, binary;
- const char **cmdArgv;
-
- if (Tcl_SplitList(interp, what+1, &cmdObjc, &cmdArgv) != TCL_OK) {
- return TCL_ERROR;
- }
-
- mode = TclGetOpenModeEx(interp, modeString, &seekFlag, &binary);
- 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:
- Tcl_Panic("Tcl_OpenCmd: invalid mode value");
- break;
- }
- chan = Tcl_OpenCommandChannel(interp, cmdObjc, cmdArgv, flags);
- if (binary && chan) {
- Tcl_SetChannelOption(interp, chan, "-translation", "binary");
- }
- }
- ckfree(cmdArgv);
- }
- if (chan == NULL) {
- return TCL_ERROR;
- }
- Tcl_RegisterChannel(interp, chan);
- Tcl_SetObjResult(interp, Tcl_NewStringObj(Tcl_GetChannelName(chan), -1));
- return TCL_OK;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * TcpAcceptCallbacksDeleteProc --
- *
- * Assocdata cleanup routine called when an interpreter is being deleted
- * to set the interp field of all the accept callback records registered
- * with the interpreter to NULL. This will prevent the interpreter from
- * being used in the future to eval accept scripts.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Deallocates memory and sets the interp field of all the accept
- * callback records to NULL to prevent this interpreter from being used
- * subsequently to eval accept scripts.
- *
- *----------------------------------------------------------------------
- */
-
- /* ARGSUSED */
-static void
-TcpAcceptCallbacksDeleteProc(
- ClientData clientData, /* Data which was passed when the assocdata
- * was registered. */
- Tcl_Interp *interp) /* Interpreter being deleted - not used. */
-{
- Tcl_HashTable *hTblPtr = clientData;
- Tcl_HashEntry *hPtr;
- Tcl_HashSearch hSearch;
-
- for (hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch);
- hPtr != NULL; hPtr = Tcl_NextHashEntry(&hSearch)) {
- AcceptCallback *acceptCallbackPtr = Tcl_GetHashValue(hPtr);
-
- acceptCallbackPtr->interp = NULL;
- }
- Tcl_DeleteHashTable(hTblPtr);
- ckfree(hTblPtr);
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * RegisterTcpServerInterpCleanup --
- *
- * Registers an accept callback record to have its interp field set to
- * NULL when the interpreter is deleted.
- *
- * Results:
- * None.
- *
- * Side effects:
- * When, in the future, the interpreter is deleted, the interp field of
- * the accept callback data structure will be set to NULL. This will
- * prevent attempts to eval the accept script in a deleted interpreter.
- *
- *----------------------------------------------------------------------
- */
-
-static void
-RegisterTcpServerInterpCleanup(
- Tcl_Interp *interp, /* Interpreter for which we want to be
- * informed of deletion. */
- AcceptCallback *acceptCallbackPtr)
- /* The accept callback record whose interp
- * field we want set to NULL when the
- * interpreter is deleted. */
-{
- Tcl_HashTable *hTblPtr; /* Hash table for accept callback records to
- * smash when the interpreter will be
- * deleted. */
- Tcl_HashEntry *hPtr; /* Entry for this record. */
- int isNew; /* Is the entry new? */
-
- hTblPtr = Tcl_GetAssocData(interp, "tclTCPAcceptCallbacks", NULL);
-
- if (hTblPtr == NULL) {
- hTblPtr = ckalloc(sizeof(Tcl_HashTable));
- Tcl_InitHashTable(hTblPtr, TCL_ONE_WORD_KEYS);
- Tcl_SetAssocData(interp, "tclTCPAcceptCallbacks",
- TcpAcceptCallbacksDeleteProc, hTblPtr);
- }
-
- hPtr = Tcl_CreateHashEntry(hTblPtr, acceptCallbackPtr, &isNew);
- if (!isNew) {
- Tcl_Panic("RegisterTcpServerCleanup: damaged accept record table");
- }
- Tcl_SetHashValue(hPtr, acceptCallbackPtr);
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * UnregisterTcpServerInterpCleanupProc --
- *
- * Unregister a previously registered accept callback record. The interp
- * field of this record will no longer be set to NULL in the future when
- * the interpreter is deleted.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Prevents the interp field of the accept callback record from being set
- * to NULL in the future when the interpreter is deleted.
- *
- *----------------------------------------------------------------------
- */
-
-static void
-UnregisterTcpServerInterpCleanupProc(
- Tcl_Interp *interp, /* Interpreter in which the accept callback
- * record was registered. */
- AcceptCallback *acceptCallbackPtr)
- /* The record for which to delete the
- * registration. */
-{
- Tcl_HashTable *hTblPtr;
- Tcl_HashEntry *hPtr;
-
- hTblPtr = Tcl_GetAssocData(interp, "tclTCPAcceptCallbacks", NULL);
- if (hTblPtr == NULL) {
- return;
- }
-
- hPtr = Tcl_FindHashEntry(hTblPtr, (char *) acceptCallbackPtr);
- if (hPtr != NULL) {
- Tcl_DeleteHashEntry(hPtr);
- }
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * AcceptCallbackProc --
- *
- * This callback is invoked by the TCP channel driver when it accepts a
- * new connection from a client on a server socket.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Whatever the script does.
- *
- *----------------------------------------------------------------------
- */
-
-static void
-AcceptCallbackProc(
- ClientData callbackData, /* The data stored when the callback was
- * created in the call to
- * Tcl_OpenTcpServer. */
- Tcl_Channel chan, /* Channel for the newly accepted
- * connection. */
- char *address, /* Address of client that was accepted. */
- int port) /* Port of client that was accepted. */
-{
- AcceptCallback *acceptCallbackPtr = callbackData;
-
- /*
- * Check if the callback is still valid; the interpreter may have gone
- * away, this is signalled by setting the interp field of the callback
- * data to NULL.
- */
-
- if (acceptCallbackPtr->interp != NULL) {
- char portBuf[TCL_INTEGER_SPACE];
- char *script = acceptCallbackPtr->script;
- Tcl_Interp *interp = acceptCallbackPtr->interp;
- int result;
-
- Tcl_Preserve(script);
- Tcl_Preserve(interp);
-
- TclFormatInt(portBuf, port);
- Tcl_RegisterChannel(interp, chan);
-
- /*
- * Artificially bump the refcount to protect the channel from being
- * deleted while the script is being evaluated.
- */
-
- Tcl_RegisterChannel(NULL, chan);
-
- result = Tcl_VarEval(interp, script, " ", Tcl_GetChannelName(chan),
- " ", address, " ", portBuf, NULL);
- if (result != TCL_OK) {
- Tcl_BackgroundException(interp, result);
- Tcl_UnregisterChannel(interp, chan);
- }
-
- /*
- * Decrement the artificially bumped refcount. After this it is not
- * safe anymore to use "chan", because it may now be deleted.
- */
-
- Tcl_UnregisterChannel(NULL, chan);
-
- Tcl_Release(interp);
- Tcl_Release(script);
- } else {
- /*
- * The interpreter has been deleted, so there is no useful way to use
- * the client socket - just close it.
- */
-
- Tcl_Close(NULL, chan);
- }
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * TcpServerCloseProc --
- *
- * This callback is called when the TCP server channel for which it was
- * registered is being closed. It informs the interpreter in which the
- * accept script is evaluated (if that interpreter still exists) that
- * this channel no longer needs to be informed if the interpreter is
- * deleted.
- *
- * Results:
- * None.
- *
- * Side effects:
- * In the future, if the interpreter is deleted this channel will no
- * longer be informed.
- *
- *----------------------------------------------------------------------
- */
-
-static void
-TcpServerCloseProc(
- ClientData callbackData) /* The data passed in the call to
- * Tcl_CreateCloseHandler. */
-{
- AcceptCallback *acceptCallbackPtr = callbackData;
- /* The actual data. */
-
- if (acceptCallbackPtr->interp != NULL) {
- UnregisterTcpServerInterpCleanupProc(acceptCallbackPtr->interp,
- acceptCallbackPtr);
- }
- Tcl_EventuallyFree(acceptCallbackPtr->script, TCL_DYNAMIC);
- ckfree(acceptCallbackPtr);
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * Tcl_SocketObjCmd --
- *
- * This function is invoked to process the "socket" Tcl command. See the
- * user documentation for details on what it does.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * Creates a socket based channel.
- *
- *----------------------------------------------------------------------
- */
-
-int
-Tcl_SocketObjCmd(
- ClientData notUsed, /* Not used. */
- Tcl_Interp *interp, /* Current interpreter. */
- int objc, /* Number of arguments. */
- Tcl_Obj *const objv[]) /* Argument objects. */
-{
- static const char *const socketOptions[] = {
- "-async", "-myaddr", "-myport", "-server", NULL
- };
- enum socketOptions {
- SKT_ASYNC, SKT_MYADDR, SKT_MYPORT, SKT_SERVER
- };
- int optionIndex, a, server = 0, port, myport = 0, async = 0;
- const char *host, *script = NULL, *myaddr = NULL;
- Tcl_Channel chan;
-
- if (TclpHasSockets(interp) != TCL_OK) {
- return TCL_ERROR;
- }
-
- for (a = 1; a < objc; a++) {
- const char *arg = Tcl_GetString(objv[a]);
-
- if (arg[0] != '-') {
- break;
- }
- if (Tcl_GetIndexFromObj(interp, objv[a], socketOptions, "option",
- TCL_EXACT, &optionIndex) != TCL_OK) {
- return TCL_ERROR;
- }
- switch ((enum socketOptions) optionIndex) {
- case SKT_ASYNC:
- if (server == 1) {
- Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "cannot set -async option for server sockets", -1));
- return TCL_ERROR;
- }
- async = 1;
- break;
- case SKT_MYADDR:
- a++;
- if (a >= objc) {
- Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "no argument given for -myaddr option", -1));
- return TCL_ERROR;
- }
- myaddr = TclGetString(objv[a]);
- break;
- case SKT_MYPORT: {
- const char *myPortName;
-
- a++;
- if (a >= objc) {
- Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "no argument given for -myport option", -1));
- return TCL_ERROR;
- }
- myPortName = TclGetString(objv[a]);
- if (TclSockGetPort(interp, myPortName, "tcp", &myport) != TCL_OK) {
- return TCL_ERROR;
- }
- break;
- }
- case SKT_SERVER:
- if (async == 1) {
- Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "cannot set -async option for server sockets", -1));
- return TCL_ERROR;
- }
- server = 1;
- a++;
- if (a >= objc) {
- Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "no argument given for -server option", -1));
- return TCL_ERROR;
- }
- script = TclGetString(objv[a]);
- break;
- default:
- Tcl_Panic("Tcl_SocketObjCmd: bad option index to SocketOptions");
- }
- }
- if (server) {
- host = myaddr; /* NULL implies INADDR_ANY */
- if (myport != 0) {
- Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "option -myport is not valid for servers", -1));
- return TCL_ERROR;
- }
- } else if (a < objc) {
- host = TclGetString(objv[a]);
- a++;
- } else {
- Interp *iPtr;
-
- wrongNumArgs:
- iPtr = (Interp *) interp;
- Tcl_WrongNumArgs(interp, 1, objv,
- "?-myaddr addr? ?-myport myport? ?-async? host port");
- iPtr->flags |= INTERP_ALTERNATE_WRONG_ARGS;
- Tcl_WrongNumArgs(interp, 1, objv,
- "-server command ?-myaddr addr? port");
- return TCL_ERROR;
- }
-
- if (a == objc-1) {
- if (TclSockGetPort(interp, TclGetString(objv[a]), "tcp",
- &port) != TCL_OK) {
- return TCL_ERROR;
- }
- } else {
- goto wrongNumArgs;
- }
-
- if (server) {
- AcceptCallback *acceptCallbackPtr =
- ckalloc(sizeof(AcceptCallback));
- unsigned len = strlen(script) + 1;
- char *copyScript = ckalloc(len);
-
- memcpy(copyScript, script, len);
- acceptCallbackPtr->script = copyScript;
- acceptCallbackPtr->interp = interp;
- chan = Tcl_OpenTcpServer(interp, port, host, AcceptCallbackProc,
- acceptCallbackPtr);
- if (chan == NULL) {
- ckfree(copyScript);
- ckfree(acceptCallbackPtr);
- return TCL_ERROR;
- }
-
- /*
- * Register with the interpreter to let us know when the interpreter
- * is deleted (by having the callback set the interp field of the
- * acceptCallbackPtr's structure to NULL). This is to avoid trying to
- * eval the script in a deleted interpreter.
- */
-
- RegisterTcpServerInterpCleanup(interp, acceptCallbackPtr);
-
- /*
- * Register a close callback. This callback will inform the
- * interpreter (if it still exists) that this channel does not need to
- * be informed when the interpreter is deleted.
- */
-
- Tcl_CreateCloseHandler(chan, TcpServerCloseProc, acceptCallbackPtr);
- } else {
- chan = Tcl_OpenTcpClient(interp, port, host, myaddr, myport, async);
- if (chan == NULL) {
- return TCL_ERROR;
- }
- }
-
- Tcl_RegisterChannel(interp, chan);
- Tcl_SetObjResult(interp, Tcl_NewStringObj(Tcl_GetChannelName(chan), -1));
- return TCL_OK;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * Tcl_FcopyObjCmd --
- *
- * This function is invoked to process the "fcopy" Tcl command. See the
- * user documentation for details on what it does.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * Moves data between two channels and possibly sets up a background copy
- * handler.
- *
- *----------------------------------------------------------------------
- */
-
-int
-Tcl_FcopyObjCmd(
- ClientData dummy, /* Not used. */
- Tcl_Interp *interp, /* Current interpreter. */
- int objc, /* Number of arguments. */
- Tcl_Obj *const objv[]) /* Argument objects. */
-{
- Tcl_Channel inChan, outChan;
- int mode, i, index;
- Tcl_WideInt toRead;
- Tcl_Obj *cmdPtr;
- static const char *const switches[] = { "-size", "-command", NULL };
- enum { FcopySize, FcopyCommand };
-
- if ((objc < 3) || (objc > 7) || (objc == 4) || (objc == 6)) {
- Tcl_WrongNumArgs(interp, 1, objv,
- "input output ?-size size? ?-command callback?");
- return TCL_ERROR;
- }
-
- /*
- * Parse the channel arguments and verify that they are readable or
- * writable, as appropriate.
- */
-
- if (TclGetChannelFromObj(interp, objv[1], &inChan, &mode, 0) != TCL_OK) {
- return TCL_ERROR;
- }
- if (!(mode & TCL_READABLE)) {
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "channel \"%s\" wasn't opened for reading",
- TclGetString(objv[1])));
- return TCL_ERROR;
- }
- if (TclGetChannelFromObj(interp, objv[2], &outChan, &mode, 0) != TCL_OK) {
- return TCL_ERROR;
- }
- if (!(mode & TCL_WRITABLE)) {
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "channel \"%s\" wasn't opened for writing",
- TclGetString(objv[2])));
- return TCL_ERROR;
- }
-
- toRead = -1;
- cmdPtr = NULL;
- for (i = 3; i < objc; i += 2) {
- if (Tcl_GetIndexFromObj(interp, objv[i], switches, "option", 0,
- &index) != TCL_OK) {
- return TCL_ERROR;
- }
- switch (index) {
- case FcopySize:
- if (Tcl_GetWideIntFromObj(interp, objv[i+1], &toRead) != TCL_OK) {
- return TCL_ERROR;
- }
- if (toRead < 0) {
- /*
- * Handle all negative sizes like -1, meaning 'copy all'. By
- * resetting toRead we avoid changes in the core copying
- * functions (which explicitly check for -1 and crash on any
- * other negative value).
- */
-
- toRead = -1;
- }
- break;
- case FcopyCommand:
- cmdPtr = objv[i+1];
- break;
- }
- }
-
- return TclCopyChannel(interp, inChan, outChan, toRead, cmdPtr);
-}
-
-/*
- *---------------------------------------------------------------------------
- *
- * ChanPendingObjCmd --
- *
- * This function is invoked to process the Tcl "chan pending" command
- * (TIP #287). See the user documentation for details on what it does.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * Sets interp's result to the number of bytes of buffered input or
- * output (depending on whether the first argument is "input" or
- * "output"), or -1 if the channel wasn't opened for that mode.
- *
- *---------------------------------------------------------------------------
- */
-
- /* ARGSUSED */
-static int
-ChanPendingObjCmd(
- ClientData unused, /* Not used. */
- Tcl_Interp *interp, /* Current interpreter. */
- int objc, /* Number of arguments. */
- Tcl_Obj *const objv[]) /* Argument objects. */
-{
- Tcl_Channel chan;
- int index, mode;
- static const char *const options[] = {"input", "output", NULL};
- enum options {PENDING_INPUT, PENDING_OUTPUT};
-
- if (objc != 3) {
- Tcl_WrongNumArgs(interp, 1, objv, "mode channelId");
- return TCL_ERROR;
- }
-
- if (Tcl_GetIndexFromObj(interp, objv[1], options, "mode", 0,
- &index) != TCL_OK) {
- return TCL_ERROR;
- }
-
- if (TclGetChannelFromObj(interp, objv[2], &chan, &mode, 0) != TCL_OK) {
- return TCL_ERROR;
- }
-
- switch ((enum options) index) {
- case PENDING_INPUT:
- if (!(mode & TCL_READABLE)) {
- Tcl_SetObjResult(interp, Tcl_NewIntObj(-1));
- } else {
- Tcl_SetObjResult(interp, Tcl_NewIntObj(Tcl_InputBuffered(chan)));
- }
- break;
- case PENDING_OUTPUT:
- if (!(mode & TCL_WRITABLE)) {
- Tcl_SetObjResult(interp, Tcl_NewIntObj(-1));
- } else {
- Tcl_SetObjResult(interp, Tcl_NewIntObj(Tcl_OutputBuffered(chan)));
- }
- break;
- }
- return TCL_OK;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * ChanTruncateObjCmd --
- *
- * This function is invoked to process the "chan truncate" Tcl command.
- * See the user documentation for details on what it does.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * Truncates a channel (or rather a file underlying a channel).
- *
- *----------------------------------------------------------------------
- */
-
-static int
-ChanTruncateObjCmd(
- ClientData dummy, /* Not used. */
- Tcl_Interp *interp, /* Current interpreter. */
- int objc, /* Number of arguments. */
- Tcl_Obj *const objv[]) /* Argument objects. */
-{
- Tcl_Channel chan;
- Tcl_WideInt length;
-
- if ((objc < 2) || (objc > 3)) {
- Tcl_WrongNumArgs(interp, 1, objv, "channelId ?length?");
- return TCL_ERROR;
- }
- if (TclGetChannelFromObj(interp, objv[1], &chan, NULL, 0) != TCL_OK) {
- return TCL_ERROR;
- }
-
- if (objc == 3) {
- /*
- * User is supplying an explicit length.
- */
-
- if (Tcl_GetWideIntFromObj(interp, objv[2], &length) != TCL_OK) {
- return TCL_ERROR;
- }
- if (length < 0) {
- Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "cannot truncate to negative length of file", -1));
- return TCL_ERROR;
- }
- } else {
- /*
- * User wants to truncate to the current file position.
- */
-
- length = Tcl_Tell(chan);
- if (length == Tcl_WideAsLong(-1)) {
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "could not determine current location in \"%s\": %s",
- TclGetString(objv[1]), Tcl_PosixError(interp)));
- return TCL_ERROR;
- }
- }
-
- if (Tcl_TruncateChannel(chan, length) != TCL_OK) {
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "error during truncate on \"%s\": %s",
- TclGetString(objv[1]), Tcl_PosixError(interp)));
- return TCL_ERROR;
- }
-
- return TCL_OK;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * ChanPipeObjCmd --
- *
- * This function is invoked to process the "chan pipe" Tcl command.
- * See the user documentation for details on what it does.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * Creates a pair of Tcl channels wrapping both ends of a new
- * anonymous pipe.
- *
- *----------------------------------------------------------------------
- */
-
-static int
-ChanPipeObjCmd(
- ClientData dummy, /* Not used. */
- Tcl_Interp *interp, /* Current interpreter. */
- int objc, /* Number of arguments. */
- Tcl_Obj *const objv[]) /* Argument objects. */
-{
- Tcl_Channel rchan, wchan;
- const char *channelNames[2];
- Tcl_Obj *resultPtr;
-
- if (objc != 1) {
- Tcl_WrongNumArgs(interp, 1, objv, "");
- return TCL_ERROR;
- }
-
- if (Tcl_CreatePipe(interp, &rchan, &wchan, 0) != TCL_OK) {
- return TCL_ERROR;
- }
-
- channelNames[0] = Tcl_GetChannelName(rchan);
- channelNames[1] = Tcl_GetChannelName(wchan);
-
- resultPtr = Tcl_NewObj();
- Tcl_ListObjAppendElement(NULL, resultPtr,
- Tcl_NewStringObj(channelNames[0], -1));
- Tcl_ListObjAppendElement(NULL, resultPtr,
- Tcl_NewStringObj(channelNames[1], -1));
- Tcl_SetObjResult(interp, resultPtr);
-
- return TCL_OK;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * TclChannelNamesCmd --
- *
- * This function is invoked to process the "chan names" and "file
- * channels" Tcl commands. See the user documentation for details on
- * what they do.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
-
-int
-TclChannelNamesCmd(
- ClientData clientData,
- Tcl_Interp *interp,
- int objc,
- Tcl_Obj *const objv[])
-{
- if (objc < 1 || objc > 2) {
- Tcl_WrongNumArgs(interp, 1, objv, "?pattern?");
- return TCL_ERROR;
- }
- return Tcl_GetChannelNamesEx(interp,
- ((objc == 1) ? NULL : TclGetString(objv[1])));
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * TclInitChanCmd --
- *
- * This function is invoked to create the "chan" Tcl command. See the
- * user documentation for details on what it does.
- *
- * Results:
- * A Tcl command handle.
- *
- * Side effects:
- * None (since nothing is byte-compiled).
- *
- *----------------------------------------------------------------------
- */
-
-Tcl_Command
-TclInitChanCmd(
- Tcl_Interp *interp)
-{
- /*
- * Most commands are plugged directly together, but some are done via
- * alias-like rewriting; [chan configure] is this way for security reasons
- * (want overwriting of [fconfigure] to control that nicely), and [chan
- * names] because the functionality isn't available as a separate command
- * function at the moment.
- */
- static const EnsembleImplMap initMap[] = {
- {"blocked", Tcl_FblockedObjCmd, TclCompileBasic1ArgCmd, NULL, NULL, 0},
- {"close", Tcl_CloseObjCmd, TclCompileBasic1Or2ArgCmd, NULL, NULL, 0},
- {"copy", Tcl_FcopyObjCmd, NULL, NULL, NULL, 0},
- {"create", TclChanCreateObjCmd, TclCompileBasic2ArgCmd, NULL, NULL, 0}, /* TIP #219 */
- {"eof", Tcl_EofObjCmd, TclCompileBasic1ArgCmd, NULL, NULL, 0},
- {"event", Tcl_FileEventObjCmd, TclCompileBasic2Or3ArgCmd, NULL, NULL, 0},
- {"flush", Tcl_FlushObjCmd, TclCompileBasic1ArgCmd, NULL, NULL, 0},
- {"gets", Tcl_GetsObjCmd, TclCompileBasic1Or2ArgCmd, NULL, NULL, 0},
- {"names", TclChannelNamesCmd, TclCompileBasic0Or1ArgCmd, NULL, NULL, 0},
- {"pending", ChanPendingObjCmd, TclCompileBasic2ArgCmd, NULL, NULL, 0}, /* TIP #287 */
- {"pipe", ChanPipeObjCmd, TclCompileBasic0ArgCmd, NULL, NULL, 0}, /* TIP #304 */
- {"pop", TclChanPopObjCmd, TclCompileBasic1ArgCmd, NULL, NULL, 0}, /* TIP #230 */
- {"postevent", TclChanPostEventObjCmd, TclCompileBasic2ArgCmd, NULL, NULL, 0}, /* TIP #219 */
- {"push", TclChanPushObjCmd, TclCompileBasic2ArgCmd, NULL, NULL, 0}, /* TIP #230 */
- {"puts", Tcl_PutsObjCmd, NULL, NULL, NULL, 0},
- {"read", Tcl_ReadObjCmd, NULL, NULL, NULL, 0},
- {"seek", Tcl_SeekObjCmd, TclCompileBasic2Or3ArgCmd, NULL, NULL, 0},
- {"tell", Tcl_TellObjCmd, TclCompileBasic1ArgCmd, NULL, NULL, 0},
- {"truncate", ChanTruncateObjCmd, TclCompileBasic1Or2ArgCmd, NULL, NULL, 0}, /* TIP #208 */
- {NULL, NULL, NULL, NULL, NULL, 0}
- };
- static const char *const extras[] = {
- "configure", "::fconfigure",
- NULL
- };
- Tcl_Command ensemble;
- Tcl_Obj *mapObj;
- int i;
-
- ensemble = TclMakeEnsemble(interp, "chan", initMap);
- Tcl_GetEnsembleMappingDict(NULL, ensemble, &mapObj);
- for (i=0 ; extras[i] ; i+=2) {
- /*
- * Can assume that reference counts are all incremented.
- */
-
- Tcl_DictObjPut(NULL, mapObj, Tcl_NewStringObj(extras[i], -1),
- Tcl_NewStringObj(extras[i+1], -1));
- }
- Tcl_SetEnsembleMappingDict(interp, ensemble, mapObj);
- return ensemble;
-}
-
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 4
- * fill-column: 78
- * End:
- */