summaryrefslogtreecommitdiffstats
path: root/generic/tclIO.c
diff options
context:
space:
mode:
authorhobbs <hobbs>2007-12-05 21:47:25 (GMT)
committerhobbs <hobbs>2007-12-05 21:47:25 (GMT)
commitd2cafa17fbc0ba124639514618fcdefecc66d927 (patch)
tree4699636d41848d0d0642de636144a9faad783163 /generic/tclIO.c
parent23b63fa1bae88cf7965c0113afc1738d23b51dd4 (diff)
downloadtcl-d2cafa17fbc0ba124639514618fcdefecc66d927.zip
tcl-d2cafa17fbc0ba124639514618fcdefecc66d927.tar.gz
tcl-d2cafa17fbc0ba124639514618fcdefecc66d927.tar.bz2
* generic/tclIO.h: Create Tcl_Obj for Tcl channels to reduce
* generic/tclIO.c: overhead in lookup by Tcl_GetChannel. New * generic/tclIOCmd.c: TclGetChannelFromObj for internal use. * generic/tclIO.c (WriteBytes, WriteChars): add opt check to avoid EOL translation when not linebuffered or using lf. [Bug 1845092]
Diffstat (limited to 'generic/tclIO.c')
-rw-r--r--generic/tclIO.c324
1 files changed, 283 insertions, 41 deletions
diff --git a/generic/tclIO.c b/generic/tclIO.c
index 5acef55..0e8346d 100644
--- a/generic/tclIO.c
+++ b/generic/tclIO.c
@@ -10,7 +10,7 @@
* See the file "license.terms" for information on usage and redistribution of
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclIO.c,v 1.132 2007/11/28 16:04:31 dgp Exp $
+ * RCS: @(#) $Id: tclIO.c,v 1.133 2007/12/05 21:47:26 hobbs Exp $
*/
#include "tclInt.h"
@@ -195,6 +195,32 @@ static void CutChannel(Tcl_Channel chan);
#define HaveOpt(minLength, nameString) \
((len > (minLength)) && (optionName[1] == (nameString)[1]) \
&& (strncmp(optionName, (nameString), len) == 0))
+
+/*
+ * The ChannelObjType type. We actually store the ChannelState structure
+ * as that lives longest and we want to return the bottomChanPtr when
+ * requested (consistent with Tcl_GetChannel). The setFromAny and
+ * updateString can be NULL as they should not be called.
+ */
+
+static void DupChannelIntRep(Tcl_Obj *objPtr, Tcl_Obj *copyPtr);
+static int SetChannelFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr);
+static void UpdateStringOfChannel(Tcl_Obj *objPtr);
+static void FreeChannelIntRep(Tcl_Obj *objPtr);
+
+Tcl_ObjType tclChannelType = {
+ "channel", /* name for this type */
+ FreeChannelIntRep, /* freeIntRepProc */
+ DupChannelIntRep, /* dupIntRepProc */
+ NULL, /* updateStringProc UpdateStringOfChannel */
+ NULL /* setFromAnyProc SetChannelFromAny */
+};
+
+#define GET_CHANNELSTATE(objPtr) \
+ ((ChannelState *) (objPtr)->internalRep.otherValuePtr)
+#define SET_CHANNELSTATE(objPtr, storePtr) \
+ ((objPtr)->internalRep.otherValuePtr = (void *) (storePtr))
+
/*
*---------------------------------------------------------------------------
@@ -673,6 +699,7 @@ DeleteChannelTable(
*/
Tcl_DeleteHashEntry(hPtr);
+ SetFlag(statePtr, CHANNEL_TAINTED);
statePtr->refCount--;
if (statePtr->refCount <= 0) {
if (!(statePtr->flags & BG_FLUSH_SCHEDULED)) {
@@ -1021,6 +1048,7 @@ DetachChannel(
return TCL_ERROR;
}
Tcl_DeleteHashEntry(hPtr);
+ SetFlag(statePtr, CHANNEL_TAINTED);
/*
* Remove channel handlers that refer to this interpreter, so that
@@ -1121,6 +1149,54 @@ Tcl_GetChannel(
}
/*
+ *---------------------------------------------------------------------------
+ *
+ * TclGetChannelFromObj --
+ *
+ * Finds an existing Tcl_Channel structure by name in a given
+ * interpreter. This function is public because it is used by
+ * channel-type-specific functions.
+ *
+ * Results:
+ * A Tcl_Channel or NULL on failure. If failed, interp's result object
+ * contains an error message. *modePtr is filled with the modes in which
+ * the channel was opened.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+int
+TclGetChannelFromObj(
+ Tcl_Interp *interp, /* Interpreter in which to find or create the
+ * channel. */
+ Tcl_Obj *objPtr,
+ Tcl_Channel *channelPtr,
+ int *modePtr, /* Where to store the mode in which the
+ * channel was opened? Will contain an ORed
+ * combination of TCL_READABLE and
+ * TCL_WRITABLE, if non-NULL. */
+ int flags)
+{
+ ChannelState *statePtr;
+
+ if (SetChannelFromAny(interp, objPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ statePtr = GET_CHANNELSTATE(objPtr);
+ *channelPtr = (Tcl_Channel) (statePtr->bottomChanPtr);
+
+ if (modePtr != NULL) {
+ *modePtr = (statePtr->flags & (TCL_READABLE|TCL_WRITABLE));
+ }
+
+ return TCL_OK;
+}
+
+/*
*----------------------------------------------------------------------
*
* Tcl_CreateChannel --
@@ -3337,11 +3413,13 @@ WriteBytes(
/* State info for channel */
ChannelBuffer *bufPtr;
char *dst;
- int dstMax, sawLF, savedLF, total, dstLen, toWrite;
+ int dstMax, sawLF, savedLF, total, dstLen, toWrite, translate;
total = 0;
sawLF = 0;
savedLF = 0;
+ translate = (statePtr->flags & CHANNEL_LINEBUFFERED)
+ || (statePtr->outputTranslation != TCL_TRANSLATE_LF);
/*
* Loop over all bytes in src, storing them in output buffer with proper
@@ -3363,27 +3441,32 @@ WriteBytes(
toWrite = srcLen;
}
- if (savedLF) {
- /*
- * A '\n' was left over from last call to TranslateOutputEOL() and
- * we need to store it in this buffer. If the channel is
- * line-based, we will need to flush it.
- */
+ if (translate) {
+ if (savedLF) {
+ /*
+ * A '\n' was left over from last call to TranslateOutputEOL()
+ * and we need to store it in this buffer. If the channel is
+ * line-based, we will need to flush it.
+ */
- *dst++ = '\n';
- dstLen--;
- sawLF++;
- }
- if (TranslateOutputEOL(statePtr, dst, src, &dstLen, &toWrite)) {
- sawLF++;
+ *dst++ = '\n';
+ dstLen--;
+ sawLF++;
+ }
+ if (TranslateOutputEOL(statePtr, dst, src, &dstLen, &toWrite)) {
+ sawLF++;
+ }
+ dstLen += savedLF;
+ savedLF = 0;
+ if (dstLen > dstMax) {
+ savedLF = 1;
+ dstLen = dstMax;
+ }
+ } else {
+ memcpy(dst, src, toWrite);
+ dstLen = toWrite;
}
- dstLen += savedLF;
- savedLF = 0;
- if (dstLen > dstMax) {
- savedLF = 1;
- dstLen = dstMax;
- }
bufPtr->nextAdded += dstLen;
if (CheckFlush(chanPtr, bufPtr, sawLF) != 0) {
return -1;
@@ -3429,7 +3512,7 @@ WriteChars(
char *dst, *stage;
int saved, savedLF, sawLF, total, dstLen, stageMax, dstWrote;
int stageLen, toWrite, stageRead, endEncoding, result;
- int consumedSomething;
+ int consumedSomething, translate;
Tcl_Encoding encoding;
char safe[BUFFER_PADDING];
@@ -3445,6 +3528,9 @@ WriteChars(
endEncoding = ((statePtr->outputEncodingFlags & TCL_ENCODING_END) != 0);
+ translate = (statePtr->flags & CHANNEL_LINEBUFFERED)
+ || (statePtr->outputTranslation != TCL_TRANSLATE_LF);
+
/*
* Loop over all UTF-8 characters in src, storing them in staging buffer
* with proper EOL translation.
@@ -3462,29 +3548,34 @@ WriteChars(
toWrite = srcLen;
}
- if (savedLF) {
- /*
- * A '\n' was left over from last call to TranslateOutputEOL() and
- * we need to store it in the staging buffer. If the channel is
- * line-based, we will need to flush the output buffer (after
- * translating the staging buffer).
- */
+ if (translate) {
+ if (savedLF) {
+ /*
+ * A '\n' was left over from last call to TranslateOutputEOL()
+ * and we need to store it in the staging buffer. If the channel
+ * is line-based, we will need to flush the output buffer (after
+ * translating the staging buffer).
+ */
- *stage++ = '\n';
- stageLen--;
- sawLF++;
- }
- if (TranslateOutputEOL(statePtr, stage, src, &stageLen, &toWrite)) {
- sawLF++;
- }
+ *stage++ = '\n';
+ stageLen--;
+ sawLF++;
+ }
+ if (TranslateOutputEOL(statePtr, stage, src, &stageLen, &toWrite)) {
+ sawLF++;
+ }
- stage -= savedLF;
- stageLen += savedLF;
- savedLF = 0;
+ stage -= savedLF;
+ stageLen += savedLF;
+ savedLF = 0;
- if (stageLen > stageMax) {
- savedLF = 1;
- stageLen = stageMax;
+ if (stageLen > stageMax) {
+ savedLF = 1;
+ stageLen = stageMax;
+ }
+ } else {
+ memcpy(stage, src, toWrite);
+ stageLen = toWrite;
}
src += toWrite;
srcLen -= toWrite;
@@ -10456,6 +10547,157 @@ Tcl_ChannelTruncateProc(
}
}
+/*
+ *----------------------------------------------------------------------
+ *
+ * DupChannelIntRep --
+ *
+ * Initialize the internal representation of a new Tcl_Obj to a copy of
+ * the internal representation of an existing string object.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * copyPtr's internal rep is set to a copy of srcPtr's internal
+ * representation.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DupChannelIntRep(
+ register Tcl_Obj *srcPtr, /* Object with internal rep to copy. Must have
+ * an internal rep of type "Channel". */
+ register Tcl_Obj *copyPtr) /* Object with internal rep to set. Must not
+ * currently have an internal rep.*/
+{
+ ChannelState *statePtr = GET_CHANNELSTATE(srcPtr);
+ SET_CHANNELSTATE(copyPtr, statePtr);
+ Tcl_Preserve((ClientData) statePtr);
+ copyPtr->typePtr = &tclChannelType;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SetChannelFromAny --
+ *
+ * Create an internal representation of type "Channel" for an object.
+ *
+ * Results:
+ * This operation always succeeds and returns TCL_OK.
+ *
+ * Side effects:
+ * Any old internal reputation for objPtr is freed and the internal
+ * representation is set to "Channel".
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+SetChannelFromAny(
+ Tcl_Interp *interp, /* Used for error reporting if not NULL. */
+ register Tcl_Obj *objPtr) /* The object to convert. */
+{
+ ChannelState *statePtr;
+
+ if (objPtr->typePtr == &tclChannelType) {
+ /*
+ * The channel is valid until any call to DetachChannel occurs.
+ * Ensure consistency checks are done.
+ */
+ statePtr = GET_CHANNELSTATE(objPtr);
+ if (statePtr->flags & (CHANNEL_TAINTED|CHANNEL_CLOSED)) {
+ ResetFlag(statePtr, CHANNEL_TAINTED);
+ Tcl_Release((ClientData) statePtr);
+ UpdateStringOfChannel(objPtr);
+ objPtr->typePtr = NULL;
+ }
+ }
+ if (objPtr->typePtr != &tclChannelType) {
+ Tcl_Channel chan;
+
+ if (objPtr->typePtr != NULL) {
+ if (objPtr->bytes == NULL) {
+ objPtr->typePtr->updateStringProc(objPtr);
+ }
+ TclFreeIntRep(objPtr);
+ }
+
+ chan = Tcl_GetChannel(interp, objPtr->bytes, NULL);
+ if (chan == NULL) {
+ return TCL_ERROR;
+ }
+
+ statePtr = ((Channel *)chan)->state;
+ Tcl_Preserve((ClientData) statePtr);
+ SET_CHANNELSTATE(objPtr, statePtr);
+ objPtr->typePtr = &tclChannelType;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * UpdateStringOfChannel --
+ *
+ * Update the string representation for an object whose internal
+ * representation is "Channel".
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The object's string may be set by converting its Unicode represention
+ * to UTF format.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+UpdateStringOfChannel(
+ Tcl_Obj *objPtr) /* Object with string rep to update. */
+{
+ if (objPtr->bytes == NULL) {
+ ChannelState *statePtr = GET_CHANNELSTATE(objPtr);
+ const char *name = statePtr->channelName;
+ if (name) {
+ size_t len = strlen(name);
+ objPtr->bytes = (char *) ckalloc(len + 1);
+ objPtr->length = len;
+ memcpy(objPtr->bytes, name, len);
+ } else {
+ objPtr->bytes = tclEmptyStringRep;
+ objPtr->length = 0;
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FreeChannelIntRep --
+ *
+ * Release statePtr storage.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * May cause state to be freed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+FreeChannelIntRep(
+ Tcl_Obj *objPtr) /* Object with internal rep to free. */
+{
+ Tcl_Release((ClientData) GET_CHANNELSTATE(objPtr));
+}
+
#if 0
/*
* For future debugging work, a simple function to print the flags of a