diff options
Diffstat (limited to 'generic/tclIOCmd.c')
-rw-r--r-- | generic/tclIOCmd.c | 939 |
1 files changed, 631 insertions, 308 deletions
diff --git a/generic/tclIOCmd.c b/generic/tclIOCmd.c index 276ccf5..14910d7 100644 --- a/generic/tclIOCmd.c +++ b/generic/tclIOCmd.c @@ -7,8 +7,6 @@ * * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. - * - * RCS: @(#) $Id: tclIOCmd.c,v 1.33 2005/11/04 22:38:38 msofer Exp $ */ #include "tclInt.h" @@ -18,16 +16,35 @@ */ typedef struct AcceptCallback { - char *script; /* Script to invoke. */ - Tcl_Interp *interp; /* Interpreter in which to run it. */ + 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, @@ -40,6 +57,35 @@ static void UnregisterTcpServerInterpCleanupProc( /* *---------------------------------------------------------------------- * + * 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 @@ -60,76 +106,82 @@ Tcl_PutsObjCmd( ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ 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_Obj *string; /* String to write. */ + Tcl_Obj *chanObjPtr = NULL; /* channel object. */ int newline; /* Add a newline at end? */ - char *channelId; /* Name of channel for puts. */ int result; /* Result of puts operation. */ int mode; /* Mode in which channel is opened. */ + ThreadSpecificData *tsdPtr; switch (objc) { - case 2: /* [puts $x] */ + case 2: /* [puts $x] */ string = objv[1]; newline = 1; - channelId = "stdout"; break; - case 3: /* [puts -nonewline $x] or [puts $chan $x] */ - if (strcmp(Tcl_GetString(objv[1]), "-nonewline") == 0) { + case 3: /* [puts -nonewline $x] or [puts $chan $x] */ + if (strcmp(TclGetString(objv[1]), "-nonewline") == 0) { newline = 0; - channelId = "stdout"; } else { newline = 1; - channelId = Tcl_GetString(objv[1]); + chanObjPtr = objv[1]; } string = objv[2]; break; - case 4: /* [puts -nonewline $chan $x] or [puts $chan $x nonewline] */ - if (strcmp(Tcl_GetString(objv[1]), "-nonewline") == 0) { - channelId = Tcl_GetString(objv[2]); + 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]; - } else { + 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. + * documented. See also [Bug #3151675]. Will be removed in Tcl 9, + * maybe even earlier. */ - char *arg; - int length; - - arg = Tcl_GetStringFromObj(objv[3], &length); - if ((length != 9) - || (strncmp(arg, "nonewline", (size_t) length) != 0)) { - Tcl_AppendResult(interp, "bad argument \"", arg, - "\": should be \"nonewline\"", NULL); - return TCL_ERROR; - } - channelId = Tcl_GetString(objv[1]); + chanObjPtr = objv[1]; string = objv[2]; + break; +#endif } - newline = 0; - break; - - default: - /* [puts] or [puts some bad number of arguments...] */ + /* Fall through */ + default: /* [puts] or + * [puts some bad number of arguments...] */ Tcl_WrongNumArgs(interp, 1, objv, "?-nonewline? ?channelId? string"); return TCL_ERROR; } - chan = Tcl_GetChannel(interp, channelId, &mode); - if (chan == (Tcl_Channel) NULL) { + 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) == 0) { - Tcl_AppendResult(interp, "channel \"", channelId, - "\" wasn't opened for writing", NULL); + if (!(mode & TCL_WRITABLE)) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "channel \"%s\" wasn't opened for writing", + TclGetString(chanObjPtr))); return TCL_ERROR; } + Tcl_Preserve(chan); result = Tcl_WriteObj(chan, string); if (result < 0) { goto error; @@ -140,6 +192,7 @@ Tcl_PutsObjCmd( goto error; } } + Tcl_Release(chan); return TCL_OK; /* @@ -151,9 +204,10 @@ Tcl_PutsObjCmd( error: if (!TclChanCaughtErrorBypass(interp, chan)) { - Tcl_AppendResult(interp, "error writing \"", channelId, "\": ", - Tcl_PosixError(interp), NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf("error writing \"%s\": %s", + TclGetString(chanObjPtr), Tcl_PosixError(interp))); } + Tcl_Release(chan); return TCL_ERROR; } @@ -180,27 +234,28 @@ Tcl_FlushObjCmd( ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { + Tcl_Obj *chanObjPtr; Tcl_Channel chan; /* The channel to flush on. */ - char *channelId; int mode; if (objc != 2) { Tcl_WrongNumArgs(interp, 1, objv, "channelId"); return TCL_ERROR; } - channelId = Tcl_GetString(objv[1]); - chan = Tcl_GetChannel(interp, channelId, &mode); - if (chan == (Tcl_Channel) NULL) { + chanObjPtr = objv[1]; + if (TclGetChannelFromObj(interp, chanObjPtr, &chan, &mode, 0) != TCL_OK) { return TCL_ERROR; } - if ((mode & TCL_WRITABLE) == 0) { - Tcl_AppendResult(interp, "channel \"", channelId, - "\" wasn't opened for writing", NULL); + if (!(mode & TCL_WRITABLE)) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "channel \"%s\" wasn't opened for writing", + TclGetString(chanObjPtr))); return TCL_ERROR; } + Tcl_Preserve(chan); if (Tcl_Flush(chan) != TCL_OK) { /* * TIP #219. @@ -210,11 +265,14 @@ Tcl_FlushObjCmd( */ if (!TclChanCaughtErrorBypass(interp, chan)) { - Tcl_AppendResult(interp, "error flushing \"", channelId, "\": ", - Tcl_PosixError(interp), NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "error flushing \"%s\": %s", + TclGetString(chanObjPtr), Tcl_PosixError(interp))); } + Tcl_Release(chan); return TCL_ERROR; } + Tcl_Release(chan); return TCL_OK; } @@ -241,31 +299,31 @@ Tcl_GetsObjCmd( ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ 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. */ int lineLen; /* Length of line just read. */ int mode; /* Mode in which channel is opened. */ - char *name; - Tcl_Obj *linePtr; + Tcl_Obj *linePtr, *chanObjPtr; + int code = TCL_OK; if ((objc != 2) && (objc != 3)) { Tcl_WrongNumArgs(interp, 1, objv, "channelId ?varName?"); return TCL_ERROR; } - name = Tcl_GetString(objv[1]); - chan = Tcl_GetChannel(interp, name, &mode); - if (chan == (Tcl_Channel) NULL) { + chanObjPtr = objv[1]; + if (TclGetChannelFromObj(interp, chanObjPtr, &chan, &mode, 0) != TCL_OK) { return TCL_ERROR; } - if ((mode & TCL_READABLE) == 0) { - Tcl_AppendResult(interp, "channel \"", name, - "\" wasn't opened for reading", NULL); + if (!(mode & TCL_READABLE)) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "channel \"%s\" wasn't opened for reading", + TclGetString(chanObjPtr))); return TCL_ERROR; } + Tcl_Preserve(chan); linePtr = Tcl_NewObj(); - lineLen = Tcl_GetsObj(chan, linePtr); if (lineLen < 0) { if (!Tcl_Eof(chan) && !Tcl_InputBlocked(chan)) { @@ -277,12 +335,14 @@ Tcl_GetsObjCmd( * 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_ResetResult(interp); - Tcl_AppendResult(interp, "error reading \"", name, "\": ", - Tcl_PosixError(interp), NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "error reading \"%s\": %s", + TclGetString(chanObjPtr), Tcl_PosixError(interp))); } - return TCL_ERROR; + code = TCL_ERROR; + goto done; } lineLen = -1; } @@ -292,11 +352,12 @@ Tcl_GetsObjCmd( return TCL_ERROR; } Tcl_SetObjResult(interp, Tcl_NewIntObj(lineLen)); - return TCL_OK; } else { Tcl_SetObjResult(interp, linePtr); } - return TCL_OK; + done: + Tcl_Release(chan); + return code; } /* @@ -322,15 +383,14 @@ Tcl_ReadObjCmd( ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ 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. */ 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. */ - char *name; - Tcl_Obj *resultPtr; + Tcl_Obj *resultPtr, *chanObjPtr; if ((objc != 2) && (objc != 3)) { Interp *iPtr; @@ -346,13 +406,12 @@ Tcl_ReadObjCmd( iPtr->flags |= INTERP_ALTERNATE_WRONG_ARGS; Tcl_WrongNumArgs(interp, 1, objv, "?-nonewline? channelId"); - iPtr->flags &= ~INTERP_ALTERNATE_WRONG_ARGS; return TCL_ERROR; } i = 1; newline = 0; - if (strcmp(Tcl_GetString(objv[1]), "-nonewline") == 0) { + if (strcmp(TclGetString(objv[1]), "-nonewline") == 0) { newline = 1; i++; } @@ -361,43 +420,51 @@ Tcl_ReadObjCmd( goto argerror; } - name = Tcl_GetString(objv[i]); - chan = Tcl_GetChannel(interp, name, &mode); - if (chan == (Tcl_Channel) NULL) { + chanObjPtr = objv[i]; + if (TclGetChannelFromObj(interp, chanObjPtr, &chan, &mode, 0) != TCL_OK) { return TCL_ERROR; } - if ((mode & TCL_READABLE) == 0) { - Tcl_AppendResult(interp, "channel \"", name, - "\" wasn't opened for reading", NULL); + 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. */ + i++; /* Consumed channel name. */ /* - * Compute how many bytes to read, and see whether the final newline - * should be dropped. + * Compute how many bytes to read. */ toRead = -1; if (i < objc) { - char *arg; + 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. + */ - arg = Tcl_GetString(objv[i]); - if (isdigit(UCHAR(arg[0]))) { /* INTL: digit */ - if (Tcl_GetIntFromObj(interp, objv[i], &toRead) != TCL_OK) { + 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 } - } else if (strcmp(arg, "nonewline") == 0) { newline = 1; - } else { - Tcl_AppendResult(interp, "bad argument \"", arg, - "\": should be \"nonewline\"", NULL); - return TCL_ERROR; +#endif } } resultPtr = Tcl_NewObj(); Tcl_IncrRefCount(resultPtr); + Tcl_Preserve(chan); charactersRead = Tcl_ReadChars(chan, resultPtr, toRead, 0); if (charactersRead < 0) { /* @@ -408,11 +475,12 @@ Tcl_ReadObjCmd( */ if (!TclChanCaughtErrorBypass(interp, chan)) { - Tcl_ResetResult(interp); - Tcl_AppendResult(interp, "error reading \"", name, "\": ", - Tcl_PosixError(interp), NULL); - Tcl_DecrRefCount(resultPtr); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "error reading \"%s\": %s", + TclGetString(chanObjPtr), Tcl_PosixError(interp))); } + Tcl_Release(chan); + Tcl_DecrRefCount(resultPtr); return TCL_ERROR; } @@ -421,15 +489,16 @@ Tcl_ReadObjCmd( */ if ((charactersRead > 0) && (newline != 0)) { - char *result; + const char *result; int length; - result = Tcl_GetStringFromObj(resultPtr, &length); + result = TclGetStringFromObj(resultPtr, &length); if (result[length - 1] == '\n') { Tcl_SetObjLength(resultPtr, length - 1); } } Tcl_SetObjResult(interp, resultPtr); + Tcl_Release(chan); Tcl_DecrRefCount(resultPtr); return TCL_OK; } @@ -458,26 +527,23 @@ Tcl_SeekObjCmd( ClientData clientData, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + 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. */ - char *chanName; int optionIndex; - static CONST char *originOptions[] = { + static const char *const originOptions[] = { "start", "current", "end", NULL }; - static int modeArray[] = {SEEK_SET, SEEK_CUR, SEEK_END}; + 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; } - chanName = Tcl_GetString(objv[1]); - chan = Tcl_GetChannel(interp, chanName, NULL); - if (chan == (Tcl_Channel) NULL) { + if (TclGetChannelFromObj(interp, objv[1], &chan, NULL, 0) != TCL_OK) { return TCL_ERROR; } if (Tcl_GetWideIntFromObj(interp, objv[2], &offset) != TCL_OK) { @@ -492,6 +558,7 @@ Tcl_SeekObjCmd( mode = modeArray[optionIndex]; } + Tcl_Preserve(chan); result = Tcl_Seek(chan, offset, mode); if (result == Tcl_LongAsWide(-1)) { /* @@ -500,12 +567,16 @@ Tcl_SeekObjCmd( * 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_AppendResult(interp, "error during seek on \"", chanName, - "\": ", Tcl_PosixError(interp), NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "error during seek on \"%s\": %s", + TclGetString(objv[1]), Tcl_PosixError(interp))); } + Tcl_Release(chan); return TCL_ERROR; } + Tcl_Release(chan); return TCL_OK; } @@ -532,11 +603,11 @@ Tcl_TellObjCmd( ClientData clientData, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { Tcl_Channel chan; /* The channel to tell on. */ - char *chanName; Tcl_WideInt newLoc; + int code; if (objc != 2) { Tcl_WrongNumArgs(interp, 1, objv, "channelId"); @@ -548,12 +619,11 @@ Tcl_TellObjCmd( * channel table of this interpreter. */ - chanName = Tcl_GetString(objv[1]); - chan = Tcl_GetChannel(interp, chanName, NULL); - if (chan == (Tcl_Channel) NULL) { + if (TclGetChannelFromObj(interp, objv[1], &chan, NULL, 0) != TCL_OK) { return TCL_ERROR; } + Tcl_Preserve(chan); newLoc = Tcl_Tell(chan); /* @@ -562,7 +632,10 @@ Tcl_TellObjCmd( * them into the regular interpreter result. */ - if (TclChanCaughtErrorBypass(interp, chan)) { + + code = TclChanCaughtErrorBypass(interp, chan); + Tcl_Release(chan); + if (code) { return TCL_ERROR; } @@ -593,22 +666,62 @@ Tcl_CloseObjCmd( ClientData clientData, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { Tcl_Channel chan; /* The channel to close. */ - char *arg; + static const char *const dirOptions[] = { + "read", "write", NULL + }; + static const int dirArray[] = {TCL_CLOSE_READ, TCL_CLOSE_WRITE}; - if (objc != 2) { - Tcl_WrongNumArgs(interp, 1, objv, "channelId"); + if ((objc != 2) && (objc != 3)) { + Tcl_WrongNumArgs(interp, 1, objv, "channelId ?direction?"); return TCL_ERROR; } - arg = Tcl_GetString(objv[1]); - chan = Tcl_GetChannel(interp, arg, NULL); - if (chan == (Tcl_Channel) NULL) { + 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 @@ -621,16 +734,15 @@ Tcl_CloseObjCmd( * a terminating newline. */ - Tcl_Obj *resultPtr; - char *string; + Tcl_Obj *resultPtr = Tcl_GetObjResult(interp); + const char *string; int len; - resultPtr = Tcl_GetObjResult(interp); if (Tcl_IsShared(resultPtr)) { resultPtr = Tcl_DuplicateObj(resultPtr); Tcl_SetObjResult(interp, resultPtr); } - string = Tcl_GetStringFromObj(resultPtr, &len); + string = TclGetStringFromObj(resultPtr, &len); if ((len > 0) && (string[len - 1] == '\n')) { Tcl_SetObjLength(resultPtr, len - 1); } @@ -663,21 +775,18 @@ Tcl_FconfigureObjCmd( ClientData clientData, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { - char *chanName, *optionName, *valueName; + 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 ?optionName? ?value? ?optionName value?..."); + Tcl_WrongNumArgs(interp, 1, objv, "channelId ?-option value ...?"); return TCL_ERROR; } - chanName = Tcl_GetString(objv[1]); - chan = Tcl_GetChannel(interp, chanName, NULL); - if (chan == (Tcl_Channel) NULL) { + if (TclGetChannelFromObj(interp, objv[1], &chan, NULL, 0) != TCL_OK) { return TCL_ERROR; } @@ -697,7 +806,7 @@ Tcl_FconfigureObjCmd( * Tcl_GetChannelOption. */ Tcl_DStringInit(&ds); - optionName = Tcl_GetString(objv[2]); + optionName = TclGetString(objv[2]); if (Tcl_GetChannelOption(interp, chan, optionName, &ds) != TCL_OK) { Tcl_DStringFree(&ds); return TCL_ERROR; @@ -707,8 +816,8 @@ Tcl_FconfigureObjCmd( } for (i = 3; i < objc; i += 2) { - optionName = Tcl_GetString(objv[i-1]); - valueName = Tcl_GetString(objv[i]); + optionName = TclGetString(objv[i-1]); + valueName = TclGetString(objv[i]); if (Tcl_SetChannelOption(interp, chan, optionName, valueName) != TCL_OK) { return TCL_ERROR; @@ -742,20 +851,16 @@ Tcl_EofObjCmd( ClientData unused, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { Tcl_Channel chan; - int dummy; - char *arg; if (objc != 2) { Tcl_WrongNumArgs(interp, 1, objv, "channelId"); return TCL_ERROR; } - arg = Tcl_GetString(objv[1]); - chan = Tcl_GetChannel(interp, arg, &dummy); - if (chan == NULL) { + if (TclGetChannelFromObj(interp, objv[1], &chan, NULL, 0) != TCL_OK) { return TCL_ERROR; } @@ -786,35 +891,30 @@ Tcl_ExecObjCmd( ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { - /* - * This function generates an argv array for the string arguments. It - * starts out with stack-allocated space but uses dynamically-allocated - * storage if needed. - */ - -#define NUM_ARGS 20 Tcl_Obj *resultPtr; - CONST char **argv; - char *string; + const char **argv; /* An array for the string arguments. Stored + * on the _Tcl_ stack. */ + const char *string; Tcl_Channel chan; - CONST char *argStorage[NUM_ARGS]; int argc, background, i, index, keepNewline, result, skip, length; - static CONST char *options[] = { - "-keepnewline", "--", NULL + int ignoreStderr; + static const char *const options[] = { + "-ignorestderr", "-keepnewline", "--", NULL }; enum options { - EXEC_KEEPNEWLINE, EXEC_LAST + EXEC_IGNORESTDERR, EXEC_KEEPNEWLINE, EXEC_LAST }; /* - * Check for a leading "-keepnewline" argument. + * Check for any leading option arguments. */ keepNewline = 0; + ignoreStderr = 0; for (skip = 1; skip < objc; skip++) { - string = Tcl_GetString(objv[skip]); + string = TclGetString(objv[skip]); if (string[0] != '-') { break; } @@ -824,13 +924,15 @@ Tcl_ExecObjCmd( } if (index == EXEC_KEEPNEWLINE) { keepNewline = 1; + } else if (index == EXEC_IGNORESTDERR) { + ignoreStderr = 1; } else { skip++; break; } } if (objc <= skip) { - Tcl_WrongNumArgs(interp, 1, objv, "?switches? arg ?arg ...?"); + Tcl_WrongNumArgs(interp, 1, objv, "?-switch ...? arg ?arg ...?"); return TCL_ERROR; } @@ -839,7 +941,7 @@ Tcl_ExecObjCmd( */ background = 0; - string = Tcl_GetString(objv[objc - 1]); + string = TclGetString(objv[objc - 1]); if ((string[0] == '&') && (string[1] == '\0')) { objc--; background = 1; @@ -850,11 +952,8 @@ Tcl_ExecObjCmd( * to hold the argc arguments plus 1 extra for the zero end-of-argv word. */ - argv = argStorage; argc = objc - skip; - if ((argc + 1) > sizeof(argv) / sizeof(argv[0])) { - argv = (CONST char **) ckalloc((unsigned)(argc + 1) * sizeof(char *)); - } + argv = TclStackAlloc(interp, (unsigned)(argc + 1) * sizeof(char *)); /* * Copy the string conversions of each (post option) object into the @@ -862,21 +961,19 @@ Tcl_ExecObjCmd( */ for (i = 0; i < argc; i++) { - argv[i] = Tcl_GetString(objv[i + skip]); + argv[i] = TclGetString(objv[i + skip]); } argv[argc] = NULL; - chan = Tcl_OpenCommandChannel(interp, argc, argv, - (background ? 0 : TCL_STDOUT | TCL_STDERR)); + chan = Tcl_OpenCommandChannel(interp, argc, argv, (background ? 0 : + ignoreStderr ? TCL_STDOUT : TCL_STDOUT|TCL_STDERR)); /* - * Free the argv array if malloc'ed storage was used. + * Free the argv array. */ - if (argv != argStorage) { - ckfree((char *)argv); - } + TclStackFree(interp, (void *) argv); - if (chan == (Tcl_Channel) NULL) { + if (chan == NULL) { return TCL_ERROR; } @@ -903,10 +1000,10 @@ Tcl_ExecObjCmd( * the regular message if nothing was found in the bypass. */ - if (!TclChanCaughtErrorBypass (interp, chan)) { - Tcl_ResetResult(interp); - Tcl_AppendResult(interp, "error reading output from command: ", - Tcl_PosixError(interp), NULL); + if (!TclChanCaughtErrorBypass(interp, chan)) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "error reading output from command: %s", + Tcl_PosixError(interp))); Tcl_DecrRefCount(resultPtr); } return TCL_ERROR; @@ -928,7 +1025,7 @@ Tcl_ExecObjCmd( */ if (keepNewline == 0) { - string = Tcl_GetStringFromObj(resultPtr, &length); + string = TclGetStringFromObj(resultPtr, &length); if ((length > 0) && (string[length - 1] == '\n')) { Tcl_SetObjLength(resultPtr, length - 1); } @@ -962,25 +1059,23 @@ Tcl_FblockedObjCmd( ClientData unused, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { Tcl_Channel chan; int mode; - char *arg; if (objc != 2) { Tcl_WrongNumArgs(interp, 1, objv, "channelId"); return TCL_ERROR; } - arg = Tcl_GetString(objv[1]); - chan = Tcl_GetChannel(interp, arg, &mode); - if (chan == NULL) { + if (TclGetChannelFromObj(interp, objv[1], &chan, &mode, 0) != TCL_OK) { return TCL_ERROR; } - if ((mode & TCL_READABLE) == 0) { - Tcl_AppendResult(interp, "channel \"", arg, - "\" wasn't opened for reading", NULL); + if (!(mode & TCL_READABLE)) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "channel \"%s\" wasn't opened for reading", + TclGetString(objv[1]))); return TCL_ERROR; } @@ -1011,10 +1106,10 @@ Tcl_OpenObjCmd( ClientData notUsed, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { int pipeline, prot; - char *modeString, *what; + const char *modeString, *what; Tcl_Channel chan; if ((objc < 2) || (objc > 4)) { @@ -1025,16 +1120,36 @@ Tcl_OpenObjCmd( if (objc == 2) { modeString = "r"; } else { - modeString = Tcl_GetString(objv[2]); + modeString = TclGetString(objv[2]); if (objc == 4) { - if (Tcl_GetIntFromObj(interp, objv[3], &prot) != TCL_OK) { + 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 = Tcl_GetString(objv[1]); + what = TclGetString(objv[1]); if (what[0] == '|') { pipeline = 1; } @@ -1047,7 +1162,7 @@ Tcl_OpenObjCmd( chan = Tcl_FSOpenFileChannel(interp, objv[1], modeString, prot); } else { int mode, seekFlag, cmdObjc, binary; - CONST char **cmdArgv; + const char **cmdArgv; if (Tcl_SplitList(interp, what+1, &cmdObjc, &cmdArgv) != TCL_OK) { return TCL_ERROR; @@ -1074,17 +1189,17 @@ Tcl_OpenObjCmd( break; } chan = Tcl_OpenCommandChannel(interp, cmdObjc, cmdArgv, flags); - if (binary) { + if (binary && chan) { Tcl_SetChannelOption(interp, chan, "-translation", "binary"); } } - ckfree((char *) cmdArgv); + ckfree(cmdArgv); } - if (chan == (Tcl_Channel) NULL) { + if (chan == NULL) { return TCL_ERROR; } Tcl_RegisterChannel(interp, chan); - Tcl_AppendResult(interp, Tcl_GetChannelName(chan), NULL); + Tcl_SetObjResult(interp, Tcl_NewStringObj(Tcl_GetChannelName(chan), -1)); return TCL_OK; } @@ -1116,19 +1231,18 @@ TcpAcceptCallbacksDeleteProc( * was registered. */ Tcl_Interp *interp) /* Interpreter being deleted - not used. */ { - Tcl_HashTable *hTblPtr; + Tcl_HashTable *hTblPtr = clientData; Tcl_HashEntry *hPtr; Tcl_HashSearch hSearch; - AcceptCallback *acceptCallbackPtr; - hTblPtr = (Tcl_HashTable *) clientData; for (hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch); hPtr != NULL; hPtr = Tcl_NextHashEntry(&hSearch)) { - acceptCallbackPtr = (AcceptCallback *) Tcl_GetHashValue(hPtr); + AcceptCallback *acceptCallbackPtr = Tcl_GetHashValue(hPtr); + acceptCallbackPtr->interp = NULL; } Tcl_DeleteHashTable(hTblPtr); - ckfree((char *) hTblPtr); + ckfree(hTblPtr); } /* @@ -1163,23 +1277,22 @@ RegisterTcpServerInterpCleanup( * smash when the interpreter will be * deleted. */ Tcl_HashEntry *hPtr; /* Entry for this record. */ - int new; /* Is the entry new? */ + int isNew; /* Is the entry new? */ - hTblPtr = (Tcl_HashTable *) - Tcl_GetAssocData(interp, "tclTCPAcceptCallbacks", NULL); + hTblPtr = Tcl_GetAssocData(interp, "tclTCPAcceptCallbacks", NULL); if (hTblPtr == NULL) { - hTblPtr = (Tcl_HashTable *) ckalloc((unsigned) sizeof(Tcl_HashTable)); + hTblPtr = ckalloc(sizeof(Tcl_HashTable)); Tcl_InitHashTable(hTblPtr, TCL_ONE_WORD_KEYS); - (void) Tcl_SetAssocData(interp, "tclTCPAcceptCallbacks", - TcpAcceptCallbacksDeleteProc, (ClientData) hTblPtr); + Tcl_SetAssocData(interp, "tclTCPAcceptCallbacks", + TcpAcceptCallbacksDeleteProc, hTblPtr); } - hPtr = Tcl_CreateHashEntry(hTblPtr, (char *) acceptCallbackPtr, &new); - if (!new) { + hPtr = Tcl_CreateHashEntry(hTblPtr, acceptCallbackPtr, &isNew); + if (!isNew) { Tcl_Panic("RegisterTcpServerCleanup: damaged accept record table"); } - Tcl_SetHashValue(hPtr, (ClientData) acceptCallbackPtr); + Tcl_SetHashValue(hPtr, acceptCallbackPtr); } /* @@ -1212,8 +1325,7 @@ UnregisterTcpServerInterpCleanupProc( Tcl_HashTable *hTblPtr; Tcl_HashEntry *hPtr; - hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp, - "tclTCPAcceptCallbacks", NULL); + hTblPtr = Tcl_GetAssocData(interp, "tclTCPAcceptCallbacks", NULL); if (hTblPtr == NULL) { return; } @@ -1251,13 +1363,7 @@ AcceptCallbackProc( char *address, /* Address of client that was accepted. */ int port) /* Port of client that was accepted. */ { - AcceptCallback *acceptCallbackPtr; - Tcl_Interp *interp; - char *script; - char portBuf[TCL_INTEGER_SPACE]; - int result; - - acceptCallbackPtr = (AcceptCallback *) callbackData; + AcceptCallback *acceptCallbackPtr = callbackData; /* * Check if the callback is still valid; the interpreter may have gone @@ -1266,12 +1372,13 @@ AcceptCallbackProc( */ if (acceptCallbackPtr->interp != NULL) { + char portBuf[TCL_INTEGER_SPACE]; + char *script = acceptCallbackPtr->script; + Tcl_Interp *interp = acceptCallbackPtr->interp; + int result; - script = acceptCallbackPtr->script; - interp = acceptCallbackPtr->interp; - - Tcl_Preserve((ClientData) script); - Tcl_Preserve((ClientData) interp); + Tcl_Preserve(script); + Tcl_Preserve(interp); TclFormatInt(portBuf, port); Tcl_RegisterChannel(interp, chan); @@ -1286,7 +1393,7 @@ AcceptCallbackProc( result = Tcl_VarEval(interp, script, " ", Tcl_GetChannelName(chan), " ", address, " ", portBuf, NULL); if (result != TCL_OK) { - Tcl_BackgroundError(interp); + Tcl_BackgroundException(interp, result); Tcl_UnregisterChannel(interp, chan); } @@ -1297,13 +1404,12 @@ AcceptCallbackProc( Tcl_UnregisterChannel(NULL, chan); - Tcl_Release((ClientData) interp); - Tcl_Release((ClientData) script); - + Tcl_Release(interp); + Tcl_Release(script); } else { /* - * The interpreter has been deleted, so there is no useful way to - * utilize the client socket - just close it. + * The interpreter has been deleted, so there is no useful way to use + * the client socket - just close it. */ Tcl_Close(NULL, chan); @@ -1336,16 +1442,15 @@ TcpServerCloseProc( ClientData callbackData) /* The data passed in the call to * Tcl_CreateCloseHandler. */ { - AcceptCallback *acceptCallbackPtr; + AcceptCallback *acceptCallbackPtr = callbackData; /* The actual data. */ - acceptCallbackPtr = (AcceptCallback *) callbackData; if (acceptCallbackPtr->interp != NULL) { UnregisterTcpServerInterpCleanupProc(acceptCallbackPtr->interp, acceptCallbackPtr); } - Tcl_EventuallyFree((ClientData) acceptCallbackPtr->script, TCL_DYNAMIC); - ckfree((char *) acceptCallbackPtr); + Tcl_EventuallyFree(acceptCallbackPtr->script, TCL_DYNAMIC); + ckfree(acceptCallbackPtr); } /* @@ -1370,31 +1475,25 @@ Tcl_SocketObjCmd( ClientData notUsed, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { - static CONST char *socketOptions[] = { - "-async", "-myaddr", "-myport","-server", NULL + static const char *const socketOptions[] = { + "-async", "-myaddr", "-myport", "-server", NULL }; enum socketOptions { SKT_ASYNC, SKT_MYADDR, SKT_MYPORT, SKT_SERVER }; - int optionIndex, a, server, port; - char *arg, *copyScript, *host, *script; - char *myaddr = NULL; - int myport = 0; - int async = 0; + int optionIndex, a, server = 0, port, myport = 0, async = 0; + const char *host, *script = NULL, *myaddr = NULL; Tcl_Channel chan; - AcceptCallback *acceptCallbackPtr; - - server = 0; - script = NULL; if (TclpHasSockets(interp) != TCL_OK) { return TCL_ERROR; } for (a = 1; a < objc; a++) { - arg = Tcl_GetString(objv[a]); + const char *arg = Tcl_GetString(objv[a]); + if (arg[0] != '-') { break; } @@ -1405,8 +1504,8 @@ Tcl_SocketObjCmd( switch ((enum socketOptions) optionIndex) { case SKT_ASYNC: if (server == 1) { - Tcl_AppendResult(interp, - "cannot set -async option for server sockets", NULL); + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "cannot set -async option for server sockets", -1)); return TCL_ERROR; } async = 1; @@ -1414,22 +1513,22 @@ Tcl_SocketObjCmd( case SKT_MYADDR: a++; if (a >= objc) { - Tcl_AppendResult(interp, - "no argument given for -myaddr option", NULL); + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "no argument given for -myaddr option", -1)); return TCL_ERROR; } - myaddr = Tcl_GetString(objv[a]); + myaddr = TclGetString(objv[a]); break; case SKT_MYPORT: { - char *myPortName; + const char *myPortName; a++; if (a >= objc) { - Tcl_AppendResult(interp, - "no argument given for -myport option", NULL); + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "no argument given for -myport option", -1)); return TCL_ERROR; } - myPortName = Tcl_GetString(objv[a]); + myPortName = TclGetString(objv[a]); if (TclSockGetPort(interp, myPortName, "tcp", &myport) != TCL_OK) { return TCL_ERROR; } @@ -1437,18 +1536,18 @@ Tcl_SocketObjCmd( } case SKT_SERVER: if (async == 1) { - Tcl_AppendResult(interp, - "cannot set -async option for server sockets", NULL); + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "cannot set -async option for server sockets", -1)); return TCL_ERROR; } server = 1; a++; if (a >= objc) { - Tcl_AppendResult(interp, - "no argument given for -server option", NULL); + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "no argument given for -server option", -1)); return TCL_ERROR; } - script = Tcl_GetString(objv[a]); + script = TclGetString(objv[a]); break; default: Tcl_Panic("Tcl_SocketObjCmd: bad option index to SocketOptions"); @@ -1457,12 +1556,12 @@ Tcl_SocketObjCmd( if (server) { host = myaddr; /* NULL implies INADDR_ANY */ if (myport != 0) { - Tcl_AppendResult(interp, "option -myport is not valid for servers", - NULL); + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "option -myport is not valid for servers", -1)); return TCL_ERROR; } } else if (a < objc) { - host = Tcl_GetString(objv[a]); + host = TclGetString(objv[a]); a++; } else { Interp *iPtr; @@ -1474,12 +1573,11 @@ Tcl_SocketObjCmd( iPtr->flags |= INTERP_ALTERNATE_WRONG_ARGS; Tcl_WrongNumArgs(interp, 1, objv, "-server command ?-myaddr addr? port"); - iPtr->flags &= ~INTERP_ALTERNATE_WRONG_ARGS; return TCL_ERROR; } if (a == objc-1) { - if (TclSockGetPort(interp, Tcl_GetString(objv[a]), "tcp", + if (TclSockGetPort(interp, TclGetString(objv[a]), "tcp", &port) != TCL_OK) { return TCL_ERROR; } @@ -1488,17 +1586,19 @@ Tcl_SocketObjCmd( } if (server) { - acceptCallbackPtr = (AcceptCallback *) ckalloc((unsigned) - sizeof(AcceptCallback)); - copyScript = ckalloc((unsigned) strlen(script) + 1); - strcpy(copyScript, script); + 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, - (ClientData) acceptCallbackPtr); - if (chan == (Tcl_Channel) NULL) { + acceptCallbackPtr); + if (chan == NULL) { ckfree(copyScript); - ckfree((char *) acceptCallbackPtr); + ckfree(acceptCallbackPtr); return TCL_ERROR; } @@ -1517,17 +1617,16 @@ Tcl_SocketObjCmd( * be informed when the interpreter is deleted. */ - Tcl_CreateCloseHandler(chan, TcpServerCloseProc, - (ClientData) acceptCallbackPtr); + Tcl_CreateCloseHandler(chan, TcpServerCloseProc, acceptCallbackPtr); } else { chan = Tcl_OpenTcpClient(interp, port, host, myaddr, myport, async); - if (chan == (Tcl_Channel) NULL) { + if (chan == NULL) { return TCL_ERROR; } } - Tcl_RegisterChannel(interp, chan); - Tcl_AppendResult(interp, Tcl_GetChannelName(chan), NULL); + Tcl_RegisterChannel(interp, chan); + Tcl_SetObjResult(interp, Tcl_NewStringObj(Tcl_GetChannelName(chan), -1)); return TCL_OK; } @@ -1554,14 +1653,13 @@ Tcl_FcopyObjCmd( ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { Tcl_Channel inChan, outChan; - char *arg; - int mode, i; - int toRead, index; + int mode, i, index; + Tcl_WideInt toRead; Tcl_Obj *cmdPtr; - static CONST char* switches[] = { "-size", "-command", NULL }; + static const char *const switches[] = { "-size", "-command", NULL }; enum { FcopySize, FcopyCommand }; if ((objc < 3) || (objc > 7) || (objc == 4) || (objc == 6)) { @@ -1575,24 +1673,22 @@ Tcl_FcopyObjCmd( * writable, as appropriate. */ - arg = Tcl_GetString(objv[1]); - inChan = Tcl_GetChannel(interp, arg, &mode); - if (inChan == (Tcl_Channel) NULL) { + if (TclGetChannelFromObj(interp, objv[1], &inChan, &mode, 0) != TCL_OK) { return TCL_ERROR; } - if ((mode & TCL_READABLE) == 0) { - Tcl_AppendResult(interp, "channel \"", arg, - "\" wasn't opened for reading", NULL); + if (!(mode & TCL_READABLE)) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "channel \"%s\" wasn't opened for reading", + TclGetString(objv[1]))); return TCL_ERROR; } - arg = Tcl_GetString(objv[2]); - outChan = Tcl_GetChannel(interp, arg, &mode); - if (outChan == (Tcl_Channel) NULL) { + if (TclGetChannelFromObj(interp, objv[2], &outChan, &mode, 0) != TCL_OK) { return TCL_ERROR; } - if ((mode & TCL_WRITABLE) == 0) { - Tcl_AppendResult(interp, "channel \"", arg, - "\" wasn't opened for writing", NULL); + if (!(mode & TCL_WRITABLE)) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "channel \"%s\" wasn't opened for writing", + TclGetString(objv[2]))); return TCL_ERROR; } @@ -1600,14 +1696,24 @@ Tcl_FcopyObjCmd( cmdPtr = NULL; for (i = 3; i < objc; i += 2) { if (Tcl_GetIndexFromObj(interp, objv[i], switches, "switch", 0, - (int *) &index) != TCL_OK) { + &index) != TCL_OK) { return TCL_ERROR; } switch (index) { case FcopySize: - if (Tcl_GetIntFromObj(interp, objv[i+1], &toRead) != TCL_OK) { + 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]; @@ -1619,9 +1725,74 @@ Tcl_FcopyObjCmd( } /* + *--------------------------------------------------------------------------- + * + * 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; +} + +/* *---------------------------------------------------------------------- * - * Tcl_ChanTruncateObjCmd -- + * ChanTruncateObjCmd -- * * This function is invoked to process the "chan truncate" Tcl command. * See the user documentation for details on what it does. @@ -1635,25 +1806,21 @@ Tcl_FcopyObjCmd( *---------------------------------------------------------------------- */ -int -TclChanTruncateObjCmd( +static int +ChanTruncateObjCmd( ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { Tcl_Channel chan; - int mode; Tcl_WideInt length; - char *chanName; if ((objc < 2) || (objc > 3)) { Tcl_WrongNumArgs(interp, 1, objv, "channelId ?length?"); return TCL_ERROR; } - chanName = TclGetString(objv[1]); - chan = Tcl_GetChannel(interp, chanName, &mode); - if (chan == NULL) { + if (TclGetChannelFromObj(interp, objv[1], &chan, NULL, 0) != TCL_OK) { return TCL_ERROR; } @@ -1666,8 +1833,8 @@ TclChanTruncateObjCmd( return TCL_ERROR; } if (length < 0) { - Tcl_AppendResult(interp, - "cannot truncate to negative length of file", NULL); + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "cannot truncate to negative length of file", -1)); return TCL_ERROR; } } else { @@ -1677,27 +1844,183 @@ TclChanTruncateObjCmd( length = Tcl_Tell(chan); if (length == Tcl_WideAsLong(-1)) { - Tcl_AppendResult(interp, - "could not determine current location in \"", chanName, - "\": ", Tcl_PosixError(interp), NULL); + 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_AppendResult(interp, "error during truncate on \"", chanName, - "\": ", Tcl_PosixError(interp), NULL); + 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: */ - |