You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
234 lines
6.3 KiB
234 lines
6.3 KiB
# -*- tcl -*- |
|
# # ## ### ##### ######## ############# |
|
# (C) 2011 Andreas Kupries |
|
|
|
# Facade wrapping around some other channel. All operations on the |
|
# facade are delegated to the wrapped channel. This makes it useful |
|
# for debugging of Tcl's activity on a channel. While a transform can |
|
# be used for that as well it does not have access to some things of |
|
# the base-channel, i.e. all the event managment is not visible to it, |
|
# whereas the facade has access to even this. |
|
|
|
# @@ Meta Begin |
|
# Package tcl::chan::facade 1.0.2 |
|
# Meta as::author {Colin McCormack} |
|
# Meta as::author {Andreas Kupries} |
|
# Meta as::copyright 2011 |
|
# Meta as::license BSD |
|
# Meta description Facade wrapping around some other channel. All |
|
# Meta description operations on the facade are delegated to the |
|
# Meta description wrapped channel. This makes it useful for debugging |
|
# Meta description of Tcl's activity on a channel. While a transform |
|
# Meta description can be used for that as well it does not have |
|
# Meta description access to some things of the base-channel, i.e. all |
|
# Meta description the event managment is not visible to it, whereas |
|
# Meta description the facade has access to even this. |
|
# Meta platform tcl |
|
# Meta require TclOO |
|
# Meta require tcl::chan::core |
|
# Meta require {Tcl 8.5} |
|
# @@ Meta End |
|
|
|
# # ## ### ##### ######## ############# |
|
## TODO document the special options of the facade |
|
## TODO log integration. |
|
## TODO document that facada takes ownership of the channel. |
|
|
|
package require Tcl 8.5 9 |
|
package require TclOO |
|
package require logger |
|
package require tcl::chan::core |
|
|
|
# # ## ### ##### ######## ############# |
|
|
|
namespace eval ::tcl::chan {} |
|
|
|
logger::initNamespace ::tcl::chan::facade |
|
proc ::tcl::chan::facade {args} { |
|
return [::chan create {read} [facade::implementation new {*}$args]] |
|
} |
|
|
|
# # ## ### ##### ######## ############# |
|
|
|
oo::class create ::tcl::chan::facade::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 the wrapped channel instead and let it handle things. |
|
|
|
constructor {thechan} { |
|
# Access to the log(ger) commands. |
|
namespace path [list {*}[namespace path] ::tcl::chan::facade] |
|
|
|
set chan $thechan |
|
|
|
# set some configuration data |
|
set created [clock milliseconds] |
|
set used 0 |
|
set user "" ;# user data - freeform |
|
|
|
# validate args |
|
if {$chan eq [self]} { |
|
return -code error "recursive chan! No good." |
|
} elseif {$chan eq ""} { |
|
return -code error "Needs a chan argument" |
|
} |
|
|
|
set blocking [::chan configure $chan -blocking] |
|
return |
|
} |
|
|
|
destructor { |
|
log::debug {[self] destroyed} |
|
if {[catch { ::chan close $chan } e o]} { |
|
log::debug {failed to close $chan [self] because "$e" ($o)} |
|
} |
|
return |
|
} |
|
|
|
variable chan used user created blocking |
|
|
|
method initialize {myself mode} { |
|
log::debug {$myself initialize $chan $mode} |
|
log::debug {$chan configured: ([::chan configure $chan])} |
|
return [next $chan $mode] |
|
} |
|
|
|
method finalize {myself} { |
|
log::debug {$myself finalize $chan} |
|
catch {::chan close $chan} |
|
catch {next $myself} |
|
catch {my destroy} |
|
return |
|
} |
|
|
|
method blocking {myself mode} { |
|
if {[catch { |
|
::chan configure $chan -blocking $mode |
|
set blocking $mode |
|
} e o]} { |
|
log::debug {$myself blocking $chan $mode -> error $e ($o)} |
|
} else { |
|
log::debug {$myself blocking $chan $mode -> $e} |
|
} |
|
return |
|
} |
|
|
|
method watch {myself requestmask} { |
|
log::debug {$myself watch $chan $requestmask} |
|
|
|
if {"read" in $requestmask} { |
|
fileevent readable $chan [my Callback Readable $myself] |
|
} else { |
|
fileevent readable $chan {} |
|
} |
|
|
|
if {"write" in $requestmask} { |
|
fileevent writable $chan [my Callback Writable $myself] |
|
} else { |
|
fileevent writable $chan {} |
|
} |
|
return |
|
} |
|
|
|
method read {myself n} { |
|
log::debug {$myself read $chan begin eof: [::chan eof $chan], blocked: [::chan blocked $chan]} |
|
set used [clock milliseconds] |
|
|
|
if {[catch { |
|
set data [::chan read $chan $n] |
|
} e o]} { |
|
log::error {$myself read $chan $n -> error $e ($o)} |
|
} else { |
|
log::debug {$myself read $chan $n -> [string length $data] bytes: [string map {\n \\n} "'[string range $data 0 20]...[string range $data end-20 end]"]'} |
|
log::debug {$myself read $chan eof = [::chan eof $chan]} |
|
log::debug {$myself read $chan blocked = [::chan blocked $chan]} |
|
log::debug {$chan configured: ([::chan configure $chan])} |
|
|
|
set gone [catch {chan eof $chan} eof] |
|
if { |
|
($data eq {}) && |
|
!$gone && !$eof && !$blocking |
|
} { |
|
log::error {$myself EAGAIN} |
|
return -code error EAGAIN |
|
} |
|
} |
|
|
|
log::debug {$myself read $chan result: [string length $data] bytes} |
|
return $data |
|
} |
|
|
|
method write {myself data} { |
|
log::debug {$myself write $chan [string length $data] / [::chan pending output $chan] / [::chan pending output $myself]} |
|
set used [clock milliseconds] |
|
::chan puts -nonewline $chan $data |
|
return [string length $data] |
|
} |
|
|
|
method configure {myself option value} { |
|
log::debug {[self] configure $myself $option -> $value} |
|
|
|
if {$option eq "-user"} { |
|
set user $value |
|
return |
|
} |
|
|
|
::chan configure $fd $option $value |
|
return |
|
} |
|
|
|
method cget {myself option} { |
|
switch -- $option { |
|
-self { return [self] } |
|
-fd { return $chan } |
|
-used { return $used } |
|
-created { return $created } |
|
-user { return $user } |
|
default { |
|
return [::chan configure $chan $option] |
|
} |
|
} |
|
} |
|
|
|
method cgetall {myself} { |
|
set result [::chan configure $chan] |
|
lappend result \ |
|
-self [self] \ |
|
-fd $chan \ |
|
-used $used \ |
|
-created $created \ |
|
-user $user |
|
|
|
log::debug {[self] cgetall $myself -> $result} |
|
return $result |
|
} |
|
|
|
# # ## ### ##### ######## ############# |
|
|
|
# Internals. Methods. Event generation. |
|
method Readable {myself} { |
|
log::debug {$myself readable $chan - [::chan pending input $chan]} |
|
::chan postevent $myself read |
|
return |
|
} |
|
|
|
method Writable {myself} { |
|
log::debug {$myself writable $chan - [::chan pending output $chan]} |
|
::chan postevent $myself write |
|
return |
|
} |
|
|
|
method Callback {method args} { |
|
list [uplevel 1 {namespace which my}] $method {*}$args |
|
} |
|
|
|
# # ## ### ##### ######## ############# |
|
} |
|
|
|
# # ## ### ##### ######## ############# |
|
package provide tcl::chan::facade 1.0.2 |
|
return
|
|
|