[Request Tcl 8.1b3]

1 view
Skip to first unread message

a.ku...@westend.com

unread,
Apr 22, 1999, 3:00:00 AM4/22/99
to

Tcl 8.1b3 Request: Generated by Scriptics' bug entry form at
http://www.scriptics.com/support/bugForm.html
Responses to this post are encouraged.
------

Submitted by: Andreas Kupries

DesiredBehavior:
With minimal changes (*) to the IO subsystem of the tcl kernel it is
possible to introduce the capability for arbitrary filtering and/or
manipulation of information flowing through channels. Possible
applications of this functionality are:

* En/Decryption,
* Error-Correction,
* Conversion (base64, uuencode, ...)
* Compression,

and many more.

Patch:
*** ./tcl.decls.orig Sat Apr 10 17:50:50 1999
--- ./tcl.decls Mon Apr 19 19:58:16 1999
***************
*** 967,977 ****
void Tcl_InitMemory(Tcl_Interp *interp)
}

# Reserved for future use (8.0.x vs. 8.1)
- # declare 281 generic {
- # }
- # declare 282 generic {
- # }
# declare 283 generic {
# }
# declare 284 generic {
--- 967,996 ----
void Tcl_InitMemory(Tcl_Interp *interp)
}

+ # Andreas Kupries <a.ku...@westend.com>, 03/21/1999
+ # "Trf-Patch for filtering channels"
+ #
+ # C-Level API for (un)stacking of channels. This allows the introduction
+ # of filtering channels with relatively little changes to the core.
+ # This patch was created in cooperation with Jan Nijtmans <nijt...@wxs.nl>
+ # and is therefore part of his plus-patches too.
+ #
+ # It would have been possible to place the following definitions according
+ # to the alphabetical order used elsewhere in this file, but I decided
+ # against that to ease the maintenance of the patch across new tcl versions
+ # (patch usually has no problems to integrate the patch file for the last
+ # version into the new one).
+
+ declare 281 generic {
+ Tcl_Channel Tcl_ReplaceChannel(Tcl_Interp *interp, \
+ Tcl_ChannelType *typePtr, ClientData instanceData, \
+ int mask, Tcl_Channel prevChan)
+ }
+ declare 282 generic {
+ void Tcl_UndoReplaceChannel(Tcl_Interp *interp, Tcl_Channel chan)
+ }
+
# Reserved for future use (8.0.x vs. 8.1)
# declare 283 generic {
# }
# declare 284 generic {
*** ./tclDecls.h.orig Sat Apr 10 17:50:50 1999
--- ./tclDecls.h Mon Apr 19 19:58:16 1999
***************
*** 875,882 ****
int * patchLevel, int * type));
/* 280 */
EXTERN void Tcl_InitMemory _ANSI_ARGS_((Tcl_Interp * interp));
! /* Slot 281 is reserved */
! /* Slot 282 is reserved */
/* Slot 283 is reserved */
/* Slot 284 is reserved */
/* Slot 285 is reserved */
--- 875,888 ----
int * patchLevel, int * type));
/* 280 */
EXTERN void Tcl_InitMemory _ANSI_ARGS_((Tcl_Interp * interp));
! /* 281 */
! EXTERN Tcl_Channel Tcl_ReplaceChannel _ANSI_ARGS_((Tcl_Interp * interp,
! Tcl_ChannelType * typePtr,
! ClientData instanceData, int mask,
! Tcl_Channel prevChan));
! /* 282 */
! EXTERN void Tcl_UndoReplaceChannel _ANSI_ARGS_((
! Tcl_Interp * interp, Tcl_Channel chan));
/* Slot 283 is reserved */
/* Slot 284 is reserved */
/* Slot 285 is reserved */
***************
*** 1434,1441 ****
void (*tcl_PanicVA) _ANSI_ARGS_((char * format, va_list argList)); /* 278 */
void (*tcl_GetVersion) _ANSI_ARGS_((int * major, int * minor, int * patchLevel, int * type)); /* 279 */
void (*tcl_InitMemory) _ANSI_ARGS_((Tcl_Interp * interp)); /* 280 */
! void *reserved281;
! void *reserved282;
void *reserved283;
void *reserved284;
void *reserved285;
--- 1440,1447 ----
void (*tcl_PanicVA) _ANSI_ARGS_((char * format, va_list argList)); /* 278 */
void (*tcl_GetVersion) _ANSI_ARGS_((int * major, int * minor, int * patchLevel, int * type)); /* 279 */
void (*tcl_InitMemory) _ANSI_ARGS_((Tcl_Interp * interp)); /* 280 */
! Tcl_Channel (*tcl_ReplaceChannel) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_ChannelType * typePtr, ClientData instanceData, int mask, Tcl_Channel prevChan)); /* 281 */
! void (*tcl_UndoReplaceChannel) _ANSI_ARGS_((Tcl_Interp * interp, Tcl_Channel chan)); /* 282 */
void *reserved283;
void *reserved284;
void *reserved285;
***************
*** 2657,2664 ****
#define Tcl_InitMemory \
(tclStubsPtr->tcl_InitMemory) /* 280 */
#endif
! /* Slot 281 is reserved */
! /* Slot 282 is reserved */
/* Slot 283 is reserved */
/* Slot 284 is reserved */
/* Slot 285 is reserved */
--- 2663,2676 ----
#define Tcl_InitMemory \
(tclStubsPtr->tcl_InitMemory) /* 280 */
#endif
! #ifndef Tcl_ReplaceChannel
! #define Tcl_ReplaceChannel \
! (tclStubsPtr->tcl_ReplaceChannel) /* 281 */
! #endif
! #ifndef Tcl_UndoReplaceChannel
! #define Tcl_UndoReplaceChannel \
! (tclStubsPtr->tcl_UndoReplaceChannel) /* 282 */
! #endif
/* Slot 283 is reserved */
/* Slot 284 is reserved */
/* Slot 285 is reserved */
*** ./tclIO.c.orig Sat Apr 10 17:50:52 1999
--- ./tclIO.c Mon Apr 19 19:58:16 1999
***************
*** 202,207 ****
--- 202,229 ----
int bufSize; /* What size buffers to allocate? */
Tcl_TimerToken timer; /* Handle to wakeup timer for this channel. */
CopyState *csPtr; /* State of background copy, or NULL. */
+
+ /* Andreas Kupries <a.ku...@westend.com>, 12/13/1998
+ * "Trf-Patch for filtering channels"
+ *
+ * The single change to the internal datastructures of the core. Every
+ * channel now maintains a reference to the channel he is stacked upon.
+ * This reference is NULL for normal channels. Only the two exported
+ * procedures (Tcl_ReplaceChannel and Tcl_UndoReplaceChannel, see at the
+ * end of 'tcl.h') use this field in a non-trivial way.
+ *
+ * Of the existing procedures the only following are affected by this
+ * change:
+ *
+ * - Tcl_RegisterChannel
+ * - Tcl_CreateChannel
+ * - CloseChannel
+ *
+ * The why is explained at the changed locations.
+ */
+
+ struct Channel* supercedes; /* Refers to channel this one was stacked upon */
+
} Channel;

/*
***************
*** 1038,1044 ****
if (chan == (Tcl_Channel) Tcl_GetHashValue(hPtr)) {
return;
}
! panic("Tcl_RegisterChannel: duplicate channel names");
}
Tcl_SetHashValue(hPtr, (ClientData) chanPtr);
}
--- 1060,1080 ----
if (chan == (Tcl_Channel) Tcl_GetHashValue(hPtr)) {
return;
}
!
! /* Andreas Kupries <a.ku...@westend.com>, 12/13/1998
! * "Trf-Patch for filtering channels"
! *
! * This is the change to 'Tcl_RegisterChannel'.
! *
! * Explanation:
! * The moment a channel is stacked upon another he
! * takes the identity of the channel he supercedes,
! * i.e. he gets the *same* name. Because of this we
! * cannot check for duplicate names anymore, they
! * have to be allowed now.
! */
!
! /* panic("Tcl_RegisterChannel: duplicate channel names"); */
}
Tcl_SetHashValue(hPtr, (ClientData) chanPtr);
}
***************
*** 1297,1302 ****
--- 1333,1352 ----
chanPtr->timer = NULL;
chanPtr->csPtr = NULL;

+ /* Andreas Kupries <a.ku...@westend.com>, 12/13/1998
+ * "Trf-Patch for filtering channels"
+ *
+ * This is the change to 'Tcl_CreateChannel'.
+ *
+ * Explanation:
+ * It is of course necessary to initialize the new field
+ * in the Channel structure. The chosen value indicates
+ * that the created channel is a normal one, and not
+ * stacked upon another.
+ */
+
+ chanPtr->supercedes = (Channel*) NULL;
+
chanPtr->outputStage = NULL;
if ((chanPtr->encoding != NULL) && (chanPtr->flags & TCL_WRITABLE)) {
chanPtr->outputStage = (char *)
***************
*** 1330,1335 ****
--- 1380,1622 ----
return (Tcl_Channel) chanPtr;
}

+ /* Andreas Kupries <a.ku...@westend.com>, 12/13/1998
+ * "Trf-Patch for filtering channels"
+ *
+ * The following two procedures are the new, exported ones. They
+ * - create a channel stacked upon an existing one and
+ * - pop a stacked channel off, thus revealing the superceded one.
+ *
+ * Please read the following completely.
+ */
+
+ /*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_ReplaceChannel --
+ *
+ * Replaces an entry in the hash table for a Tcl_Channel
+ * record. The replacement is a new channel with same name,
+ * it supercedes the replaced channel. Input and output of
+ * the superceded channel is now going through the newly
+ * created channel and allows the arbitrary filtering/manipulation
+ * of the dataflow.
+ *
+ * Results:
+ * Returns the new Tcl_Channel.
+ *
+ * Side effects:
+ * See above.
+ *
+ *----------------------------------------------------------------------
+ */
+
+ Tcl_Channel
+ Tcl_ReplaceChannel(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. */
+ ClientData instanceData; /* Instance specific data for the new
+ * channel. */
+ int mask; /* TCL_READABLE & TCL_WRITABLE to indicate
+ * if the channel is readable, writable. */
+ Tcl_Channel prevChan; /* The channel structure to replace */
+ {
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+ Channel *chanPtr, *pt, *prevPt;
+
+ /*
+ * Find the given channel in the list of all channels, compute enough
+ * information to allow easy removal after the conditions are met.
+ */
+
+ prevPt = (Channel*) NULL;
+ pt = (Channel*) tsdPtr->firstChanPtr;
+
+ while (pt != (Channel *) prevChan) {
+ prevPt = pt;
+ pt = pt->nextChanPtr;
+ }
+
+ /*
+ * 'pt == prevChan' now
+ */
+
+ if (!pt) {
+ return (Tcl_Channel) NULL;
+ }
+
+ /*
+ * Here we check if the given "mask" matches the "flags"
+ * of the already existing channel.
+ *
+ * | - | R | W | RW |
+ * --+---+---+---+----+ <=> 0 != (chan->mask & prevChan->mask)
+ * - | | | | |
+ * R | | + | | + | The superceding channel is allowed to
+ * W | | | + | + | restrict the capabilities of the
+ * RW| | + | + | + | superceded one !
+ * --+---+---+---+----+
+ */
+
+ if ((mask & Tcl_GetChannelMode (prevChan)) == 0) {
+ 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.
+ */
+
+ chanPtr->inputTranslation = TCL_TRANSLATE_LF;
+ chanPtr->outputTranslation = TCL_TRANSLATE_LF;
+ chanPtr->inEofChar = 0;
+ chanPtr->outEofChar = 0;
+
+ 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;
+
+ /* 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.
+ *
+ * [*] I'm talking about the conversion between UNICODE and other
+ * representations, like ASCII.
+ */
+
+ chanPtr->encoding = pt->encoding;
+ chanPtr->inputEncodingState = pt->inputEncodingState;
+ chanPtr->inputEncodingFlags = pt->inputEncodingFlags;
+ chanPtr->outputEncodingState = pt->outputEncodingState;
+ chanPtr->outputEncodingFlags = pt->outputEncodingFlags;
+
+ chanPtr->outputStage = NULL;
+
+ if ((chanPtr->encoding != NULL) && (chanPtr->flags & TCL_WRITABLE)) {
+ chanPtr->outputStage = (char *)
+ ckalloc((unsigned) (chanPtr->bufSize + 2));
+ }
+
+ chanPtr->supercedes = (Channel*) prevChan;
+
+ chanPtr->channelName = (char *) ckalloc (strlen(pt->channelName)+1);
+ strcpy (chanPtr->channelName, pt->channelName);
+
+ if (prevPt) {
+ prevPt->nextChanPtr = chanPtr;
+ } else {
+ tsdPtr->firstChanPtr = chanPtr;
+ }
+
+ chanPtr->nextChanPtr = pt->nextChanPtr;
+
+ Tcl_RegisterChannel (interp, (Tcl_Channel) chanPtr);
+
+ /*
+ * The superceded channel is effectively unregistered
+ */
+
+ /*chanPtr->supercedes->refCount --;*/
+
+ return (Tcl_Channel) chanPtr;
+ }
+
+ /*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_UndoReplaceChannel --
+ *
+ * Unstacks an entry in the hash table for a Tcl_Channel
+ * record. This is the reverse to 'Tcl_ReplaceChannel'.
+ * The old, superceded channel is uncovered and re-registered
+ * in the appropriate datastructures.
+ *
+ * Results:
+ * Returns the old Tcl_Channel, i.e. the one which was stacked over.
+ *
+ * Side effects:
+ * See above.
+ *
+ *----------------------------------------------------------------------
+ */
+
+ void
+ Tcl_UndoReplaceChannel (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? */
+
+ /*
+ * 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'.
+ */
+
+ chanPtr->supercedes->nextChanPtr = tsdPtr->firstChanPtr;
+ tsdPtr->firstChanPtr = chanPtr->supercedes;
+
+ hTblPtr = GetChannelTable (interp);
+ hPtr = Tcl_CreateHashEntry (hTblPtr, chanPtr->channelName, &new);
+
+ Tcl_SetHashValue(hPtr, (ClientData) chanPtr->supercedes);
+ chanPtr->refCount --;
+
+ /*
+ * The superceded channel is effectively registered again
+ */
+
+ /*chanPtr->supercedes->refCount ++;*/
+ }
+
+ /*
+ * 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).
+ */
+
+ chanPtr->supercedes = NULL;
+
+ if (chanPtr->refCount == 0) {
+ Tcl_Close (interp, chan);
+ }
+ }
+
/*
*----------------------------------------------------------------------
*
***************
*** 2003,2008 ****
--- 2290,2330 ----
if (errorCode != 0) {
Tcl_SetErrno(errorCode);
}
+ }
+
+ /* Andreas Kupries <a.ku...@westend.com>, 12/13/1998
+ * "Trf-Patch for filtering channels"
+ *
+ * This is the change to 'CloseChannel'.
+ *
+ * Explanation
+ * Closing a filtering channel closes the one it
+ * superceded too. This basically ripples through
+ * the whole chain of filters until it reaches
+ * the underlying normal channel.
+ *
+ * This is done by reintegrating the superceded
+ * channel into the (thread) global list of open
+ * channels and then invoking a regular close.
+ * There is no need to handle the complexities of
+ * this process by ourselves.
+ *
+ * *Note*
+ * This has to be done after the call to the
+ * 'closeProc' of the filtering channel to allow
+ * that one the flushing of internal buffers into
+ * the underlying channel.
+ */
+
+ if (chanPtr->supercedes != (Channel*) NULL) {
+ /* Insert the channel we were stacked upon back into
+ * the list of open channels, then do a regular close.
+ */
+
+ chanPtr->supercedes->nextChanPtr = tsdPtr->firstChanPtr;
+ tsdPtr->firstChanPtr = chanPtr->supercedes;
+ chanPtr->supercedes->refCount --; /* is deregistered */
+ Tcl_Close (interp, (Tcl_Channel) chanPtr->supercedes);
}

/*
*** ./tclStubInit.c.orig Sat Apr 10 17:50:52 1999
--- ./tclStubInit.c Mon Apr 19 19:58:16 1999
***************
*** 350,357 ****
Tcl_PanicVA, /* 278 */
Tcl_GetVersion, /* 279 */
Tcl_InitMemory, /* 280 */
! NULL, /* 281 */
! NULL, /* 282 */
NULL, /* 283 */
NULL, /* 284 */
NULL, /* 285 */
--- 350,357 ----
Tcl_PanicVA, /* 278 */
Tcl_GetVersion, /* 279 */
Tcl_InitMemory, /* 280 */
! Tcl_ReplaceChannel, /* 281 */
! Tcl_UndoReplaceChannel, /* 282 */
NULL, /* 283 */
NULL, /* 284 */
NULL, /* 285 */
*** ./tclStubs.c.orig Sat Apr 10 17:50:52 1999
--- ./tclStubs.c Mon Apr 19 19:58:16 1999
***************
*** 3263,3267 ****
--- 3263,3288 ----
(tclStubsPtr->tcl_ServiceModeHook)(mode);
}

+ /* Slot 345 */
+ Tcl_Channel
+ Tcl_ReplaceChannel(interp, typePtr, instanceData, mask, prevChan)
+ Tcl_Interp * interp;
+ Tcl_ChannelType * typePtr;
+ ClientData instanceData;
+ int mask;
+ Tcl_Channel prevChan;
+ {
+ return (tclStubsPtr->tcl_ReplaceChannel)(interp, typePtr, instanceData, mask, prevChan);
+ }
+
+ /* Slot 346 */
+ void
+ Tcl_UndoReplaceChannel(interp, chan)
+ Tcl_Interp * interp;
+ Tcl_Channel chan;
+ {
+ (tclStubsPtr->tcl_UndoReplaceChannel)(interp, chan);
+ }
+

/* !END!: Do not edit above this line. */


PatchFiles:
tcl.decls tclIO.c
(Derived files: tclDecls.h tclStubInit.c tclStubLib.c tclStubs.c)

Comments:
Comments:
* See Id 1576 in the bug/rfe database too.
* This RFE supercedes 1576 as the patch submitted here fixes an error in
the
interaction between the tcl core and my handling of a channel
refCount, found
by Matt Newman <ma...@novadigm.com>.
* To ease backporting to 8.0.6 the new functions moved to the slots 281
and 282 of the stub-table
* Will be part of the upcoming plus-patches.


Alexandre Ferrieux

unread,
Apr 22, 1999, 3:00:00 AM4/22/99
to
a.ku...@westend.com wrote:
>
> [request: use the Trf patch !!!]

Count me on your side !

One additional application to motivate this position: "Sound as I/O".
With a nifty tool like this Trf framework, creating a low-level sound
library based on I/O would be much easier than in the current setup. It
could even be done while preserving the higher level API of (e.g.)
Snack.

-Alex

Jeffrey Hobbs

unread,
Apr 22, 1999, 3:00:00 AM4/22/99
to a.ku...@westend.com
a.ku...@westend.com (courtesy cc'ed) wrote:
> Tcl 8.1b3 Request: Generated by Scriptics' bug entry form at
> Submitted by: Andreas Kupries
>
> DesiredBehavior:
> With minimal changes (*) to the IO subsystem of the tcl kernel it is
> possible to introduce the capability for arbitrary filtering and/or
> manipulation of information flowing through channels. Possible
> applications of this functionality are:
>
> * En/Decryption,
> * Error-Correction,
> * Conversion (base64, uuencode, ...)
> * Compression,
>
> and many more.
....

I'd love to see this core as well, but I have one clarification
about something we discussed on the newsgroup a ways back (and
I'm too lazy to analyze the patch):

1) Does this allow Tcl level access to the above?
1a) Does this allow "hijacking/rerouting" of std channels,
as would be nice to give total control of stdin/stdout
to Tcl apps. As in, I could write a channel filter for
wish's stdout to reroute everything to a text widget,
or jack into stdin to supply the data via Tcl?

There was a solution for this, but I'm not sure whether it is
part of your work above (good job, BTW).

** Jeffrey Hobbs jeff.hobbs @SPAM acm.org **
** I'm really just a Tcl-bot My opinions are MY opinions **

Matt Newman

unread,
Apr 22, 1999, 3:00:00 AM4/22/99
to
>I'd love to see this core as well, but I have one clarification
>about something we discussed on the newsgroup a ways back (and
>I'm too lazy to analyze the patch):
>
>1) Does this allow Tcl level access to the above?

Yes - Trf provides a generic tcl-based transform based upon this facility -
it works blocking and non-blocking and plays well with fileevents.

>1a) Does this allow "hijacking/rerouting" of std channels,
> as would be nice to give total control of stdin/stdout
> to Tcl apps. As in, I could write a channel filter for
> wish's stdout to reroute everything to a text widget,
> or jack into stdin to supply the data via Tcl?

You bet.

IMHO the ability to stack channels is essential for the core. In fact if it
had been done in the first place we would have ended up with a much cleaner
abstraction that the one that is there now.

Matt Newman

Andreas Kupries

unread,
Apr 22, 1999, 3:00:00 AM4/22/99
to

Jeffrey Hobbs <Jeffre...@icn.siemens.de> writes:

> a.ku...@westend.com (courtesy cc'ed) wrote:
> > Tcl 8.1b3 Request: Generated by Scriptics' bug entry form at
> > Submitted by: Andreas Kupries
>> [the Trf patch / stacked channel]

> I'd love to see this core as well, but I have one clarification
> about something we discussed on the newsgroup a ways back (and
> I'm too lazy to analyze the patch):
>
> 1) Does this allow Tcl level access to the above?

Yes, the Trf extension contains a 'transform' command allowing you to
write a transformation in Tcl itself.

> 1a) Does this allow "hijacking/rerouting" of std channels,
> as would be nice to give total control of stdin/stdout
> to Tcl apps. As in, I could write a channel filter for
> wish's stdout to reroute everything to a text widget,
> or jack into stdin to supply the data via Tcl?

Yes to both.

trf1.5/tools/mmencode is a simple emulation of the 'mmencode' command
(see 'metamail') and uses both techniques, 'transform' and attaching
to a standard channel. I have 'mmdecode' too.

Hm, it's length is only 65 lines. Ok, copied into this message, after
my sig.

Other applications: Monitoring the stream of bytes going through a
channel and doing statistics, like distribution of values, pairs,
whatever.

Way back, then I hacked up the first implementation of transformation,
embedding them into the generic buffering system, I implemented a
huffman (de)compressor this way. I guess that I do have the source
lying around somewhere, if its of interest.

<braindump>
I guess, the moment someone writes a 'tee'-like things can get
very complicated. Not thinking about 'join' now. But add some
sort of event-driven data pumps (*) and we have a base for
data flow oriented computations. Especially good for streamed
information. Hm. Sound, Video, 3D graphics(?), animation
control ?
</braindump>

(*) I am not sure that 'fileevent' is/will be sufficient.


> There was a solution for this, but I'm not sure whether it is
> part of your work above (good job, BTW).
>
> ** Jeffrey Hobbs jeff.hobbs @SPAM acm.org **
> ** I'm really just a Tcl-bot My opinions are MY opinions **

I'm not, but I have one running. :-).

--
Sincerely,
Andreas Kupries <a.ku...@westend.com>
<http://www.westend.com/~kupries/>
-------------------------------------------------------------------------------
#!/usr/local/bin/tclsh
# demonstration application, encodes the incoming byte stream to base64
package require Trf


# line buffer for parcel below.
set line ""

proc parcel {max cmd buffer} {
# this transformation takes the incoming byte stream and parcels it into
# lines of at most $max characters each. The last line is possibly shorter.
# only the write part is implemented.
#
# hm, this could be generalized into a transformation parceling the input
# into packets and adding some sort of frame around these.

#puts stderr "$max $cmd [string length $buffer]"

global line
switch -- $cmd {
create/write {
set line ""
}
delete/write {
set line ""
}
write {
append line $buffer
set res ""

while {[string length $line] > $max} {
append res "[string range $line 0 [expr {$max-1}]]\n"
set line [string range $line $max end]
}

return $res
}
flush/write {
if {[string length $line] > 0} {
set res "$line\n"
set line ""
return $res
} else {
return {}
}
}
clear/write {
set line ""
}
}
}

# setup base64 encoder and restricting lines to 72 characters each.

transform -attach stdout -command {parcel 72}
base64 -attach stdout -mode encode

fconfigure stdin -translation binary
fcopy stdin stdout
close stdout

# close is *required* to flush out the internal buffers.
# This is not done during a simple exit :-(

exit

Andreas Kupries

unread,
Apr 22, 1999, 3:00:00 AM4/22/99
to

a.ku...@westend.com writes:

> Tcl 8.1b3 Request: Generated by Scriptics' bug entry form at
> http://www.scriptics.com/support/bugForm.html
> Responses to this post are encouraged.
> ------

Oops. Just saw that I forgot the subject, my apologies.

It should have been
'Introduction of stacked channels / filtering channels'

Reply all
Reply to author
Forward
0 new messages