1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
|
# -*- 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
|