summaryrefslogtreecommitdiffstats
path: root/generic/tclIO.c
diff options
context:
space:
mode:
Diffstat (limited to 'generic/tclIO.c')
-rw-r--r--generic/tclIO.c447
1 files changed, 337 insertions, 110 deletions
diff --git a/generic/tclIO.c b/generic/tclIO.c
index f9e7db6..ab3dfaa 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.9 1999/06/30 17:47:28 welch Exp $
+ * RCS: @(#) $Id: tclIO.c,v 1.10 1999/07/02 19:51:29 welch Exp $
*/
#include "tclInt.h"
@@ -204,7 +204,7 @@ typedef struct Channel {
CopyState *csPtr; /* State of background copy, or NULL. */
struct Channel* supercedes; /* Refers to channel this one was stacked upon.
This reference is NULL for normal channels.
- See Tcl_ReplaceChannel. */
+ See Tcl_StackChannel. */
} Channel;
@@ -1352,7 +1352,7 @@ Tcl_CreateChannel(typePtr, chanName, instanceData, mask)
/*
*----------------------------------------------------------------------
*
- * Tcl_ReplaceChannel --
+ * Tcl_StackChannel --
*
* Replaces an entry in the hash table for a Tcl_Channel
* record. The replacement is a new channel with same name,
@@ -1365,16 +1365,21 @@ Tcl_CreateChannel(typePtr, chanName, instanceData, mask)
* "Trf-Patch for filtering channels"
*
* Results:
- * Returns the new Tcl_Channel.
+ * Returns the new Tcl_Channel, which actually contains the
+ * saved information about prevChan.
*
* Side effects:
- * See above.
+ * A new channel structure is allocated and linked below
+ * the existing channel. The channel operations and client
+ * data of the existing channel are copied down to the newly
+ * created channel, and the current channel has its operations
+ * replaced by the new typePtr.
*
*----------------------------------------------------------------------
*/
Tcl_Channel
-Tcl_ReplaceChannel(interp, typePtr, instanceData, mask, prevChan)
+Tcl_StackChannel(interp, typePtr, instanceData, mask, prevChan)
Tcl_Interp* interp; /* The interpreter we are working in */
Tcl_ChannelType *typePtr; /* The channel type record for the new
* channel. */
@@ -1385,27 +1390,43 @@ Tcl_ReplaceChannel(interp, typePtr, instanceData, mask, prevChan)
Tcl_Channel prevChan; /* The channel structure to replace */
{
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
- Channel *chanPtr, *pt, *prevPt;
+ Channel *chanPtr, *pt;
+
+ /*
+ * AK, 06/30/1999
+ *
+ * Tcl_StackChannel differs from Tcl_ReplaceChannel of the
+ * original "Trf" patch. Instead of seeing the
+ * newly created structure as the *new* channel to cover the specified
+ * one use it to *save* the current state of the specified channel and
+ * then reinitialize the current structure for the given transformation.
+ *
+ * Advantages:
+ * - No splicing into the (thread-)global list of channels (or the per-
+ * interp hash-tables).
+ * - Users of the C-API still have valid channel references even after
+ * the call to this procedure.
+ *
+ * Disadvantages:
+ * - Untested code.
+ */
/*
- * Find the given channel in the list of all channels, compute enough
- * information to allow easy removal after the conditions are met.
+ * Find the given channel in the list of all channels.
*/
- prevPt = (Channel*) NULL;
pt = (Channel*) tsdPtr->firstChanPtr;
while (pt != (Channel *) prevChan) {
- prevPt = pt;
- pt = pt->nextChanPtr;
+ pt = pt->nextChanPtr;
}
/*
- * 'pt == prevChan' now
+ * 'pt == prevChan' now (or NULL, if not found).
*/
if (!pt) {
- return (Tcl_Channel) NULL;
+ return (Tcl_Channel) NULL;
}
/*
@@ -1422,94 +1443,167 @@ Tcl_ReplaceChannel(interp, typePtr, instanceData, mask, prevChan)
*/
if ((mask & Tcl_GetChannelMode (prevChan)) == 0) {
- return (Tcl_Channel) NULL;
+ return (Tcl_Channel) NULL;
}
chanPtr = (Channel *) ckalloc((unsigned) sizeof(Channel));
- chanPtr->flags = mask;
/*
- * Set the channel up initially in no Input translation mode and
- * no Output translation mode.
+ * Save some of the current state into the new structure,
+ * reinitialize the parts which will stay with the transformation.
+ *
+ * Remarks:
+ * - We cannot discard the buffers, and they cannot be used from the
+ * transformation placed later into the 'pt' structure. Save them,
+ * and believe that Tcl_SetChannelOption (buffering, none) will do
+ * the right thing.
+ * - encoding and EOL-translation control information is initialized
+ * to values for 'binary'. This is later reinforced via
+ * Tcl_SetChanneloption to get the handling of flags and the event
+ * system right.
+ * - The 'interestMask' of the saved channel is cleared, but the
+ * transformations WatchProc is used to establish the connection
+ * between transformation and underlying channel. This should
+ * reestablish the correct mask.
+ * - TTO = Transform Takes Over. The hidden channel no longer
+ * needs to perform this function.
*/
- chanPtr->inputTranslation = TCL_TRANSLATE_LF;
- chanPtr->outputTranslation = TCL_TRANSLATE_LF;
- chanPtr->inEofChar = 0;
- chanPtr->outEofChar = 0;
+ chanPtr->channelName = (char *) ckalloc (strlen(pt->channelName)+1);
+ strcpy (chanPtr->channelName, pt->channelName);
- chanPtr->unreportedError = 0;
- chanPtr->instanceData = instanceData;
- chanPtr->typePtr = typePtr;
- chanPtr->refCount = 0;
- chanPtr->closeCbPtr = (CloseCallback *) NULL;
- chanPtr->curOutPtr = (ChannelBuffer *) NULL;
- chanPtr->outQueueHead = (ChannelBuffer *) NULL;
- chanPtr->outQueueTail = (ChannelBuffer *) NULL;
- chanPtr->saveInBufPtr = (ChannelBuffer *) NULL;
- chanPtr->inQueueHead = (ChannelBuffer *) NULL;
- chanPtr->inQueueTail = (ChannelBuffer *) NULL;
- chanPtr->chPtr = (ChannelHandler *) NULL;
- chanPtr->interestMask = 0;
- chanPtr->scriptRecordPtr = (EventScriptRecord *) NULL;
- chanPtr->bufSize = CHANNELBUFFER_DEFAULT_SIZE;
- chanPtr->timer = NULL;
- chanPtr->csPtr = NULL;
+ chanPtr->flags = pt->flags; /* Save */
- /* 06/12/1998: New for Tcl 8.1
- *
- * Take over the encoding from the superceded channel, so that it will be
- * executed in the future despite the replacement, and at the proper time
- * (*after* / *before* our transformation, depending on the direction of
- * the dataflow).
- *
- * *Important*
- * The I/O functionality of the filtering channel has to use 'Tcl_Read' to
- * get at the underlying information. This will circumvent the de/encoder
- * stage [*] in the superceded channel and removes the need to trouble
- * ourselves with 'ByteArray's too.
+ chanPtr->encoding = (Tcl_Encoding) NULL; /* == 'binary' */
+ chanPtr->inputEncodingState = (Tcl_EncodingState) NULL;
+ chanPtr->inputEncodingFlags = TCL_ENCODING_START;
+ chanPtr->outputEncodingState = (Tcl_EncodingState) NULL;
+ chanPtr->outputEncodingFlags = TCL_ENCODING_START;
+
+ chanPtr->inputTranslation = TCL_TRANSLATE_LF; /* == 'binary' */
+ chanPtr->outputTranslation = TCL_TRANSLATE_LF; /* == 'binary' */
+ chanPtr->inEofChar = pt->inEofChar; /* Save */
+ chanPtr->outEofChar = pt->outEofChar; /* Save */
+
+ chanPtr->unreportedError = pt->unreportedError; /* Save */
+ chanPtr->instanceData = pt->instanceData; /* Save */
+ chanPtr->typePtr = pt->typePtr; /* Save */
+ chanPtr->refCount = 0; /* None, as the structure is covered */
+ chanPtr->closeCbPtr = (CloseCallback*) NULL; /* TTO */
+
+ chanPtr->outputStage = (char*) NULL;
+ chanPtr->curOutPtr = pt->curOutPtr; /* Save */
+ chanPtr->outQueueHead = pt->outQueueHead; /* Save */
+ chanPtr->outQueueTail = pt->outQueueTail; /* Save */
+ chanPtr->saveInBufPtr = pt->saveInBufPtr; /* Save */
+ chanPtr->inQueueHead = pt->outQueueHead; /* Save */
+ chanPtr->inQueueTail = pt->inQueueTail; /* Save */
+
+ chanPtr->chPtr = (ChannelHandler *) NULL; /* TTO */
+ chanPtr->interestMask = 0;
+ chanPtr->nextChanPtr = (Channel*) NULL; /* Is not in list! */
+ chanPtr->scriptRecordPtr = (EventScriptRecord *) NULL; /* TTO */
+ chanPtr->bufSize = CHANNELBUFFER_DEFAULT_SIZE;
+ chanPtr->timer = (Tcl_TimerToken) NULL; /* TTO */
+ chanPtr->csPtr = (CopyState*) NULL; /* TTO */
+
+ /*
+ * Place new block at the head of a possibly existing list of previously
+ * stacked channels, then do the missing initializations of translation
+ * and buffer system.
+ */
+
+ chanPtr->supercedes = pt->supercedes;
+
+ Tcl_SetChannelOption (interp, (Tcl_Channel) chanPtr,
+ "-translation", "binary");
+ Tcl_SetChannelOption (interp, (Tcl_Channel) chanPtr,
+ "-buffering", "none");
+
+ /*
+ * Save accomplished, now reinitialize the (old) structure for the
+ * transformation.
*
- * [*] I'm talking about the conversion between UNICODE and other
- * representations, like ASCII.
+ * - The information about encoding and eol-translation is taken
+ * without change. There is no need to fiddle with
+ * refCount et. al.
*/
- chanPtr->encoding=Tcl_GetEncoding(interp,Tcl_GetEncodingName(pt->encoding));
- chanPtr->inputEncodingState = pt->inputEncodingState;
- chanPtr->inputEncodingFlags = pt->inputEncodingFlags;
- chanPtr->outputEncodingState = pt->outputEncodingState;
- chanPtr->outputEncodingFlags = pt->outputEncodingFlags;
+ pt->flags = mask;
- chanPtr->outputStage = NULL;
+ /*
+ * EDITORS NOTE: all the lines with "take it as is" should get
+ * deleted once this code has been debugged.
+ */
- if ((chanPtr->encoding != NULL) && (chanPtr->flags & TCL_WRITABLE)) {
- chanPtr->outputStage = (char *)
- ckalloc((unsigned) (chanPtr->bufSize + 2));
- }
+ /* pt->encoding, take it as is */
+ /* pt->inputEncodingState, take it as is */
+ /* pt->inputEncodingFlags, take it as is */
+ /* pt->outputEncodingState, take it as is */
+ /* pt->outputEncodingFlags, take it as is */
- chanPtr->supercedes = (Channel*) prevChan;
+ /* pt->inputTranslation, take it as is */
+ /* pt->outputTranslation, take it as is */
- chanPtr->channelName = (char *) ckalloc (strlen(pt->channelName)+1);
- strcpy (chanPtr->channelName, pt->channelName);
+ /*
+ * No special EOF character, that condition is determined by the
+ * old channel
+ */
+
+ pt->inEofChar = 0;
+ pt->outEofChar = 0;
+
+ pt->unreportedError = 0; /* No errors yet */
+ pt->instanceData = instanceData; /* Transformation state */
+ pt->typePtr = typePtr; /* Transformation type */
+ /* pt->refCount, take it as it is */
+ /* pt->closeCbPtr, take it as it is */
+
+ /* pt->outputStage, take it as it is */
+ pt->curOutPtr = (ChannelBuffer *) NULL;
+ pt->outQueueHead = (ChannelBuffer *) NULL;
+ pt->outQueueTail = (ChannelBuffer *) NULL;
+ pt->saveInBufPtr = (ChannelBuffer *) NULL;
+ pt->inQueueHead = (ChannelBuffer *) NULL;
+ pt->inQueueTail = (ChannelBuffer *) NULL;
+
+ /* pt->chPtr, take it as it is */
+ /* pt->interestMask, take it as it is */
+ /* pt->nextChanPtr, take it as it is */
+ /* pt->scriptRecordPtr, take it as it is */
+ pt->bufSize = CHANNELBUFFER_DEFAULT_SIZE;
+ /* pt->timer, take it as it is */
+ /* pt->csPtr, take it as it is */
/*
- * Link the new channel into the same spot in the per-interp
- * channel list as the old channel was.
+ * Have the transformation reference the new structure containing
+ * the saved channel.
*/
- if (prevPt) {
- prevPt->nextChanPtr = chanPtr;
- } else {
- tsdPtr->firstChanPtr = chanPtr;
- }
+ pt->supercedes = chanPtr;
+
+ /*
+ * Don't forget to reinitialize the output buffer used for encodings.
+ */
- chanPtr->nextChanPtr = pt->nextChanPtr;
+ if ((chanPtr->encoding != NULL) && (chanPtr->flags & TCL_WRITABLE)) {
+ chanPtr->outputStage = (char *)
+ ckalloc((unsigned) (chanPtr->bufSize + 2));
+ }
/*
- * The following call replaces the hash table mapping from name
- * to channel with a pointer to the new channel.
+ * Event handling: If the information in the old channel shows
+ * that there was interest in some events call the 'WatchProc'
+ * of the transformation to establish the proper connection
+ * between them.
*/
- Tcl_RegisterChannel (interp, (Tcl_Channel) chanPtr);
+ if (pt->interestMask) {
+ int interest = pt->interestMask;
+
+ pt->interestMask = 0;
+ (pt->typePtr->watchProc) (pt->instanceData, interest);
+ }
/*
* The superceded channel is effectively unregistered
@@ -1518,7 +1612,6 @@ Tcl_ReplaceChannel(interp, typePtr, instanceData, mask, prevChan)
* Don't add the following code:
*
* chanPtr->supercedes->refCount --;
- *
*/
return (Tcl_Channel) chanPtr;
@@ -1527,12 +1620,12 @@ Tcl_ReplaceChannel(interp, typePtr, instanceData, mask, prevChan)
/*
*----------------------------------------------------------------------
*
- * Tcl_UndoReplaceChannel --
+ * Tcl_UnstackChannel --
*
* Unstacks an entry in the hash table for a Tcl_Channel
- * record. This is the reverse to 'Tcl_ReplaceChannel'.
+ * record. This is the reverse to 'Tcl_StackChannel'.
* The old, superceded channel is uncovered and re-registered
- * in the appropriate datastructures.
+ * in the appropriate data structures.
*
* Results:
* Returns the old Tcl_Channel, i.e. the one which was stacked over.
@@ -1544,60 +1637,193 @@ Tcl_ReplaceChannel(interp, typePtr, instanceData, mask, prevChan)
*/
void
-Tcl_UndoReplaceChannel (interp, chan)
-Tcl_Interp* interp; /* The interpreter we are working in */
-Tcl_Channel chan; /* The channel to unstack */
+Tcl_UnstackChannel (interp, chan)
+ Tcl_Interp* interp; /* The interpreter we are working in */
+ Tcl_Channel chan; /* The channel to unstack */
{
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
Channel* chanPtr = (Channel*) chan;
if (chanPtr->supercedes != (Channel*) NULL) {
- Tcl_HashTable *hTblPtr; /* Hash table of channels. */
- Tcl_HashEntry *hPtr; /* Search variable. */
- int new; /* Is the hash entry new or does it exist? */
+ /*
+ * Instead of manipulating the per-thread / per-interp list/hashtable
+ * of registered channels we wind down the state of the transformation,
+ * and then restore the state of underlying channel into the old
+ * structure.
+ */
+
+ Tcl_DString ds; /* storage to copy option information */
+ Channel top; /* Save area for current transformation */
+ Channel* chanDownPtr = chanPtr->supercedes;
+ int interest; /* interest mask of transformation before destruct. */
/*
- * Insert the channel we were stacked upon back into
- * the list of open channels. Place it back into the hashtable too.
- * Correct 'refCount', as this actually unregisters 'chan'.
+ * Event handling: Disallow the delivery of events from the
+ * old, now uncovered channel to the transformation.
+ *
+ * This is done before everything else to avoid problems
+ * after our heavy-duty shuffling of pointers around.
*/
- chanPtr->supercedes->nextChanPtr = tsdPtr->firstChanPtr;
- tsdPtr->firstChanPtr = chanPtr->supercedes;
+ interest = chanPtr->interestMask;
+ (chanPtr->typePtr->watchProc) (chanPtr->instanceData, 0);
+
+ /*
+ * Save the transformation, then restore the old channel from the
+ * first structure downstream. The overall effect is that
+ * transformation and underlying channel swapped places, and the
+ * transformation is cut loose from the stack. Without the latter
+ * a Tcl_Close on the transformation would be impossible, as that
+ * procedure will free the structure, making 'top' unusable.
+ *
+ * Beware, same information must not take part in the swap,
+ * use correction code to ensure this.
+ */
- hTblPtr = GetChannelTable (interp);
- hPtr = Tcl_CreateHashEntry (hTblPtr, chanPtr->channelName, &new);
+ memcpy ((void*) &top, (void*) chanPtr, sizeof (Channel));
+ memcpy ((void*) &chanPtr, (void*) chanDownPtr, sizeof (Channel));
+ top.supercedes = (Channel*) NULL;
+ memcpy ((void*) &chanDownPtr, (void*) &top, sizeof (Channel));
+
+ chanPtr->refCount = chanDownPtr->refCount;
+ chanPtr->closeCbPtr = chanDownPtr->closeCbPtr;
+ chanPtr->chPtr = chanDownPtr->chPtr;
+ chanPtr->nextChanPtr = chanDownPtr->nextChanPtr;
+ chanPtr->scriptRecordPtr = chanDownPtr->scriptRecordPtr;
+ chanPtr->timer = chanDownPtr->timer;
+ chanPtr->csPtr = chanDownPtr->csPtr;
+
+ chanDownPtr->refCount = 0;
+ chanDownPtr->closeCbPtr = (CloseCallback*) NULL;
+ chanDownPtr->chPtr = (ChannelHandler*) NULL;
+ chanDownPtr->nextChanPtr = (Channel*) NULL;
+ chanDownPtr->scriptRecordPtr = (EventScriptRecord*) NULL;
+ chanDownPtr->timer = (Tcl_TimerToken) NULL;
+ chanDownPtr->csPtr = (CopyState*) NULL;
- Tcl_SetHashValue(hPtr, (ClientData) chanPtr->supercedes);
- chanPtr->refCount --;
+ /*
+ * Now it is possible to wind down the transformation (in 'top'),
+ * especially to copy the current encoding and translation control
+ * information down.
+ */
+
+ /*
+ * Move the currently active encoding from the transformation
+ * to the now uncovered channel. We assume here that this
+ * channel uses 'encoding binary' (==> encoding == NULL, etc.
+ * This allows us to simply copy the pointers without having to
+ * think about refcounts and deallocation of the old encoding.
+ */
+
+ chanPtr->encoding = chanDownPtr->encoding;
+ chanPtr->inputEncodingState = chanDownPtr->inputEncodingState;
+ chanPtr->inputEncodingFlags = chanDownPtr->inputEncodingFlags;
+ chanPtr->outputEncodingState = chanDownPtr->outputEncodingState;
+ chanPtr->outputEncodingFlags = chanDownPtr->outputEncodingFlags;
+
+ /*
+ * Prevent the accidential removal of the encoding during
+ * the destruction of the transformation channel.
+ */
+
+ chanDownPtr->encoding = (Tcl_Encoding) NULL;
+ chanDownPtr->inputEncodingState = (Tcl_EncodingState) NULL;
+ chanDownPtr->inputEncodingFlags = TCL_ENCODING_START;
+ chanDownPtr->outputEncodingState = (Tcl_EncodingState) NULL;
+ chanDownPtr->outputEncodingFlags = TCL_ENCODING_START;
/*
- * The superceded channel is effectively registered again
- * Don't add the following code because we didn't
- * decremented the reference count at replace time.
+ * And don't forget to reenable the EOL-translation used by the
+ * transformation. Using a DString to do this *is* a bit awkward,
+ * but still the best way to handle the complexities here, like
+ * flag manipulation and event system.
+ */
+
+ Tcl_DStringInit (&ds);
+ Tcl_GetChannelOption (interp, (Tcl_Channel) chanDownPtr,
+ "-translation", &ds);
+ Tcl_SetChannelOption (interp, (Tcl_Channel) chanPtr,
+ "-translation", ds.string);
+
+ Tcl_DStringSetLength (&ds, 0);
+
+ Tcl_GetChannelOption (interp, (Tcl_Channel) chanDownPtr,
+ "-buffering", &ds);
+ Tcl_SetChannelOption (interp, (Tcl_Channel) chanPtr,
+ "-buffering", ds.string);
+
+ Tcl_DStringFree (&ds);
+
+ /*
+ * Now a little trick: Add the transformation structure to the
+ * per-thread list of existing channels (which it never were
+ * part of so far), or Tcl_Close/FlushChannel will panic
+ * ("damaged channel list").
*
- * chanPtr->supercedes->refCount ++;
+ * Afterward do a regular close upon the transformation.
+ * This may cause flushing of data into the old channel (if the
+ * transformation remembered its own channel in itself).
*
+ * We know that its refCount dropped to 0.
*/
- }
- /*
- * Disconnect the channels, then do a regular close upon the
- * stacked one, the filtering channel. This may cause flushing
- * of data into the superceded channel (if the filtering channel
- * ('chan') remembered its parent in itself).
- */
+ chanDownPtr->nextChanPtr = tsdPtr->firstChanPtr;
+ tsdPtr->firstChanPtr = chanDownPtr;
+
+ Tcl_Close (interp, (Tcl_Channel)chanDownPtr);
+
+ /*
+ * Event handling: If the information from the now destroyed
+ * transformation shows that there was interest in some events
+ * call the 'WatchProc' of the now uncovered channel to renew
+ * that interest with underlying channels or the driver.
+ */
- chanPtr->supercedes = NULL;
+ if (interest) {
+ chanPtr->interestMask = 0;
+ (chanPtr->typePtr->watchProc) (chanPtr->instanceData,
+ interest);
+ }
+
+ } else {
+ /* This channel does not cover another one.
+ * Simply do a close, if necessary.
+ */
- if (chanPtr->refCount == 0) {
- Tcl_Close (interp, chan);
+ if (chanPtr->refCount == 0) {
+ Tcl_Close (interp, chan);
+ }
}
}
/*
*----------------------------------------------------------------------
*
+ * Tcl_GetStackedChannel --
+ *
+ * Determines wether the specified channel is stacked upon another.
+ *
+ * Results:
+ * NULL if the channel is not stacked upon another one, or a reference
+ * to the channel it is stacked upon. This reference can be used in
+ * queries, but modification is not allowed.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Tcl_Channel
+Tcl_GetStackedChannel(Tcl_Channel chan)
+{
+ Channel* chanPtr = (Channel*) chan;
+ return (Tcl_Channel) chanPtr->supercedes;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* Tcl_GetChannelMode --
*
* Computes a mask indicating whether the channel is open for
@@ -7902,3 +8128,4 @@ SetBlockMode(interp, chanPtr, mode)
}
return TCL_OK;
}
+