summaryrefslogtreecommitdiffstats
path: root/tcllib/modules/virtchannel_base/cat.tcl
diff options
context:
space:
mode:
Diffstat (limited to 'tcllib/modules/virtchannel_base/cat.tcl')
-rw-r--r--tcllib/modules/virtchannel_base/cat.tcl139
1 files changed, 139 insertions, 0 deletions
diff --git a/tcllib/modules/virtchannel_base/cat.tcl b/tcllib/modules/virtchannel_base/cat.tcl
new file mode 100644
index 0000000..b72e218
--- /dev/null
+++ b/tcllib/modules/virtchannel_base/cat.tcl
@@ -0,0 +1,139 @@
+# -*- tcl -*-
+# # ## ### ##### ######## #############
+# (C) 2011 Andreas Kupries
+
+# Facade concatenating the contents of the channels it was constructed
+# with. Owns the sub-ordinate channels and closes them on exhaustion and/or
+# when closed itself.
+
+# @@ Meta Begin
+# Package tcl::chan::cat 1.0.1
+# Meta as::author {Andreas Kupries}
+# Meta as::copyright 2011
+# Meta as::license BSD
+# Meta description Facade concatenating the contents of the channels it
+# Meta description was constructed with. Owns the sub-ordinate channels
+# Meta description and closes them on exhaustion and/or when closed itself.
+# Meta platform tcl
+# Meta require TclOO
+# Meta require tcl::chan::core
+# Meta require {Tcl 8.5}
+# @@ Meta End
+
+# # ## ### ##### ######## #############
+
+package require Tcl 8.5
+package require TclOO
+package require tcl::chan::core
+
+# # ## ### ##### ######## #############
+
+namespace eval ::tcl::chan {}
+
+proc ::tcl::chan::cat {args} {
+ return [::chan create {read} [cat::implementation new {*}$args]]
+}
+
+oo::class create ::tcl::chan::cat::implementation {
+ superclass ::tcl::chan::core ; # -> initialize, finalize.
+
+ # We are not using the standard event handling class, because here
+ # it will not be timer-driven. We propagate anything related to
+ # events to catin and catout instead and let them handle things.
+
+ constructor {args} {
+ set channels $args
+ # Disable encoding and translation processing in the wrapped channels.
+ # This will happen in our generic layer instead.
+ foreach c $channels {
+ fconfigure $c -translation binary
+ fconfigure $c -translation binary
+ }
+ set delay 10
+ set watching 0
+ return
+ }
+
+ destructor {
+ foreach c $channels {
+ ::close $c
+ }
+ return
+ }
+
+ variable channels timer delay watching
+
+ method watch {c requestmask} {
+ if {"read" in $requestmask} {
+ # Activate event handling. Either drive an eof home via
+ # timers, or activate things in the foremost sub-ordinate.
+
+ set watching 1
+ if {![llength $channels]} {
+ set timer [after $delay \
+ [namespace code [list my Post $c]]]
+ } else {
+ set c [lindex $channels 0]
+ fileevent readable $c [list chan postevent $c read]
+ }
+ } else {
+ # Stop events. Kill timer, or disable in the foremost
+ # sub-ordinate.
+
+ set watching 0
+ if {![llength $channels]} {
+ catch { after cancel $timer }
+ } else {
+ fileevent readable [lindex $channels 0] {}
+ }
+ }
+ return
+ }
+
+ method read {c n} {
+ if {![llength $channels]} {
+ # Actually should be EOF signal.
+ return {}
+ }
+
+ set buf {}
+ while {([string length $buf] < $n) &&
+ [llength $channels]} {
+
+ set in [lindex $channels 0]
+ set toread [expr {$n - [string length $buf]}]
+ append buf [::read $in $toread]
+
+ if {[eof $in]} {
+ close $in
+ set channels [lrange $channels 1 end]
+
+ # The close above also killed any fileevent handling
+ # we might have attached to this channel. We may have
+ # to update the settings (i.e. move to next channel,
+ # or to timer-based, to drive the eof home).
+
+ if {$watching} {
+ my watch $c read
+ }
+ }
+ }
+
+ if {$buf eq {}} {
+ return -code error EAGAIN
+ }
+
+ return $buf
+ }
+
+ method Post {c} {
+ set timer [after $delay \
+ [namespace code [list my Post $c]]]
+ chan postevent $c read
+ return
+ }
+}
+
+# # ## ### ##### ######## #############
+package provide tcl::chan::cat 1.0.2
+return